Создание дистрибутивов для разных операционных систем в Java 9 и 10

В статье рассказывается о построении полноценных дистрибутивов для Windows, macOS и Linux стандартными средствами Java 9 и 10.

Дополняет ранее опубликованную статью об уменьшении размера дистрибутива, делая акцент не на модульности, а на особенностях создания дистрибутива для разных операционных систем.

Перечисляются произошедшие с Java 9 изменения. Описывается последовательность шагов, выполняемых скриптом сборки, с указанием мест, где возможно изменение поведения и настройка. Приводится история со счастливым финалом преодоления особенностей и ошибок, появившихся в Java 9.

bsx0db6hdkcbuxdoyvyem-bl93a.jpeg

Пролог


Настольные (desktop) приложения, написанные на языке програмирования Java, по-прежнему имеют право на существование. Самым ярким примером является IntelliJ IDEA, дистрибутивы для установки которой существуют для разных операционных систем.

В JDK включена утилита командной строки Java Packager, служащая для компиляции, создания цифровой подписи и сборки дистрибутивов Java-приложений. Утилита впервые появилась в JDK 7 Update 6. Кроме использования в командной строке её функции доступны в виде задач (tasks) для Ant. В документации они именуются JavaFX Ant Tasks.

При написании статьи были использованы:

  • операционные системы Windows 10, macOS High Sierra 10.13, Ubuntu 16.04.3 LTS;
  • JDK 9 и JDK 10 Early-Access Builds для всех трёх операционных систем;
  • Apache Maven 3.5.0;
  • Inno Setup 5.5.9;
  • IDE с поддержкой Java 9 и Java 10 (например, IntelliJ IDEA 2017.3.2 или позднее).


Проект может быть найден на GitHub. Компиляция и сборка дистрибутива осуществляется при помощи Maven. Проект примера включает три части (модуля в терминологии Maven):

  • multiplatform-distribution-client с кодом приложения;
  • multiplatform-distribution-distrib со вспомогательными файлами дистрибутива;
  • multiplatform-distribution-resources с ресурсами для инсталляторов.


Сборка дистрибутива


В таблице перечислены в порядке выполнения этапы формирования дистрибутивов. Специально выделены жирным те этапы, на которых может возникнуть необходимость или желание изменить поведение, внешний вид или состав дистрибутивов.

№ п/п Модуль Maven Плагин Maven Фаза жизненного цикла Maven Этап
1 multiplatform-distribution-resources maven-resources-plugin process-resources Подстановка строковых значений в файлах .iss (для Windows) и .plist (для macOS)
2 multiplatform-distribution-client maven-dependency-plugin generate-sources Формирование списка зависимостей для манифеста
3 build-helper-maven-plugin generate-sources Замена разделителя в списке зависимостей для манифеста
4 maven-compiler-plugin compile Компиляция кода
5 maven-jar-plugin package Создание файла .jar
6 maven-dependency-plugin package Копирование зависимостей для дистрибутива
7 maven-resources-plugin package Копирование дополнительных файлов для дистрибутива
8 maven-antrun-plugin package
  1. Использование графических и конфигурационных файлов для сборки дистрибутива
  2. Создание образа JRE
  3. Формирование дистрибутива для конкретной операционной системы

9 maven-assembly-plugin package Формирование файла дистрибутива .tar.gz для Linux (только при запуске на Linux)
10 multiplatform-distribution-distrib maven-assembly-plugin package
  1. Подстановка строковых значений в файлах .bat (для Windows), .sh (для macOS и Linux) и лицензии
  2. Формирование универсального дистрибутива .zip (без JRE)


Чтобы создать дистрибутивы в macOS и Linux, нужны только JDK и Maven. В операционной системе Windows предварительно необходимо установить Inno Setup или WiX Toolset. Далее подразумевается, что используется Inno Setup.

Запуск компиляции и сборки дистрибутива в Windows и macOS:

mvn clean package -P native-deploy


Запуск компиляции и сборки дистрибутива в Linux (дополнительно создаётся файл архива tar.gz):

mvn clean package -P native-deploy,tar-gz


Файлы созданного дистрибутива с расширением exe, dmg располагаются в каталоге multiplatform-distribution-client/target/deploy/native, с расширением tar.gz — в каталоге multiplatform-distribution-client/target.

Универсальный дистрибутив с расширением zip, пригодный для любой операционной системы и не содержащий в себе JRE, создаётся в каталоге multiplatform-distribution-distrib/target.

Особенности Java 9 и 10


Долгожданный выход Java 9 деструктивно повлиял на скрипт сборки дистрибутива, до этого успешно работавший в предыдущей версии Java.

