Gradle: Add property for setting the `--release` compiler argument

Created on 19 Jul 2017  Â·  40Comments  Â·  Source: gradle/gradle

Instead of asking users to install and use the oldest Java version supported by the project, Java 9 can be used thanks to JEP 247. Summary:

A new command-line option, --release, is defined, which automatically configures the compiler to produce class files that will link against an implementation of the given platform version. --release N is roughly equivalent to:

for N < 9: -source N -target N -bootclasspath [documented-APIs-from-N],
for N >= 9: -source N -target N --system [documented-APIs-from-N].

http://openjdk.java.net/jeps/247

Expected Behavior

Similar to how one can currently set sourceCompatibility = 1.7, one should be able to set releaseCompatibility = 7 (or 1.7 maybe). If Java 9 is being used, then --release 7 (1.7 is not supported) would be passed to javac during compilation.

For older Java versions or tools that don't support --release, it probably makes sense to fallback to "--source 1.7 --target 1.7" and print a warning.

Note that IntelliJ already supports this:

"language level is automatically interpreted as -release option when compiling sources with javac 9 or newer"
https://youtrack.jetbrains.com/issue/IDEA-148501

And so does Maven:

https://issues.apache.org/jira/browse/MCOMPILER-270

Current Behavior

This feature is not currently supported. The easiest option at the moment is to use an older JDK. An alternative is to use -source -target and -bootclasspath, but then we need a mechanism to find the older JDK as well and write the code to set the bootclasspath. In some cases people use -source -target with a newer JDK and hope it will be OK (causing subtle issues at times).

Context

Apache Kafka still supports Java 7, so developers have to install this version even though it's no longer available from the Oracle website without a login.

@jvm feature contributor jvm-ecosystem modular-java

Most helpful comment

~Starting with 6.4 the --release flag can be set like this for all compile tasks in a project:~

(removed snippets that did not make it into 6.4 after all)

All 40 comments

@eljobe Do you have any thoughts on this?

@ijuma Thanks for filing this issue. Sorry it has taken so long for me to notice it and put together a response.

In the short-term, there is a pretty easy workaround to get the behavior you desire. On the projects you want to build with JDK 9 and target Java 7, add the --release and 7 arguments to the compilerArgs on the options of the compileJava task added by whichever java plugin you have applied to your project. One way to do that is described in the API documentation for compilerArgs

A more complete example would look like this:

compileJava {
  options.compilerArgs.addAll(['--release', '7')]
}

In the longer-term. Once we prioritize first-class Java 9 support on our overall roadmap, we will likely add some short-hand for the option you are suggesting above. But, the suggested workaround is already available.

I understand that it doesn't quite have the magic property of working equally well if the project is built with JDK 7, JDK 8, or JDK 9. But, it's what we can offer right now. If you want, you can wrap adding the compilerArgs in an if block that uses the JavaVersion api to confirm that you're running on Java 9.

I'll leave this issue open so that we can pick this work up when we come back to first-class Java 9 support.

Thanks for the reply and workaround @eljobe. We'll do that in the meantime.

Excellent. And, obviously, do let us know if for some reason that workaround doesn't fix your problem.

Just to confirm that the workaround worked as expected with Gradle 4.1-rc2.

I'll leave this issue open so that we can pick this work up when we come back to first-class Java 9 support.

Gradle could also support this property, but emitting a warning when the compiling JDK is below Java 9.

Changing the title here, as users can already pass this argument, there just is no statically typed method for it at the moment.

@oehme sorry, but for me, the workaround doesn't work:

> error: option -source cannot be used together with --release

You'll also have to set source&target compatibility to null.

this would be nice with jdk 8 itself EOL, but still having reasons to target the JRE 8

I'll leave this issue open so that we can pick this work up when we come back to first-class Java 9 support.

@eljobe are there any updates on this issue?

We should move towards using --release (instead of -source and -target) as a default for Java9+.

