leiningen with multiple main classes
Asked Answered
P

3

18

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
Platitudinous answered 13/6, 2012 at 21:44 Comment(2)
why not use clojure cli github.com/clojure/tools.cli and pass a cmd line argument to call each of the different options?Hearing
Ultimately it was a temporary situation to have multiple mains (rapid dev/testing). But I wasn't aware of tools.cli so thanks for the link. I'm sure I'll use it in the future.Platitudinous
P
10

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

Platitudinous answered 13/6, 2012 at 22:16 Comment(1)
Where did you add it?Hightoned
C
18

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.

Conchoid answered 24/9, 2013 at 17:31 Comment(1)
Thanks for the great answer. A small suggestion: IMO, it is clearer to have "run" in the alias name; e.g. lein run-main-a. (I say this because lein run is provided by default.)Vestryman
P
10

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

Platitudinous answered 13/6, 2012 at 22:16 Comment(1)
Where did you add it?Hightoned
S
2

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

Sprayberry answered 29/8, 2016 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.