jdk
jdk copied to clipboard
8311302: Implement JEP 493: Linking Run-Time Images without JMODs
Please review this patch which adds a jlink mode to the JDK which doesn't need the packaged modules being present. A.k.a run-time image based jlink. 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 JRTArchive
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 jdk.jlink) <(../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 ../java.se-runtime --verbose
java.base jrt:/java.base (run-image)
java.compiler jrt:/java.compiler (run-image)
java.datatransfer jrt:/java.datatransfer (run-image)
java.desktop jrt:/java.desktop (run-image)
java.instrument jrt:/java.instrument (run-image)
java.logging jrt:/java.logging (run-image)
java.management jrt:/java.management (run-image)
java.management.rmi jrt:/java.management.rmi (run-image)
java.naming jrt:/java.naming (run-image)
java.net.http jrt:/java.net.http (run-image)
java.prefs jrt:/java.prefs (run-image)
java.rmi jrt:/java.rmi (run-image)
java.scripting jrt:/java.scripting (run-image)
java.se jrt:/java.se (run-image)
java.security.jgss jrt:/java.security.jgss (run-image)
java.security.sasl jrt:/java.security.sasl (run-image)
java.sql jrt:/java.sql (run-image)
java.sql.rowset jrt:/java.sql.rowset (run-image)
java.transaction.xa jrt:/java.transaction.xa (run-image)
java.xml jrt:/java.xml (run-image)
java.xml.crypto jrt:/java.xml.crypto (run-image)
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 run-time image based link 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 fail the link if a runtime image file has been modified. 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 run-time image based 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 and JDKs built with--enable-linkable-runtime
). See for example the latest run and here. - [x] Some tests on our internal infra. It didn't show any regressions.
- [x] Added tests as part of the patch (11 of them). All pass on the major platforms.
Thoughts?
Progress
- [x] Change must not contain extraneous whitespace
- [x] Commit message must refer to an issue
- [ ] Change requires a JEP request to be targeted
- [ ] Change requires CSR request JDK-8317420 to be approved
- [ ] Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)
Issues
- JDK-8311302: Implement JEP 493: Linking Run-Time Images without JMODs (Enhancement - P4)
- JDK-8333799: JEP 493: Linking Run-Time Images without JMODs (JEP)
- JDK-8317420: Implement JEP 493: Linking Run-Time Images without JMODs (CSR)
Reviewers
- Erik Joelsson (@erikj79 - Reviewer) 🔄 Re-review required (review applies to b72648ba)
- Magnus Ihse Bursie (@magicus - Reviewer) 🔄 Re-review required (review applies to 7a8f839e)
Contributors
- Mandy Chung
<[email protected]>
Reviewing
Using git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/14787/head:pull/14787
$ git checkout pull/14787
Update a local copy of the PR:
$ git checkout pull/14787
$ git pull https://git.openjdk.org/jdk.git pull/14787/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 14787
View PR using the GUI difftool:
$ git pr show -t 14787
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/14787.diff
Webrev
:wave: Welcome back sgehwolf! A progress list of the required criteria for merging this PR into master
will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.
@jerboaa The following label will be automatically applied to this pull request:
-
core-libs
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.
Webrevs
- 34: Full (59997873)
- 33: Full (7e2bc4e1)
- 32: Full (04cd98f8)
- 31: Full - Incremental (7a8f839e)
- 30: Full - Incremental (b72648ba)
- 29: Full (0eb1e48d)
- 28: Full (e1e3f02f)
- 27: Full (8cca277e)
- 26: Full - Incremental (be30a182)
- 25: Full (67aea4ca)
- 24: Full - Incremental (84d4feff)
- 23: Full (ce04f42a)
- 22: Full - Incremental (5a6d2e4f)
- 21: Full (e25dd440)
- 20: Full - Incremental (2ea9cab8)
- 19: Full - Incremental (1fe6161b)
- 18: Full (5404d4e8)
- 17: Full - Incremental (b034cce2)
- 16: Full (00caf77c)
- 15: Full (cd47f3d9)
- 14: Full - Incremental (1e69bc9f)
- 13: Full (9635ba52)
- 12: Full - Incremental (821857ab)
- 11: Full (a797ea69)
- 10: Full - Incremental (3745cb8e)
- 09: Full - Incremental (f1130d1c)
- 08: Full - Incremental (3539c6a5)
- 07: Full - Incremental (ac01194c)
- 06: Full - Incremental (fea34e3a)
- 05: Full - Incremental (b82efb08)
- 04: Full - Incremental (ef1bca8c)
- 03: Full - Incremental (2e73e300)
- 02: Full - Incremental (738b8a31)
- 01: Full (baeaaf5d)
- 00: Full (a001d8ce)
@jerboaa This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration!
Please keep open, bot.
@jerboaa Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration. See OpenJDK Developers’ Guide for more information.
/csr needed
@jerboaa has indicated that a compatibility and specification (CSR) request is needed for this pull request.
@jerboaa please create a CSR request for issue JDK-8311302 with the correct fix version. This pull request cannot be integrated until the CSR request is approved.
@AlanBateman Hi! Moving the discusion to this PR now. I've updated this patch to do single-hops only by default now. Looks like this:
$ bin/jlink --add-modules java.base --output ../testme-java-base/ --verbose
java.base jrt:/java.base (run-image)
Providers:
java.base provides java.nio.file.spi.FileSystemProvider used by java.base
java.base provides java.util.random.RandomGenerator used by java.base
Error: Run image links only allow single-hop.
I'm not particularly fond of that, though, as there is a need to know what is single hop is and what not. My approach is adding a 0-sized stamp file to the lib/modules
jimage, but that has the issue of now breaking comparison between a packaged module jlink vs. a runtime image using one. The same would be true for adding a file or appending the jmod_resources
file with some info to know it's multi-hop. Some state needs to be there unless I'm missing something for this. For now an option suppresses creating that file so as to be able to keep asserting packaged vs run-image link and other properties in tests. I'd argue most of the time multi-hop would be OK, but default off and a switch to turn it on seems reasonable to me. Up for discussion...
Also, if a file is modified the link fails unless it's turned off with an option. Looks like this:
$ ./bin/jlink --add-modules java.base --output ../abort-on-mod --verbose
java.base jrt:/java.base (run-image)
Providers:
java.base provides java.nio.file.spi.FileSystemProvider used by java.base
java.base provides java.util.random.RandomGenerator used by java.base
Error: java.lang.IllegalArgumentException: /disk/openjdk/upstream-sources/git/jdk-jdk/build/jmodless-copy-jmods-removed/conf/net.properties has been modified. Please double check!
$ echo $?
1
Both things will have CLI options to a) make the exit on mod a warning only and b) allow multi-hop - some tests needed that. Hence, I've marked this PR as needing a CSR for now.
More thoughts?
@AlanBateman Gentle ping.
@AlanBateman Gentle ping.
On my list, it's a lot to get through and a number of aspects to this that I think will require refinement and discussion.
@AlanBateman Gentle ping.
On my list, it's a lot to get through and a number of aspects to this that I think will require refinement and discussion.
Thanks for the heads-up! Your input is much appreciated.
I did a pass over this to see where this proposal is currently at. At a high-level I think good progress since the discussion on leyden-dev some time ago. A few comments this from this pass:
If I read it correctly, the default behavior is now to fail if jlink is executed from a run-time image that has been modified from the original and the packaged modules are not observable on the module path (this includes the default module path effectively appended by jlink). That seems the right policy as it avoids modifications by plugins or conf changes silently propagating.
If I read the code correctly, the error when a hash doesn't match is a very terse "Run image links only allow single-hop" so that probably needs to be looked to make sure the message says that the run-time image has been modified and maybe include one of the files/resources that has changed so there is something real to go with the error.
The command line options to override and change the error to a warning or silently ignore seems to be "-run-time-ignore-single-hop" and "--run-image-only-warnings". Maybe it should be reduced to just one option and maybe it should contain the word "clone" to something that makes it more obvious that it's just copying or cloning the contents of the modules in the run-time image (I suspect "single hop" won't be understood).
Adding a new jdk.tools.jlink.internal.Archive implementation where the resources come from the run-time image seems a sensible approach. I don't particularly like the name "JmodLessArchive" because the original module may not have been packaged as a JMOD file, it may have been a modular JAR. Same comment on the BOM file "jmod_resources" added to the top-level directory of each module. I think it's clever to add this to each module in the container image but part of me wonders if it should be hidden in some way to avoid tools depending on it. I don't have a concrete suggestion except to wonder if jrtfs should filter it out, same thing in the SystemModuleReader code as ModuleReader.find("jmod_resources") will find the resource, could be an attractive nuisance.
I did a pass over this to see where this proposal is currently at. At a high-level I think good progress since the discussion on leyden-dev some time ago. A few comments this from this pass:
Thanks, Alan!
If I read it correctly, the default behavior is now to fail if jlink is executed from a run-time image that has been modified from the original and the packaged modules are not observable on the module path (this includes the default module path effectively appended by jlink). That seems the right policy as it avoids modifications by plugins or conf changes silently propagating.
Correct.
If I read the code correctly, the error when a hash doesn't match is a very terse "Run image links only allow single-hop" so that probably needs to be looked to make sure the message says that the run-time image has been modified and maybe include one of the files/resources that has changed so there is something real to go with the error.
Not quite. See the example I've added in the previous comment. It already includes that info. It looks like this:
Error: java.lang.IllegalArgumentException: /disk/openjdk/upstream-sources/git/jdk-jdk/build/jmodless-copy-jmods-removed/conf/net.properties has been modified. Please double check!
The message can of course be modified what we think is best.
The command line options to override and change the error to a warning or silently ignore seems to be "-run-time-ignore-single-hop" and "--run-image-only-warnings". Maybe it should be reduced to just one option and maybe it should contain the word "clone" to something that makes it more obvious that it's just copying or cloning the contents of the modules in the run-time image (I suspect "single hop" won't be understood).
I believe keeping them both makes sense. Or at least make it so that it takes an argument to be able to distinguish between the two. The reason why I think this is useful is in the container case, where say - the JDK got installed by an installer - which didn't include packaged modules, but is otherwise a full JDK (with all modules). The installer already does integrity checking of the files and it might be desirable to do multi-hop evolutions, while still wanting to get warnings/errors on modified configuration files, for example. Happy to change the name of that flag/flags to --clone-run-image=ignore-multi,warn-only
or something similar.
Adding a new jdk.tools.jlink.internal.Archive implementation where the resources come from the run-time image seems a sensible approach. I don't particularly like the name "JmodLessArchive" because the original module may not have been packaged as a JMOD file, it may have been a modular JAR. Same comment on the BOM file "jmod_resources" added to the top-level directory of each module.
OK. How about RunimageArchive
and runimage_resources
or clone_resources
?
I think it's clever to add this to each module in the container image but part of me wonders if it should be hidden in some way to avoid tools depending on it. I don't have a concrete suggestion except to wonder if jrtfs should filter it out, same thing in the SystemModuleReader code as ModuleReader.find("jmod_resources") will find the resource, could be an attractive nuisance.
That seems fine. I can add code to filter those resources out. On the other hand, no special handling is being made for jdk/internal/vm/options
. Perhaps that should be handled uniformly?
The command line options to override and change the error to a warning or silently ignore seems to be "-run-time-ignore-single-hop" and "--run-image-only-warnings". Maybe it should be reduced to just one option and maybe it should contain the word "clone" to something that makes it more obvious that it's just copying or cloning the contents of the modules in the run-time image
After some more thought I've changed my mind and I've come to the conclusion that it makes sense to combine them. The latest patch uses --unlock-run-image
as the "force" option which demotes the error to a warning.
Perhaps it makes sense to get some consensus on the naming parts at this point. The CSR seems a good vehicle for that and I've fleshed it out a bit now.
Current GHA failure of gc/TestJNICriticalStressTest#id0
on Windows seems unrelated. FWIW, this check which also includes jlink tests passed fine.
I looked at the latest proposal and CSR. Overall I think the proposal is good and you've got the right default to fail if the run-time image has been modified. So I think we are down to a few lesser topics now.
I'm wondering if the CLI option to override, meaning a first run-time image containing the jdk.jlink module generates a second run-time image, and the first run-time image has been modified, whether this is really needed. I'm wondering about this because this CLI option is hard to explain, will developers really understand it? If there is an CLI option then we'll need to find a better name, I don't think "--unlock-run-time" works as a name (the usage of options talk about "runtime image" for example, maybe "-ignore-signing-information" can provide inspiration as an override too).
Can --add-run-image-resources be dropped? Exposing this in the interface feels like it's an attractive nuisance and not clear (to me anyway) what developers would do with this.
A few very minor things that I jotted down while looking at the current proposal:
-
Adding a resource to serve as a marker that indicates it was created without the packaged modules is fine. I think the name should be looked as "runimage" is a bit consistent for this area. I'm also wondering if it would be better to hide in jdk/internal somewhere to avoid any tooling assuming it's a supported interface.
-
The error message exclaims "Please double check!", I think that error message will need to be tweaked once we get down to details like this.
@AlanBateman Thanks for having taken another look!
I looked at the latest proposal and CSR. Overall I think the proposal is good and you've got the right default to fail if the run-time image has been modified. So I think we are down to a few lesser topics now.
Glad to hear it. Hopefully we can still get this into JDK 22.
I'm wondering if the CLI option to override, meaning a first run-time image containing the jdk.jlink module generates a second run-time image, and the first run-time image has been modified, whether this is really needed. I'm wondering about this because this CLI option is hard to explain, will developers really understand it? If there is an CLI option then we'll need to find a better name, I don't think "--unlock-run-time" works as a name (the usage of options talk about "runtime image" for example, maybe "-ignore-signing-information" can provide inspiration as an override too).
I think we need the option. Be it only for users wanting to validate links with and without packaged modules present (as one of the tests does). How about we call the option --force-run-image-link
? Or maybe --ignore-base-run-image-changes
. I'm not very good with coming up with names.
Can --add-run-image-resources be dropped? Exposing this in the interface feels like it's an attractive nuisance and not clear (to me anyway) what developers would do with this.
It's not clear what you mean by that. Dropped from what? The CSR? Something else? The run-image based link needs the --add-run-image-resources
plugin for it to work.
A few very minor things that I jotted down while looking at the current proposal:
* Adding a resource to serve as a marker that indicates it was created without the packaged modules is fine. I think the name should be looked as "runimage" is a bit consistent for this area. I'm also wondering if it would be better to hide in jdk/internal somewhere to avoid any tooling assuming it's a supported interface.
How does jdk/internal/runimage
as a name sound?
* The error message exclaims "Please double check!", I think that error message will need to be tweaked once we get down to details like this.
Sure. I can just drop that phrase.
I did one pass through the changes. Looks like the plugin author has to consider how it should work to support linking without packaged modules.
The current implementation --system-modules
re-applies for every jlink invocation since the transformation depends on the set of modules to be linked in the resulting image.
OTOH --vendor-bug-url
, --vendor-version
and --vendor-vm-bug-url
plugins are auto-applied
to the new image created via this run-time image based linking when these options are not specified.
I was wondering what plugins that were applied in the first image are carried automatically during the run-time image based linking? I would assume this will generate an image equivalent to running jlink with the packaged modules present. Therefore nothing should be carried automatically from the current run-time image, shouldn't it?
I agree that --add-run-image-resources
should be hidden and it's not target for developers to use. I consider it's jlink internal implementation. It's auto-enabled and always generates the per-module runimage_resources
. I think we might need to add Plugin::showPlugin
or hidePlugin
to specify if this plugin should be hidden from --list-plugins
.
It's good to move runimage_resources
to some package under jdk/internal
but each module will need to be a different package name. BTW I found runimage
a confusing term.
I am also not certain if we should support --unlock-run-time
option. Are you trying to make the users do less work to configure some properties files once in the first image and all the custom runtime images created from that will not need to do the configuration?
It's not clear what you mean by that. Dropped from what? The CSR? Something else? The run-image based link needs the
--add-run-image-resources
plugin for it to work.
I mean the list of plugins that are listed by --list-plugins. I see Mandy has the same comment and has a suggestion on how to hide this.
BTW: We need to replace all uses of "run-image" with "run-time image" to keep it consistent with the existing JEPs, docs, and other places.
I did one pass through the changes.
Thanks for the review!
Looks like the plugin author has to consider how it should work to support linking without packaged modules.
Yes, to some extent.
The current implementation
--system-modules
re-applies for every jlink invocation since the transformation depends on the set of modules to be linked in the resulting image.
Yes. The same is true for a run-time image based link for the system modules plugin.
OTOH
--vendor-bug-url
,--vendor-version
and--vendor-vm-bug-url
plugins are auto-applied to the new image created via this run-time image based linking when these options are not specified.
Since some of those things are also possible to specify at build-time (with --with-vendor-name
and friends), this applies to the packaged-modules link as well.
I was wondering what plugins that were applied in the first image are carried automatically during the run-time image based linking? I would assume this will generate an image equivalent to running jlink with the packaged modules present.
I'm not sure I fully understand the question.
Since the run-time image link is fundamentally based on using the modules image (lib/modules
) plus files on the filesystem of the JDK install (minus jmods
folder), everything that modifies class files, adds resources, modifies files like stripping debug info from binaries, carries over.
Say, you'd have a link using packaged modules:
jlink --add-modules jdk.jlink --strip-debug --output jdk.jlink-stripped
A link using the run-time image, then would produce a stripped java.base module with this step:
./jdk.jlink-stripped/bin/jlink --add-modules java.base --output java.base-stripped
IMO that's a nice use-case to have. If you want a re-setted link, use the packaged modules instead and start afresh.
Therefore nothing should be carried automatically from the current run-time image, shouldn't it?
Could you clarify what "carried automatically" means?
I agree that
--add-run-image-resources
should be hidden and it's not target for developers to use. I consider it's jlink internal implementation. It's auto-enabled and always generates the per-modulerunimage_resources
. I think we might need to addPlugin::showPlugin
orhidePlugin
to specify if this plugin should be hidden from--list-plugins
.
OK. Done in the latest version. I've added Plugin::isHidden
, defaulting to false
.
It's good to move
runimage_resources
to some package underjdk/internal
but each module will need to be a different package name. BTW I foundrunimage
a confusing term.
Unfortunately, adding packages like that isn't allowed, since jlink
performs validation of the known packages using the module descriptor. By adding the resource to a new package, we'd somehow have to modify the module descriptor as well for every module. For now, I've resorted to renaming the resource file to - a still package-less - jdk_internal_runimage
.
I am also not certain if we should support
--unlock-run-time
option. Are you trying to make the users do less work to configure some properties files once in the first image and all the custom runtime images created from that will not need to do the configuration?
Yes, that could be a use-case. Personally, I liked the possibility of doing recursive links. There was some beauty to it, since it allows multiple teams to do links suitable to their use-case. But this is now gone with the latest version which uses a marker file to prevent multiple recursive links based on the run-time image.
On the other hand, there are tests part of this patch which verify that a default link for java.se
with default options using packaged modules and using the run-time image based link are binary identical. Since any extra resource (like the marker file), destroy this property, I'd like to keep some way to still verify this.
Perhaps doing this less visibly than an actual new CLI option is more amenable? A property akin to jlink.debug
perhaps? Something like -Djlink.no-marker
? It could be an undocumented thing.
It's not clear what you mean by that. Dropped from what? The CSR? Something else? The run-image based link needs the
--add-run-image-resources
plugin for it to work.I mean the list of plugins that are listed by --list-plugins. I see Mandy has the same comment and has a suggestion on how to hide this.
OK, thanks. Done. I guess since it's now a hidden plugin does it still need mentioning in the CSR?
BTW: We need to replace all uses of "run-image" with "run-time image" to keep it consistent with the existing JEPs, docs, and other places.
Makes sense. Did a first pass fixing those.
A few very minor things that I jotted down while looking at the current proposal:
* Adding a resource to serve as a marker that indicates it was created without the packaged modules is fine. I think the name should be looked as "runimage" is a bit consistent for this area. I'm also wondering if it would be better to hide in jdk/internal somewhere to avoid any tooling assuming it's a supported interface.
How does
jdk/internal/runimage
as a name sound?
Coming back to this. Since jlink
verifies the packages in the module descriptor are equal to the observed packages, we cannot add packages that way without also changing the module descriptor. I've renamed the resource file to jdk_internal_runimage
for now. Hope that's sufficient.
The current implementation
--system-modules
re-applies for every jlink invocation since the transformation depends on the set of modules to be linked in the resulting image.Yes. The same is true for a run-time image based link for the system modules plugin.
OTOH
--vendor-bug-url
,--vendor-version
and--vendor-vm-bug-url
plugins are auto-applied to the new image created via this run-time image based linking when these options are not specified.Since some of those things are also possible to specify at build-time (with
--with-vendor-name
and friends), this applies to the packaged-modules link as well.
Better to explain with an example:
$ jdk22/bin/jlink --add-modules jdk.compiler,jdk.jlink --output image1 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true"
$ image1/bin/jlink --add-modules jdk.jlink --output image2 --add-options "-Dcom.acme.name=BAR"
How can the user know what plugins are applied to image2
? i.e. what is the jlink command to produce image2
if running from jdk22
with the packaged modules present?
Reading the changes, I'm not sure but I think it's not equivalent to:
jdk22/bin/jlink --add-modules jdk.jlink --output image2 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true" --add-options "-Dcom.acme.name=BAR"
I think this behavior should be documented.
Thanks.
How can the user know what plugins are applied to
image2
? i.e. what is the jlink command to produceimage2
if running fromjdk22
with the packaged modules present?
The only way to know is by knowing the chain of jlink
commands yielding up to the final image. Let jlink'
be the jlink using packaged modules. Currently this can be at most two. In addition, the contents of argfile
needs to be known. That doesn't seem to be very different to the status quo, though. See below.
Reading the changes, I'm not sure but I think it's not equivalent to:
jdk22/bin/jlink --add-modules jdk.jlink --output image2 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true" --add-options "-Dcom.acme.name=BAR"
Note that plugins like --add-options
have been modified so that only the options passed at the current CLI will propagate to the final image. So in this case, image1
would have property com.foo.XYZ
set, but not image2
. Incidentally, what you probably intended to use was `--add-options "-Dcom.foo.XYZ=true -Dcom.acme.name=BAR".
Having said that, depending on the contents of argfile
, those could be equivalent. In fact, they are with --unlock-run-image
, and an empty argfile
. --unlock-run-image
avoids adding the marker file, which is the only difference when we extract the image.
--save-jlink-argfiles
brings a strange angle to this discussion, but it's conceivable to get a similarly different image, even with --keep-packaged-modules. Consider:
echo '--add-options=-XX:+UseParallelGC' > argfile
./jdk22/bin/jlink --save-jlink-argfiles argfile --strip-debug --add-modules jdk.jlink --keep-packaged-modules ./image_a/jmods --output image_a
./image_a/bin/jlink --add-modules java.base --output image_b
It's not very apparent that image_b
will have -XX:+UseParallelGC
. So you need some sort of track record what you've used previously already in order to know how image_b
got produced (short of --save-opts
)?
I think this behavior should be documented.
Makes a lot of sense. Where? In the CSR?
Note that plugins like
--add-options
have been modified so that only the options passed at the current CLI will propagate to the final image. So in this case,image1
would have propertycom.foo.XYZ
set, but notimage2
. Incidentally, what you probably intended to use was `--add-options "-Dcom.foo.XYZ=true -Dcom.acme.name=BAR".Having said that, depending on the contents of
argfile
, those could be equivalent. In fact, they are with--unlock-run-image
, and an emptyargfile
.--unlock-run-image
avoids adding the marker file, which is the only difference when we extract the image.
--save-jlink-argfiles
brings a strange angle to this discussion, but it's conceivable to get a similarly different image, even with --keep-packaged-modules.
Right. This example intends to show that the behavior is not straight-forward for users to follow and also subject to the implementation of each of the plugins. I think we need an easy-to-understand model for developers to understand. Some possible options:
Option 1: all plugins applied in image1
are auto-applied to image2
by default
Option 2: all plugins applied in image1
are not applied to image2
by default
When there is an exception, it should be documented clearly by the plugin (possibly in the output from --list-plugins
). I also think option 1 may be more useful to the developers. I'm not sure how many plugins can undo the transformation done in image1
when creating image2
.
For example, with option 1,
$ jdk22/bin/jlink --add-modules jdk.compiler,jdk.jlink --output image1 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true"
$ image1/bin/jlink --add-modules jdk.jlink --output image2 --add-options "-Dcom.acme.name=BAR"
# equivalent to:
$ jdk22/bin/jlink --add-modules jdk.jlink --output image2 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true -Dcom.acme.name=BAR"
$ image1/bin/jlink --add-modules java.base --output image3 --vendor-bug.url https://com.acme/bugs
# equivalent to:
$ jdk22/bin/jlink --add-modules java.base --output image3 --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true" --vendor-bug.url https://com.acme/bugs
Discussion points are:
-
--save-jlink-argfiles
is only applicable whenjdk.jlink
is added to the custom module. I think this one is not an issue. -
--add-options
concatenates the options? -
-vendor-bug.url https://xyz.com/bugs --vendor-bug.url https://com.acme/bugs
last one wins?
We need to go through each plugin and decide on its behavior. I'm also pondering how the Plugin API should support this run-time image based linking.
Thanks.
How can the user know what plugins are applied to
image2
? i.e. what is the jlink command to produceimage2
if running fromjdk22
with the packaged modules present?
The only way to know is by knowing the chain of jlink
commands yielding up to the final image. Let jlink'
be the jlink using packaged modules. Currently this can be at most two. In addition, the contents of argfile
needs to be known. That doesn't seem to be very different to the status quo, though. See below.
Reading the changes, I'm not sure but I think it's not equivalent to:
jdk22/bin/jlink --add-modules jdk.jlink --output image2 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true" --add-options "-Dcom.acme.name=BAR"
Note that plugins like --add-options
have been modified so that only the options passed at the current CLI will propagate to the final image. So in this case, image1
would have property com.foo.XYZ
set, but not image2
. Incidentally, what you probably intended to use was `--add-options "-Dcom.foo.XYZ=true -Dcom.acme.name=BAR".
Having said that, depending on the contents of argfile
, those could be equivalent. In fact, they are with --unlock-run-image
, and an empty argfile
. --unlock-run-image
avoids adding the marker file, which is the only difference when we extract the image.
--save-jlink-argfiles
brings a strange angle to this discussion, but it's conceivable to get a similarly different image, even with --keep-packaged-modules. Consider:
echo '--add-options=-XX:+UseParallelGC' > argfile
./jdk22/bin/jlink --save-jlink-argfiles argfile --strip-debug --add-modules jdk.jlink --keep-packaged-modules ./image_a/jmods --output image_a
./image_a/bin/jlink --add-modules java.base --output image_b
It's not very apparent that image_b
will have -XX:+UseParallelGC
. So you need some sort of track record what you've used previously already in order to know how image_b
got produced (short of --save-opts
)?
I think this behavior should be documented.
Makes a lot of sense. Where? In the CSR?
Note that plugins like
--add-options
have been modified so that only the options passed at the current CLI will propagate to the final image. So in this case,image1
would have propertycom.foo.XYZ
set, but notimage2
. Incidentally, what you probably intended to use was--add-options "-Dcom.foo.XYZ=true -Dcom.acme.name=BAR". Having said that, depending on the contents of
argfile, those could be equivalent. In fact, they are with
--unlock-run-image, and an empty
argfile.
--unlock-run-imageavoids adding the marker file, which is the only difference when we extract the image.
--save-jlink-argfiles` brings a strange angle to this discussion, but it's conceivable to get a similarly different image, even with --keep-packaged-modules.Right. This example intends to show that the behavior is not straight-forward for users to follow and also subject to the implementation of each of the plugins. I think we need an easy-to-understand model for developers to understand. Some possible options:
Option 1: all plugins applied in
image1
are auto-applied toimage2
by default
Option 1 seems most appealing. I can massage --add-options
to concatenate. The exception of the rule seems --system-modules
for obvious reasons.
Option 2: all plugins applied in
image1
are not applied toimage2
by defaultWhen there is an exception, it should be documented clearly by the plugin (possibly in the output from
--list-plugins
). I also think option 1 may be more useful to the developers. I'm not sure how many plugins can undo the transformation done inimage1
when creatingimage2
.For example, with option 1,
$ jdk22/bin/jlink --add-modules jdk.compiler,jdk.jlink --output image1 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true" $ image1/bin/jlink --add-modules jdk.jlink --output image2 --add-options "-Dcom.acme.name=BAR" # equivalent to: $ jdk22/bin/jlink --add-modules jdk.jlink --output image2 --vendor-bug.url https://xyz.com/bugs --save-jlink-argfiles argfile --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true -Dcom.acme.name=BAR" $ image1/bin/jlink --add-modules java.base --output image3 --vendor-bug.url https://com.acme/bugs # equivalent to: $ jdk22/bin/jlink --add-modules java.base --output image3 --generate-jli-classes jli_trace.txt --strip-debug --add-options "-Dcom.foo.XYZ=true" --vendor-bug.url https://com.acme/bugs
Discussion points are:
* `--save-jlink-argfiles` is only applicable when `jdk.jlink` is added to the custom module. I think this one is not an issue.
For the runtime image link that's a requirement: jdk.jlink
part of the image that is being used to create the image to perform the runtime image link. But I agree that this use-case seems rather expert.
* `--add-options` concatenates the options?
Sure, that can be done.
* `-vendor-bug.url https://xyz.com/bugs --vendor-bug.url https://com.acme/bugs` last one wins?
Yes. That's also how it works if the build got configured with specific vendor and then a jlink is being performed using packaged modules.
We need to go through each plugin and decide on its behavior.
OK. I'll add documentation to the --list-plugins
help should there be deviations.
I'm also pondering how the Plugin API should support this run-time image based linking.
It could, but doesn't need to IMO. OTOH, transform()
could grow an argument to indicate packaged-modules vs runtime image link.
We need to go through each plugin and decide on its behavior.
OK. I'll add documentation to the
--list-plugins
help should there be deviations.
It'd be helpful if you write down your proposed behavior for each plugin for discussion. Otherwise, we will have to find out from the code.
I'm also pondering how the Plugin API should support this run-time image based linking.
It could, but doesn't need to IMO. OTOH,
transform()
could grow an argument to indicate packaged-modules vs runtime image link.
I'm not sure if it needs the extra argument. As the plugin knows what files it adds, it can always check its existence as the current implementation is doing. What I'm thinking if the plugin will need to indicate if it'd concatenate, retain or drop the transformation from image1
when creating `image2. This will get the plugin author to consider that when the plugin is developed.
Regarding the name of the runimage_resources
file, this file is only used by jlink and so it can be a resource file in jdk.jlink
module like jdk.jlink/jdk/tools/jlink/internal/runtime/$MODULE_resources
(e.g. jdk.jlink/jdk/tools/jlink/internal/runtime/java.base_resources
)
@mlchung Just as an FYI, I'm working on it. Thanks!
It'd be helpful if you write down your proposed behavior for each plugin for discussion. Otherwise, we will have to find out from the code.
Here it goes. This was a very useful exercise and uncovered some bugs. Fundamentally, each plugin's Category
is what I was looking at.
Unique categories (for pre-existing plug-ins):
- FILTER
- TRANSFORMER
- ADDER
- METADATA_ADDER
- SORTER
- PROCESSOR
- COMPRESSOR
SORTER, PROCESSOR and COMPRESSOR. Those plugins affect the final image only so
they work the same as when using packaged modules. There is nothing to leak
from a previous link as the output directory will contain a new JDK image. Note
that classes.jsa and classes_nocoops.jsa are not part of the jmod files, nor
are they classes or resources in the jimage. Thus, they are not tracked with
add-run-image-resources
and don't end up in the final JDK image when performing
a run-time based link.
ADDER, METADATA_ADDER plugins add files to the final jimage and/or JDK image ('release' file), which aren't there without those plugins. They get appropriately filtered at link time, so there isn't any oberservable difference. Exception: save-jlink-arg-file. The arg files are processed too early so they carry forward for the link, but not into the final image of that link. Therefore I've marked them as equivalent.
FILTER, TRANSFORMER. Those change class files or native resources or exclude specific resources. Since those changes are then in effect in the resulting runtime image, and the link is performed from such an image, they carry over to the derived image. Certain transformers can be overridden by the final run-time image based link (e.g. vendor-version plugin). Filters remove things from the image, so those changes carry forward. Exceptions: system-modules and generate-jli-classes. Both behave the same as in packaged-modules case.
For each individual plug-in the planned behaviour is in the "Current behaviour in run-time image link mode" column.
Plugin name | Description | Current behaviour in run-time | Notes
| | image link mode |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
add-options |Prepend the specified <options> string, which may | Same as with packaged-modules. | Category: ADDER
|include whitespace, before any other options when | /java.base/jdk/internal/vm/ |
|invoking the virtual machine in the resulting | options file is being filtered.|
|image. | | Adds a file in jimage:
| | | /java.base/jdk/internal/vm/options
-------------------------------------------------------------------------------------------------------------------------------------------------------------
compress |Compression to use in compressing resources: | Same as with packaged-modules | Category: COMPRESSOR
|Accepted values are: | |
|zip-[0-9], where zip-0 provides no compression, | | Combination of 'zip'
|and zip-9 provides the best compression. | | and 'compact-cp'
|Default is zip-6. | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
dedup-legal-notices |De-duplicate all legal notices. | Accumulate with base run-time |
|If error-if-not-same-content is specified then | image (if any). |Category: TRANSFORMER
|it will be an error if two files of the same | |
|filename are different. | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
exclude-files | | Accumulate with base run-time | Category: FILTER
| Specify files to exclude. | image (if any). |
| e.g.: **.java,glob:/java.base/lib/client/** | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
exclude-jmod-section | | Accumulate with base run-time | Category: FILTER
|Specify a JMOD section to exclude. | image (if any). |
|Where <section-name> is "man" or "headers". | | Excludes resources of type man/header
-------------------------------------------------------------------------------------------------------------------------------------------------------------
exclude-resources |Specify resources to exclude. | Accumulate with base run-time | Category: FILTER
|e.g.: **.jcov,glob:**/META-INF/** | image (if any). |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
generate-cds-archive |Generate CDS archive if the runtime image supports | Same as with packaged-modules | Category: PROCESSOR
|the CDS feature. | | Generates classes.jsa classes_nocoops.jsa
| | | in $JAVA_HOME
-------------------------------------------------------------------------------------------------------------------------------------------------------------
generate-jli-classes |Specify a file listing the java.lang.invoke | Same as with packaged-modules | Category: TRANSFORMER
|classes to pre-generate. By default, this plugin | |
|may use a builtin list of classes to pre-generate. | Potential existing BMH.Species |
|If this plugin runs on a different runtime version | classes are filtered to match | Generates BoundMethodHandle$Species*
|than the image being created then code generation | java.base jmod | classes.
|will be disabled by default to guarantee | |
|correctness add ignore-version=true | |
|to override this. | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
include-locales |BCP 47 language tags separated by a comma, | Accumulate with base run-time | Category: FILTER
|allowing | image (if any). |
|locale matching defined in RFC 4647. | | Filters specific locales in the target
|e.g.: en,ja,*-IN | | image.
-------------------------------------------------------------------------------------------------------------------------------------------------------------
order-resources |Order resources. | Same as with packaged-modules | Category: SORTER
|e.g.: **/module-info.class,@classlist, | |
|/java.base/java/lang/** | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
release-info |<file> option is to load release properties from | Same as with packaged-modules | Category: METAINFO_ADDER
|the supplied file. | |
|add: is to add properties to the release file. | | Adds a file 'release' to the final image
|Any number of <key>=<value> pairs can be passed. | | in the top folder. Since that file is not
|del: is to delete the list of keys in release file. | | in packaged modules nor in the jimage,
| | | it doesn't get carried forward
-------------------------------------------------------------------------------------------------------------------------------------------------------------
save-jlink-argfiles |Save the specified argument files that contain | If specified, options in an | Category: ADDER
|the arguments to be prepended to the execution of | existing file are replaced by |
|jlink in the output image. <filenames> is a | the new options. Otherwise, | Adds a file to the jdk.jlink module,
|: (; on Windows) separated list of command-line | options from the base image | /jdk.jlink/jdk/tools/jlink/internal/options
|argument files. | affect the run, but don't |
| | carry forward. |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
strip-debug |Strip debug information from the output image | Accumulate with base run-time | Category: TRANSFORMER
| | image (if any). | 'strip-java-debug-attributes' and
| | | 'strip-native-debug-symbols' combined
-------------------------------------------------------------------------------------------------------------------------------------------------------------
strip-java-debug-attributes |Strip Java debug attributes from | Accumulate with base run-time | Category: TRANSFORMER
|classes in the output image | image (if any). |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
strip-native-commands |Exclude native commands (such as java/java.exe) | Accumulate with base run-time | Category: FILTER
|from the image. | image (if any). |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
strip-native-debug-symbols |Strip debug symbols from native libraries (if any). | Accumulate with base run-time | Category: TRANSFORMER
|This plugin requires at least one option: | image (if any). |
| objcopy: The path to the objcopy binary. | |
| Defaults to objcopy in PATH. | |
| exclude-debuginfo-files: Exclude debug info | |
| files. Defaults to true. | |
| keep-debuginfo-files[=<ext>]: Keep debug info | |
| files in <file>.<ext>. | |
| Defaults to <file>.debuginfo | |
|Examples: --strip-native-debug-symbols | |
| keep-debuginfo-files:objcopy=OBJPATH | |
| --strip-native-debug-symbols | |
| exclude-debuginfo-files | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
system-modules |The batch size specifies the maximum number of | Same as with packaged modules. | Category: TRANSFORMER
|modules be handled in one method to workaround if | |
|the generated bytecode exceeds the method size | Generated classes get filtered | Changes SystemModulesMap.class and adds
|limit. The default batch size is 75. | so they don't populate. | SystemModules${<N>,all,default}.class
-------------------------------------------------------------------------------------------------------------------------------------------------------------
vendor-bug-url |Override the vendor bug URL baked into the build. | Accumulate with base run-time | Category: TRANSFORMER
|The value of the system property | image (if any). |
|"java.vendor.url.bug" will be <vendor-url-bug>. | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
vendor-version |Override the vendor version string baked into the | Accumulate with base run-time | Category: TRANSFORMER
|build,if any. The value of the system property | image (if any). |
|"java.vendor.version" will be <vendor-version>. | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
vendor-vm-bug-url |Override the vendor VM bug URL baked | Accumulate with base run-time | Category: TRANSFORMER
|into the build. The URL displayed in VM error | image (if any). |
|logs will be <vendor-vm-bug-url>. | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
vm |Select the HotSpot VM in the output image. | Accumulate with base run-time | Category: FILTER
|Default is all | image (if any). |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Regarding the name of the
runimage_resources
file, this file is only used by jlink and so it can be a resource file injdk.jlink
module likejdk.jlink/jdk/tools/jlink/internal/runtime/$MODULE_resources
(e.g.jdk.jlink/jdk/tools/jlink/internal/runtime/java.base_resources
)
That's a good observation. Thanks. I've modified the patch and CSR to mention that. Those resource files now live in /jdk.jlink/jdk/tools/jlink/internal/runlink_$MODULE_resources
.
The option --unlock-run-image
to get equivalence between a link using packaged modules and run-time based link is now hidden (similar to --keep-packaged-modules
). This is really advanced usage and doesn't need a prominent place in jlink --help
.
Other things of note:
jlink
orders plugin's by their Category
. So in general, the processing pipeline is: FILTER->ADDER->TRANSFORMER->MODULEINFO_TRANSFORMER->SORTER->METAINFO_ADDER->COMPRESSOR->VERIFIER->PROCESSOR->PACKAGER
. In this updated patch I've used this info in order to re-establish the invariant of certain classes and resources not being in packaged modules. Things like SystemModules$*.class
and BMH$Species_*.class
. Those are now being filtered (using exclude-resource
) prior it being processed by the TRANSFORMER
or ADDER
phases. Furthermore, it prevents those classes to populate if plug-ins that generated them aren't used. This also removes the need to actually modify any of the existing plugins.
More thoughts?