В статье рассказывается о построении полноценных дистрибутивов для 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.

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


Проект может быть найден на 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.

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

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



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

Хроника событий:

  1. Клонирован репозиторий OpenJFX и найдена ошибка.
  2. Обнаружен уже существующий JDK-8179033 с описанием тех же симптомов.
  3. По совету lany (спасибо ему большое) написаны письма раз и два в группу openjfx-dev, с предложением изменений, требуемых для исправления ошибки, и советом, как их правильность проверить.
  4. Переписка с исполнителями JDK-8179033 — благодарю Виктора Дроздова за доброжелательное отношение и терпение.
  5. Ошибка исправлена и исправление попало в очередную сборку предварительной версии Java 10jdk-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 значения свойств с именами <app.*>, содержащие:

    • полное наименование приложения;
    • краткое наименование приложения;
    • год (-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:

Комментарии (8)


  1. Tishka17
    27.12.2017 18:33
    +1

    Правильно ли я понял, что под линукс создание rpm/deb/etc. не рассматривалось? Без них не так интересно


    1. dbelob Автор
      27.12.2017 19:04

      При сборке дистрибутива под Linux сейчас создаются все возможные виды дистрибутивов при текущих значениях параметров (см. nativeBundles="all" у элемента <jfxdeploy> в файле pom.xml для модуля multiplatform-distribution-client).

      Для задачи <fx:deploy> все возможные значения параметра nativeBundles приведены в документации по указанной ссылке. Среди форматов есть и deb, и rpm. Про all сказано, что:

      Value all produces all applicable self-contained application packages for the platform on which the Ant tasks are run.


  1. kovserg
    28.12.2017 02:52

    А как создать desktop дистрибутив для 32bit windows?


    1. dbelob Автор
      28.12.2017 14:49

      Начиная с версии 9, Oracle Java только 64-битная. Для предыдущих версий (7 и 8) можно создать 32-битный дистрибутив.

      Требуемые действия:

      1. Скачать и установить, например, 32-битную Java 8.
      2. Изменить требуемые переменные окружения (JAVA_HOME, Path) операционной системы.
      3. В файле pom.xml (главном) заменить значение переменной на <project.build.javaVersion>1.8</project.build.javaVersion>.
      4. В файле pom.xml (модуля multiplatform-distribution-client) заменить значение системного пути к файлу ant-javafx.jar на <systemPath>${java.home}/../lib/ant-javafx.jar</systemPath>.


      1. kovserg
        28.12.2017 22:01
        +1

        Грусть, печаль владельцам 32bit десктопов или ну её нафиг java9+?


        1. dbelob Автор
          28.12.2017 22:53

          В предварительных сборках (Early-Access Builds) для Java 9 поддержка 32-битности ещё была, но в окончательном выпуске — уже нет. Достаточно подробное изложение истории в хронологическом порядке в ответе на Stack Overflow.

          При наличии 32-разрядных операционных систем тогда уж пользоваться Java 8, при 64-разрядной — самой последней версией, какая есть.


          1. kovserg
            29.12.2017 04:10

            Планируется переход на частый выпуск новых версий, oracle забивает на 32bit платформы. Sun WORE потихоньку становится Oracle whore (что бы всё работало нужно больше золота).
            Кстати java 10 не будет будут 18.3, 18.9,… Adobe уже довела до совершенства flash. Чем Oracle хуже. Вобщем продолжаем наблюдения, но пака не поздно переходим обратно на C++.

            ps: Несмотря на все улучшения и другие навороты все программы написанные на java досих пор пользуются славой как самые прожорливые и не поворотливые, имеющее внутри эпическое количество абстракций. При этом 64бит java как и раньше имеет 16 битые индексы на поля, константы и методы.

            pps: Изображенный на картинке Дюк, кидающий windows, macos и linux как бы намекает будет вам еще java-core


            1. dbelob Автор
              29.12.2017 12:34

              Кстати java 10 не будет будут 18.3, 18.9,…
              Уже ранее успели передумать, следующая версия будет как раз Java 10: письмо Марка Рейнхольда, новость о том же в еженедельном дайджесте JUG.ru, упоминание об этом в недавнем обзоре на InfoWorld и т.д.

              Предварительная версия именно JDK 10 упоминается и в данном хабрапосте.