Jenesis - a modern Java build tool
Java-native config, plugin-free, with
module-info.javatreated as a feature, not an afterthought.
A bootstrap for executable jars that keeps real Java modularity. The launcher is shaded into the jar root
and run as its Main-Class, so java -jar foo.jar starts the application - while modular dependencies are
resolved into a fresh java.lang.ModuleLayer and non-modular ones become the unnamed module of the same
loader. Each dependency is exploded into its own subfolder of the outer jar, and class and resource bytes are
read straight from the still-open jar on demand: nothing is merged into a flat jar or held in memory, and
only native libraries are ever extracted to disk.
📖 The user documentation lives at jenesis.build/launcher. How a launch proceeds, the jar layout, bundled agents, module-access grants, troubleshooting, and the full descriptor reference are all there. What follows is for people working on this repository.
You do not normally depend on this artifact yourself. It is published as
build.jenesis:build.jenesis.launcher and consumed by the Jenesis build tool, which shades it into the jars
it produces when a project asks for one:
# build.jenesis/packaging.properties
launcher=truejava build/jenesis/Project.java # the jar lands under target/build/…/launcher/bundle/output/launcher/
java -jar foo.jar [args...] # run it
java -javaagent:foo.jar=args -jar app.jar # a hand-assembled jar with no mainClass is an agentThe build tool writes mainClass, mainModule and classpath into the jar's application.properties; the
agent, module-access and signer keys the launcher also understands are for jars assembled by other means.
Requires a JDK 25 or newer (the module compiles at release 25; CI builds on 26). The build is the project's own Java source - no wrapper, no plugins:
git submodule update --init --depth 1 # the pinned Jenesis build tool
java build/jenesis/Project.java # compile, package, run the tests
java build/jenesis/Project.java stage # stage the published artifact under target/stageThe build tool is tracked as a shallow submodule under .jenesis/upstream, pinned to the commit this project
builds against, so a fresh clone plus that one command is the whole setup.
The suite is the reason this project can be trusted with a class loader. It synthesises class files and
exploded-bundle fixtures with the JDK Class-File API and drives Launcher#run end to end, covering:
- Layout and loading - class-path and modular applications, automatic-module naming, declared class-path order, a rejected duplicate module name, split-package shadowing, and a strict module's non-exported main.
- Resources -
jar:andfile:URLs from both a jar and an exploded directory, names confined to the bundle root, a bundle path with spaces,getResourcesacross a module and the class path, and module resources honouring encapsulation (a non-open package's resource stays hidden). - Faithfulness to the JDK - multi-release class and resource selection, native-library extraction, package
metadata and sealing from the manifest, a sealing violation across class-path jars, a module class's
CodeSourcelocation, and signer identity reconstructed from asignature.<dep>property. - Agents and grants -
premainin declaration order with arguments,agentmainon attach, an agent bundle with no main started throughrunAgents, andaddExports/addOpens/addReads.
A change to how the graph is assembled should arrive with the test that pins the behaviour it changes.
.github/workflows/build.yml runs on every push and pull request: it checks out the submodule, sets up a JDK,
and runs java build/jenesis/Project.java, which builds and tests in one step.
.github/workflows/release.yml is dispatched by hand from the Actions tab, so any commit is releasable: the
optional sha input names the commit (default: the head it runs on) and the optional tag input names the tag
(v1.2.3 or 1.2.3; left empty, the minor of the latest v* tag is bumped). It stages
with sources and documentation, then hands the tree to JReleaser (jreleaser.yml), which signs, publishes to
Maven Central and tags v<version>. project.properties carries the POM metadata.
Apache License 2.0 - see LICENSE. Copyright Rafael Winterhalter.
The license covers the launcher itself, and travels with the code: a jar that shades the launcher in redistributes Apache-licensed bytes under these terms. It does not extend to the application that jar starts, or to the dependencies the launcher resolves, which keep the licenses their own authors chose.