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

Java 9's javac has a new flag --release:

> javac --help
...

--release <release>
    Compile for a specific VM version. Supported targets: 6, 7, 8, 9

How is it different from -source and -target flags? Is it just a shortcut for -source X -target X?


Solution 1:

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.

Solution 2:

--release X is more than just a shortcut to -source X -target X because -source and -target are not sufficient to safely compile to an older release. You also need to set a -bootclasspath flag which must correspond to the older release (and this flag is often forgotten). So, in Java 9 they made a single --release flag which is a replacement for three flags: -source, -target and -bootclasspath.

So, this is an example of compiling to Java 1.7:

javac --release 7 <source files>

Note that you don't even need to have JDK 7 installed on your computer. JDK 9 already contains the needed information to prevent you from accidental linking to symbols that did not exist in JDK 7.

Another important difference is that you can only use public APIs when compiling with --release:

> javac --add-exports java.base/sun.invoke=ALL-UNNAMED --release 11 Main.java
error: exporting a package from system module java.base is not allowed with --release
1 error

If you want to use internal APIs, you have to remove the option --release:

> javac --add-exports java.base/sun.invoke=ALL-UNNAMED Main.java

or

> javac --add-exports java.base/sun.invoke=ALL-UNNAMED -source 11 -target 11  Main.java