This tutorial covers creating a native binary from a Clojure hello world program using GraalVM.
Download and install the most current GraalVM Community Edition for your operating system. There are Java 17 and Java 20 versions. It doesn’t matter which one you pick for this tutorial.
❗
|
Ensure that your $ native-image --version |
💡
|
You can optionally use a tool like SDKMAN! to download and install GraalVM. |
💡
|
You may be interested in watching @borkdude’s GraalVM native Clojure: hello world video. It is now a bit dated, but walks you through this process and includes interesting observations and tips. |
We assume you have the current clojure
CLI installed.
$ mkdir -p hello-world-clj/src/hello_world
$ cd hello-world-clj
Create a deps.edn
.
Specify current versions of:
-
Clojure to take advantage of any and all GraalVM fixes.
-
graal-build-time to automatically initialize Clojure classes
{:deps {org.clojure/clojure {:mvn/version "1.11.1"}
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}}}
Paste the following code into a src/hello_world/main.clj
file:
(ns hello-world.main
(:gen-class))
(defn -main [& _args]
(println "Hello world!"))
Run your hello world program using clojure
to prove to yourself that it works:
$ clojure -M -m hello-world.main
Hello world!
Create a classes
folder. This is where Clojure compiles source files to .class
files:
$ mkdir classes
Then run:
$ clojure -M -e "(compile 'hello-world.main)"
to compile the main namespace (and transitively everything it requires).
Verify that the program works when run from the JVM using compiled classes:
$ java -cp $(clojure -Spath):classes hello_world.main
Hello world!
Run the following to create a native image:
$ native-image \
-cp "$(clojure -Spath):classes" \
-H:Name=hello-world \
-H:+ReportExceptionStackTraces \
--features=clj_easy.graal_build_time.InitClojureClasses \
--verbose \
--no-fallback \
hello_world.main
ℹ️
|
we reference our built classes via -cp
|
This creates hello-world
, a native image version of hello world program.
Another approach is to build an uberjar with leiningen
first.
$ mkdir -p hello-world-lein/src/hello_world
$ cd hello-world-lein
Create a project.clj
.
Specify current versions of:
-
Clojure to take advantage of any and all GraalVM fixes.
-
graal-build-time to automatically initialize Clojure classes
(defproject hello-world "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.11.1"]
[com.github.clj-easy/graal-build-time "1.0.5"]]
:main hello-world.main
:aot :all)
Paste the following code into a src/hello_world/main.clj
file:
(ns hello-world.main
(:gen-class))
(defn -main [& _args]
(println "Hello world!"))
Run your hello world program using lein
to prove to yourself that it works:
$ lein run
Hello world!
$ lein uberjar
Verify that the uberjar works when run from the JVM:
$ java -jar target/hello-world-0.1.0-SNAPSHOT-standalone.jar
Hello world!
Run the following to create a native image:
native-image \
-jar target/hello-world-0.1.0-SNAPSHOT-standalone.jar \
-H:Name=hello-world \
-H:+ReportExceptionStackTraces \
--features=clj_easy.graal_build_time.InitClojureClasses \
--verbose \
--no-fallback
ℹ️
|
we reference our built -jar
|
This creates hello-world
, a native image for your program.
Our hello world examples are designed to get you started. Here are some real world compile script examples from the wild:
And be sure to read our tips and tricks and share back your discoveries!