openjdk.ruOpenJDK на русском

JEP 223: New Version-String Scheme

Новая схема строк версий

AuthorsIris Clark, Mark Reinhold
ОтветственныйIris Clark
ТипFeature
ОбластьSE
СтатусClosed / Delivered
Выпуск9
Обсуждениеverona dash dev at openjdk dot java dot net
ТрудоёмкостьM
ДлительностьM
Связан с8085822: JEP 223: New Version-String Scheme (initial integration)
РецензентыBrian Goetz, Roger Riggs
ОдобренBrian Goetz
Создан2014/10/20 18:27
Обновлён2021/10/03 12:50
Задача8061493

Аннотация

Определить схему строк версий, в которой легко различать основные и промежуточные выпуски и выпуски с обновлениями безопасности, и применить её к JDK.

Цели

  • Схема должна быть легко понятна людям и легко разбираться программами.

  • Соответствовать современной отраслевой практике, в частности Semantic Versioning (семантическому версионированию).

  • Схема должна подходить для существующих систем пакетов и механизмов развёртывания на платформах, включая RPM, dpkg, IPS и Java Network Launching Protocol (JNLP).

  • Отказаться от нынешней практики кодировать в одном элементе строки версии два вида информации, т. е. номер промежуточного выпуска и уровень безопасности: такую запись трудно расшифровать, и из-за неё пропускается много номеров версий.

  • Предоставить простой API для разбора, проверки и сравнения строк версий.

Что не является целью

  • Изменять формат строки версии, который используется в любом выпуске, предшествующем выпуску, в который запланирован этот JEP.

Мотивация

Какой выпуск содержит все самые последние исправления безопасности: JDK 7 Update 55 или JDK 7 Update 60?

Похоже, что JDK 7 Update 60 вышел на пять выпусков позже Update 55, а значит, он должен содержать больше исправлений безопасности, верно?

Этот вывод, к сожалению, неверен: оба выпуска содержат ровно одни и те же исправления безопасности. Чтобы понять этот ответ, сначала нужно разобраться в текущей схеме нумерации выпусков JDK Update. Промежуточные выпуски, в которых есть изменения помимо исправлений безопасности, имеют номера, кратные 20. Выпуски безопасности, основанные на предыдущем промежуточном выпуске, имеют нечётные номера, увеличенные на пять или, если нужно, чтобы номер обновления остался нечётным, на шесть. Чтобы понять, действительно ли промежуточный выпуск безопаснее более раннего, в конечном счёте приходится смотреть примечания к выпуску или исходный код.

Чем отличаются выпуски с названиями «JDK 7 Update 60», «1.7.0_60» и «JDK 7u60»?

Это просто разные названия одного и того же выпуска. Из-за этих различий трудно определять и проверять эквивалентные выпуски. Простого поэлементного сравнения последовательностей разобранных токенов недостаточно: нужен довольно сложный алгоритм. Использование строчной буквы «u» не является отраслевым стандартом и не нейтрально по отношению к языку.

Давно пора перейти на более простую и интуитивно понятную схему версий.

Описание

Номера версий

Номер версии, $VNUM, — это непустая последовательность элементов, разделённых точками (U+002E). Элемент — это либо ноль, либо беззнаковое целое число без ведущих нулей. Последний элемент номера версии не должен быть нулём. Формат:

[1-9][0-9]*((\.0)*\.[1-9][0-9]*)*

Последовательность может быть произвольной длины, но первым трём элементам присвоены определённые значения:

