JEP 247: 为旧平台版本编译
概述
增强 javac
,使其能够编译 Java 程序以在选定的旧版本平台上运行。
动机
javac
提供了两个命令行选项:-source
和 -target
,它们分别用于选择编译器接受的 Java 语言版本以及它生成的类文件版本。然而,默认情况下,javac
是针对最新版本的平台 API 进行编译的。因此,编译后的程序可能会意外地使用仅在当前平台版本中可用的 API。无论为 -source
和 -target
选项传递什么值,这样的程序都无法在旧版本的平台上运行。这是一个长期存在的易用性痛点,因为用户期望通过使用这些选项,他们能够获得可以在由 -target
指定的平台版本上运行的类文件。
描述
定义了一个新的命令行选项 --release
,它会自动配置编译器以生成针对给定平台版本的实现进行链接的类文件。--release N
大致等同于:
- 对于 N < 9:
-source N -target N -bootclasspath <documented-APIs-from-N>
, - 对于 N >= 9:
-source N -target N --system <documented-APIs-from-N>
。
对于 N < 9,记录的 API 包括 JDK N 中 javac 默认引导类路径上的公共 API。
对于 N >= 9,有文档记录的 API 包括:(i) 从 JDK 映像中那些属于 JDK N 文档部分的模块导出的 API;以及 (ii) 从 jdk.unsupported 模块导出的 API(在 JEP 260 中有文档记录)。也就是说,有文档记录的 API 主要是通过 JDK 映像中的模块与为 JDK N 记录文档的模块的 交集 所导出的 API。JDK 映像中的其他模块不可见。如果使用了 --limit-modules,则它只能进一步限制可见模块,而不能观察到额外的模块。不允许访问 JDK 映像中模块的内部内容。
--release N
选项与影响平台或系统类集合的其他选项不兼容。这包括:
- 对于 N < 9:设置平台类的选项包括
-bootclasspath
、-Xbootclasspath
、-Xbootclasspath/a:
、-Xbootclasspath/p:
、-endorseddirs
、-Djava.endorsed.dirs
、-extdirs
和-Djava.ext.dirs
。 - 对于 N >= 9:设置系统模块(即 JDK 映像中的模块)的选项包括
--system
和--upgrade-module-path
,以及如果修改系统模块时使用的--add-exports
、--add-reads
和--patch-module
选项。(允许对非系统模块使用--add-exports
、--add-reads
和--patch-module
,即不属于 JDK 映像的模块。) - 对于任何 N,
-source
和-target
选项会被自动设置为 N。
假定已记录的 API 仅在主要发行版本中更改。对于 JDK 6 的次要发行版本中将 JAX-WS 从 2.0 更新到 2.1 这一遗留情况,JAX-WS 2.1 被视为已记录的 API。
作为 --release
实现的一个限制,在编译期间不会使用给定目标平台的 Unicode 版本,而是使用当前平台的 Unicode 版本。
实现
对于 JDK N 和 --release M
,M < N,需要平台的 M 版本文档化 API 的签名数据。这些数据存储在 $JDK_ROOT/lib/ct.sym
文件中,该文件与 JDK 8 中同名文件类似,但并不相同。ct.sym
文件是一个 ZIP 文件,其中包含与目标平台版本的类文件相对应的简化类文件。
对于 JDK N 和 --release N
,JDK 自身的映像被用作编译时类文件的来源。然而,可观察模块的列表仅限于已记录的模块和 jdk.unsupported 模块。
风险与假设
JDK 源代码仓库需要包含对过去版本的平台 API 的描述。该描述的规模可能相当大,因此生成的 JDK 构建结果也会更大。已经采取措施尽可能减少这些空间开销。