Compile Clojure to native binary using GraalVM
We’ll write echo
command on Clojure and compile it to native binary by GraalVM.
🥇 Setup
The first thing first. Create a project directory echo
mkdir /tmp/echo
add basic deps.edn
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.11.0"}}}
Create src/echo/main.clj
. This would be our entry point:
(ns echo.main)(defn -main [& args]
(apply println args))
Run clj -M -m echo.main
to be sure that all works fine
clj -M -m echo.main foo bar
foo bar
🥈 Build uberjar
Add build.clj
to project’s root directory. This file contains logic how to build a Java jar
(ns build
(:require [clojure.tools.build.api :as b]))(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def jar-file "target/echo.jar")(defn clean [_]
(b/delete {:path "target"}))(defn uberjar [_]
(clean nil)
(b/copy-dir {:src-dirs ["src"]
:target-dir class-dir})
(b/compile-clj {:basis basis
:src-dirs ["src"]
:class-dir class-dir})
(b/uber {:class-dir class-dir
:uber-file jar-file
:basis basis
:main 'echo.main}))
Now let’s add tools.build and :build
task to deps.edn
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.11.0"}}
:aliases {:build
{:deps
{io.github.clojure/tools.build {:git/tag "v0.8.1" :git/sha "7d40500"}}
:ns-default build}}}
And the last thing is to mark our echo.main
as :gen-class
(ns echo.main
(:gen-class))(defn -main [& args]
(apply println args))
Now we are ready to build uberjar
clj -T:build uberjar
This command will create a jar in the target folder. You can run it by java
java -jar target/echo.jar foo bar
foo bar
🥉 Build native
To build native script, we should install GraalVM after that we should add com.github.clj-easy/graal-build-time as a dependency to automate declaration of libraries by — initialize-at-build-time
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.11.0"}
com.github.clj-easy/graal-build-time {:mvn/version "0.1.4"}}
:aliases
{:build
{:deps
{io.github.clojure/tools.build {:git/tag "v0.8.1" :git/sha "7d40500"}}
:ns-default build}}}
Rebuild jar
clj -T:build uberjar
and we are ready to run native-image
to build binary
native-image -jar target/echo.jar --no-fallback --no-server target/echo
This command will generate a native binary from the jar to target/echo
Let’s test our binary
./target/echo foo bar
The end 🎉