Jar to exe
Сегодня речь пойдёт о десктоп программах написанных на java и их распространении. Со временем я столкнулся с проблемой — программы, которые я хочу показать знакомым не запускаются на их компьютере из-за отсутствия Java. Рассказывать о том как установить Java и запустить jar сборку — то ещё удовольствие. Особенно для пользователей Windows, которые привыкли запускать приложения двойным кликом.
В своё время я практиковал «exe» сборки собранные через Launch4j, но это не избавляло от необходимости установки Java. А рассказ знакомым про переменные среды и передачу аргументов заставлял их отказываться от идеи посмотреть мою программу.
Итак, ближе к делу, начиная с Java 9 появился славный инструмент jlink, который позволяет создать пользовательскую среду исполнения с минимальным количеством модулей. То есть свою Java с блек-джеком и распутными женщинами, которой мы будем передавать свой jar.
Для пользователя это будет выглядеть как «exe» файл и пара папок. И ни каких дополнительных установок делать не нужно. Ниже мы напишем самую простую программу на Java Swing (так как он встроен в Java), соберем Jar. Сделаем образ среды исполнения и «exe» файл. Даже со своим не скучным значком.
Программа и сборка jar:
Итак, создадим простое окно с надписью внутри Hello world . Откроем Ide, там создадим простой java проект, назову его SwingExempl.

Внутри напишем код для создания окна. Вот его листинг
import javax.swing.*;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {//Приложение Swing запускается в отдельном потоке событий. Это он:
@Override
public void run() {
JFrame frame = new JFrame("SwingExempl");//Создаём окно и задаём ему название:
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);// Это для того, чтобы при нажатии на «x» окно закрывалось.
frame.setSize(300,100);//тут его размер
JPanel panel = new JPanel();// Это панель для окна
panel.add(new JLabel("Hello world"));// В панель вставляем надпись
frame.add(panel);//Вставляем в окно панель
frame.setVisible(true);//Делаем всё видимым
}
});
}
Тут я написал комментарий к коду, что-то писать дополнительно не имеет смысла.
Теперь нам надо создать Jar архив с помощью IntelliJ IDEA, это не сложно. Для этого перейдём File — Project Structure — Artifacts
Там нажмём кнопку «+», где выберем jar, а там вкладку From modules with dependencies...

В появившемся окне следует указать Main класс и нажать кнопку ок

Теперь можно собрать наш Jar, для этого выберем пункт Build — Build artifacts ...

В появившемся окне выбрать SwingExampl.jar — Build

Теперь всё в папке out/artifacts/SwingExempl_jar/ появился наш архив SwingExempl.jar.
Его можно запустить двойным кликом и убедиться, что программа работает.

Зависимости и среда исполнения:
Теперь нам нужно создать среду исполнения, это будет что-то типа JDK, но изрядно кастрированная. Для того, чтобы не пихать в неё все модули, нам нужно узнать текущие модули используемые в нашей программе. В этом нам поможет утилита jdeps.
В папке с нашим jar архивом вызовем терминал, где наберём команду
jdeps.exe -s SwingExempl.jar
В моем случае на скриншоте путь до утилиты jdeps указан абсолютный, так как в переменных средах у меня нет jdeps. У меня jdk 20 и утилита jdeps находится там.

В нашем случае утилита вывела всего две зависимости:
SwingExempl.jar -> java.base
SwingExempl.jar -> java.desktop
Естественно в больших проектах будет больше модулей, часть из них может быть подключена в виде jar библиотек. Тогда следует запаковать их в свой jar и прописать в файле манифеста.
Зная наши зависимости,
Посмотреть все изображения
собираем среду исполнения утилитой jlink
jlink.exe —no-header-files —no-man-pages —compress=2 —strip-debug —add-modules java.base,java.desktop,jdk.accessibility —output ваш_путь\out\artifacts\SwingExempl_jar\out3
Кстати, папка out3 не должна существовать, иначе утилита выдаст ошибку. То есть она создаст папку сама. В итоге у нас появляется директория out3, в которой есть папка bin, а там наша java.exe
Хотя утилита не писала модуль jdk.accessibility мы его добавим, иначе будет ошибка при попытке запуска программы.
Настало время её проверить, для этого запустим наш jar из той java, которую мы собрали. Команда будет такой:
ваш_путь\out\artifacts\SwingExempl_jar\out3\bin\java.exe -jar SwingExempl.jar

