leiningen with multiple main classes

2019-03-11 07:05发布

问题:

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

回答1:

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.



回答2:

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



回答3:

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