Let's say I create a new Leiningen project (lein new app example
) and add some code in example/src/example/core.clj
that makes use of :gen-class
:
(ns example.core
(:gen-class :extends javafx.application.Application))
(defn -start [this stage]
(.show stage))
(defn -main [& args]
(javafx.application.Application/launch example.core args))
If I then create a JAR (lein uberjar
) and run it, everything works fine. However, if I instead try to run my app directly (lein run
), I get a ClassNotFoundException
. In addition, if I open a REPL (lein repl
), I first get the same error as before, but after running this code:
(compile 'example.core)
the error no longer appears in lein run
or lein repl
.
Could someone please explain to me what exactly is going on here, and how I can run my app directly without needing to manually compile my code from a REPL?
Edit: After fooling around a bit more, I found that the solution to this problem is to add
:aot [example.core]
to project.clj
. (Thanks @Mars!) I'm still confused, though, because I had previously tried simply removing ^:skip-aot
, which (according to the docs) should work:
This will be AOT compiled by default; to disable this, attach
^:skip-aot
metadata to the namespace symbol.
But it doesn't. Why?
Another edit (if I should split any of this into a separate question, let me know and I'll do so): I've been playing with hyphens (lein new app my-example
), and weird stuff has been happening. This doesn't work:
(ns my-example.core
(:gen-class :extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.core args))
But this does:
(ns my-example.core
(:gen-class
:name my-example.Core
:extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.Core args))
So my class name can either start with a lowercase letter (example.core
) or contain a hyphen (my-example.Core
), but not both? I really don't get it.
And finally, lein uberjar
fails on that final example (with the explicit :name
), because
Warning: The Main-Class specified does not exist within the jar. It may not be executable as expected. A gen-class directive may be missing in the namespace which contains the main method.
As far as I can tell, the only way to fix that is to split the Application
subclass into a separate namespace. Is there another way?
Agreed with @Mars, the problem is that lein run
does not AOT the example.core
namespace. The default Leiningen template made the example.core
non AOT:
(defproject example "0.1.0-SNAPSHOT"
...
:main ^:skip-aot example.core
...)
My best guess is that you could define your app using defrecord
and use that as a class instead of the namespace.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With