We anyway want to introduce new properties using the lazy property API on the java { } extension. We should consider introducing a release property that eventually replaces sourceCompatibility and targetCompatibility.

Regarding the workaround mentioned above, my project is written entirely in Kotlin (with some Groovy Spock tests), and the following does not work

java {
// Kotlin DSL won't let me set these to null, so I'm trying to just _not_ set them.
//  sourceCompatibility = JavaVersion.VERSION_1_8
//  targetCompatibility = JavaVersion.VERSION_1_8
}

tasks.withType<JavaCompile>().configureEach {
  options.compilerArgs.addAll(listOf("--release", "8"))
}

tasks.withType<KotlinCompile>().configureEach {
  kotlinOptions {
    jvmTarget = "1.8"
  }
}

when I say "does not work", I am referring to the fact that, I can't seem to do the following:

  1. Build my gradle plugin with JDK 14 and
  2. Run test kit tests with JDK 8

Build scan: https://scans.gradle.com/s/sywi65pxfu6xy
Conversation on gradle community slack: https://gradle-community.slack.com/archives/CA745PZHN/p1585105976001400

@autonomousapps Sorry it was never clarified on this issue it seems, but this:

You'll also have to set source&target compatibility to null.

Should not be necessary (since 3.0). If --release is set, the options are ignored. Not sure why this was a problem for some reporters here.

So what you are doing should work. What's the issue with setting the java executable for your functional test task? E.g.:

val functionalTest by tasks.creating<Test> {
    ...
    executable = "/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/bin/java"
}

I just tried that it seems to work fine.

~Starting with 6.4 the --release flag can be set like this for all compile tasks in a project:~

(removed snippets that did not make it into 6.4 after all)

