JEP 322:基于时间的发布版本控制
概述
修订 Java SE 平台和 JDK 的版本字符串方案以及相关版本信息,以适应当前和未来的基于时间的发布模型。
目标
-
重新构建由 JEP 223 引入的版本号方案,以便更好地适应基于时间的发布模型,这些模型定义了 功能发布(可以包含新功能)和 更新发布(仅修复错误)。
-
允许使用不同于 当前模型 的基于时间的发布模型,这些模型可能有不同的节奏,或者有比功能发布小但比更新发布大的 临时发布。
-
保持与整体 JEP 223 版本字符串方案的兼容性。
-
让开发者或最终用户能够轻松判断某个发布的年龄,以便他们决定是否将其升级到具有最新安全修复的新版本,并且可能包含额外的功能。
-
提供一种方法,让实现者表明某个发布属于一系列长期支持的发布之一。
-
提供一种方法,让实现者包含并显示一个额外的、特定于实现者的版本字符串,以使某个发布与其相关产品对齐。
非目标
修订现有版本字符串方案并不是为了适应基于时间的发布模型要求之外的其他要求。
修订 java
启动器之外的命令行工具的版本报告输出并不是目标。这样做是可取的,但并非至关重要,可以稍后再进行。
动机
JEP 223 方案的主要难点在于,一个发行版本的版本号编码了其相对于之前版本的重要性与兼容性。然而,在基于时间的发布模型中,这些特性是无法提前获知的。它们在整个发布开发周期中都可能发生变化,直到最终功能被集成。因此,一个发行版本的版本号也无法提前确定。
根据 JEP 223 的版本号语义,每个参与 JDK 发布的人员,或在其基础上构建或使用组件的人员,最初都必须提及该发布的发布日期,然后在版本号确定后切换到提及版本号。维护库、框架和工具的开发人员必须准备好在每个 JDK 发布周期的后期更改检查版本号的代码。这对所有相关人员来说都是尴尬且令人困惑的。
因此,这里提出的主要变更就是:将版本号重新塑造为不以兼容性和重要性来编码,而是根据发布周期来记录时间的流逝。 这对于基于时间的发布模型而言是更好的匹配,因为每个发布周期,以及每次发布的版本号,总是能提前很好地确定。
描述
版本号
一个版本号,$VNUM
,是由句点字符(U+002E)分隔的非空元素序列。元素要么是零,要么是不带前导零的无符号整数数字。版本号的最后一个元素不能为零。当一个元素递增时,所有后续元素都会被移除。格式如下:
[1-9][0-9]*((\.0)*\.[1-9][0-9]*)*
该序列可以是任意长度,但前四个元素被赋予了特定的含义,如下所示:
$FEATURE.$INTERIM.$UPDATE.$PATCH
-
$FEATURE
— 功能发布计数器,每次功能发布时都会递增,无论发布内容如何。功能可能会在功能发布中被添加;如果提前至少一个功能发布给出了通知,也可能会被移除。当有正当理由时,可能会进行不兼容的更改。(以前称为$MAJOR
。) -
$INTERIM
— 临时发布计数器,针对包含兼容性错误修复和增强功能但没有不兼容更改、没有功能移除且没有对标准 API 进行更改的非功能发布递增。(以前称为$MINOR
。) -
$UPDATE
— 更新发布计数器,针对修复安全问题、回归问题以及新功能中 bug 的兼容更新发布递增。(以前称为$SECURITY
,但递增规则较为复杂。) -
$PATCH
— 紧急补丁发布计数器,仅在需要制作紧急发布以修复关键问题时递增。(为此目的使用额外的元素可以尽量减少对正在处理更新发布的开发者和用户的干扰。)
版本号的第五个及后续元素保留给 JDK 代码库的下游使用者使用。第五个元素可以用来标识实现者特定的补丁发布,例如(e.g.)。
版本号从不会有尾随的零元素。如果一个元素及其后面所有逻辑上的值都为零,那么这些元素都将被省略。
版本号中的数字序列会以数值、逐点的方式与另一个这样的序列进行比较;例如,10.0.4
小于 10.1.2
。如果一个序列比另一个短,则较短序列中缺失的元素被认为小于较长序列中的对应元素;例如,10.0.2
小于 10.0.2.1
。
六个月发布模型中的版本号
在 六个月发布模型 下,版本号的组成部分变化如下:
-
每隔六个月
$FEATURE
会递增一次:2018 年 3 月的发布版本是 JDK 10,2018 年 9 月的发布版本是 JDK 11,以此类推。 -
$INTERIM
始终为零,因为六个月的模型不包含临时版本。我们在此保留它以增加灵活性,以便将来对发布模型的修订可以包含此类版本,并说明 JDK$N.1
和 JDK$N.2
是 JDK$N
的兼容升级版。例如,JDK 1.4.1 和 1.4.2 版本本质上是临时版本,在此方案下会被编号为 4.1 和 4.2。 -
在
$FEATURE
递增一个月后,$UPDATE
会递增,此后每三个月递增一次:2018 年 4 月的发布版本是 JDK 10.0.1,7 月的发布版本是 JDK 10.0.2,以此类推。
我们确实预计大多数功能发布将包含至少一到两个重要功能,并且更新发布永远不会包含不兼容的更改。结合 $INTERIM
始终为零这一事实,实际上,这种方案常常会定义出与 JEP 223 方案所定义的版本号差别不大的版本号。
版本字符串
版本字符串的整体格式与 JEP 223 中定义的格式相同。版本字符串是一个版本号 $VNUM
,后可选择性跟随预发布、构建以及其他可选信息,其中之一为:
$VNUM(-$PRE)?\+$BUILD(-$OPT)?
$VNUM-$PRE(-$OPT)?
$VNUM(+-$OPT)?
其中,$PRE
是预发布标识符(例如,ea
),$BUILD
是构建编号,而 $OPT
是可选的构建信息。
如果某个发行版本属于实施者提供长期支持的系列发行版本之一,那么 $OPT
的值应以 "LTS"
开头,例如:11.0.2+13-LTS。这将会使 "LTS"
在 java --version
等命令的输出中显著显示,更多内容见下文。
API
我们对 JEP 223 定义的 Runtime.Version
API 进行了如下修订:
-
为上述定义的版本号主要组成部分新增四个返回
int
类型的访问器方法:feature()
、interim()
、update()
和patch()
。 -
重新定义现有的访问器方法
major()
、minor()
和security()
,使其分别返回与feature()
、interim()
和update()
相同的值。 -
弃用现有的访问器方法(但不移除),并建议使用对应的新方法。这将有助于让开发者更清楚版本号的新语义。
系统属性
对于 JEP 223 中提到的系统属性,我们添加了两个新属性:
java.version.date
— 此版本的正式发布(GA)日期,采用 ISO-8601 的 YYYY-MM-DD 格式。对于早期访问版本,这将是预期的正式发布日期,即未来的某个日期。
这个新属性使你可以轻松了解一个发行版的发布时间,这样作为用户就可以知道你落后了多少。它也反映了发行版的安全级别:如果某个 GA(General Availability)发行版的版本日期不早于任何其他 GA 发行版,那么该发行版就包含了最新的安全修复。
java.vendor.version
— 一个由特定实现的生产者分配的实现者特定的产品版本字符串。如果在构建时未分配,则该属性没有值;否则,其值为一个非空字符串,且符合正则表达式\p{Graph}+
。
这一新属性使实现者可以提供额外的版本信息,这可能是为了与相关产品保持一致所必需的。如果某个实现者的产品线使用了基于日期的版本形式(例如 $YEAR.$MONTH
),那么他们可以相应地设置此属性,以便其 JDK 发行版能够清楚地与其其他发行版相关联。(该属性被命名为 java.vendor.version
而不是更直观的 java.implementor.version
,这是为了与现有系统属性 的命名保持一致,这些属性的名称中包含 vendor
。)
启动器
java
启动器将显示如下版本字符串和系统属性,假设是 JDK 10.0.1 的第 13 次构建:
$ java --version
openjdk 10.0.1 2018-04-19
OpenJDK Runtime Environment (build 10.0.1+13)
OpenJDK 64-Bit Server VM (build 10.0.1+13, mixed mode)
$
同样,对于假设的 JDK 11 的第 42 次构建,这是一个长期支持(LTS)版本:
$ java --version
openjdk 11 2018-09-20 LTS
OpenJDK Runtime Environment (build 11+42-LTS)
OpenJDK 64-Bit Server VM (build 11+42-LTS, mixed mode)
$
如果一个实现者为 JDK 11 LTS 构建分配了一个供应商版本字符串,例如 18.9
,那么它将显示为:
$ java --version
openjdk 11 2018-09-20 LTS
OpenJDK Runtime Environment 18.9 (build 11+42-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11+42-LTS, mixed mode)
$
详细来说,java
启动器的版本报告选项的输出格式如下:如果 $OPT
的前三个字符是 "LTS"
,则 ${LTS}
将扩展为 "\u0020LTS"
;如果定义了该系统属性,则 ${JVV}
将扩展为 "\u0020${java.vendor.version}"
:
$ java --version
openjdk ${java.version} ${java.version.date}${LTS}
${java.runtime.name}${JVV} (build ${java.runtime.version})
${java.vm.name}${JVV} (build ${java.vm.version}, ${java.vm.info})
$
$ java --show-version < ... >
openjdk ${java.version} ${java.version.date}${LTS}
${java.runtime.name}${JVV} (build ${java.runtime.version})
${java.vm.name}${JVV} (build ${java.vm.version}, ${java.vm.info})
[ ... ]
$
$ java --full-version
openjdk ${java.runtime.version}
$
$ java -version
openjdk version \"${java.version}\" ${java.version.date}${LTS}
${java.runtime.name}${JVV} (build ${java.runtime.version})
${java.vm.name}${JVV} (build ${java.vm.version}, ${java.vm.info})
$
$ java -showversion < ... >
openjdk version \"${java.version}\" ${java.version.date}${LTS}
${java.runtime.name}${JVV} (build ${java.runtime.version})
${java.vm.name}${JVV} (build ${java.vm.version}, ${java.vm.info})
[ ... ]
$
$ java -fullversion
openjdk full version \"${java.runtime.version}\"
$
@since
JavaDoc 标签
@since
JavaDoc 标记使用的值继续与系统属性 java.specification.version
保持一致,因此在 JDK 10 中引入的 API 将被标记为 @since 10
。
Mercurial 变更集标签
标识提升构建的 Mercurial 标签的一般语法保持不变:jdk\-$VNUM\+$BUILD
。
构建配置与输出
三个现有的与版本相关的配置选项将被弃用,因此将被忽略,并且相关的 Make 变量将不再被定义:
--with-version-major VERSION_MAJOR
--with-version-minor VERSION_MINOR
--with-version-security VERSION_SECURITY
将定义五个新选项以及相应的变量:
--with-version-feature VERSION_FEATURE
--with-version-interim VERSION_INTERIM
--with-version-update VERSION_UPDATE
--with-version-date VERSION_DATE
--with-vendor-version-string VENDOR_VERSION_STRING
(不需要定义 --with-version-patch
和 VERSION_PATCH
,因为它们已经存在。)
写入 JDK 映像根目录的 release
文件除了定义现有的 JAVA_VERSION
变量外,还会定义 JAVA_VERSION_DATE
,其值为 java.version.date
系统属性的值;如果定义了 java.vendor.version
系统属性,还会定义 IMPLEMENTOR_VERSION
,其值为该属性的值。
替代方案
六个月为周期的时间发布模型提案 建议功能版本的版本号采用 $YEAR.$MONTH
的形式。因此,次年三月的版本号将是 18.3,九月的版本号将是 18.9,依此类推。
在对该方案提出合理反对意见后,我们审查了版本号中编码的各种类型的信息并提出了一些替代方案,然后总结并回复了随后的讨论,最后发布了一个提案,该提案受到好评,因此成为了此 JEP 的基础。
测试
这种较新的版本字符串方案与 JEP 223 中定义的方案基本兼容,因此测试应该很简单。主要的区别在于可能将第四部分用于紧急补丁发布,这可能需要一些新的单元测试用例。对相关构建配置选项的更改将需要简单的手动测试。
风险与假设
这里描述的更改引入了三个小的不兼容性:
-
JEP 223 指定了
Runtime.Version
API 的security()
方法返回版本号中$SECURITY
元素的值。当其前一个元素$MINOR
增加时,该元素不会递增。本提案将$SECURITY
元素重命名为$UPDATE
,并在其前一个元素$INTERIM
(以前称为$MINOR
)增加时清除此元素。因此,从理论上讲,用update()
重新定义security()
是一种不兼容的更改。然而,此 API 是在 JDK 9 中引入的,并且没有计划发布$MINOR
值非零的 JDK 9 版本,因此实际上这一更改的影响应该很小。 -
java
启动器的版本报告选项的输出现在在第一行末尾包含版本日期,后面可能跟随"\u0020LTS"
。假设行中的最后一个标记是版本号的现有代码可能需要进行调整。 -
如果
java.vendor.version
系统属性的值与java.version
的值不同,则java
启动器的--version
、--show-version
、-version
和-showversion
选项的输出将在第二和第三行包含该值。解析此输出的现有代码可能需要进行调整。