Hmm, wonder why I sometimes get
java.lang.IllegalStateException: Can't change/establish root binding of: *warn-on-reflection* with set
at clojure.lang.Var.set(Var.java:228)
at buddy.core.bytes__init.load(Unknown Source)
at buddy.core.bytes__init.<clinit>(Unknown Source)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1161)
(internal stack frames of the image generator are omitted)
with native-image, but not always, and the trace that causes this seems to vary (the above is from buddy.core.bytes__init.load, but on another run, there might be a different source). It seems as if things get run in different order during different runs.
I'm currently creating an uberjar, then running it with native-image-agent and then running native-image with the configuration generated by the agentI'm using com.github.clj-easy/graal-build-time, and buddy.core shows up in
[clj-easy/graal-build-time] Registering packages for build time initialization: clojure, aero, backend, borkdude, buddy.core, buddy.sign, buddy.util, camel_snake_kebab, cheshire, clj_easy.graal_build_time, cognitect, common, edamame, hiccup, hiccup2, hikari_cp, integrant, jsonista, malli, meta_merge, migratus, muuntaja, next, org.tobereplaced, potemkin, reitit, ring, weavejester
but seeing an error which is probably from (set! *warn-on-reflection* true) makes me think that there are a lot of namespaces that possibly do this...0% native-image --version
native-image 23.0.1 2024-10-15
GraalVM Runtime Environment Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01)
Substrate VM Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11, serial gc, compressed references)hmm, there might be something that I don't quite understand with bindings, there are other errors from say *unchecked-math* too
hmm, maybe the agent collected too much stuff, removed all clojure related things from reachability-metadata.json, and seems I get this compiled succesfully a few times now
I'm using Hikari, but got it working without anything specific with the metadata that the agent produced, just that sometimes the native image compilation fails 😛
apparently had to allow HikariConfig.isAllowPoolSuspension in reflection config, seems that this is present in the metadata repository, wonder if anyone has tried out that metadata repo stuff: https://github.com/oracle/graalvm-reachability-metadata/blob/master/metadata/com.zaxxer/HikariCP/5.0.1/reflect-config.json#L263
or rather, there are gradle and maven plugins, might be neat to have glue for Clojure
I've compiled projects that (set! *warn-on-reflection* true) that seem to work fine. It's kind of a hard to diagnose a complicated build. I would try starting with an empty project and slowly adding pieces.
How are you AOT compiling?
(b/compile-clj {:basis @basis
:class-dir class-dir
:compile-opts {:direct-linking true}})
did try without direct-linking toohere's what mine looks like:
(defn compile [_]
(b/compile-clj {:basis (b/create-basis basis*)
:ns-compile '[com.phronemophobic.llama-cli]
:class-dir class-dir
:jvm-opts ["-Dtech.v3.datatype.graal-native=true"
"-Dclojure.compiler.direct-linking=true"
"-Dclojure.spec.skip-macros=true"]}))I think I got this to work by trimming the reachability-metadata.json created by native-image-agent, I removed every clojure stuff, by looking first for clj/cljc resources, getting names from those and then stripping those names in other parts of the metadata file, heurestic being that [clj-easy/graal-build-time] would be enough
seems that now I haven't run into Can't change/establish root binding of errors
those happened for other dynamic vars too, not just `warn-on-reflection`
presumably, the direct linking option is similar to setting the system property. One other difference is that you may want to include an :ns-compile that includes your main namespace.
Another recommendation is to make sure and compile using the graalvm java version.
I've previously noticed weird behavior when making changes and not cleaning the build directory. Cleaning the build directory before AOT compiling may also help.
Right, aot was with same graalvm jdk as native-image image I think what happened was that the native-image-agent recorded Clojure compiler calls for finding clj files and classes, and these ended up in reflection config and somehow lead to re-initialization after being included at build time Not sure if that makes sense :P
I'm not sure it's the same thing, but I have noticed when using the agent that it includes clojure stuff. I have a fix-config step for some projects that rely on the agent.
(defn fix-reflect-config [f]
(let [config (with-open [rdr (io/reader f)]
(json/read rdr))
new-config (->> config
(remove (fn [{:strs [name]}]
(str/ends-with? name "__init"))))]
(with-open [w (io/writer f)]
(json/write new-config w))))
(defn fix-config [_]
(fix-reflect-config (io/file "native-image" "config" "reflect-config.json")))
I then call clj -T:fix-config after running the agent.I usually keep my config checked into the repo. The agent can be helpful, but can also be "too helpful" for some clojure projects.
Yeah, I think I read somewhere that the config that the agent produces should be taken as a starting point The thing I was doing is for a project template so I don't know the libraries used beforehand, so tried out the agent approach and did also a post process step