В статье рассказывается о построении полноценных дистрибутивов для Windows, macOS и Linux стандартными средствами Java 9 и 10.
Дополняет ранее опубликованную статью об уменьшении размера дистрибутива, делая акцент не на модульности, а на особенностях создания дистрибутива для разных операционных систем.
Перечисляются произошедшие с Java 9 изменения. Описывается последовательность шагов, выполняемых скриптом сборки, с указанием мест, где возможны изменение поведения и настройка. Приводится история со счастливым финалом преодоления особенностей и ошибок, появившихся в Java 9.
Пролог
Настольные (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 |
|
|
9 | maven-assembly-plugin | package | Формирование файла дистрибутива .tar.gz для Linux (только при запуске на Linux) |
|
10 | multiplatform-distribution-distrib | maven-assembly-plugin | package |
|
Чтобы создать дистрибутивы в 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
(для Windows), dmg
(для macOS) располагаются в каталоге multiplatform-distribution-client/target/deploy/native
, с расширением tar.gz
(для Linux) — в каталоге multiplatform-distribution-client/target
.Универсальный дистрибутив с расширением
zip
, пригодный для любой операционной системы и не содержащий в себе JRE, создаётся в каталоге multiplatform-distribution-distrib/target
.Особенности Java 9 и 10
Долгожданный выход Java 9 деструктивно повлиял на скрипт сборки дистрибутива, до этого успешно работавший в предыдущей версии Java.
Во-первых, задача <fx:deploy>, выполняющая формирование образа JRE и создание дистрибутива, немного изменила структуру используемых ею каталогов. Скрипт сборки был изменён, чтобы учесть это.
Во-вторых, при создании инсталляторов дистрибутивов перестали загружаться текстовые и графические ресурсы, см. JDK-8186683. Невозможность загрузки ресурсов из classpath при сборке дистрибутива в Java 9 компенсирована добавлением нового аргумента dropinResourcesRoot. В качестве значения аргумента рекомендуется указать путь к каталогу с ресурсами. В описании задачи в файле
pom.xml
это будет выглядеть так:В-третьих, из-за допущенных ошибок в реализации в Java 9 пропала возможность включать в дистрибутив подкаталоги с файлами, например, подкаталог
lib
с библиотеками-зависимостями программы. Ошибка проявлялась только в Windows и macOS. Преодоление этой проблемы превратилось в целую детективную историю и вынужденно растянулось на долгое время, отсрочив публикацию статьи. Хроника событий:
- Клонирован репозиторий OpenJFX и найдена ошибка.
- Обнаружен уже существующий JDK-8179033 с описанием тех же симптомов.
- По совету lany (спасибо ему большое) написаны письма раз и два в группу openjfx-dev, с предложением изменений, требуемых для исправления ошибки, и советом, как их правильность проверить.
- Переписка с исполнителями JDK-8179033 — благодарю Виктора Дроздова за доброжелательное отношение и терпение.
- Ошибка исправлена и исправление попало в очередную сборку предварительной версии Java 10 — jdk-10-ea+36.
- Сборка дистрибутива, запущенная из командной строки, стала выполняться успешно.
- При попытке в IntelliJ IDEA добавить jdk-10-ea+36 в список SDK — ошибка (в отличие от предыдущих сборок), создан IDEA-183920.
- Комментатором IDEA-183920 указана причина ошибки добавления JDK — исчезнование именно в этой сборке JDK утилиты javah, в том числе наличие которой требовалось для успешной идентификации.
- Выход IntelliJ IDEA 2017.3.2, в которой ошибка идентификации JDK 10 исправлена.
- Сборка дистрибутива стала возможна из среды разработки тоже.
Адаптация примера для собственного использования
- Переименовать модули Maven проекта, заменив префикс multiplatform-distribution в наименовании на что-то другое.
- Переименовать файлы
multiplatform-distribution.bat
иmultiplatform-distribution.sh
, находящиеся в модуле multiplatform-distribution-distrib. - Изменить в файлах
pom.xml
:
- имена переименованных модулей Maven;
- имена каталогов, соответствующих переименованным модулям Maven.
- Изменить в файлах
assembly.xml
:
- имена переименованных модулей Maven;
- упоминания изменённых имён файлов
multiplatform-distribution.bat
иmultiplatform-distribution.sh
.
- Изменить в файле корневого
pom.xml
значения свойств с именами <app.*>, содержащие:
- полное наименование приложения;
- краткое наименование приложения;
- год (-ы) copyright;
- код приложения в именах файлах;
- пакет приложения по умолчанию;
- наименование класса для запуска приложения.
- Добавить в модуль multiplatform-distribution-client собственный код.
- Изменить содержимое файлов
license.txt
иreadme.txt
в модуле multiplatform-distribution-client. - Изменить содержимое графических файлов и их имена в модуле multiplatform-distribution-resources.
Выводы
- стандартными средствами JDK 9 и 10 можно построить дистрибутивы для различных операционных систем;
- кастомизация состава, поведения и вида дистрибутивов относительно проста;
- в Java 9 появились некоторые особенности, которые были учтены при написании данной статьи.
Недавно появился JEP 311: Java Packager API & CLI для создания нового API и CLI (интерфейса командной строки) для Java Packager. JEP в настоящее время отклонён, ранее изменения планировалось произвести в JDK 10 и JDK 11. При возобновлении активности данная статья может получить продолжение в ближайшем будущем.
UPD: Имеется продолжение данной статьи.
На предстоящих 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)