配布可能な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」にチェックを入れていますので、よしなにモジュールはインポートされます。
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クラスを作成
import javafx.application.Application;
public class Main {
public static void main(String... args){
Application.launch(MyApplication.class);
}
}
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ファイルが作成されるかと思います。
このように、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なので、もっと色々できるようにすると重たくなってしまうのかな?
以上です。ありがとうございました!