134

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?

Andrew Li
  • 55,805
  • 14
  • 125
  • 143
ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155

2 Answers2

157

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.

Community
  • 1
  • 1
Andrew Li
  • 55,805
  • 14
  • 125
  • 143
  • 1
    one doubt, would be possible to use features from jdk 9-11 in code and it still run on java runtime 8 ? – Cristiano Dec 28 '18 at 12:36
  • 2
    No, they would not be present in the jre 8 binary – Rogue Feb 26 '19 at 02:13
  • 1
    What do you men by "Platform API"? Is something at the byte-code level? or something related to the underlying x86 platform or OS APIs? – Jose Cifuentes Dec 28 '19 at 19:18
  • 3
    @JoseCifuentes, "Platform API" here is given JDK API its version, without the `--release` flag, would be inferred from JDK used for compiling which generally differs from the JDK you target using `-source` and `-target`. This can bite you in case you happen to use classes/methods introduced in never JDK then the one you target. This is extra subtle in case the compiler chooses a method _overload_ that was added in later version over the one from previous you intended thus silently breaking binary compatibility. – Oliver Gondža Mar 19 '20 at 08:33
53

--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
ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155