Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to specify minor, hotfix and build number for java toolchain #16628

Open
mfussenegger opened this issue Mar 24, 2021 · 22 comments
Open

Allow to specify minor, hotfix and build number for java toolchain #16628

mfussenegger opened this issue Mar 24, 2021 · 22 comments
Labels
a:feature A new functionality in:toolchains Java Toolchains

Comments

@mfussenegger
Copy link

mfussenegger commented Mar 24, 2021

Expected Behavior

Be able to specify the full java version for the toolchain, for example like this:

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(16, 0, 0, 36)
        vendor = JvmVendorSpec.ADOPTOPENJDK
    }
}

or:

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of('16+36')
        vendor = JvmVendorSpec.ADOPTOPENJDK
    }
}

Current Behavior

Currently only the major version can be specified.

Context

For CrateDB we currently have a custom JDK
download plugin. We use the JDK that it downloads in the compile/test tasks,
but also in the release task where we bundle the JDK into the distribution.

It would be great if we could replace the custom plugin with the toolchain feature, but we would
need to be able to have deterministic builds. We want to be able to re-create a
release artifacts with the exact same JDK version.

@stale
Copy link

stale bot commented Apr 17, 2022

This issue has been automatically marked as stale because it has not had recent activity. Given the limited bandwidth of the team, it will be automatically closed if no further activity occurs. If you're interested in how we try to keep the backlog in a healthy state, please read our blog post on how we refine our backlog. If you feel this is something you could contribute, please have a look at our Contributor Guide. Thank you for your contribution.

@stale stale bot added the stale label Apr 17, 2022
@alkum
Copy link

alkum commented Apr 21, 2022

Not stale.

See the latest Critical Patch Update (CPU) from Oracle, which fixes critical security flaws1 affecting all major JDK versions2.

These fixes are delivered with the latest minor release versions (11.0.15, 17.0.3 and 18.0.1).

With that in mind, it would be very useful if the toolchain can also specify a minor version or patch level.

Footnotes

  1. https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/

  2. Affects Java versions 7u331, 8u321, 11.0.14, 17.0.2 and18 as per https://www.oracle.com/security-alerts/cpuapr2022.html#AppendixJAVA

@stale stale bot removed the stale label Apr 21, 2022
@DER-SSt
Copy link

DER-SSt commented Jul 11, 2022

Furthermore, the minor version of the java toolchain can break the gradle cache [source]:

Gradle tracks only the major version of Java as an input for compilation and test execution. Currently, it does not track the vendor nor the minor version. Still, the vendor and the minor version may influence the bytecode produced by compilation.
[...] you may have cache misses for developers due to some locally compiled class files which constitute an input to test execution.

Explicitly specifying the minor version can help to increase cache-hits.

@little-fish
Copy link

little-fish commented Dec 13, 2022

