graalvm

viesti 2024-11-03T07:56:57.690739Z

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 agent

viesti 2024-11-03T07:59:42.604099Z

I'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...

viesti 2024-11-03T08:00:13.715479Z

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)

viesti 2024-11-03T08:24:18.948289Z

hmm, there might be something that I don't quite understand with bindings, there are other errors from say *unchecked-math* too

viesti 2024-11-03T12:28:19.576529Z

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

viesti 2024-11-03T12:29:36.116389Z

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 😛

viesti 2024-11-03T12:30:43.074069Z

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

viesti 2024-11-03T12:31:13.735419Z

or rather, there are gradle and maven plugins, might be neat to have glue for Clojure

phronmophobic 2024-11-03T17:28:19.334279Z

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?

viesti 2024-11-03T17:35:36.551839Z

(b/compile-clj {:basis @basis
                  :class-dir class-dir
                  :compile-opts {:direct-linking true}})
did try without direct-linking too

phronmophobic 2024-11-03T17:38:03.896829Z

here'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"]}))

viesti 2024-11-03T17:38:05.556959Z

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

viesti 2024-11-03T17:38:23.245059Z

those happened for other dynamic vars too, not just `warn-on-reflection`

phronmophobic 2024-11-03T17:39:27.210489Z

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.

phronmophobic 2024-11-03T17:41:09.535579Z

Another recommendation is to make sure and compile using the graalvm java version.

phronmophobic 2024-11-03T17:42:20.227739Z

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.

viesti 2024-11-03T18:01:34.779749Z

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

👍 1
phronmophobic 2024-11-03T18:05:09.642309Z

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.

phronmophobic 2024-11-03T18:06:34.408899Z

I usually keep my config checked into the repo. The agent can be helpful, but can also be "too helpful" for some clojure projects.

viesti 2024-11-03T18:21:55.096739Z

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