$MAJOR.$MINOR.$SECURITY
  • $MAJOR — номер основной версии. Увеличивается при основном выпуске, который содержит значительные новые возможности, описанные в новой редакции спецификации платформы Java SE, например JSR 337 для Java SE 8. В основном выпуске возможности могут быть удалены при условии предупреждения не менее чем за один основной выпуск, а несовместимые изменения могут вноситься, если это обосновано. Номер версии $MAJOR у JDK 8 — 8, номер версии $MAJOR у JDK 9 — 9. При увеличении $MAJOR все последующие элементы удаляются.

  • $MINOR — номер промежуточной версии. Увеличивается при промежуточном выпуске обновления, который может содержать совместимые исправления ошибок, изменения стандартных API, предписанные Maintenance Release соответствующей спецификации платформы, и возможности реализации за пределами этой спецификации, например новые API, специфичные для JDK, дополнительные поставщики служб, новые сборщики мусора и порты на новые аппаратные архитектуры.

  • $SECURITY — уровень безопасности. Увеличивается при выпуске обновления безопасности, который содержит критические исправления, в том числе необходимые для повышения безопасности. $SECURITY не сбрасывается в ноль при увеличении $MINOR. Поэтому более высокое значение $SECURITY при заданном значении $MAJOR всегда означает более безопасный выпуск, независимо от значения $MINOR.

Четвёртый и последующие элементы номера версии свободны для использования потребителями кодовой базы JDK ниже по цепочке. Такой потребитель может, например, использовать четвёртый элемент для обозначения выпусков-патчей, которые содержат небольшое число критических исправлений, не связанных с безопасностью, в дополнение к исправлениям безопасности из соответствующего выпуска безопасности.

Номер версии не содержит нулевых элементов в конце; т. е. $SECURITY опускается, если его значение равно нулю, а $MINOR опускается, если и $MINOR, и $SECURITY равны нулю.

Последовательность чисел в номере версии сравнивается с другой такой последовательностью численно, поэлементно; например, 9.9.1 меньше 9.10.3. Если одна последовательность короче другой, недостающие элементы более короткой последовательности считаются меньше соответствующих элементов более длинной; например, 9.1.2 меньше 9.1.2.1.

Строки версий

Строка версии, $VSTR, состоит из номера версии $VNUM, описанного выше, за которым могут следовать сведения о предварительном выпуске и сборке в одном из следующих форматов:

$VNUM(-$PRE)?\+$BUILD(-$OPT)?
$VNUM-$PRE(-$OPT)?
$VNUM(+-$OPT)?

где:

  • $PRE, соответствует ([a-zA-Z0-9]+), — идентификатор предварительного выпуска. Обычно ea — для выпуска Early Access (ранний доступ), который находится в активной разработке и может быть нестабилен, или internal — для внутренней сборки разработчика.

    При сравнении двух строк версий строка с идентификатором предварительного выпуска всегда меньше строки с равным $VNUM, но без такого идентификатора. Идентификаторы предварительного выпуска сравниваются численно, если состоят только из цифр, и лексикографически в остальных случаях. Числовые идентификаторы считаются меньше нечисловых.

  • $BUILD, соответствует (0|[1-9][0-9]*), — номер сборки, увеличивается для каждой продвинутой сборки. $BUILD сбрасывается в единицу при увеличении любой части $VNUM.

    При сравнении двух строк версий с равными компонентами $VNUM и $PRE строка без компонента $BUILD всегда меньше строки с компонентом $BUILD; в остальных случаях номера $BUILD сравниваются численно.

  • $OPT, соответствует ([-a-zA-Z0-9\.]+), — дополнительные сведения о сборке, если нужны. В случае сборки internal здесь часто будут указаны дата и время сборки.

    При сравнении двух строк версий значение $OPT, если оно есть, может учитываться или не учитываться в зависимости от выбранного способа сравнения.

Номер версии 10-ea соответствует $VNUM = "10" и $PRE = "ea". Номер версии 10+-ea соответствует $VNUM = "10" и $OPT = "ea".

В следующей таблице сравниваются возможные строки версий для JDK 9 в существующем и предлагаемом форматах:

Existing                Proposed
Release Type    long           short    long           short
------------    --------------------    --------------------
Early Access    1.9.0-ea-b19    9-ea    9-ea+19        9-ea
Major           1.9.0-b100      9       9+100          9
Security #1     1.9.0_5-b20     9u5     9.0.1+20       9.0.1
Security #2     1.9.0_11-b12    9u11    9.0.2+12       9.0.2
Minor #1        1.9.0_20-b62    9u20    9.1.2+62       9.1.2
Security #3     1.9.0_25-b15    9u25    9.1.3+15       9.1.3
Security #4     1.9.0_31-b08    9u31    9.1.4+8        9.1.4
Minor #2        1.9.0_40-b45    9u40    9.2.4+45       9.2.4

