Standalone, reproducible build of a pruned JavaBeans-only java.desktop runtime
for the gdcc toolchain, plus the packaging and release tooling that publishes it.
This repository is an extraction of the beans-module route described in the
earlier research in script-gd/probe-env (see DESKTOP_FREE_REPORT.md there).
Only that single route is included: rebuild the JDK's java.desktop module down
to the JavaBeans classes gdcc needs, then link a small runtime with jlink.
The gdcc application and its dependencies are not part of this repository. This repository only produces a runtime image that an application can be started with.
- Upstream runtime: Eclipse Temurin OpenJDK 25 (HotSpot), version
25.0.2+10. - CI pin:
actions/setup-javawithdistribution: temurinandjava-version: '25.0.2+10.0.LTS'. - Linked modules:
java.base,java.desktop,java.logging,java.xml,jdk.httpserver. - The rebuilt
java.desktopkeeps its original module name but contains only 230 classes:java/beans/**(151 classes)com/sun/beans/**(76 classes)javax/swing/SwingContainer.classjavax/swing/tree/TreeNode.classjavax/swing/tree/TreePath.class
The module name is kept on purpose: java.base grants internal package access
and a built-in class loader mapping to java.desktop, and java.beans packages
cannot be defined by the application class loader. A differently named module is
rejected at boot with prohibited package name. The build runs a negative test
that asserts this failure.
- JavaBeans introspection through
java.beans.Introspectorand thecom.sun.beansimplementation, as used by FreeMarker's object wrapper. - The Swing tree types referenced by FreeMarker core signatures and interfaces
(
javax.swing.tree.TreeNode,javax.swing.tree.TreePath), and thejavax.swing.SwingContainerannotation read byjava.beans.BeanDescriptor. - A
jlinked runtime containing exactly the five modules listed above.
The earlier research on script-gd/probe-env validated this route for gdcc,
including a generated C source comparison. That comparison has not been
rerun in this repository yet. Do not assume it has.
- Full AWT/Swing GUI. There is no GUI toolkit, no display or headless AWT, and no native graphics libraries (font manager, freetype, image or sound natives, and so on).
- JavaBeans features that reach into GUI code: icon and image property editors,
GUI
PropertyEditors,Customizers, the bean context GUI bridge, and XML bean persistence delegates. Calls that hit these paths may fail. - The full
java.desktopAPI surface. The kept classes are a subset, not a drop-in replacement for the whole module. - Java SE compliance. This is a custom OpenJDK image; it has not been tested with the TCK and is not vendor-supported.
- Any guarantee beyond the tested platform matrix below.
The build script is scripts/build.py (a standalone extraction of
beans-runtime.py from the research repository):
Requirements: Python 3.12 or later, the native-platform pinned Temurin JDK, and network access to GitHub (JMODs) and Maven Central (packaging smoke test). There are no third-party Python dependencies. Build on the target OS and CPU.
python3 scripts/build.py --jdk /path/to/temurin-25.0.2+10 --out build/local --negative-test--jdkdefaults toJAVA_HOME.--outmust not already exist; the build refuses to overwrite output.- The JDK must be the pinned Temurin
25.0.2+10. The matching platform JMOD archive is downloaded and SHA-256 verified. The host must be one of the supported platforms. --negative-testadditionally builds a differently named module and asserts that the boot layer refuses it.
Outputs under --out:
build/local/jre/ linked runtime image
build/local/provenance.json inputs, hashes, modules, verification results
build/local/audit/ pruned descriptor, class list, negative-test log
build/local/original/ unmodified java.desktop.jmod and its descriptor
The packaging script is scripts/package.py:
python3 scripts/package.py --build-dir build/local --version 1.0.0 --output dist--build-dirpoints at the directory produced byscripts/build.py.--versionis the distribution version used in the archive names.- The script defaults to
JAVA_HOMEwhere a JDK is needed. - Output archives and
SHA256SUMSare written under--output. - Before publishing a package, it is extracted into a path containing spaces. Its module/class set is checked, and the original FreeMarker 2.3.34, JavaBeans, Swing tree types, XML, logging, HTTP loopback and an FFM native downcall are exercised against both the relocated image and the full JDK. The test-only FreeMarker JAR is SHA-256 pinned and is not included in the runtime package.
CI builds each platform on a native GitHub-hosted runner (no cross-compilation), then packages and uploads the artifacts:
| Platform | Runner | Archive |
|---|---|---|
| windows-x64 | windows-2025 | gdcc-jre-<v>-windows-x64.zip |
| linux-x64 | ubuntu-24.04 | gdcc-jre-<v>-linux-x64.tar.gz |
| linux-aarch64 | ubuntu-24.04-arm | gdcc-jre-<v>-linux-aarch64.tar.gz |
| macos-aarch64 | macos-15 | gdcc-jre-<v>-macos-aarch64.tar.gz |
Release 1.0.0 contains exactly:
gdcc-jre-1.0.0-windows-x64.zip
gdcc-jre-1.0.0-linux-x64.tar.gz
gdcc-jre-1.0.0-linux-aarch64.tar.gz
gdcc-jre-1.0.0-macos-aarch64.tar.gz
SHA256SUMS
OpenJDK25U-jdk-sources_25.0.2_10.tar.gz
The tag 1.0.0 is the version of this distribution and its tooling, not the
Java version. The bundled runtime remains OpenJDK 25 (Temurin 25.0.2+10).
Pushes and pull requests build and test without publishing. Pushing a plain
MAJOR.MINOR.PATCH tag builds all four targets again; only after every build
passes does the release job recheck archive hashes, download and verify the
matching upstream source, and publish the GitHub release. Reproducibility here
means pinned inputs and documented build steps, not byte-identical archives:
archive and JMOD timestamps can differ between builds.
POSIX platforms use .tar.gz to preserve the executable bit and symlinks (for
example the macOS legal/ notices); Windows uses .zip.
Extract the archive for the target platform. The runtime is under jre/, and
jre/bin/java is the entry point. The image contains no javac.
If the application uses the Foreign Function & Memory API (FFM) or other native access, start it with:
--enable-native-access=ALL-UNNAMED --illegal-native-access=deny
These options are only relevant when the application performs native access; the runtime image itself does not require them.
- Linux artifacts are produced and tested on Ubuntu 24.04. Wider Linux libc/distribution compatibility is not assumed until tested.
- Windows x64 is only asserted for the CI configuration that builds it; no MSVC, MinGW or libc assumptions beyond that runner are made.
- aarch64 artifacts are built and tested on native ARM64 CI runners, not under emulation.
- Upstream runtime source: the matching Adoptium/Temurin release
jdk-25.0.2%2B10(https://github.com/adoptium/temurin25-binaries/releases/tag/jdk-25.0.2%2B10). - The release ships the upstream OpenJDK source archive
OpenJDK25U-jdk-sources_25.0.2_10.tar.gz(SHA-2564f2df6cdf3870adb23ca71d162dec9238e71834785c81cbd393579aa18cdd045). - The scripts, module descriptor and exact steps needed to reproduce the modified runtime are in this repository under the matching release tag.
The pinned 25.0.2+10 is a reproducibility baseline for this research, not a
recommendation to stay on an old security update. Move to a maintained Java 25
patch level and re-validate before production use.
See LICENSE and NOTICE.md. Upstream OpenJDK and this repository's own scripts and descriptor are provided under the GNU General Public License, version 2, with the Classpath Exception.