Статья рассказывает о построении дистрибутивов приложений для операционных систем Windows, macOS и Linux в случае использования Java 21 и 22.
Продолжает ранее опубликованную статью о создании дистрибутивов в предыдущих версиях Java, подробно описывая кардинальные изменения, произошедшие с того времени.
История вопроса
Инструменты, позволяющие выполнять компиляцию, сборку, создание цифровой подписи и дистрибутивов Java-приложений, впервые появились в JDK 7 Update 6. Первоначально они предназначались в первую очередь для JavaFX-приложений и были доступны в виде утилиты командной строки javafxpackager и в виде задач (tasks) для Ant. В официальной документации задачи для Ant имеют наименование JavaFX Ant Tasks.
В Java 8 утилита командной строки изменила наименование на Java Packager. JavaFX Ant Tasks сохранили своё название. Вошедший в Java 9 JEP 275: Modular Java Application Packaging был направлен на интеграцию модульности в Java Packager, в том числе для создания образа JRE, включающего только те модули, которые использует приложение. Предыдущая статья демонстрировала использование JavaFX Ant Tasks посредством их запуска из Maven с помощью плагина Maven AntRun Plugin.
Появившийся в 2017 году JEP 311: Java Packager API & CLI был направлен на создание нового API и CLI (интерфейса командной строки) для Java Packager. Изменения планировали произвести в JDK 10 и JDK 11, но данный JEP был отклонён и не вошёл ни в одну из последующих версий Java.
Попытка в 2018 году была возобновлена в рамках JEP 343: Packaging Tool (Incubator), результат работы впервые появился в JDK 14 в виде экспериментальной версии (т.н. incubator) утилиты командной строки jpackage. Продолжением стал созданный в 2020 году JEP 392: Packaging Tool и окончательная версия jpackage, вошедшая в JDK 16.
Существуют плагины для Maven и Gradle, являющиеся обёрткой над утилитой jpackage (например, этот, этот и этот), однако наиболее удобным и популярным на настоящий момент является гибридный плагин для Maven и Gradle JavaPackager, написанный Francisco Vargas Ruiz. Плагин JavaPackager никак не использует утилиту jpackage, выполняя все действия самостоятельно (или с помощью других Java-библиотек и утилит), содержит значительное количество коммитов и имеет много звёзд на GitHub.
Выполняемые плагином JavaPackager действия
Плагин JavaPackager выполняет следующую последовательность действий:
- Создаёт структуру каталогов дистрибутива.
- Добавляет в дистрибутив файл лицензии и прочие файлы ресурсов, если они были указаны.
- Создаёт подкаталог
libs
и копирует в него файлы зависимостей Java-приложения. - Собирает для приложения исполняемый файл
jar
. - Формирует образ JRE, включающий только те модули, которые использует приложение. Для определения списка используемых модулей Java применяется утилита jdeps, для формирования образа JRE — утилита jlink.
- Создаёт исполняемый файл, который будет использован на следующем шаге:
- в Windows — файл формата
exe
, по умолчанию для этого используется инструмент Launch4j (можно использовать вместо него WinRun4J или Why); - в macOS — файл shell-скрипта на основе universalJavaApplicationStub, запускающего исполняемый файл
jar
, созданный на предыдущем шаге; - в Linux — файл shell-скрипта, являющегося обёрткой исполняемого файла
jar
, созданного на предыдущем шаге.
- в Windows — файл формата
- Формирует файл инсталлятора, выполняющего действия по установке дистрибутива в операционной системе:
- в Windows в виде файла
exe
(будет использован Inno Setup), файлаmsi
иmsm
(их формирование выполняет WiX Toolset); - в macOS в виде файлов
dmg
(используется утилита hdiutil) иpkg
(используется утилита pkgbuild); - в Linux в виде файлов
deb
(для его создания используется библиотека jdeb),rpm
(создаётся с помощью библиотеки Redline), AppImage (создаётся с помощью утилиты appimagetool).
- в Windows в виде файла
- Собирает архивы формата
zip
иtar.gz
с дистрибутивом без инсталлятора.
Практически на всех шагах поведение может быть настроено и изменено в определённых пределах. Например, может быть оставлено формирование только нужных форматов файлов дистрибутивов.
Пример сборки дистрибутива
Репозиторий с кодом примера находится на GitHub. Для проверки запуска сборки дистрибутивов были использованы:
- операционные системы Windows 10 21H2, macOS 12.7.5 (Monterey), Ubuntu 22.04 LTS (Jammy Jellyfish);
- JDK 21.0.3 и JDK 22.0.1 для всех трёх операционных систем;
- Apache Maven 3.9.7;
- Inno Setup 6.2.2.
Компиляция и сборка дистрибутива осуществляется при помощи Maven. Проект примера включает две части (модуля в терминологии Maven):
- multiplatform-distribution-client с кодом приложения;
- multiplatform-distribution-distrib со вспомогательными файлами дистрибутива.
В таблице перечислены в порядке выполнения этапы формирования дистрибутивов. Специально выделены жирным те этапы, на которых может возникнуть необходимость или желание изменить поведение, внешний вид или состав дистрибутивов.
№ п/п | Модуль Maven | Плагин Maven | Фаза жизненного цикла Maven | Этап |
---|---|---|---|---|
1 | multiplatform-distribution-client | maven-dependency-plugin | generate-sources | Формирование списка зависимостей для манифеста |
2 | build-helper-maven-plugin | generate-sources | Замена разделителя в списке зависимостей для манифеста | |
3 | maven-compiler-plugin | compile | Компиляция кода | |
4 | maven-jar-plugin | package | Создание файла .jar
|
|
5 | maven-resources-plugin | package | Копирование дополнительных файлов для дистрибутива | |
6 | javapackager | package |
|
|
7 | copy-rename-maven-plugin | package | Переименование файлов дистрибутива | |
8 | multiplatform-distribution-distrib | maven-assembly-plugin | package |
|
Чтобы создать дистрибутивы в macOS и Linux, нужны только JDK и Maven. В операционной системе Windows предварительно необходимо установить Inno Setup.
Запуск компиляции и сборки дистрибутивов:
mvn clean package -P native-deploy
Файлы созданного дистрибутива с расширением
exe
(для Windows), dmg
(для macOS), с расширениями tar.gz
, deb
, rpm
(для Linux) располагаются в каталоге multiplatform-distribution-client/target
.Универсальный дистрибутив с расширением
zip
, пригодный для любой операционной системы и не содержащий в себе JRE, создаётся в каталоге multiplatform-distribution-distrib/target
.Для получения только файла универсального дистрибутива с расширением
zip
:mvn clean package
Адаптация примера для собственного использования
- Переименовать модули 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. - Изменить содержимое файлов и их имена в подкаталоге
assets
модуля multiplatform-distribution-client.
Как собираются дистрибутивы IntelliJ IDEA для разных операционных систем
Компания JetBrains поставляет дистрибутивы самой популярной среды для Java-разработки IntelliJ IDEA в виде файлов следующих форматов:
- для Windows — файлов форматов
exe
иzip
; - для macOS — файла формата
dmg
; - для Linux — файла формата
tar.gz
.
Каким образом выполняется сборка файлов дистрибутивов, можно узнать, изучив исходный код IntelliJ IDEA Community Edition из репозитория на GitHub. Для сборки дистрибутивов не используется ни утилита jpackage, ни какие-либо плагины. Для формирования файлов дистрибутивов предназначен скрипт
installers.cmd
, расположенный в корневом каталоге репозитория. Данный скрипт последовательно использует классы, начиная с общего OpenSourceCommunityInstallersBuildTarget
и заканчивая специфичными для каждой из операционных систем WindowsDistributionBuilder
, MacDistributionBuilder
и LinuxDistributionBuilder
. Cкрипт выполняет следующие действия:- Создаёт структуру каталогов дистрибутива.
- Добавляет в дистрибутив необходимые файлы ресурсов.
- Создаёт подкаталог и копирует в него файлы зависимостей Java-приложения.
- Собирает для приложения файлы
jar
. - Добавляет каталог с JetBrains Runtime.
- Формирует файл инсталлятора, выполняющего действия по установке дистрибутива в операционной системе:
- в Windows в виде файла
exe
(используется NSIS, Nullsoft Scriptable Install System); - в macOS в виде файлов
dmg
(используется утилита hdiutil); - в Linux в виде файлов
tar.gz
(создаётся с помощью библиотеки Apache Commons Compress).
- в Windows в виде файла
- Собирает архив формата
zip
, содержащий файлы для всех операционных систем, используя для его создания стандартные классы JDK из пакетаjava.util.zip
. Данный архив имеет в имени суффикс.portable
и не выкладывается на веб-сайт для скачивания.
Выводы
- Стандартным средством создания файлов дистрибутивов для различных операционных систем в JDK сейчас служит утилита jpackage.
- Наиболее удобным и популярным средством сборки с использованием Maven и Gradle является плагин JavaPackager.
- Самостоятельное создание собственного инструмента для сборки дистрибутивов подобно компании JetBrains возможно, но потребует много ресурсов для написания кода и его сопровождения.
- В настоящее время разумно выбрать JavaPackager при необходимости распространения Java-приложений с возможностью их установки пользователем в Windows, macOS и Linux.
9 октября 2024 года (онлайн) и 15-16 октября 2024 года (офлайн в Санкт-Петербурге и онлайн) состоится конференция для Java-разработчиков Joker 2024, на которую открыт приём заявок на доклады и можно купить билеты.