Please review this patch which adds a "jmodless" jlink mode to the JDK. Fundamentally this patch adds an option to use `jlink` even though your JDK install might not come with the packaged modules (directory `jmods`). This is particularly useful to further reduce the size of a jlinked runtime. After the removal of the concept of a JRE, a common distribution mechanism is still the full JDK with all modules and packaged modules. However, packaged modules can incur an additional size tax. For example in a container scenario it could be useful to have a base JDK container including all modules, but without also delivering the packaged modules. This comes at a size advantage of `~25%`. Such a base JDK container could then be used to `jlink` application specific runtimes, further reducing the size of the application runtime image (App + JDK runtime; as a single image *or* separate bundles, depending on the app being modularized).
The basic design of this approach is to add a jlink plugin for tracking non-class and non-resource files of a JDK install. I.e. files which aren't present in the jimage (`lib/modules`). This enables producing a `JmodLessArchive` class which has all the info of what constitutes the final jlinked runtime. Basic usage example: $ diff -u <(./bin/java --list-modules --limit-modules java.se) <(../linux-x86_64-server-release/images/jdk/bin/java --list-modules --limit-modules java.se) $ diff -u <(./bin/java --list-modules --limit-modules java.se) <(../linux-x86_64-server-release/images/jdk/bin/java --list-modules --limit-modules jdk.jlink) $ ls ../linux-x86_64-server-release/images/jdk/jmods java.base.jmod java.net.http.jmod java.sql.rowset.jmod jdk.crypto.ec.jmod jdk.internal.opt.jmod jdk.jdi.jmod jdk.management.agent.jmod jdk.security.auth.jmod java.compiler.jmod java.prefs.jmod java.transaction.xa.jmod jdk.dynalink.jmod jdk.internal.vm.ci.jmod jdk.jdwp.agent.jmod jdk.management.jfr.jmod jdk.security.jgss.jmod java.datatransfer.jmod java.rmi.jmod java.xml.crypto.jmod jdk.editpad.jmod jdk.internal.vm.compiler.jmod jdk.jfr.jmod jdk.management.jmod jdk.unsupported.desktop.jmod java.desktop.jmod java.scripting.jmod java.xml.jmod jdk.hotspot.agent.jmod jdk.internal.vm.compiler.management.jmod jdk.jlink.jmod jdk.naming.dns.jmod jdk.unsupported.jmod java.instrument.jmod java.security.jgss.jmod jdk.accessibility.jmod jdk.httpserver.jmod jdk.jartool.jmod jdk.jpackage.jmod jdk.naming.rmi.jmod jdk.xml.dom.jmod java.logging.jmod java.security.sasl.jmod jdk.attach.jmod jdk.incubator.vector.jmod jdk.javadoc.jmod jdk.jshell.jmod jdk.net.jmod jdk.zipfs.jmod java.management.jmod java.se.jmod jdk.charsets.jmod jdk.internal.ed.jmod jdk.jcmd.jmod jdk.jsobject.jmod jdk.nio.mapmode.jmod java.management.rmi.jmod java.smartcardio.jmod jdk.compiler.jmod jdk.internal.jvmstat.jmod jdk.jconsole.jmod jdk.jstatd.jmod jdk.random.jmod java.naming.jmod java.sql.jmod jdk.crypto.cryptoki.jmod jdk.internal.le.jmod jdk.jdeps.jmod jdk.localedata.jmod jdk.sctp.jmod $ ls jmods ls: cannot access 'jmods': No such file or directory $ ./bin/jlink --version 22-internal $ ./bin/jlink --add-modules java.se --output ../build/java.se-runtime --verbose java.base jrt:/java.base (jmod-less) java.compiler jrt:/java.compiler (jmod-less) java.datatransfer jrt:/java.datatransfer (jmod-less) java.desktop jrt:/java.desktop (jmod-less) java.instrument jrt:/java.instrument (jmod-less) java.logging jrt:/java.logging (jmod-less) java.management jrt:/java.management (jmod-less) java.management.rmi jrt:/java.management.rmi (jmod-less) java.naming jrt:/java.naming (jmod-less) java.net.http jrt:/java.net.http (jmod-less) java.prefs jrt:/java.prefs (jmod-less) java.rmi jrt:/java.rmi (jmod-less) java.scripting jrt:/java.scripting (jmod-less) java.se jrt:/java.se (jmod-less) java.security.jgss jrt:/java.security.jgss (jmod-less) java.security.sasl jrt:/java.security.sasl (jmod-less) java.sql jrt:/java.sql (jmod-less) java.sql.rowset jrt:/java.sql.rowset (jmod-less) java.transaction.xa jrt:/java.transaction.xa (jmod-less) java.xml jrt:/java.xml (jmod-less) java.xml.crypto jrt:/java.xml.crypto (jmod-less) Providers: java.desktop provides java.net.ContentHandlerFactory used by java.base java.base provides java.nio.file.spi.FileSystemProvider used by java.base java.naming provides java.security.Provider used by java.base java.security.jgss provides java.security.Provider used by java.base java.security.sasl provides java.security.Provider used by java.base java.xml.crypto provides java.security.Provider used by java.base java.base provides java.util.random.RandomGenerator used by java.base java.management.rmi provides javax.management.remote.JMXConnectorProvider used by java.management java.management.rmi provides javax.management.remote.JMXConnectorServerProvider used by java.management java.desktop provides javax.print.PrintServiceLookup used by java.desktop java.desktop provides javax.print.StreamPrintServiceFactory used by java.desktop java.management provides javax.security.auth.spi.LoginModule used by java.base java.desktop provides javax.sound.midi.spi.MidiDeviceProvider used by java.desktop java.desktop provides javax.sound.midi.spi.MidiFileReader used by java.desktop java.desktop provides javax.sound.midi.spi.MidiFileWriter used by java.desktop java.desktop provides javax.sound.midi.spi.SoundbankReader used by java.desktop java.desktop provides javax.sound.sampled.spi.AudioFileReader used by java.desktop java.desktop provides javax.sound.sampled.spi.AudioFileWriter used by java.desktop java.desktop provides javax.sound.sampled.spi.FormatConversionProvider used by java.desktop java.desktop provides javax.sound.sampled.spi.MixerProvider used by java.desktop java.logging provides jdk.internal.logger.DefaultLoggerFinder used by java.base java.desktop provides sun.datatransfer.DesktopDatatransferService used by java.datatransfer One nice property of this patch is that it can produce an identical (as in binary identical) `java.se` JDK image in "jmod-less" mode as compared to a regular jlink with packaged modules present. This has been asserted in newly added tests. In order to prevent accidental copy of modified files in the base JDK a checksum mechanism is in place to warn about this when jlinking. This is also asserted in tests. One limitation of this mode is that there is no way to use it for cross-linking (at least currently). That is, a jmod-less `jlink` needs to happen on the same runtime platform the resulting image is intended to get deployed on. Testing: - [x] GHA (including `jdk/tools/jlink` tests). See for example [the latest run](https://github.com/jerboaa/jdk/actions/runs/5477743443) and [here](https://github.com/jerboaa/jdk/actions/runs/5475484765). - [x] Some tests on our internal infra (JDK 17-based). It didn't show any regressions. - [x] Added tests as part of the patch (8 of them). All pass on the major platforms. Thoughts? ------------- Commit messages: - 8311302: Allow for jlinking a custom runtime without packaged modules being present Changes: https://git.openjdk.org/jdk/pull/14787/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=14787&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8311302 Stats: 2083 lines in 23 files changed: 2016 ins; 33 del; 34 mod Patch: https://git.openjdk.org/jdk/pull/14787.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/14787/head:pull/14787 PR: https://git.openjdk.org/jdk/pull/14787