Kevin
Kevin

Reputation: 25269

leiningen with multiple main classes

I'd like to have two main classes (or more) with leiningen, and then be able to choose which one at the java command line. For example I have:

(ns abc (:gen-class))
(defn -main [] (println "abc"))

(ns def (:gen-class))
(defn -main [] (println "def"))

With a project.clj having:

(defproject my-jar "0.0.1"
 :description "test"
 :dependencies [
 ]
 :main abc)

Then I build with lein uberjar, and run:

java -cp my-jar-0.0.1-standalone.jar abc
java -cp my-jar-0.0.1-standalone.jar def

I get it that when I specified :main abc in the project.clj it was calling that out as the main-class in the manifest, but I couldn't get it to run without putting something. But either way when I try to run the 'def' main, I get a class not found:

Exception in thread "main" java.lang.NoClassDefFoundError: def

Upvotes: 18

Views: 4205

Answers (3)

Bob Kuhar
Bob Kuhar

Reputation: 11140

What worked for me in both lein 2.7.0's run task as well as from the resulting uberjar is as follows...

project.clj:

(defproject many-mains "0.1.0-SNAPSHOT"
  :description "Project containing multiple main methods"
  :dependencies [[org.clojure/clojure "1.8.0"]]
  :main nil
  :target-path "target/%s"
  :profiles {:main-abc {:main many-mains.abc}
             :main-def {:main many-mains.def}
             :main-ghi {:main org.rekdev.mm.ghi}
             :core {:main many-mains.core}
             :uberjar {:aot :all}})

For source like...

$ cat src/many_mains/abc.clj
(ns many-mains.abc
  (:gen-class))

(defn -main
  ""
  [& args]
  (println "Hello, from many-mains.abc!"))

This lets lein run work like...

$ lein with-profile main-abc run
Hello, from many-mains.abc!

From the command line the '-' in many-mains needs to become a '_' which makes it a legal Java classname.

$ java -cp target/uberjar/many-mains-0.1.0-SNAPSHOT-standalone.jar many_mains.abc
Hello, from many-mains.abc!

There seems to have been some behavior changes between Lein 2.7.0 and prior around the effect of :main nil on the MANIFEST.MF. What I've got here works like a champ in Lein 2.7.0. The full source is at https://github.com/robertkuhar/many-mains

Upvotes: 2

Michael
Michael

Reputation: 181

This works at least with leiningen 2.0+

(defproject my-jar "0.0.1"
 :description "test"
 :dependencies [
 ]
 :profiles {:main-a {:main abc}
           {:main-b {:main def}}
 :aliases {"main-a" ["with-profile" "main-a" "run"]
           "main-b" ["with-profile" "main-b" "run"]})

Then you can run each main like so:

lein main-a
lein main-b

Which expands to this:

lein with-profile main-a run
lein with-profile main-b run

I'm using this in one of my projects and it works perfectly.

Upvotes: 18

Kevin
Kevin

Reputation: 25269

I added :aot [abc def] to the project.clj to generate compiled code and it worked.

Upvotes: 10

Related Questions