Automatically detect and initialize Clojure classes during
⬆️ Upgrading to from v0 to v1?
--features=clj_easy.graal_build_time.InitClojureClasses to your
native-image command line.
We compile our Clojure sources to
.class files so that GraalVM
native-image can in turn compile them into a binary executable.
Due to their nature, Clojure
.class files typically must be initialized at build time by GraalVM
If they are not initialized at build time, GraalVM will still create your binary executable, but when you run it, it probably won't work.
You will likely see it fail with an error that includes:
java.io.FileNotFoundException: Could not locate clojure/core__init.class, clojure/core.clj or clojure/core.cljc on classpath
In the early days, our solution was to use
--initialize-at-build-time to globally initialize all classes at build time.
Because global initialization of all classes can be problematic, GraalVM deprecated this usage.
Instead, you must be explicit and specify the packages of the classes you need to initialize at build time.
But, this can be tedious and error-prone.
This library automatically detects
.class files created by Clojure and asks
native-image to initialize them at build time.
We assume you are using a current stable release of GraalVM.
We don't support older releases.
- If you are using
--initialize-at-build-time, remove it.
- Include the
graal-build-time library on your classpath.
This is typically done by adding this library to your project dependencies (see the clojars link above).
native-image build process, you will see a line of output from
graal-build-time describing the packages it has detected:
[clj-easy/graal-build-time] Registering packages for build time initialization: clojure, clj_easy.graal_build_time
graal-build-time hooks into the GraalVM
native-image build process via a GraalVM
It inspects the classpath.
Each class file that ends with
__init.class is assumed to have been created by Clojure.
The packages of these classes are then added to the list of packages to be initialized at build time.
If there are classes in packages that you would like to initialize at runtime, you can
For example, when using http-kit, the
org.httpkit package will be included for
build-time initialization. You will need to override one class via
This library doesn't work with single segment namespaces.
A single segment namespace is one without any
. characters in it, for example:
A single segment namespace, becomes, in the eyes of the JVM, package-less and ends up in the JVM default package.
Single segment namespaces are problematic in general in Clojure and, because they are package-less, will not be initialized by
graal-build-time will emit a warning when it detects
.class files generated from a single segment namespace, for example:
[clj-easy/graal-build-time] WARN: Single segment namespace found for class: digest__init.class. Because this class has no package, it cannot be registered for initialization at build time.
bb tasks for all relevant project tasks.
Tasks attempt to avoid unnecessary work by comparing source and target file dates.
If you want to skip this optimization, run
bb clean before running your task.
bb native-image-test to run our integration tests.
This task builds native images for a hello world app, and then runs them.
The hello world Clojure sources are compiled to Java classes.
We use GraalVM's
graal-build-time on the classpath to create 2 variants of the same app:
- one built from the uberjar
- the other built directly from the classes dir
Note that we omit
--initialize-at-build-time when creating the native images.
The work that this deprecated option carried out is now taken care of by
During native image creation, you'll see output that looks like:
[clj-easy/graal-build-time] WARN: Single segment namespace found for class: single_segment_example__init.class. Because this class has no package, it cannot be registered for initialization at build time.
[clj-easy/graal-build-time] Registering packages for build time initialization: clojure, clj_easy.graal_build_time, gbt_test_org, hello, hello_world
[clj-easy/graal-build-time] Registering packages for build time initialization... will always appear
[clj-easy/graal-build-time] WARN: Single segment namespace found... will appear to warn you of the repercussions of a compiled Clojure single segment namespace found in your build.
Licensed under the MIT license, see LICENSE.
Copyright © 2021-2023 Michiel Borkent, Eric Dallo, Rahul Dé, Lee Read and contributors.