What Is the --Release Flag in the Java 9 Compiler

What is the --release flag in the Java 9 compiler?

Not exactly.

JEP 247: Compile for Older Platform Versions defines this new command-line option, --release:

We defined a new command-line option, --release, which automatically configures the compiler to produce class files that will link against an implementation of the given platform version. For the platforms predefined in javac, --release N is equivalent to -source N -target N -bootclasspath <bootclasspath-from-N>. (emphasis mine)

So no, it is not equivalent to -source N -target N. The reason for this addition is stated in the "Motivation" section:

javac provides two command line options, -source and -target, which can be used to select the version of the Java language accepted by the compiler and the version of the class files it produces, respectively. By default, however, javac compiles against the most-recent version of the platform APIs. The compiled program can therefore accidentally use APIs only available in the current version of the platform. Such programs cannot run on older versions of the platform, regardless of the values passed to the -source and -target. options. This is a long-term usability pain point, since users expect that by using these options they'll get class files that can run on the specified platform version.

In short, specifying the source and target options are not sufficient for cross-compilation. Because javac, by default, compiles against the most recent of the platform APIs, they can't be guaranteed to run on older versions. You also need to specify the -bootclasspath option corresponding to the older version to cross-compile correctly. This would include the correct API version to compile against and allow for execution on older version. Since it was very often forgotten, it was decided to add one command line option which did all the necessary things to correctly cross-compile.

Further reading in the mailing list and Oracle Docs. The original bug was filed here. Note that since the integration of this option, JDK builds have come bundled with descriptions of the platform APIs of older releases, mentioned under section "Risks and Assumptions". That means you don't need the older version installed on your machine for cross-compilation to work.

Java 11: What is the difference between javac --release and -source and -target command line parameters?

I have found the answer in the Java 19 SDK source code for the java compiler:

/**
* Handles the {@code --release} option.
*
* @param additionalOptions a predicate to handle additional options implied by the
* {@code --release} option. The predicate should return true if all the additional
* options were processed successfully.
* @return true if successful, false otherwise
*/
public boolean handleReleaseOptions(Predicate<Iterable<String>> additionalOptions) {
String platformString = options.get(Option.RELEASE);

checkOptionAllowed(platformString == null,
option -> reportDiag(Errors.ReleaseBootclasspathConflict(option)),
Option.BOOT_CLASS_PATH, Option.XBOOTCLASSPATH, Option.XBOOTCLASSPATH_APPEND,
Option.XBOOTCLASSPATH_PREPEND,
Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS,
Option.EXTDIRS, Option.DJAVA_EXT_DIRS,
Option.SOURCE, Option.TARGET,
Option.SYSTEM, Option.UPGRADE_MODULE_PATH);

if (platformString != null) {
PlatformDescription platformDescription =
PlatformUtils.lookupPlatformDescription(platformString);

if (platformDescription == null) {
reportDiag(Errors.UnsupportedReleaseVersion(platformString));
return false;
}

options.put(Option.SOURCE, platformDescription.getSourceVersion());
options.put(Option.TARGET, platformDescription.getTargetVersion());

context.put(PlatformDescription.class, platformDescription);

if (!additionalOptions.test(platformDescription.getAdditionalOptions()))
return false;

JavaFileManager platformFM = platformDescription.getFileManager();
DelegatingJavaFileManager.installReleaseFileManager(context,
platformFM,
getFileManager());
}

return true;
}

As the code shows, the --release option will set both source and target to the same value.

In fact, there is a check that forbids using the --release parameter if source or target have already been set.

void checkOptionAllowed(boolean allowed, ErrorReporter r, Option... opts) {
if (!allowed) {
Stream.of(opts)
.filter(options :: isSet)
.forEach(r :: report);
}
}

What is release element on configuration for the maven-compiler-plugin in a JavaFX app

tl;dr

In your POM, replace the two tags source & target as seen here:

    <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--The following `source` and `target` tags are now replaced by `release` seen further down below.-->
<!--<maven.compiler.source>14</maven.compiler.source>-->
<!--<maven.compiler.target>14</maven.compiler.target>-->
</properties>

…with the new release tag, placed further down in POM:

<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>


<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!--Enable Java language preview features. Specify Java version.-->
<!--This `release` tag replaced the pair of `source` and `target` tags seen commented-out near top of this POM.-->
<release>14</release>
</configuration>
</plugin>

…to tell the Java compiler the version of Java on which you intend to deploy. This tag passes a release flag to the Java compiler. The compiler errors out any of your code trying to use an API added to later versions of Java.

See another Question: What is the --release flag in the Java 9 compiler?

Details

Both Answers by Naman and by ernest_k are correct and important. But I need to write this Answer to combine them and show the direct solution.

Problem

The issue is that JEP 247: Compile for Older Platform Versions added a feature in Java 9 for a new compiler flag -release to replace the combination of older -source, -target, and -bootclasspath flags. This plugs a hole that plagued programmers trying to work on the latest compiler while writing code limited to making API calls of an earlier version of Java.

