Nix has a source-bootstrapped OpenJDK

ingve1 pts0 comments

Nix finally has a source-bootstrapped OpenJDK | Farid Zakaria’s Blog

Skip to content

One of the earliest requested issues we had opened on GuixPkgs was to add more packages, specifically OpenJDK.

“Specifically I would like to see openjdk translated. openjdk is not bootstrapped from source code in nixpkgs” [issue#3]

Ever since I learned about the stage0 bootstrap chain and how Guix announced full-source bootstrap for all packages in 2023, I was in awe. They provided a package graph of more than 22,000 nodes rooted in a 357-byte program, including the JDK.11Nixpkgs has made a lot of progress on this front as well, however it is still not fully bootstrapped and relies on plenty of prebuilt binaries.

GuixPkgs can now build OpenJDK 25 🎉

$ nix build .#openjdk \<br>--option filter-syscalls false \<br>--option sandbox-paths '' \<br>--print-out-paths<br>/nix/store/kihk714ljmvhqh36cigc7mfimv0q0rbr-openjdk-25-guix-wrapped

$ /nix/store/kihk71…-openjdk-25-guix-wrapped/bin/java --version<br>openjdk 25 2025-09-16<br>OpenJDK Runtime Environment (build 25+-adhoc.nixbld.source)<br>OpenJDK 64-Bit Server VM (build 25+-adhoc.nixbld.source, mixed mode, sharing)

Does it really work? Let’s take it for a spin.

javac lives in the jdk output, since Guix splits the package.

$ nix build .#openjdk.unwrapped^jdk<br>$ cat Hello.java<br>void main() { IO.println("Hello from a source-bootstrapped JDK"); }

$ ./result-jdk/bin/java Hello.java<br>Hello from a source-bootstrapped JDK

§Why a JDK is a hard package

javac is written in Java. HotSpot is C++, but the class library it needs is Java, and the compiler that compiles the class library is Java, and it runs on a JVM that needs a class library… and so on. This is a bootstrapping problem.

Every distribution resolves this the same way in practice: download a JDK and use it to build your JDK. Debian documents the pain, and the Bootstrappable project has a whole page on it. They are fun reads, I highly recommend them. What makes JDK special is that there is no actively maintained JDK that can be built from source without a JDK. The authors had to go back quite a few years to find one, and bring it back to life.

Nixpkgs does the same. openjdk-25.0.4 is built by temurin-bin-25.0.3: a 135 MB prebuilt-tarball.

$ nix-store -qR $(nix eval --raw nixpkgs#openjdk25.drvPath) \<br>| grep -iE 'temurin|jdk_x64'<br>/nix/store/…-OpenJDK25U-jdk_x64_linux_hotspot_25.0.3_9.tar.gz.drv<br>/nix/store/…-temurin-bin-25.0.3.drv

Nixpkgs makes it pretty easy to audit in the meta.sourceProvenance of the package.

$ nix eval --json nixpkgs#temurin-bin-25.meta.sourceProvenance<br>[{"isSource":false,"shortName":"binaryNativeCode"},<br>{"isSource":false,"shortName":"binaryBytecode"}]

What does the source provenance of GuixPkgs’ openjdk-25 look like?

It’s a little whacky but the overall build chain in Guix is the following:

jikes 1.22 (C++) compiles GNU Classpath 0.93 , a free reimplementation of the Java class library.

Classpath is enough to bring up JamVM 1.5.1 , a JVM written in C. Now something can run Java.

Now we can finally use Java tools: Ant and ecj , the Eclipse Compiler for Java.

Then it doubles back and rebuilds all of that with the newer versions to get more modern JDK features.

That stack finally compiles IcedTea 2.6.13 (OpenJDK 7) → IcedTea 3.19.0 (OpenJDK 8) → OpenJDK 9 .

After which it is one rung at a time: 9 builds 10, 10 builds 11, …, 24 builds 25.

Nineteen complete JDK builds, from a C++ program.

§Analyzing the closure

We can use nix-store -q --graph to emit the entire closure as a dot file to visualize the differences.

I restyled the nodes and edges: dots instead of labelled boxes, and the red to highlight the derivations that exist only because of the JDK bootstrap.

The upper panel’s entire Java story is those two red dots and the one edge between them: temurin-bin-25.0.3 → openjdk-25.0.4. The lower panel’s is the 33-node constellation.

Why is the Nix graph still so big if I claimed it was from a binary distribution?

It turns out that a JDK closure is mostly not Java.

It is a large C++ program that wants X11, cups, fontconfig, freetype, alsa and zlib, sitting on a C toolchain that both sides bootstrap from source. That sub-graph is the same in both and it swamps everything.

I was curious to zoom-in and see the source-bootstrap portion of the derivation graph. Since that shared sub-graph is drowning the signal, I chose to filter it out.

Which derivations exist in this closure only because of how the JDK is bootstrapped? 🤔

That is a reachability query. We delete the Java-provenance nodes from the graph, see what is still reachable from the root, and whatever is left are the derivations that exist only because of the JDK bootstrap.

attributable = reachable(root) - reachable(root, without=java_nodes)

total closure<br>sub-graph<br>exists only for JDK

nixpkgs openjdk-25.0.4<br>2758<br>2756

guixpkgs openjdk-25<br>3556<br>2680<br>876

This lines up with our intuition. Nixpkgs only needs two derivations to bootstrap the JDK, while...

openjdk java source from graph bootstrapped

Related Articles