Clojure Compilation
I’ve heard that Clojure can be compiled into a JAR file, but never actually tried it before. So can we do this?
First, we need a project to compile. We’ll go with the classic “Hello, World!” app.
> lein new hello-world
> cd hello-world
Now, if your Clojure code doesn’t have a -main
method and is just a library,
then you can just run lein uberjar
, and you should see some output like this:
> lein uberjar
Created /Users/Projects/hello-world/target/hello-world-0.1.0-SNAPSHOT.jar
Created /Users/Projects/hello-world/target/hello-world-0.1.0-SNAPSHOT-standalone.jar
Once you have that, you can use the JAR wherever you need it!
Compiling with -main
If you have a -main
method somewhere, then there are a couple extra steps
that need to be taken. First, you’ll need to add (:gen-class)
to the top
of your main Clojure file:
(ns hello-world.core
(:gen-class))
(defn -main []
(println "Hello, World!"))
Without this (:gen-class)
tag, uberjar won’t recognize your -main
function and your project won’t compile properly.
Next, you’ll need to update your project.clj with a couple configurations:
:main
and :aot
, both with the namespace that your -main
methods exist.
(defproject
; ...
:main hello-world.core
:aot [hello-world.core])
:main
tells uberjar what namespace to start the project in. The project will
still compile without this setting, but instead of your application you’ll
get a nice little REPL prompt.
:aot
isn’t required at this time; your JAR will still run without it.
However, if you exclude it then you’ll get a lengthy warning message
like this:
> lein uberjar
Warning: specified :main without including it in :aot.
Implicit AOT of :main will be removed in Leiningen 3.0.0.
If you only need AOT for your uberjar, consider adding :aot :all into your
:uberjar profile instead.
Once you add those settings to project.clj, you’re ready to compile and run!
> lein uberjar
Compiling hello-world.core
Created /Users/Projects/hello-world/target/hello-world-0.1.0-SNAPSHOT.jar
Created /Users/Projects/hello-world/target/hello-world-0.1.0-SNAPSHOT-standalone.jar
> java -jar /Users/Projects/hello-world/target/hello-world-0.1.0-SNAPSHOT-standalone.jar
Hello, World!
Again, you’ll want to run your application from the “standalone” jar. Otherwise you’ll get an error like this:
> java -jar /Users/Projects/hello-world/target/hello-world-0.1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: clojure/lang/Var
at hello_world.core.<clinit>(Unknown Source)
Caused by: java.lang.ClassNotFoundException: clojure.lang.Var
at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 1 more