JDK-8058150 : JEP 247: Compile for Older Platform Versions
  • Type: JEP
  • Component: tools
  • Sub-Component: javac
  • Priority: P3
  • Status: Closed
  • Resolution: Delivered
  • Fix Versions: 9
  • Submitted: 2014-09-10
  • Updated: 2020-12-21
  • Resolved: 2017-05-16
Related Reports
Blocks :  
Blocks :  
Blocks :  
Relates :  
Relates :  
Relates :  
Description
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.


Description
-----------

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>`.

For N < 9, the documented APIs consist of the public APIs that were on javac's
default bootclasspath for JDK N. 

For N >= 9, the documented APIs consist of (i) the APIs exported from those modules 
in the JDK image which are part of the documentation of JDK N; and 
(ii) the API exported from the jdk.unsupported module (documented in JEP 260). 
That is, the documented APIs are primarily the APIs exported by the *intersection* 
of the modules in the JDK image and the modules documented for JDK N. 
No other modules in the JDK image are observable. If --limit-modules is used, 
then it can only further limit the observable modules, not observe additional modules. 
Access to internals of the modules in the JDK image is not allowed.

The `--release N` option is incompatible with other options that affect the set of
platform or system classes. This includes:

  * for N < 9: `-bootclasspath`, `-Xbootclasspath`, 
`-Xbootclasspath/a:`, `-Xbootclasspath/p:`, `-endorseddirs`, `-Djava.endorsed.dirs`,
`-extdirs`, `-Djava.ext.dirs` options that set platform classes.
  * for N >= 9: the `--system` and `--upgrade-module-path` options 
that set system modules (that is, modules in the JDK image), 
and the `--add-exports`, `--add-reads`, and `--patch-module` options if they modify
system modules. (Use of `--add-exports`, `--add-reads`, and `--patch-module`
is allowed for non-system modules, that is, modules that are not part of the JDK image.)
  * for any N, the `-source` and `-target` options, as those are automatically set to N.

It is assumed that documented APIs will change only in major releases.
For the legacy case where JAX-WS was updated from 2.0 to 2.1 in a minor release of JDK 6,
JAX-WS 2.1 is considered the documented API.

As a limitation of the `--release` implementation, the Unicode version of
the given target platform is not used during compilation; the Unicode
version of the current platform is used instead.

### Implementation

For JDK N and `--release M`, M < N, signature data of the documented APIs of
release M of the platform is needed.  This data is stored in the
`$JDK_ROOT/lib/ct.sym` file, which is similar, but not the same, as the
file of the same name in JDK 8.  The `ct.sym` file is a ZIP file
containing stripped-down class files corresponding to class files from the
target platform versions.

For JDK N and `--release N`,the JDK's own image is used as the source
of the class files to compile against. The list of observable modules is limited, however,
to the documented modules and the jdk.unsupported module.

Risks and Assumptions
---------------------

The JDK source-code repository needs to contain a description of the
platform APIs of past releases.  The size of the description may be
considerable, and the resulting JDK builds will be larger.  Care has been
taken to reduce these space overheads as much as possible.

Comments
@Keimpe: Conceptually, the list is -source, -target and any options which might contribute to the platform class path, which is the bootclasspath and files in the extension directory and endorsed standards directory. In other words, anything that might conflict with the effect of -release. The javac code is as follows checkOptionAllowed(platformString == null, option -> error("err.release.bootclasspath.conflict", option.getText()), Option.BOOTCLASSPATH, Option.XBOOTCLASSPATH, Option.XBOOTCLASSPATH_APPEND, Option.XBOOTCLASSPATH_PREPEND, Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS, Option.EXTDIRS, Option.DJAVA_EXT_DIRS, Option.SOURCE, Option.TARGET); Translated, that means the options are -bootclasspath -Xbootclasspath -Xbootclasspath/a: -Xbootclasspath/p: -endorseddirs -Djava.endorsed.dirs -extdirs -Djava.ext.dirs -source -target
25-09-2015

@Douglas: Currently there are no plans to include "popular internal APIs" such as Unsafe. What makes this a potentially interesting question is the recent decision to admit the existence of "critical internal APIs" in JDK 9 (see JEP 260, http://openjdk.java.net/jeps/260 ). That means when JDK 10 rolls around, we may need to support access to some of these APIs with "-release 9". What makes the interesting question much harder is, what about any internal API that was unofficially available in an old release like JDK 7, but which has since been replaced by an official API in a later release. That would require us to determine the critical internal API in older releases. For now, the recommended solution for anyone using internal API in older releases is to put the appropriate version of rt.jar on the bootclasspath.
25-09-2015

JEP 247 states: "The -release command line option is incompatible with numerous javac command line options". Would you please supply the list of incompatible options so I can filter them out when -release is used?
24-09-2015

We'd like to use the -release option in the Truffle project for building certain components to be deployed on 1.7. However, some of these components use sun.misc.Unsafe which is not made available in the ct.sym file provided in the current jdk9-ea build. Are there plans to add support to -release for popular internal APIs such as Unsafe from older JDKs?
23-09-2015

I've re-phrased the note on Unicode - thanks for the comment!
12-05-2015

Generally, nice updates.
12-05-2015

Re: As a consequence of running the compiler on a newer version of the platform and merely compiling for an older one, the Unicode version of the newer version is used by the compiler. The phrasing makes it seem like this is a good thing, whereas in fact it is a limitation of the implementation. Ideally, if one is using -source N or -platform N, one should be using the Unicode standard that was specified for that release. In reality, that's hard, and so it is a limitation of -platform that it does not attempt to address the Unicode issue.
12-05-2015

This looks pretty good; two minor comments: - The Summary text would be more appropriate in the Motivation section. Please condense the Summary down to one or two sentences describing what you're going to do, leaving the "why" in the Motivation. - There is no Open Issues section in the JEP template; please make this a subsection of the Description section. Address these issues, assign the JEP back to me, and I'll move it to Candidate.
13-02-2015

Thanks for the comments. I did my best to adjust the text accordingly - please let me know if something needs to be improved further. Thanks.
13-02-2015

Consider linking to Joe's blog entry https://blogs.oracle.com/darcy/entry/how_to_cross_compile_for in the description as an illustration of the problem we are wanting to address.
25-09-2014

35% increase for ct.sym seems acceptable to the point of being good. Somewhere you might want to emphasise that -platform is *just* a more convenient form of specifying -source, -target and the platform classes. It is a non-goal to exactly emulate bug-for-bug behavior of older versions of javac. -platform will be incompatible with any options to explicitly specify platform classes.
25-09-2014

In the current prototype, the versioned ct.sym which includes classfiles for all standard bootclasspath jars is about 35% bigger than the versioned ct.sym that only contains data for the classfiles from ct.sym.
24-09-2014

(Thanks for all the comments so far.) Yes, I agree that (ideally) -platform N should give the correct versions of the classes from the standard bootclasspath, including classes that don't originate in rt.jar/ct.sym. The only trouble I see with that is the size of the "ct.sym" and/or its source data. I'll experiment to see how much bigger it gets.
22-09-2014

ct.sym has always been (just) a stripped down version of classes in rt.jar. I think the going in position should be that -platform N should give you the same classes down to the signature level) that you would see on JDK N with no options that would affect PLATFORM_CLASS_PATH I would start by writing a small app to list the contents of PLATFORM_CLASS_PATH (with ct.sym enabled) on each supported platform.
19-09-2014

@Jan, It may be helpful to allow a user to opt-in to an "Java SE only" set of files on their effective bootclasspath (but I don't think this should be the default behavior). The construction process for ct.sym defined different treatment for various JDK APIs, including grand fathering in visibility to some APIs that if they were added today would not be visible. Updates to the standalone technology list are rare enough that just providing a view of the latest version of jax-ws is sufficient. However, there can be some changes in non-SE APIs in the lifetime of an update release train. There may or may not be need for a finer grained versioning there.
12-09-2014

@Joe, thanks for interesting questions. I turned them both into open issues. As for the JDK-specific APIs, there are three possibilities: -if the API is in rt.jar (or more precisely in ct.sym) of the given target platform version, it should be (presumably) automatically versioned using -platform -if the API is in a location that is not automatically added to the bootclasspath, then there is (probably) nothing to do: the user has to put the location on the classpath manually, and should use the correct version -if the API is not in rt.jar and is automatically added to the bootclasspath, this is an open issue
11-09-2014

A few questions to consider: What JDK-specific APIs, if any, are included as being visible using -target? Does there need to be a facility to specify the versions of stand-alone technologies included in the Java SE and the JDK? For example, in the 6 update train, the version of JAX-WS included in the JDK was updated from 2.0 to 2.1. This update included API changes.
10-09-2014