After more internal discussion about the topic, we did not add a configuration option to the java {} extension as I wrote in the previous comment. We have however started to work on the topic of Java toolchains (#2203) which will bring more conveniences for everything discuss here, including ways to set the release flag (or rather have Gradle set it automatically for you).

For now, you can continue to configure release like directly as compiler option and then you do not need to set source/target compatibility:

E.g.:

```
tasks.withType {
options.compilerArgs.addAll(['--release', '11')]
}

@jjohannes Is the work around the JavaToolChain something that will extend into enabling users to use the compiler infrastructure or its more about internal work inside Gradle? I'm about to start a big stream of work around compilers and toolchain for native support and some overlap with how Gradle handles both. I want to know if it's something user can expect to use and extend or not. Cheers!

It's worth noting that the Scala compiler also supports --release now.

@lacasseio the work around Java tool chains is to provide public API/DSL constructs to declare (or let Gradle automatically find) Java installations that are then used by multiple-tasks (like compile and test). So that you can simply to say "compile and run my tests for Java 14" even if Gradle itself runs on Java 11.

While the focus for now is on the Java ecosystem, it would make sense to align this with a more general "Tool Chain" concept that also works for Native. Or at least keep those use cases in mind in the design of the feature. Work is tracked in #2203 and @bmuskalla is leading the effort. Feel free to reach out on the issue or in the community slack.

@jjohannes: I've found that setting just the "--release" flag can be dangerous as Gradle will publish the Gradle module metadata with the wrong JVM version (I guess this is filled via the "target" property?!). So relying only on the "--release" is not a good idea.

@jjohannes @bmuskalla IIUC, the work on Java tool chains can take quite some time. Have you thought about introducing this support for -release flag in the meantime? You have ready feature that will solve actual problems of some people. Is it reasonable to withhold it until you have more complete functionality?

@iNikem Yes, in my eyes, the --release support is something that will make the toolchain support easier. That being said, we're just talking about how exactly we want the toolchains to look like and want to ensure we have a better understanding of how we want the --release support to tie into that work. Once known, I don't think we need to build everything together. Supporting the release flag can just be one of the first things we introduce with the rest of the toolchain work building on top of that.

We would be interested in first class support for --release to ensure we can still use the build cache from using Java 11 to Java 14 --release 11 which in theory are the same thing. Currently gradle invalidates the cache between the 2.

Work has resumed on this topic.

A release property will be added to JavaCompile which:

  • takes precedence over source and target compatibility.
  • Is used for setting the target JVM version in published variants.

Behavior when the JVM running the compilation does not support the flag still needs to be determined.
Caching impact still needs to be studied as well.

Will this be added to ScalaCompile too?

Yes, still debating whether or not I need a different issue to track it 😉

Awesome. :)

Behavior when the JVM running the compilation does not support the flag still needs to be determined.

Since Java 8 is still widely used and it does not support --release, it's pretty important to have the option to fallback automatically to --source and --target if Java 8 is being used to compile.

Are there any impacts on Kotlin compilation, here?

On Thu, Jun 4, 2020, 7:14 AM Ismael Juma notifications@github.com wrote:

Behavior when the JVM running the compilation does not support the flag
still needs to be determined.

Since Java 8 is still widely used and it does not support --release, it's
pretty important to have the option to fallback automatically to --source
and --target if Java 8 is being used to compile.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/gradle/gradle/issues/2510#issuecomment-638876072, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/ABJG5PKGM2A6BKYJYUSSCZLRU6T2XANCNFSM4DTSBLOQ
.

@ijuma Indeed, however Gradle does not always know that. But when it knows, the plan would be to set source and target instead with a warning saying it will not be equivalent because of the bootclasspath.

@autonomousapps It should not since Kotlin compilation uses its own task. Unless you think about something in particular?

To be clear, there is no longer a plan to have the flag on the java extension. Only at the task level. What will happen at the Java extension level is toolchains support.

compileJava {
  options.compilerArgs.addAll(['--release', '7')]
}

should be (['--release', '7']) (fixed order of ] and ))

Support for release will be added in Gradle 6.6. It is currently limited to Java compilation.

Additional support: see #13762 for joint compilation and Scala support, see #12898 for Javadoc support.

If you believe support should be added elsewhere as well, please file an issue referencing this one.

Thanks @ljacomet. What happens if the newly added release support is used when compiling with Java 8?

It will fail as this flag is not supported for Java versions below 9.
However it is no longer necessary to tweak sourceCompatibility and targetCompatibility, the new option takes precedence.

There is currently no plan to support interpreting this flag as source + target instead.

Especially as we are looking into decoupling the JVM running Gradle from the JVM used in Java tasks at a higher level. See work done in #2200, #2203 and #13692

Good to know, thanks. It's worth mentioning this in the documentation. Most libraries still support Java 8, so conditional logic will be required (similar to the current state).

@ijuma But "supporting java 8" does not mean "building on java 8". The point of this --release flag is to allow to run your build on any recent version of java and still produce artifact usable on java 8 runtime.

@iNikem It does in many cases. Apache Kafka, for example, builds and tests with all supported Java versions. For us, the point of this flag is to free developers to use any Java version. But CI builds and tests with every Java version to make sure it works with every supported Java version. Bugs and intended changes of behavior do exist, so one must verify.

You got me confused. I totally understand the need to test on different java versions. But why would you want to _build_ on different versions?

Because results of building with newer versions of Java are not necessarily compatible with older versions at runtime, regardless setting source- and target compatibility.

https://github.com/apache/felix/pull/114#issue-133299256
https://github.com/lmdbjava/lmdbjava/issues/116#issuecomment-458335241
https://github.com/zeromq/jeromq/pull/692#issuecomment-472184941

@ijuma I believe that the upcoming support for specifying the JDK to use is going to improve the situation for the scenario you describe.

@ams-tschoening All the issues you listed are supposed to be resolved by the release flag since it is equivalent to source + target + setting the bootclasspath.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bmuschko picture bmuschko  Â·  45Comments

bmuschko picture bmuschko  Â·  44Comments

big-guy picture big-guy  Â·  44Comments

johnzielke picture johnzielke  Â·  52Comments

sschuberth picture sschuberth  Â·  84Comments