Для справки: в этой таблице показаны строки версий в новом формате в том виде, в каком они гипотетически использовались бы для некоторых выпусков обновлений и выпусков безопасности JDK 7:

Actual               Hypothetical
Release Type        long           short    long          short
------------        --------------------    -------------------
Security 2013/04    1.7.0_21-b11    7u21    7.4.10+11    7.4.10
Security 2013/06    1.7.0_25-b15    7u25    7.4.11+15    7.4.11
Minor    2013/09    1.7.0_40-b43    7u40    7.5.11+43    7.5.11
Security 2013/10    1.7.0_45-b18    7u45    7.5.12+18    7.5.12
Security 2014/01    1.7.0_51-b13    7u51    7.5.13+13    7.5.13
Security 2014/04    1.7.0_55-b13    7u55    7.5.14+13    7.5.14
Minor    2014/05    1.7.0_60-b19    7u60    7.6.14+19    7.6.14
Security 2014/07    1.7.0_65-b20    7u65    7.6.15+20    7.6.15

Удаление начального элемента 1 из номеров версий

Это предложение удаляет начальный элемент 1 из номеров версий JDK. То есть предлагается, чтобы первый выпуск JDK 9 имел номер версии 9.0.0, а не 1.9.0.0.

Спустя почти двадцать лет ясно, что второй элемент текущей схемы номеров версий — это де-факто номер версии $MAJOR JDK. Мы увеличиваем этот элемент, когда добавляем значительные новые возможности, а также когда вносим несовместимые изменения.

Мы могли бы начать считать начальный элемент текущей схемы номером версии $MAJOR, но тогда JDK 9 получил бы номер версии 2.0.0, хотя все уже называют его «JDK 9». Это никому бы не помогло.

Если сохранить начальный 1, номера версий JDK будут и дальше нарушать принципы Semantic Versioning, а разработчики, впервые знакомящиеся с Java, будут и дальше путаться в разнице между, например, 1.9 и 9.

Удаление начального 1 связано с некоторым риском. Сравнивать номера версий можно многими способами; одни будут работать правильно, другие нет.

  • Существующий код, который сравнивает номера версий, разбирая их элементы и сравнивая их численно, продолжит работать, поскольку девять больше единицы; т. е. 9.0.0 будет считаться более поздней версией, чем 1.8.0.

  • Существующий код, который пропускает начальный элемент, если его значение равно 1, тоже продолжит работать, поскольку в новой схеме начальный элемент никогда не будет иметь такого значения.

  • Однако существующий код, который предполагает, что начальный элемент имеет значение 1, и поэтому при сравнении номеров версий всегда переходит сразу ко второму элементу, будет работать неправильно; например, такой код будет считать, что 9.0.1 предшествует 1.8.0.

Отдельные наблюдения говорят о том, что существующий код третьей категории встречается не очень часто, но мы будем рады данным, говорящим об обратном.

API

Будет определён простой Java API для разбора, проверки и сравнения строк версий (8072379, 8144062):

package java.lang;

import java.util.Optional;

public class Runtime {

    public static Version version();

    public static class Version
        implements Comparable<Version>
    {

        public static Version parse(String);

        public int major();
        public int minor();
        public int security();

        public List<Integer> version();
        public Optional<String> pre();
        public Optional<Integer> build();
        public Optional<String> optional();

        public int compareTo(Version o);
        public int compareToIgnoreOpt(Version o);

        public boolean equals(Object o);
        public boolean equalsIgnoreOpt(Object o);

        public String toString();
        public int hashCode();
    }
}

Будет определён эквивалентный C API, скорее всего на основе изменённой структуры jvm_version_info.