Во-первых, задача , выполняющая формирование образа JRE и создание дистрибутива, немного изменила структуру используемых ею каталогов. Скрипт сборки был изменён, чтобы учесть это.

Во-вторых, при создании инсталляторов дистрибутивов перестали загружаться текстовые и графические ресурсы, см. JDK-8186683. Невозможность загрузки ресурсов из classpath при сборке дистрибутива в Java 9 компенсирована добавлением нового аргумента dropinResourcesRoot. В качестве значения аргумента рекомендуется указать путь к каталогу с ресурсами. В описании задачи в файле pom.xml это будет выглядеть так:

59f06afec050a962312316.png

В-третьих, из-за допущенных ошибок в реализации в Java 9 пропала возможность включать в дистрибутив подкаталоги с файлами, например, подкаталог lib с библиотеками-зависимостями программы. Ошибка проявлялась только в Windows и macOS. Преодоление этой проблемы превратилось в целую детективную историю и вынужденно растянулось на долгое время, отсрочив публикацию статьи. Хроника событий:

  1. Клонирован репозиторий OpenJFX и найдена ошибка.
  2. Обнаружен уже существующий JDK-8179033 с описанием тех же симптомов.
  3. По совету lany (спасибо ему большое) написаны письма раз и два в группу openjfx-dev, с предложением изменений, требуемых для исправления ошибки, и советом, как их правильность проверить.
  4. Переписка с исполнителями JDK-8179033 — благодарю Виктора Дроздова за доброжелательное отношение и терпение.
  5. Ошибка исправлена и исправление попало в очередную сборку предварительной версии Java 10 — jdk-10-ea+36.
  6. Сборка дистрибутива, запущенная из командной строки, стала выполняться успешно.
  7. При попытке в IntelliJ IDEA добавить jdk-10-ea+36 в список SDK — ошибка (в отличие от предыдущих сборок), создан IDEA-183920.
  8. Комментатором IDEA-183920 указана причина ошибки добавления JDK — исчезнование именно в этой сборке JDK утилиты javah, в том числе наличие которой требовалось для успешной идентификации.
  9. Выход IntelliJ IDEA 2017.3.2, в которой ошибка идентификации JDK 10 исправлена.
  10. Сборка дистрибутива стала возможна из среды разработки тоже.


Адаптация примера для собственного использования


  1. Переименовать модули Maven проекта, заменив префикс multiplatform-distribution в наименовании на что-то другое.
  2. Переименовать файлы multiplatform-distribution.bat и multiplatform-distribution.sh, находящиеся в модуле multiplatform-distribution-distrib.
  3. Изменить в файлах pom.xml:
    • имена переименованных модулей Maven;
    • имена каталогов, соответствующих переименованным модулям Maven.
  4. Изменить в файлах assembly.xml:
    • имена переименованных модулей Maven;
    • упоминания изменённых имён файлов multiplatform-distribution.bat и multiplatform-distribution.sh.
  5. Изменить в файле корневого pom.xml значения свойств с именами , содержащие:
    • полное наименование приложения;
    • краткое наименование приложения;
    • год (-a) copyright;
    • код приложения в именах файлах;
    • пакет приложения по умолчанию;
    • наименование класса для запуска приложения.
  6. Добавить в модуль multiplatform-distribution-client собственный код.
  7. Изменить содержимое файлов license.txt и readme.txt в модуле multiplatform-distribution-client.
  8. Изменить содержимое графических файлов и их имена в модуле multiplatform-distribution-resources.


Выводы


  • стандартными средствами JDK 9 и 10 можно построить дистрибутивы для различных операционных систем;
  • кастомизация состава, поведения и вида дистрибутивов относительно проста;
  • в Java 9 появились некоторые особенности, которые были учтены при написании данной статьи.


Недавно появился JEP 311: Java Packager API & CLI для создания нового API и CLI (интерфейса командной строки) для Java Packager. JEP в настоящее время отклонён, ранее изменения планировалось произвести в JDK 10 и JDK 11. При возобновлении активности данная статья может получить продолжение в ближайшем будущем.

На предстоящих 4 марта (JBreak 2018 в Новосибирске) и 6–7 апреля (JPoint 2018 в Москве) конференциях имеется возможность посетить доклады на близкие темы по Java 9 и будущим версиям Java:

  • JDK 9, Mission Accomplished: What next for Java? (Simon Ritter)
  • Migrating to Java 9 modules (Sander Mak)
  • Refactoring your code to Java 9 modules (Rabea Gransberger)
  • JavaFX on JDK 9 (Gerrit Grunwald)

© Habrahabr.ru