Do we have ETA for it?
To add support to existing API (https://api.adoptium.net/q/swagger-ui/#/) this feature should you release_name from /v3/binary/version/... endpoint.

@guillerodriguez
Copy link

We have a project where we want to build an executable using jlink. For this we need to be able to specify the exact toolchain version to use as we depend on bugfixes that are only implemented starting from a certain minor version.

@arlaneenalra
Copy link

Could this be solved by adding a new string property that separates the language version from the JDK version? A lot of the issues I've run into represent changes to the JDK itself and not necessarily the Java language. It makes sense to me to see that as a separate value...

@breskeby
Copy link
Contributor

This one hits us hard as we migrated away from our custom jdk download plugin towards toolchains. I was under the impression that a CustomJdkResolver would redownload a jdk if the underlaying URL for that jdk changes, implicating a different minor version.

@breskeby
Copy link
Contributor

With openjdk 21 update just been released, I realized there Is no way to reliable ensure our gradle toolchains would use the latest release. IMO when using custom ToolChainResolver implemenations the sdks avalable as toolchains should be linked to the url they've been downloaded from. So when the url of the custom resolver changes (e.g. bug fix version changed) the change in the URL would enforce gradle to reevaluate (download) the jdk

@guillerodriguez
Copy link

the change in the URL would enforce gradle to reevaluate (download) the jdk

I don't think this is a good idea. Builds would be non-reproducible. Things that worked yesterday may suddenly break with no apparent reason, simply because a JDK update was just released. I would very much vote against this or at least make it strictly opt-in.

In any case this seems to be a separate feature request, not really related to the topic of this issue (the ability to specify minor, hotfix, build number explicitly).

@ihormkl
Copy link

ihormkl commented Feb 20, 2024

Any update on this?

@jixxed
Copy link

jixxed commented Feb 28, 2024

Currently running into an issue with Temurin 21.0.2 that I didn't have with 21.0.1. Without an easy way to specify the minor version it becomes cumbersome to release new versions of my app as I can't rely on the current build pipeline. Locking in the specific version is highly desired!

@jjohannes
Copy link
Contributor

Did anyone find a reliable/acceptable workaround for this?

I have a project that contains module-info.java files. When compiled, the module-info.class files contain the version number of the Java core modules that are required. This version is the major.minor.patch version of the JDK.

To get reproducible builds, we had to add the full major.minor.patch JDK version as additional input to the JavaCompile tasks.

Now this JDK should be used on all machines running the build. Otherwise, we won't get remote build cache hits for the compilation. Right now, the only solid solution I see is to not use any auto-provisioning and instead add a custom check that fails the build if Gradle is not started with the major.minor.patch JDK we expect. Then the user has to manually get Gradle to start with that JDK. And then we do not define anything in java.toolchain { } and just let Gradle use the JDK it was started with. (Which is like not using the toolchain mechanism at all. 😞)

@gredler
Copy link

gredler commented Apr 15, 2024

To get reproducible builds, we had to add the full major.minor.patch JDK version as additional input to the JavaCompile tasks.

If this is relevant for reproducible builds, you may want to add a comment to #28806

@guillerodriguez
Copy link

Did anyone find a reliable/acceptable workaround for this?

In my case my workaround was to avoid Gradle's native toolchain mechanism and use JReleaser instead.

@renatoathaydes
Copy link

renatoathaydes commented Apr 18, 2024

the change in the URL would enforce gradle to reevaluate (download) the jdk

I don't think this is a good idea. Builds would be non-reproducible.

The builds would only be non-reproducible if the toolchain resolver plugin produced a different URL without any build settings having changed. However, that's not what people are askin here.

What we want is a way to upgrade JDK minor versions by being able to specify that explicitly. When you change the minor version, the toolchain resolver would produce a different URL and Gradle would download and use the updated JDK.

This is the way to solve this problem. The current implementation is unacceptable: it freezes the JDK you're using to build at first build run, because it caches it and there's nothing you can do to update it other than:

  • going into the Gradle cache directory and deleting it yourself to force a new download (we do that currently, but it's not sustainable).
  • changing the major Java version, which is not what you're trying to do as there's no major version to change.

The current behaviour prevents:

  • testing that JDK fixes in minor versions fixed problems you may be facing
  • building up-to-date jlink based applications as jlink will run with the old, cached JDK version.
  • actually making builds reproducible, because which version of the JDK you get does NOT depend on your build definition.

@guillerodriguez
Copy link

The builds would only be non-reproducible if the toolchain resolver plugin produced a different URL without any build settings having changed. However, that's not what people are askin here.

The comment I was replying to was specifically asking for a way to "reliably ensuring gradle toolchains would use the latest release". This means that when a new patch release is made, that would be used automatically. That is what I was objecting to.

What we want is a way to upgrade JDK minor versions by being able to specify that explicitly.

Yes, that is what I said as well. We want to be able to explicitly request a specific JDK version. We do NOT want that the resolved JDK version changes automatically when new patch releases are made.

@sdavids
Copy link
Contributor

sdavids commented Apr 18, 2024

A)

gradle toolchains would use the latest release

B)

We want to be able to explicitly request a specific JDK version

Both would be useful for different purposes.

My personal opinion:

If you request a major version or a major/minor version you get A … automatic updates; in case of major/minor only patch updates. Similar to npm‘s x-Ranges.

if you specify major/minor/patch you get B … you pin that version, i.e. no automatic updates.


If there is only development capacity for one option I would prefer B.

@renatoathaydes
Copy link

The comment I was replying to was specifically asking for a way to "reliably ensuring gradle toolchains would use the latest release".

@guillerodriguez I understand. But notice that Gradle normally allows the user to decide whether they want automatic updates or not. For example, you can declare your dependencies with version ranges and Gradle will update them automatically.

While I agree this is not a good default, I don't see why Gradle should prevent that from being possible.

