[OpenJDK11&JavaFX13]IntelliJ+gradleでjavaFXアプリをビルドし、軽量JREと合わせて配布可能なパッケージを作成する


配布可能なJavaFXアプリを作成したかったのですが、試行錯誤したのでこちらにまとめます。

環境

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

※macOS Mojave(10.14.6)でも同様の手順で作成できました。

$ java --version
openjdk version "11.0.4" 2019-07-16
OpenJDK Runtime Environment (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3)
OpenJDK 64-Bit Server VM (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3, mixed mode, sharing)

$ jlink --version
11.0.4

JDKの選定

最初はJavaFXを同梱しているAmazon CorrettoなどのJDKを使用しようと思っていました。
しかし、以下の記事を読んで、今回はOpenjdk-11を使用し、JavaFXはライブラリとして使用し、配布時に軽量JREを生成することにしました。
OpenJFX時代のJDK選び - もしくはOpenJFX時代のアプリケーション配布

手順

IntelliJでgradleプロジェクトを作成

IntelliJで、新規gradleプロジェクトを作成します。

build.gradleにjavafx用の設定を追加

OpenJFXの公式サイトに記載されていた以下のソースを参考に、build.gradleを作成します。
https://github.com/openjfx/samples/blob/master/IDE/IntelliJ/Modular/Gradle/hellofx/build.gradle

なお、IntelliJの設定でもともと設定で「Automattically import this project on changes in build script files」にチェックを入れていますので、よしなにモジュールはインポートされます。

build.gradle
plugins {
    id 'java'
    id "org.openjfx.javafxplugin" version "0.0.8" // 追加
}

group 'com.example'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

// 追加
javafx {
    version = "13"
    modules = [ 'javafx.controls', 'javafx.fxml' ]
}

// 実行可能Jarを作成する際のMainクラスを指定
jar {
    manifest {
        attributes 'Main-Class': 'Main'
    }
}

テスト用のJavaFxクラスを作成

Main.java
import javafx.application.Application;

public class Main {
    public static void main(String... args){
        Application.launch(MyApplication.class);
    }
}
MyApplication.java
import javafx.application.Application;
import javafx.stage.Stage;

public class MyApplication extends Application {
    public MyApplication(){
        super();
    }

    @Override
    public void start(Stage primaryStage) throws Exception{
        primaryStage.setTitle("Hello");
        primaryStage.setWidth(300);
        primaryStage.setHeight(200);
        primaryStage.show();
    }
}

こちらを実行した結果、以下の画面が立ち上がります。

buildして、実行可能JARファイルを作成

コンソールにて、gradle buildを実行します。
特に問題がなければ、実行可能jarファイルが作成されるかと思います。

この状態で、jarファイルを実行してみると・・・

このように、javafx.application.Applicationクラスが無いよと怒られます。
これは、openjdk-11.0.4のjre内に、JavaFXモジュールが存在していないからですね。

軽量JREを作成する

では、jlinkを使って今回のjarファイルを動作させるための軽量jreを作成していきましょう!
今回は、以下の記事を参考にさせていただきました。
配布用の軽量 JRE の作り方

まず、以下ページのDownloadsより、jmodsをダウンロードします。
https://openjfx.io/

ダウンロードしたzipファイルを展開し、適当なパスに配置します。
(今回は、/usr/lib/jvm/javafx-13-openjfx/javafx-jmods-13に配置しています。)

なお、今回はLinux用jmodsをダウンロードしました。
※各実行環境のOSに合わせ、モジュールをダウンロードしましょう。

次に、jdeps ${jarファイルパス}コマンドにて、今回のjarファイルを実行するために必要なモジュールを調べます。

今回は、以下のモジュールを使えば良さそうです。
・java.lang -> java-base
・java.application -> javafx.base
・javafx.stage -> javafx.controls

※本当は、jdeps --list-deps ${jarファイルパス}とすると、どのモジュールを使えばよいかわかるようですが、何故かこのオプションが使用できず・・・。

必要なモジュールがわかったので、軽量jreを作成します。
以下のjlinkコマンドで、上記のモジュールを指定して作成できました。

$ jlink --compress=2 --module-path /usr/lib/jvm/java-11-openjdk-amd64/jmods:/usr/lib/jvm/javafx-13-openjfx/javafx-jmods-13 --add-modules java.base,javafx.base,javafx.controls --output jre

カレントパスにjreディレクトリが作成されました。
jre/bin/javaのバイナリも作成されています。

作成したJREを使用して、JARファイルを実行する。

では、最後に作成したjreを利用して、プログラムを実行してみましょう。

$ ./jre/bin/java -jar build/libs/javafx-sample-1.0-SNAPSHOT.jar 

無事に実行できました!

あとはJARファイルとjreをよしなにディレクトリにまとめ、実行用のシェルやbatファイルを作成するなり、appパッケージにまとめるなりすると完成です。
(以下は、フォルダにまとめ、シェルを作った例)

$ ls
javafx-sample-1.0-SNAPSHOT.jar  jre  start.sh

$ cat start.sh
#!/bin/sh
BASE=$(cd $(dirname $0); pwd)
$BASE/jre/bin/java -jar $BASE/javafx-sample-1.0-SNAPSHOT.jar

## sh start.shで実行可能

お疲れ様でした!

初めてjlinkやjdepsなどのコマンドを使い軽量JREを作成しましたが、使用者の環境(jdkなど)に依存しないで実行できるJavaFXアプリを作る際は、この方法が簡単かなと思います。
なお、今回のjreディレクトリのサイズは368.3MBなので、もっと色々できるようにすると重たくなってしまうのかな?

以上です。ありがとうございました!