Весь код в JDK, который анализирует и сравнивает строки версий JDK, будет переведён на эти API. Разработчикам, чьи библиотеки или приложения анализируют и сравнивают строки версий JDK, будет рекомендовано использовать эти API.

Системные свойства

Этот JEP изменяет значения, возвращаемые следующими системными свойствами. Общий синтаксис таков:

Name                            Syntax
------------------------------  --------------
java.version                    $VNUM(\-$PRE)?  
java.runtime.version            $VSTR
java.vm.version                 $VSTR
java.specification.version      $VNUM
java.vm.specification.version   $VNUM

Системное свойство java.class.version не затрагивается.

В следующей таблице показаны существующие и предлагаемые значения для разных типов выпусков:

System Property                   Existing      Proposed
-------------------------------   ------------  --------
Early Access 
  java.version                    1.9.0-ea      9-ea
  java.runtime.version            1.9.0-ea-b73  9-ea+73
  java.vm.version                 1.9.0-ea-b73  9-ea+73
  java.specification.version      1.9           9
  java.vm.specification.version   1.9           9

Major (GA)
  java.version                    1.9.0         9
  java.runtime.version            1.9.0-b100    9+100
  java.vm.version                 1.9.0-b100    9+100
  java.specification.version      1.9           9
  java.vm.specification.version   1.9           9

Minor #1 (GA)
  java.version                    1.9.0_20      9.1.2
  java.runtime.version            1.9.0_20-b62  9.1.2+62
  java.vm.version                 1.9.0_20-b62  9.1.2+62
  java.specification.version      1.9           9
  java.vm.specification.version   1.9           9

Security #1 (GA)
  java.version                    1.9.0_5       9.0.1
  java.runtime.version            1.9.0_5-b20   9.0.1+20
  java.vm.version                 1.9.0_5-b20   9.0.1+20
  java.specification.version      1.9           9
  java.vm.specification.version   1.9           9

Обратите внимание: весь код, который исторически распознавал . в любом из этих системных свойств при определении версии, нужно будет проверить и, возможно, изменить. Например, System.getProperty("java.version").indexof('.') для основных выпусков будет возвращать -1.

Лаунчер

В реализации лаунчера java в OpenJDK системные свойства используются при выводе сведений о версии, например java -version, java -fullversion и java -showversion.

Вывод лаунчера по-прежнему зависит от системных свойств следующим образом:

$ java -version
openjdk version \"${java.version}\"
${java.runtime.name} (build ${java.runtime.version})
${java.vm.name} (build ${java.vm.version}, ${java.vm.info})

$ java -showversion < ... >
openjdk version \"${java.version}\"
${java.runtime.name} (build ${java.runtime.version})
${java.vm.name} (build ${java.vm.version}, ${java.vm.info})
[ ... ]

$ java -fullversion
openjdk full version \"${java.runtime.version}\"

Подробности реализации можно найти в исходном коде.

Тег JavaDoc @since

Значение тега JavaDoc @since по-прежнему будет согласовано с системным свойством java.specification.version; следовательно, новые API JDK 9 будут обозначаться как @since 9.

Теги наборов изменений Mercurial

Теги Mercurial используются для обозначения наборов изменений, вошедших в продвижение сборки. Такие инструменты, как jcheck из Code Tools, который используется для проверки всех наборов изменений, отправляемых в леса репозиториев выпусков JDK, будут доработаны для поддержки тегов в новой схеме версий.

Общий синтаксис тегов Mercurial: jdk\-$VNUM\+$BUILD. В следующей таблице показаны предлагаемые значения для разных типов выпусков:

Release Type      Proposed
----------------  -----------
Major (GA)        jdk-9+100
Minor #1 (GA)     jdk-9.1.2+27
Security #1 (GA)  jdk-9.0.1+3

Некоторым инструментам может потребоваться поддержка как существующего, так и предлагаемого формата тегов.

Тестирование

Изменение синтаксиса и семантики строк версий потребует обширного тестирования всех компонентов. Существующие тесты, не зависящие от строки версии JDK, должны по-прежнему проходить.