About build reproducibility: the current JDK toolchain implementation in Gradle does NOT make builds reproducible, because the exact JDK version is not declared anywhere, so which version you get depends only on what the toolchain resolver plugin you chose to use happens to download the first time you run your build. That JDK then gets used by Gradle seemingly "forever" for the Java major version and vendor you're using. Clearly, if you just delete the globally cached Gradle JDKs and run the build again, you may get a newer JDK version (which is what we are currently having to do, in fact).

On your CI, you may get a newer JDK version every time your run a build if you don't have global caches!

So, in my view, there are two possible solutions to fix this:

  1. allow the toolchain resolver to determine which JDK is used for every build.
  2. make the minor version a build input, like the major version and vendor (there are other possible inputs as well, e.g. Azul's CRaC or SA versions).

I believe that @breskeby was asking for solution 1 (as if the exact JDK inputs are determined by the plugin's own config, then Gradle can only know which JDK to use when it resolves the JDK download URL), which I also agree with, though solution 2 could be enough for most people.

Solution 1 has a big advantage: it lets the exact JDK version to be used be determined by custom config of the toolchain resolver plugin you're using, and that config could include anything the plugin can support, not just what Gradle predicted would be useful.

The plugin could, for example, be configured to auto-update the JDK when it detects a new version, or to "lock" the version until you change it explicitly, making the build more reproducible.

It could also let you specify more vendor-specific parameters (like CRaC I mentioned above, but also whether you need JavaFX and probably many more).

I believe Gradle's intention with NOT adding a whole lot of input parameters to the toolchain config means they must be thinking of using solution 1... but seemingly, they haven't yet completed the implementation!?

@guillerodriguez
Copy link

guillerodriguez commented Apr 19, 2024

The comment I was replying to was specifically asking for a way to "reliably ensuring gradle toolchains would use the latest release".

@guillerodriguez I understand. But notice that Gradle normally allows the user to decide whether they want automatic updates or not. For example, you can declare your dependencies with version ranges and Gradle will update them automatically.

While I agree this is not a good default, I don't see why Gradle should prevent that from being possible.

Nobody is talking about dependencies here, and nobody is saying that the current behaviour should be "prevented from being possible". Let's stick to the topic being discussed.

So, in my view, there are two possible solutions to fix this:

  1. allow the toolchain resolver to determine which JDK is used for every build.
  2. make the minor version a build input, like the major version and vendor (there are other possible inputs as well, e.g. Azul's CRaC or SA versions).

I believe that @breskeby was asking for solution 1 (as if the exact JDK inputs are determined by the plugin's own config, then Gradle can only know which JDK to use when it resolves the JDK download URL), which I also agree with, though solution 2 could be enough for most people.

I don't believe these two are "solutions" to the same problem.

This whole issue is about what you call "solution 2", as the title and original issue description say: having a way to specify the exact JDK version that will be used. This is needed in a number of different scenarios and for different reasons, some of which are described in the comments.

@breskeby then suggested what you call "solution 1". In my opinion this is not a "solution" to the problem described by this issue -- it is actually a "solution" to a different problem. I pointed out that in my opinion this would make the current situation (relative to the problem that this issue is about) worse.

If we have a way to specify an exact JDK version, then the question of "what is the default behaviour when an exact version is not specified" is less critical.

@jjohannes
Copy link
Contributor

I think this could be improved with a relative minimal (backward compatible) extension of JavaToolchainSpec.

  • Do not touch the existing Property<JavaLanguageVersion> getLanguageVersion() property
  • Instead, add an additional Property<String> getImplementationVersion() (which is optional)
  • And, extend the toolchain matching to also match the implementation version if present. The implementation version is what you get from System.getProperty("java.version"). Which is, if I read the code correctly, information that is already collected for each toolchain.

I would deliberately model this as String. Then you may do something custom, for example if you build a JDK yourself inside your company. Nothing more needs to be added to Gradle core. For auto-provisioning, you can then write your own plugin that respects the implementation version.

@breskeby
Copy link
Contributor

breskeby commented May 7, 2024

@jjohannes that sounds like a quite pragmatic solution to get this issue sorted. Highly in favour for this. An not super ideal solution is better than no solution IMO and how its handled now makes toolchains sort of useless for us

@renatoathaydes
Copy link

The solution proposed by @jjohannes would solve our problem as long as Gradle does notice when the implementationVersion is modified, and requests the resolver plugin to re-download the JDK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:feature A new functionality in:toolchains Java Toolchains
Projects
None yet
Development

No branches or pull requests