Мы запускаем программу не от java, которая у нас в переменных средах Path, а от java.exe, которую мы собрали.\
Если вы видите окно программы, значит все идёт по плану. Вам удалось собрать среду исполнения и она работает. Фактически эту папку out3 мы и будем давать всем, кому хотим показать нашу программу.
Осталось создать в ней папку jar в директории ваш_путь\out\artifacts\SwingExempl_jar\out3\ и в неё засунуть наш SwingExempl.jar и уже можно передавать программу так. Так как той java, которую мы создали, хватит для запуска программы. Но мы хотим «exe». Придётся немного попрограммировать на «С» и поставить mingw.
Делаем «exe», пишем на «С»:
Для начала нам нужно скачать компилятор mingw Я распаковал его в корень C:/, получилось как-то так:

Потом прописал его в «переменных средах» — «системных переменных» — «Переменная Path» там создадим ещё одну переменную с путём в папку bin C:\MinGW64\bin

Теперь проверим наш компилятор, для этого вводим в терминале:
gcc --version
Если вы видите что-то в духе названия и версии, то все хорошо. В противном случае поищите статьи про установку mingw

Теперь настало время создать «exe» файл. Для этого я создам в директории ваш_путь\out\artifacts\SwingExempl_jar\ папку ccfolder

В неё положу значок моего будущего «exe» файла. Это будет кружка с кофе. Значок должен быть в формате ICO, у меня он будет называться Java.ico. Тут же я создам файл с названием resources.rc
В него с помощью блокнота запишу:
001 ICON "Java.ico"
Как то так:

Сохраняем данные и потом компилируем файл с помощью утилиты windres.exe Команда будет выглядеть так:
ваш_путь\out\artifacts\SwingExempl_jar\ccfolder> windres —use-temp-file resources.rc resources.o
В результате будет создан файл resources.o в той же папке.
Теперь напишем маленькую программку на «С», в которой мы будем запускать нашу программу jar\SwingExempl.jar. Для этого создадим файл runjar.c, а внутри напишем следующее... Листинг ниже.
#include <stdio.h>
#include <process.h>
int main (int argc, char *argv[], char **penv) {
_execl("bin\\javaw.exe", "bin\\javaw.exe", "-jar", "jar\\SwingExempl.jar", NULL);
return 0;
}
Получится как то так:

Теперь заключительный момент, создадим «exe» файл сразу в папке out3 с нашим дистрибутивом. Он будет всего лишь запускать наш jar в пользовательской среде. Для этого введём команду:
ваш_путь\out\artifacts\SwingExempl_jar\ccfolder> gcc -Wall -O3 -mwindows -o ваш_путь\out\artifacts\SwingExempl_jar\out3\SwingExempl.exe runjar.c resources.o
После этого файл SwingExempl.exe должен появиться в папке out3, в той же папке в директории jar должен лежать наш SwingExempl.jar . На этом всё. Двойным кликом по SwingExempl.exe запускается наша простая программа. Структура каталога out3 выглядит так:

Теперь можно переименовать папку out3 как вам угодно, заархивировать и передавать её друзьям с windows , программа будет запускаться без установки jdk или jre. Правда вес нашей программы вырос многократно. Сама jar весит 2кб., а папка со средой и нашим архивом около 50мб. Но это маленькая плата за то, что нам не надо рассказывать как установить java .
PS: Методика взята из книги JavaFX в подлиннике (Николай Прохоренок). Исправлены опечатки, несколько переработан материал.