Gluon 編譯 JavaFx -> exe
能力強的夥伴可以直接參考官方文件
需要注意的問題說在前面
寫這篇文章是為了將自己踩過的坑和熬過的夜以經驗的方式分享出來,給對這方面技術也感興趣的同志們一點點參考價值,希望這個技術棧的文章越來越多吧。
java AOT 編譯 區別於 jlink編譯
mvn gluonfx:runagent 首次編譯的時候這個必須執行,主要是自動生成graalvm需要的反射配置,因此要確保程式裡所有有反射的地方都要跑到。執行完畢後會生成這樣幾個檔案
mvn gluonfx:build ,這是編譯命令,執行的時間較長,執行完畢後,會在 target\gluonfx\x86_64-windows 目錄下生成對應的 exe檔案,就是最終的可以執行程式
mvn gluonfx:nativerun ,這個命令執行exe檔案,與直接執行exe不同,透過此命令執行能在控制檯列印出堆疊資訊,用於排查問題
windows exe編譯的過程涉及的環境的搭建較為複雜,如果只想構建apk的同志,請參考文章 Gluon 編譯 JavaFx -> android apk
開發工具
- idea 2023.3
- idea gluon plugin
- git
- apache-maven-3.8.4
環境準備
- vs 2022 community 版本 (使用微軟官方的安裝器安裝,社群版即可)
- jdk 11 or 17+ (可以使用idea進行下載安裝)
- GraalVM CE Gluon 22.1.0.1-Final
vs 2022的安裝明細
(來自官網文件/platforms/windows這一節)
可以參考我的安裝明細
以上步驟之後,新增一個路徑到Path環境變數中
(因為後續編譯的時候,會用到這個路徑下的cl,預設沒有新增到path,下面的版本號 14.29.30133
根據自己的安裝情況設定)
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.29.30133\bin\HostX86\x86
配置上jdk環境變數
# 新增環境變數
JAVA_HOME=D:\development\env\java\openjdk-21.0.2
CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
# Path新增條目
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
安裝GraalVM CE Gluon 22.1.0.1-Final
下載解壓到合適的目錄後配置好環境變數
GRAALVM_HOME=D:\development\env\java\graalvm-svm-java17-windows-gluon-22.1.0.1-Final
編譯官網示例專案
(到這裡,我預設你的環境已經安裝了git、mvn等工具,並配置相應的環境變數,且以上的步驟都沒有問題)
拉取專案到本地
git clone https://github.com/gluonhq/gluon-samples.git
使用idea開啟專案,設定專案的jdk為17+,給檔案 gluon-samples/HelloFX/pom.xml
檔案增加幾個build配置項
<properties>
<main.class>hellofx.HelloFX</main.class>
<gluonfx.target>host</gluonfx.target>
<gluonfx.maven.plugin.version>1.0.23</gluonfx.maven.plugin.version>
<javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<release>17</release>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.maven.plugin.version}</version>
<configuration>
<mainClass>${main.class}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>gluonfx-maven-plugin</artifactId>
<version>${gluonfx.maven.plugin.version}</version>
<configuration>
<target>${gluonfx.target}</target>
<mainClass>${main.class}</mainClass>
<reflectionList>
<list>.*\\.db$</list>
<list>.*\\.xlsx$</list>
</reflectionList>
</configuration>
</plugin>
</plugins>
</build>
新建build.bat檔案
在專案路徑 gluon-samples/HelloFX
新建一個build.bat檔案
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
mvn gluonfx:build -DskipTests=true -P desktop
執行編譯
cd 專案路徑
./build.bat
檢視結果
編譯結果輸出到了 gluon-samples\HelloFX\target\gluonfx\x86_64-windows
異常處理
1.java.io.IOException: Cannot run program "cl" (in directory "D:\workspace\code\mycode\Gluon\gluon-samples\HelloFX\target\gluonfx\x86_64-windows\gvm\HelloFX"): CreateProcess error=2, 系統找不到指定的檔案。
出現這個異常是因為上面的cl指令路徑沒有新增到path環境變數中
2.java.lang.IllegalArgumentException: We currently can't compile to aarch64-linux-android when running on x86_64-microsoft-windows
這個異常是編譯在x86_64的環境中編譯aarch64-linux-android,我們搭建的環境只能編譯exe,導致這個錯誤的原因是專案的profiles設定如下
而且是直接到
這裡面去執行的編譯,在這裡執行沒有預先執行vcvars64.bat,這也是前面寫那個build指令碼的原因
如果想要構建apk,請參考文章 Gluon 編譯 JavaFx -> android apk
修復方法就是將專案profiles選擇為desktop,同時使用指令碼去執行。
3.exe複製到其他機器執行不了,沒有任何反應
我排查下來發現是其他機器配置了一個jdk環境,使用的java版本和我編譯的版本有差別,執行的時候出現錯誤,但是沒搞懂為啥沒有任何提示資訊,將其他機器的java_home 環境變數失效化,程式就正常執行了,這裡可以臨時寫一下bat指令碼,將java_home改成其他路徑
將desktop編譯動作繫結packge到生命週期
這樣會將build指令碼執行和package進行捆綁
<properties>
<skip.exec>true</skip.exec>
</properties>
<profiles>
<profile>
<id>ios</id>
<properties>
<gluonfx.target>ios</gluonfx.target>
</properties>
</profile>
<profile>
<id>android</id>
<properties>
<gluonfx.target>android</gluonfx.target>
</properties>
</profile>
<profile>
<id>desktop</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env>desktop</env>
<skip.exec>false</skip.exec>
<gluonfx.target>host</gluonfx.target>
</properties>
</profile>
</profiles>
<build>
<plugins>
<!-- Exec Maven 外掛 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>run-prepare-env</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>./bin/build.bat</executable>
<arguments>
</arguments>
</configuration>
</execution>
</executions>
<configuration>
<skip>${skip.exec}</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.plugin.version}</version>
<configuration>
<mainClass>${mainClassName}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>gluonfx-maven-plugin</artifactId>
<version>${gluonfx.plugin.version}</version>
<configuration>
<target>${gluonfx.target}</target>
<attachList>
<list>display</list>
<list>lifecycle</list>
<list>statusbar</list>
<list>storage</list>
</attachList>
<reflectionList>
<list>com.xxx.PrimaryPresenter</list>
<list>com.xxx.SecondaryPresenter</list>
</reflectionList>
<mainClass>${mainClassName}</mainClass>
</configuration>
</plugin>
</plugins>
</build>