For example, I may be writing on my Mac using Java 12 yet deploying to a server running Java 8. I want the compiler to stop me from accidentally using features that arrived in later versions of Java. Otherwise, my app will succeed at compile-time yet fail at run-time when those features are unavailable on the older JVM.

To quote the JEP:

Summary

Enhance javac so that it can compile Java programs to run on selected older versions of the platform.

Motivation

javac provides two command line options, -source and -target, which can be used to select the version of the Java language accepted by the compiler and the version of the class files it produces, respectively. By default, however, javac compiles against the most-recent version of the platform APIs. The compiled program can therefore accidentally use APIs only available in the current version of the platform. Such programs cannot run on older versions of the platform, regardless of the values passed to the -source and -target options. This is a long-term usability pain point, since users expect that by using these options they'll get class files that can run on the the platform version specified by -target.

Solution

In a Maven-driven project, we pass those flags to the Java compiler by setting tags on our Maven POM file.

In your Maven POM file’s tag hierarchy of:

<project> …
<build> …
<pluginManagement> …
<plugins> …
<plugin>
<artifactId>maven-compiler-plugin</artifactId>

…nest the following tag hierarchy, within which we specify our desired deployment version of Java.

<configuration> 
<release>14</release>

By the way, if using a version of Java offering "preview" features, we can nest a further tag and value if we wish to enable those preview features.

<compilerArgs>--enable-preview</compilerArgs>

The old-school settings replaced by the new release tag were a pair of tags, source and target. These two could be set in Maven to be passed along to the Java compiler. So if you add the release tag seen above, check to see if your POM has this pair of tags. If found, delete them.

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>14</maven.compiler.source> Delete if using `release` tag.
<maven.compiler.target>14</maven.compiler.target> Delete if using `release` tag.
</properties>

Example POM file

Here is a complete example POM file for Maven 3.6.3, for a basic Java project.

We are using all the latest versions of various plugins and dependencies. This example uses Java 15 with preview features such as Records enabled.

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>work.basil.demo</groupId>
<artifactId>Demo5</artifactId>
<version>1.0-SNAPSHOT</version>

<name>Demo5</name>
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--The following `source` and `target` tags are now replaced by `release` seen further down below.-->
<!--<maven.compiler.source>15</maven.compiler.source>-->
<!--<maven.compiler.target>15</maven.compiler.target>-->
</properties>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0-M1</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>

<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!--Enable Java language preview features. Specify Java version.-->
<!--This `release` tag replaced the pair of `source` and `target` tags seen commented-out near top of this POM.-->
<release>15</release>
<compilerArgs>
--enable-preview
</compilerArgs>
</configuration>
</plugin>

<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>3.0.0-M1</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.0.0-M1</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.8.2</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

JavaFX

You mentioned JavaFX in your Question. Note that this discussion applies to any and all Maven-driven Java projects. This includes JavaFX projects as well as Jakarta Servlets, console apps, and so on. Nothing here is specific to JavaFX.

What is the “Use '--release' option” in IntelliJ 2018.1 preferences?

The help section can be found here:

By default, this option is selected. IntelliJ IDEA deduces from
project settings when the cross-compilation is needed and
automatically applies the --release compiler option for Java 9.

What is the --release option? It is a new command-line option, defined in JEP 247: Compile for Older Platform Versions.

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.

Discussed in this good answer here.

Also check IDEA-184333 for the background why this option was added in the IDE. In short, some users need to specify only -source and -target versions for their projects without also setting the -bootclasspath option.

Error:java: invalid flag: -release

Java 9 support is cutting edge and should be tried with the most current version. At the moment, this is the public preview of 2016.3, available here.

java9 '-release 8' with internal packages (e.g. sun.misc.Unsafe)

Firstly, as stated in the comments, the way I built the multi-release jar (following the link I provided) was not recommended. Using the example given by nullpointer,

I would prefer to use something like https://github.com/meterware/multirelease-parent/blob/master/pom.xml

I used Maven toolchains, which enable to compile using different JDKs. This way, my problem disappeared.

Secondly, the answer to why I get a compilation error when compiling in jdk11 with --release 8 was actually given by khmarbaise who pointed to https://stackoverflow.com/a/43103038/296328:

javac provides two command line options, -source and -target, which can be used to select the version of the Java language accepted by the compiler and the version of the class files it produces, respectively. By default, however, javac compiles against the most-recent version of the platform APIs. The compiled program can therefore accidentally use APIs only available in the current version of the platform. Such programs cannot run on older versions of the platform, regardless of the values passed to the -source and `-target. options. This is a long-term usability pain point, since users expect that by using these options they'll get class files that can run on the specified platform version.

Nevertheless, it seems there are workarounds: see Maven: javac: source release 1.6 requires target release 1.6 for example.



Related Topics



Leave a reply



Submit