1: はじめに
本資料では Java がインストールされていない環境で、Eclipse で開発した自作プログラムを .exe ファイルとして実行できるようにする方法を紹介しています。
1-1: 参考資料
参考資料として紹介している記事を読んでいただくのが一番早いかと思います。もう少し丁寧に、ということでまとめ直させていただきました。
1-2: 実施環境
- Windows 11 Pro
- Eclipse 2023-12
- e(fx)clipse 3.8.0
- Oracle OpenJDK 21
- JavaFX 21
- Launch4j 3.50
2: 作業方法
2-1: サンプル Eclipse プロジェクトについて
サンプルの Eclipse プロジェクト名は「HelloFXSample」です。
これから、「HelloFXSample.exe」として実行できるようにしていきます。
動作は、ボタンがクリックされたら "Hello JavaFX World!" を表示するだけの単純なものです。
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
public class SampleController {
@FXML private Button button;
@FXML private Label label;
@FXML void onButtonClicked() {
label.setText("Hello JavaFX World!");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController">
<children>
<Button fx:id="button" layoutX="25.0" layoutY="55.0" mnemonicParsing="false" onAction="#onButtonClicked" text="Button" />
<Label fx:id="label" layoutX="25.0" layoutY="100.0" text="Label" />
</children>
</AnchorPane>
2-2: Eclipse プロジェクトのエクスポート
exe ファイル化したいプロジェクトを右クリックし、エクスポートを選択します。
「実行可能JARファイル」を選択し、次へ進みます。
必要な設定を行い、JARファイルを生成します。
項目 | 選択 |
---|---|
起動構成 | Main (ドロップダウンリストにある) |
エクスポート先 | JAR ファイルの保存先 |
ライブラリー処理 | 生成されるJARの隣のサブフォルダーに必須ライブラリーをコピー |
エクスポートすると、保存先に JAR ファイルと外部ライブラリ入りのフォルダが作成されます。
2-3: アプリ実行用の最小構成 JRE を作成する
Java の実行には JRE (Java Runtime Environment) が必要です。開発環境の JRE をそのままコピーしても動作しますが、配布用なので、なるべく最小限のサイズにしたいということで、最小構成の JRE を作成します。
2-3-1: jdeps によるモジュールの依存関係の確認
jdeps コマンドを使い、モジュールの依存関係を確認します。jdeps コマンドは JDK に含まれているため、パスが通っていれば利用できます。
> jdeps --module-path [JavaFX SDK の lib ディレクトリ] -s [JAR ファイル]
--module-path
オプションの [JavaFX SDK の lib ディレクトリ] には、インストールしているはずの JavaFX SDK の lib ディレクトリのパスを指定します。複数パスを指定する場合は ;
で区切れば良さそうです。
こちらの環境では、SDK を "C:\pleiades\javafx-sdk-21.0.2" に格納しているので、次のように指定しました。
> jdeps --module-path "C:\pleiades\javafx-sdk-21.0.2\lib" -s HelloFXSample.jar
HelloFXSample -> java.base
HelloFXSample -> javafx.base
HelloFXSample -> javafx.controls
HelloFXSample -> javafx.fxml
HelloFXSample -> javafx.graphics
最小構成 JRE 化するにあたって、jdeps コマンドで列挙された項目を、次で使う jlink コマンドの --add-modules
に指定します。
- java.base (不要)
- javafx.base
- javafx.controls
- javafx.fxml
- javafx.graphics (参考資料によると、不要とのこと)
自作モジュールを含む場合
自作モジュールを含む場合は、次のようなエラーが出るかもしれません。
> jdeps --module-path C:\pleiades\javafx-sdk-21.0.2\lib -s HelloFXSample.jar
Exception in thread "main" java.lang.module.FindException: Module ImportedSample2 not found, required by JarImportSample2
at java.base/java.lang.module.Resolver.findFail(Resolver.java:892)
at java.base/java.lang.module.Resolver.resolve(Resolver.java:192)
at java.base/java.lang.module.Resolver.resolve(Resolver.java:141)
at java.base/java.lang.module.Configuration.resolve(Configuration.java:420)
at java.base/java.lang.module.Configuration.resolve(Configuration.java:254)
at jdk.jdeps/com.sun.tools.jdeps.JdepsConfiguration$Builder.build(JdepsConfiguration.java:564)
at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.buildConfig(JdepsTask.java:607)
at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:561)
at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:537)
at jdk.jdeps/com.sun.tools.jdeps.Main.main(Main.java:50)
自作モジュールがあるとき、JAR ファイルのエクスポート時、JAR ファイルの隣に生成されている ~~_lib
フォルダに、依存する JAR ファイルがコピーされています。
そこで、 ~~_lib
ファイルも --module-path
に ;
で区切って指定します。
コマンド実行環境 (PowerShellなど) によっては ;
がコマンドの区切りとして認識されてしまうため、ダブルクォートします。
> jdeps --module-path "C:\pleiades\javafx-sdk-21.0.2\lib;HelloFXSample" -s HelloFXSample.jar
HelloFXSample -> ImportedSample2 (自作モジュール)
HelloFXSample -> java.base (無視してOK)
HelloFXSample -> javafx.base
HelloFXSample -> javafx.controls
HelloFXSample -> javafx.fxml
HelloFXSample -> javafx.graphics (無視してOK)
ここでは ImportedSample2 モジュールにも依存していることが分かります。
- ImportedSample2 (必要)
- java.base (不要)
- javafx.base
- javafx.controls
- javafx.fxml
- javafx.graphics (参考資料によると、不要とのこと)
2-3-2: jlink コマンドによる JRE の作成
jlink コマンドを使い、配布アプリケーション専用の JRE を作成します。
> jlink --module-path [JavaFX の jmods ディレクトリ] --add-modules java.base,javafx.base,javafx.controls,javafx.fxml --output jre-min
--module-path
オプションの [JavaFX の jmods ディレクトリ] には JavaFX SDK 配布サイトでダウンロードできる jmods ファイルのディレクトリを指定します。
--add-modules
オプションには jdeps コマンドで見つかった依存関係のライブラリをカンマ区切りで指定します。
> jlink --module-path "C:\pleiades\javafx-jmods-21.0.2" --add-modules java.base,javafx.base,javafx.controls,javafx.fxml --output jre-min
うまく行けば jre-min ディレクトリが生成されています。
自作モジュールを含む場合
自作モジュールを含む場合は、次のようなエラーが発生するかもしれません。
> jlink --module-path c:\pleiades\javafx-jmod
s-21.0.2\ --add-modules java.base,javafx.base,javafx.controls,javafx.fxml,ImportedSample2 --ou
tput jre-min
エラー: Module ImportedSample2 not found
java.lang.module.FindException: Module ImportedSample2 not found
at java.base/java.lang.module.Resolver.findFail(Resolver.java:892)
at java.base/java.lang.module.Resolver.resolve(Resolver.java:129)
at java.base/java.lang.module.Configuration.resolve(Configuration.java:420)
at java.base/java.lang.module.Configuration.resolve(Configuration.java:254)
at jdk.jlink/jdk.tools.jlink.internal.Jlink$JlinkConfiguration.resolve(Jlink.java:217)
at jdk.jlink/jdk.tools.jlink.internal.JlinkTask.createImageProvider(JlinkTask.java:523)
at jdk.jlink/jdk.tools.jlink.internal.JlinkTask.createImage(JlinkTask.java:411)
at jdk.jlink/jdk.tools.jlink.internal.JlinkTask.run(JlinkTask.java:286)
at jdk.jlink/jdk.tools.jlink.internal.Main.run(Main.java:56)
at jdk.jlink/jdk.tools.jlink.internal.Main.main(Main.java:34)
こちらも jdeps コマンドのときと同様に --module-path
に ~~_lib
フォルダを指定しましょう。そして、 --add-modules
オプションに自作モジュールも追記することを忘れないようにしましょう。
> jlink --module-path "c:\pleiades\javafx-jmods-21.0.2\;HelloFXSample_lib/ImportedSample2.jar" --add-modules java.base,javafx.base,javafx.controls,javafx.fxml,ImportedSample2 --output jre-min
続きの手順は、同じ操作で作成できるはずです。
2-4: JARファイルを動作確認する
PowerShell または cmd.exe で JAR ファイルを保存したディレクトリで作業します。
次のようなコマンドで、JAR ファイルからプログラムが実行できることを確認します。
> .\jre-min\bin\java.exe -jar [JAR ファイル]
> .\jre-min\bin\java.exe -jar HelloFXSample.jar
2-5: EXE ファイルに変換する
変換には Launch4j を使用します。次のサイトからダウンロードし、インストールしてください。
http://launch4j.sourceforge.net/
Basicタブでは次のような設定を行います。
設定項目 | 設定内容 |
---|---|
Output file | 出力する exe ファイル名を設定します。JRE から相対パスで指定できるよう jre-min フォルダの横に指定します。 |
Jar | 変換するJARファイルを指定します |
JREタブでは次のような設定を行います。
設定項目 | 設定内容 |
---|---|
JRE paths | exe ファイルを基準に jre-min ディレクトリを相対パスで指定します |
Min JRE version | 必須項目です。21.0.2 のように Java のバージョンを指定します |
最後に、画面上部の歯車マーク (Build Wrapper) で設定ファイルを保存し、実行します。実行時のログに "Successfully created" があれば成功しているはずです。
画面上部の再生マーク (Test Wrapper) で実行して動作を確認します。
作業ディレクトリにある、jre-min ディレクトリと exe ファイルを配布することで、どこでも実行できます。
こちらの環境では、lib ディレクトリや jar ファイルは必要ありませんでした。
3: おまけ(自作モジュールを含むプロジェクト)
JavaFX プロジェクトを作成するとき、自作プログラムをインポートしようとしてもうまく動かないことがあるかもしれません。
そして、原因の一つに、Java 9 から導入されたモジュールシステムがあるかもしれません。
Java 9 からはパッケージなし(デフォルトパッケージ)は非推奨となっており、パッケージなしの JAR ファイルをインポートするとうまく動かなくなります。
これから説明する失敗を起こさないためには、何かしらのパッケージに所属させ、module_info.java を適切に指定してください。
3-1: 再現手順 (非推奨のデフォルトパッケージで作ってしまう)
まず、EXE ファイル化しようとする Eclipse プロジェクトは、次のような ImportedSample プロジェクトで作成された JAR ファイルをインポートしていたとします。
3-1-1: インポートされる ImportedSample プロジェクトの作成
動作としては、main メソッドを実行すると、sayHello メソッドの戻り値 "Hello.sayHello" を表示するだけです。
Eclipse プロジェクトの状態(デフォルトパッケージの状態)
public class Hello {
public static void main(String[] args) {
System.out.println(sayHello());
}
public static String sayHello() {
return "Hello.sayHello";
}
}
ImportedSample プロジェクトを、他の Java プロジェクトとして使えるように JAR ファイル化します。
JAR ファイル化の手順は、JavaFX プロジェクトのときと同様ですが、エクスポート時の処理にて「生成される JAR に必須ライブラリをパッケージ」を選択しておきます。
次のようなコマンドで、生成した JAR ファイル単体の動作確認を行います。
> java -jar ImportedSample.jar
Hello.sayHello (出力結果)
3-1-2: プロジェクトの作成と JAR ファイルのインポート
JAR ファイルをインポートする JavaFX プロジェクトを作成します。
今回は JarImportSample という名前で作成しました。
JarImportSample のフォルダに、先ほど作成した JAR ファイルをコピーしておきます。(図を見るとツリーに JAR ファイルが追加されている)
プロジェクト名を右クリック > [ビルド・パス] > [ビルド・パスの構成] を選択します。
次のようにプログラムを書き換え、JAR でインポートした Hello クラスを利用しようとしてみます。
package application;
import Hello;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
public class SampleController {
@FXML private Button button;
@FXML private Label label;
@FXML void onButtonClicked() {
label.setText(Hello.sayHello());
}
}
残念ながら、Hello クラスが見つからないと言われてしまいます。
3-2: 原因
こちらで解説されています。(すみません。あまり理解していません)
3-3: 解決方法
3-3-1: インポートされる ImportedSample2 プロジェクトの再作成
ImportedSample プロジェクトの作り方が間違っていますので、ImportedSample2 として作り直します。
注意点としては、次のとおりです。
- クラスはなにかしらのパッケージの中につくること
- 今回は application2 パッケージに所属させています (わざと別名)
- つまり、Hello.java は application2 フォルダの中に作成されています
- module-info.java を作成する
- Eclipse ならプロジェクト作成時のオプションで指定できます
- モジュール名は、パッケージ名と同じにしました
Hello.java の先頭には package application2;
の追加が必要です。
package application2;
public class Hello {
public static void main(String[] args) {
System.out.println(sayHello());
}
public static String sayHello() {
return "Hello.sayHello2";
}
}
module-info.java には、application パッケージの公開設定を指定します。
module ImportedSample2 {
exports application2;
}
先ほどと同様の手順で JAR ファイルを生成し直し、単体で動作確認します。
> java -jar ImportedSample2.jar
Hello.sayHello2 (出力結果)
3-3-2: プロジェクトの作成と JAR ファイルのインポート
うまく動いたら、JAR ファイルを JavaFX プロジェクトで読み込み直してみましょう。
ここでは新しく作り直して JarImportSample2 としています。
次のようにプログラムを書き換え、JAR でインポートした Hello クラスを利用します。
package application;
import application2.Hello; // application2 パッケージに所属し直したため
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
public class SampleController {
@FXML private Button button;
@FXML private Label label;
@FXML void onButtonClicked() {
label.setText(Hello.sayHello());
}
}
この時点では、application2/Hello
が存在しないと言われてしまいます。
module-info.java の中身を次のように書き換え、ImportedSample2
モジュールを読み込むように指定します。
module JarImportSample2 {
requires javafx.controls;
requires javafx.fxml;
requires ImportedSample2;
opens application to javafx.graphics, javafx.fxml;
}
すると、動くようになっているはずです。