getResourceBundleProperty() {
17 | return resourceBundleProperty;
18 | }
19 |
20 | public ResourceBundle getResourceBundle() {
21 | return getResourceBundleProperty().get();
22 | }
23 |
24 | public void setResourceBundle(ResourceBundle resourceBundle) {
25 | getResourceBundleProperty().set(resourceBundle);
26 | }
27 |
28 | public StringBinding getStringBinding(String key) {
29 | return new StringBinding() {
30 | {
31 | bind(resourceBundleProperty);
32 | }
33 |
34 | @Override
35 | protected String computeValue() {
36 | return getResourceBundle().getString(key);
37 | }
38 | };
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/.github/workflows/windows.yml:
--------------------------------------------------------------------------------
1 | name: Build installer for Windows
2 | on:
3 | push:
4 | branches:
5 | - java21
6 |
7 | jobs:
8 | build-app:
9 | strategy:
10 | matrix:
11 | os: [ windows-2019 ]
12 | name: Build the application
13 | runs-on: ${{ matrix.os }}
14 | steps:
15 | - name: checkout
16 | uses: actions/checkout@v2
17 |
18 | - name: Setup Java
19 | uses: actions/setup-java@v3
20 | with:
21 | distribution: 'temurin'
22 | java-version: 21
23 |
24 | - run: $Env:Path += ";C:\Program Files (x86)\WiX Toolset v3.11\bin"
25 |
26 | - name: build
27 | run: .\gradlew.bat jpackage
28 | shell: pwsh
29 |
30 | - name: copy
31 | run: New-Item -Name "staging" -ItemType "directory"; Copy-Item ".\build\build-package\*.msi" -Destination "staging"
32 | shell: pwsh
33 |
34 | - name: get current time
35 | uses: 1466587594/get-current-time@v2
36 | id: current-time
37 | with:
38 | format: YYYYMMDD-HH
39 | utcOffset: "+08:00"
40 |
41 | - name: upload build result
42 | uses: actions/upload-artifact@v4
43 | with:
44 | name: JavaFXSample-${{ steps.current-time.outputs.formattedTime }}-${{ matrix.os }}
45 | path: staging
46 |
--------------------------------------------------------------------------------
/README.en-US.md:
--------------------------------------------------------------------------------
1 | JavaFX-Package-Sample
2 | JavaFX Project Packaging Example
3 | English | 中文
4 |
5 | ## Branches
6 | - `java21` Package a `modular` JavaFX project using `Maven`, `Gradle`, and `GraalVM`.
7 | - `java21-non-modular` [Link](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java21-non-modular) Package a `non-modular` JavaFX project using `Maven`.
8 | - `java22-win32` [Link](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java22-win32) A JavaFX project that implements Windows global keyboard event listening by utilizing the `Foreign Function and Memory (FFM) API`.
9 |
10 | ## Requirements
11 | PowerShell:
12 | ```
13 | $env:JAVA_HOME = "C:\CommandLineTools\Java\jdk-21\"
14 | ```
15 | ```
16 | $env:GRAALVM_HOME = "C:\CommandLineTools\Java\graalvm-jdk-21.0.2+13.1\"
17 | ```
18 |
19 | ## Getting Started
20 | Gradle:
21 | ```
22 | .\gradlew.bat jpackageImage
23 | .\gradlew.bat jpackage
24 | ```
25 |
26 | Maven:
27 | ```
28 | mvn clean
29 | mvn package
30 | mvn exec:exec@image
31 | mvn exec:exec@installer
32 | ```
33 |
34 | GraalVM:
35 | ```
36 | .\gradlew.bat nativeRunAgent
37 | .\gradlew.bat nativeBuild
38 | .\gradlew.bat nativeRun
39 | ```
40 | ```
41 | mvn -Pwin gluonfx:build
42 | mvn -Pwin gluonfx:run
43 |
44 | mvn -Pmac gluonfx:build
45 | mvn -Pmac gluonfx:run
46 | ```
--------------------------------------------------------------------------------
/src/main/java/com/icuxika/AppResource.java:
--------------------------------------------------------------------------------
1 | package com.icuxika;
2 |
3 | import com.icuxika.i18n.ObservableResourceBundleFactory;
4 | import javafx.beans.binding.StringBinding;
5 | import javafx.beans.property.ObjectProperty;
6 | import javafx.beans.property.SimpleObjectProperty;
7 |
8 | import java.net.URL;
9 | import java.util.Arrays;
10 | import java.util.List;
11 | import java.util.Locale;
12 | import java.util.ResourceBundle;
13 |
14 | public class AppResource {
15 |
16 | /**
17 | * 默认语言文件 Base Name
18 | */
19 | private static final String LANGUAGE_RESOURCE_NAME = "LanguageResource";
20 |
21 | /**
22 | * 语言资源工厂
23 | */
24 | private static final ObservableResourceBundleFactory LANGUAGE_RESOURCE_FACTORY = new ObservableResourceBundleFactory();
25 |
26 | /**
27 | * 支持的语言集合,应与语言资源文件同步手动更新
28 | */
29 | public static final List SUPPORT_LANGUAGE_LIST = Arrays.asList(Locale.SIMPLIFIED_CHINESE, Locale.ENGLISH);
30 |
31 | /**
32 | * 记录当前所选时区
33 | */
34 | private static final ObjectProperty currentLocale = new SimpleObjectProperty<>();
35 |
36 | public static ObjectProperty currentLocaleProperty() {
37 | return currentLocale;
38 | }
39 |
40 | public static void setCurrentLocale(Locale locale) {
41 | currentLocaleProperty().set(locale);
42 | }
43 |
44 | /**
45 | * 更换语言的组件使用此方法初始化自己的值,调用 {@link AppResource#setLanguage(Locale)} 来更新界面语言
46 | *
47 | * @return 当前界面语言
48 | */
49 | public static Locale getCurrentLocale() {
50 | return currentLocaleProperty().get();
51 | }
52 |
53 | /**
54 | * 更新界面语言
55 | *
56 | * @param locale 区域
57 | */
58 | public static void setLanguage(Locale locale) {
59 | setCurrentLocale(locale);
60 | LANGUAGE_RESOURCE_FACTORY.setResourceBundle(ResourceBundle.getBundle(LANGUAGE_RESOURCE_NAME, locale));
61 | }
62 |
63 | /**
64 | * 获取指定标识的字符串绑定
65 | *
66 | * @param key 标识
67 | * @return 对应该标识的字符串属性绑定
68 | */
69 | public static StringBinding getLanguageBinding(String key) {
70 | return LANGUAGE_RESOURCE_FACTORY.getStringBinding(key);
71 | }
72 |
73 | /**
74 | * 有此类所在路径决定相对路径基于/com/icuxika
75 | *
76 | * @param path 资源文件相对路径
77 | * @return 资源文件路径
78 | */
79 | public static URL load(String path) {
80 | return AppResource.class.getResource(path);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/native-image/jni-config.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name":"[Lcom.sun.glass.ui.Screen;"
4 | },
5 | {
6 | "name":"com.sun.glass.ui.Cursor",
7 | "methods":[{"name":"getNativeCursor","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }]
8 | },
9 | {
10 | "name":"com.sun.glass.ui.Screen",
11 | "methods":[{"name":"","parameterTypes":["long","int","int","int","int","int","int","int","int","int","int","int","int","int","int","int","float","float","float","float"] }]
12 | },
13 | {
14 | "name":"com.sun.glass.ui.Size",
15 | "methods":[{"name":"","parameterTypes":["int","int"] }]
16 | },
17 | {
18 | "name":"com.sun.javafx.font.directwrite.DWRITE_GLYPH_METRICS",
19 | "fields":[{"name":"advanceHeight"}, {"name":"advanceWidth"}, {"name":"bottomSideBearing"}, {"name":"leftSideBearing"}, {"name":"rightSideBearing"}, {"name":"topSideBearing"}, {"name":"verticalOriginY"}],
20 | "methods":[{"name":"","parameterTypes":[] }]
21 | },
22 | {
23 | "name":"com.sun.javafx.font.directwrite.DWRITE_GLYPH_RUN",
24 | "fields":[{"name":"advanceOffset"}, {"name":"ascenderOffset"}, {"name":"bidiLevel"}, {"name":"fontEmSize"}, {"name":"fontFace"}, {"name":"glyphAdvances"}, {"name":"glyphIndices"}, {"name":"isSideways"}],
25 | "methods":[{"name":"","parameterTypes":[] }]
26 | },
27 | {
28 | "name":"com.sun.javafx.font.directwrite.DWRITE_MATRIX",
29 | "fields":[{"name":"dx"}, {"name":"dy"}, {"name":"m11"}, {"name":"m12"}, {"name":"m21"}, {"name":"m22"}],
30 | "methods":[{"name":"","parameterTypes":[] }]
31 | },
32 | {
33 | "name":"com.sun.javafx.font.directwrite.RECT",
34 | "fields":[{"name":"bottom"}, {"name":"left"}, {"name":"right"}, {"name":"top"}],
35 | "methods":[{"name":"","parameterTypes":[] }]
36 | },
37 | {
38 | "name":"com.sun.prism.impl.PrismSettings",
39 | "fields":[{"name":"disableD3D9Ex"}, {"name":"forceGPU"}, {"name":"isVsyncEnabled"}, {"name":"verbose"}]
40 | },
41 | {
42 | "name":"java.lang.Class",
43 | "methods":[{"name":"forName","parameterTypes":["java.lang.String","boolean","java.lang.ClassLoader"] }]
44 | },
45 | {
46 | "name":"java.lang.Runnable",
47 | "methods":[{"name":"run","parameterTypes":[] }]
48 | },
49 | {
50 | "name":"java.lang.String",
51 | "methods":[{"name":"toLowerCase","parameterTypes":["java.util.Locale"] }]
52 | },
53 | {
54 | "name":"java.util.ArrayList",
55 | "methods":[{"name":"","parameterTypes":["int"] }, {"name":"add","parameterTypes":["java.lang.Object"] }]
56 | },
57 | {
58 | "name":"java.util.HashMap",
59 | "methods":[{"name":"containsKey","parameterTypes":["java.lang.Object"] }, {"name":"put","parameterTypes":["java.lang.Object","java.lang.Object"] }]
60 | }
61 | ]
62 |
--------------------------------------------------------------------------------
/src/main/java/com/icuxika/MainApp.java:
--------------------------------------------------------------------------------
1 | package com.icuxika;
2 |
3 | import io.github.palexdev.materialfx.controls.MFXButton;
4 | import io.github.palexdev.materialfx.enums.ButtonType;
5 | import javafx.application.Application;
6 | import javafx.geometry.Insets;
7 | import javafx.geometry.Pos;
8 | import javafx.scene.Scene;
9 | import javafx.scene.control.Label;
10 | import javafx.scene.layout.Background;
11 | import javafx.scene.layout.BackgroundFill;
12 | import javafx.scene.layout.CornerRadii;
13 | import javafx.scene.layout.VBox;
14 | import javafx.scene.paint.Color;
15 | import javafx.stage.Stage;
16 | import org.slf4j.Logger;
17 | import org.slf4j.LoggerFactory;
18 |
19 | import java.util.Locale;
20 |
21 | public class MainApp extends Application {
22 |
23 | private static final Logger LOGGER = LoggerFactory.getLogger(MainApp.class);
24 |
25 | @Override
26 | public void start(Stage primaryStage) throws Exception {
27 | AppResource.setLanguage(Locale.SIMPLIFIED_CHINESE);
28 |
29 | Label label = new Label();
30 | label.textProperty().bind(AppResource.currentLocaleProperty().asString().concat(": ").concat(AppResource.getLanguageBinding("title")));
31 |
32 | MFXButton zhButton = new MFXButton("中文");
33 | zhButton.setPrefSize(120, 40);
34 | zhButton.setButtonType(ButtonType.FLAT);
35 | zhButton.setTextFill(Color.WHITE);
36 | zhButton.setBackground(new Background(new BackgroundFill(Color.DODGERBLUE, new CornerRadii(4), Insets.EMPTY)));
37 | zhButton.setOnAction(event -> AppResource.setLanguage(Locale.SIMPLIFIED_CHINESE));
38 |
39 | MFXButton enButton = new MFXButton("英文");
40 | enButton.setPrefSize(120, 40);
41 | enButton.setButtonType(ButtonType.FLAT);
42 | enButton.setTextFill(Color.WHITE);
43 | enButton.setBackground(new Background(new BackgroundFill(Color.DODGERBLUE, new CornerRadii(4), Insets.EMPTY)));
44 | enButton.setOnAction(event -> AppResource.setLanguage(Locale.ENGLISH));
45 |
46 | VBox vBox = new VBox();
47 | vBox.setAlignment(Pos.CENTER);
48 | vBox.setSpacing(10);
49 | vBox.getChildren().addAll(label, zhButton, enButton);
50 |
51 | primaryStage.titleProperty().bind(AppResource.getLanguageBinding("title"));
52 | primaryStage.setScene(new Scene(vBox, 600, 400));
53 | primaryStage.show();
54 |
55 | LOGGER.trace("[trace]日志控制台输出");
56 | LOGGER.debug("[debug]日志控制台输出");
57 | LOGGER.info("[info]日志记录到logs/application.log中");
58 | LOGGER.warn("[warn]日志记录到logs/application.log中");
59 | LOGGER.error("[error]日志记录到logs/application.log中");
60 | }
61 |
62 | public static void main(String[] args) {
63 | launch(args);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 |
88 | @rem Execute Gradle
89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
90 |
91 | :end
92 | @rem End local scope for the variables with windows NT shell
93 | if "%ERRORLEVEL%"=="0" goto mainEnd
94 |
95 | :fail
96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
97 | rem the _cmd.exe /c_ return code!
98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
99 | exit /b 1
100 |
101 | :mainEnd
102 | if "%OS%"=="Windows_NT" endlocal
103 |
104 | :omega
105 |
--------------------------------------------------------------------------------
/Windows下遇到编码问题可以看看.md:
--------------------------------------------------------------------------------
1 | Windows下一些编码问题的记录
2 | ==========
3 |
4 | ## 添加代码
5 | 在`MainApp`的`start`方法末尾添加
6 | ```
7 | System.out.println("你好");
8 | ```
9 |
10 | ## PowerShell
11 | 查看当前编码
12 | ```shell
13 | [System.Console]::OutputEncoding
14 | ```
15 | ```shell
16 | BodyName : gb2312
17 | EncodingName : 简体中文(GB2312)
18 | HeaderName : gb2312
19 | WebName : gb2312
20 | WindowsCodePage : 936
21 | IsBrowserDisplay : True
22 | IsBrowserSave : True
23 | IsMailNewsDisplay : True
24 | IsMailNewsSave : True
25 | IsSingleByte : False
26 | EncoderFallback : System.Text.InternalEncoderBestFitFallback
27 | DecoderFallback : System.Text.InternalDecoderBestFitFallback
28 | IsReadOnly : False
29 | CodePage : 936
30 | ```
31 |
32 | ### 1.使用`mvn javafx:run`
33 | > Java标准输出流(GB2312) -> PowerShell(GB2312)
34 | ```shell
35 | 你好
36 | ```
37 |
38 | ### 2.使用`.\gradlew.bat run`
39 | > Java标准输出流(GB2312) -> gradle(UTF-8) -> PowerShell(GB2312)
40 | ```shell
41 | 锟斤拷锟?
42 | ```
43 | ### 3.修改`build.gradle`中的`application`配置块为
44 | > 使标准输出流使用`UTF-8`编码
45 | ```shell
46 | application {
47 | applicationName = "JavaFXSample"
48 | mainModule.set("sample")
49 | mainClass.set("com.icuxika.MainApp")
50 | applicationDefaultJvmArgs = [
51 | // ZGC
52 | "-XX:+UseZGC",
53 | // 当遇到空指针异常时显示更详细的信息
54 | "-XX:+ShowCodeDetailsInExceptionMessages",
55 | "-Dsun.java2d.opengl=true",
56 | // 不添加此参数,打包成exe后,https协议的网络图片资源无法加载
57 | "-Dhttps.protocols=TLSv1.1,TLSv1.2",
58 | "-Dsun.stdout.encoding=UTF-8",
59 | "-Dsun.stderr.encoding=UTF-8"
60 | ]
61 | }
62 | ```
63 | ### 4.再次执行`.\gradlew.bat run`
64 | > Java标准输出流(UTF-8) -> gradle(UTF-8) -> PowerShell(GB2312)
65 | ```shell
66 | 浣犲ソ
67 | ```
68 |
69 | ### 5.设置`PowerShell`使用`UTF-8`编码
70 | ```shell
71 | [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
72 | ```
73 | ```shell
74 | Preamble :
75 | BodyName : utf-8
76 | EncodingName : Unicode (UTF-8)
77 | HeaderName : utf-8
78 | WebName : utf-8
79 | WindowsCodePage : 1200
80 | IsBrowserDisplay : True
81 | IsBrowserSave : True
82 | IsMailNewsDisplay : True
83 | IsMailNewsSave : True
84 | IsSingleByte : False
85 | EncoderFallback : System.Text.EncoderReplacementFallback
86 | DecoderFallback : System.Text.DecoderReplacementFallback
87 | IsReadOnly : False
88 | CodePage : 65001
89 | ```
90 |
91 | ### 6.使用`mvn javafx:run`
92 | > Java标准输出流(GB2312) -> PowerShell(UTF-8)
93 | ```shell
94 | ���
95 | ```
96 |
97 | ### 7.使用`.\gradlew.bat run`
98 | > Java标准输出流(UTF-8) -> PowerShell(UTF-8)
99 | ```shell
100 | 你好
101 | ```
102 |
103 | ### 8.修改`build.gradle`中的`application`配置块为
104 | ```shell
105 | application {
106 | applicationName = "JavaFXSample"
107 | mainModule.set("sample")
108 | mainClass.set("com.icuxika.MainApp")
109 | applicationDefaultJvmArgs = [
110 | // ZGC
111 | "-XX:+UseZGC",
112 | // 当遇到空指针异常时显示更详细的信息
113 | "-XX:+ShowCodeDetailsInExceptionMessages",
114 | "-Dsun.java2d.opengl=true",
115 | // 不添加此参数,打包成exe后,https协议的网络图片资源无法加载
116 | "-Dhttps.protocols=TLSv1.1,TLSv1.2"
117 | ]
118 | }
119 | ```
120 |
121 | ### 9.再次执行`.\gradlew.bat run`
122 | > Java标准输出流(GB2312) -> gradle(UTF-8) -> PowerShell(UTF-8)
123 | >
124 | ```shell
125 | ���
126 | ```
127 |
128 | ### 总结
129 | - java 默认标准输出流使用的编码由操作系统的编码决定
130 | - maven 以`Maven plugin for JavaFX`插件为例,它实际获取到`java`和`jlink`的路径后通过`cmd /c java`的方式来执行命令,且插件没有提供参数让你传递`vm`选项,因此无法传递`sun.stdout.encoding`之类的属性给到插件里的命令,想要不乱码,只能使显示输出的前端编码匹配操作系统的默认编码。注意:其他的maven项目执行代码的`java`程序如果没有隔了这么一层,通过对`maven-compiler-plugin`设置``来设置相关选项
131 | - gradle 由于`gradle run`没有像`mvn javafx:run`这样隔了一层间接调用`java`可以通过`applicationDefaultJvmArgs`来设置选项。注意:`gradle jpackageImage`的情况就跟上面很像了
132 | - gradle 还有一处特别,gradle编译项目时,相关流会经历 gradle -> gradle daemon -> java compiler,java compiler -> gradle daemon -> gradle,其中 `gradle daemon -> gradle` 的时候,默认使用了`UTF-8`来进行序列化,所以gradle相关的命令注释多了中间一层,同时由于中间默认多了层`UTF-8`转化,且插件无法传入`vm`选项时,这个乱码无解,只能Windows操作系统全局设置`UTF-8`
133 | - 上面所说的`隔了一层`主要意思是指能不能传递`vm`选项给到`java`或`jlink`
134 |
135 | ### 补充
136 | 如果你在`PowerShell`中执行`[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8`后,发现`java --help`乱码了,你可以执行`[System.Console]::OutputEncoding = [System.Text.Encoding]::Default`回到默认设置,也可以
137 | ```shell
138 | C:\CommandLineTools\Java\jdk-21\bin\java.exe -D"sun.stdout.encoding"=UTF-8 -D"sun.stderr.encoding"=UTF-8 --help
139 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --help -J-D"sun.stdout.encoding"=UTF-8 -J-D"sun.stderr.encoding"=UTF-8
140 | ```
141 | > 注意目标为`java`时,`-D`要在`--help`的前面,目标为`jpackage`时,`-J-D`可以放在后面
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | JavaFX-Package-Sample
2 | JavaFX 打包示例(完结篇)
3 | English | 中文
4 |
5 | ## 分支说明
6 |
7 | - `java21` 使用`Maven`,`Gradle`和`GraalVM`打包一个模块化的JavaFX项目。
8 | - `java21-non-modular` [链接](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java21-non-modular) 使用
9 | `Maven`打包一个非模块化的JavaFX项目。
10 | - `java22-win32` [链接](https://github.com/JavaFX-Starter/JavaFX-Package-Sample/tree/java22-win32) 一个利用
11 | `Foreign Function and Memory (FFM) API`实现Windows全局键盘事件监听的JavaFX项目。
12 |
13 | ## HelloWorld [链接](https://github.com/icuxika/HelloWorld)
14 |
15 | 这个项目演示了如何完全使用`PowerShell7`来编译一个Java项目并创建可执行的jar包和可运行exe文件,项目存在包结构并引入第三方依赖。
16 |
17 | ## 环境设置
18 | #### `jpackage` 构建出安装包依赖的程序(如果只需要构建出可执行程序就可以跳过此配置)
19 | - Windows: 需要安装 [WIX TOOLSET](https://wixtoolset.org/) ,并将其bin目录添加到环境变量中
20 | - macOS/Linux: 缺失的包一般可以根据提示通过包管理器直接安装
21 |
22 | #### 在IDEA中构建项目
23 | - 项目结构 SDK 选择 JDK21
24 | - Gradle:Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> 指定 Gradle JVM 为项目 SDK
25 |
26 | #### 使用命令行(此处演示使用Windows的PowerShell,macOS和Linux假设你熟悉在命令行中设置临时环境变量)
27 | > `$env:xxx = "yyyy"`是设置一个临时环境变量,重启终端或者开启一些新的PowerShell标签页会失效
28 |
29 | - 设置`JAVA_HOME`环境变量 `$env:JAVA_HOME = "C:\CommandLineTools\Java\jdk-21\"`
30 | - > 使用 `gradlew`或者`mvn`的时候,只需要存在`JAVA_HOME`这个环境变量就可以,不需要`java`等命令配置在环境变量PATH中
31 | - 设置`GRAALVM_HOME`环境变量 `$env:GRAALVM_HOME = "C:\CommandLineTools\Java\graalvm-jdk-21+35.1"`
32 | - 设置`JAVA_TOOL_OPTIONS`环境变量可以为`gradle`设置代理
33 | `$env:JAVA_TOOL_OPTIONS = "-DsocksProxyHost=localhost -DsocksProxyPort=7890"`
34 | - 若要进行`GraalVM`构建,需要使用`Maven 3.8.8`
35 |
36 | ## 项目构建
37 |
38 | ### `jpackage`
39 |
40 | ##### gradle
41 |
42 | > 构建可执行程序
43 |
44 | ```shell
45 | .\gradlew.bat jpackageImage
46 | ```
47 |
48 | > 构建安装包
49 |
50 | ```shell
51 | .\gradlew.bat jpackage
52 | ```
53 |
54 | ##### maven
55 |
56 | 请根据不同操作系统修改`exec-maven-plugin`插件中有关下方两个命令的`--icon`的参数值,Windows为
57 | `${project.basedir}/src/main/resources/application.ico`,macOS为`${project.basedir}/src/main/resources/application.icns`
58 | ,Linux为`${project.basedir}/src/main/resources/application.png`
59 | > 运行(Maven plugin for JavaFX)
60 |
61 | ```shell
62 | mvn javafx:run
63 | ```
64 |
65 | > 构建出`.bat`形式的可执行程序(Maven plugin for JavaFX)
66 |
67 | ```shell
68 | mvn javafx:jlink
69 | ```
70 |
71 | > 构建可执行程序(手动拼接jpackage命令),`此命令构建出的结果其实与上面差别不大,但是是.exe的形式,并且图标已经设置好了`
72 |
73 | ```shell
74 | mvn clean
75 | mvn package
76 | mvn exec:exec@image
77 | ```
78 |
79 | > 构建安装包(手动拼接jpackage命令)
80 |
81 | ```shell
82 | mvn clean
83 | mvn package
84 | mvn exec:exec@installer
85 | ```
86 |
87 | ### `Oracle GraalVM`
88 |
89 | - 下载[Oracle GraalVM](https://www.graalvm.org/downloads/)
90 | - 请运行在由`Visual Studio`提供的命令行环境中,`Windows 终端`可以直接在标签页中打开`Developer PowerShell for VS 2022`
91 | ,其他用户可以在开始菜单的`Visual Studio 2022`文件夹中找到
92 | - 不关注 `module-info.java` 文件的存在与否
93 |
94 | #### JAVA_HOME和GRAALVM_HOME可以设置为相同值
95 |
96 | ```shell
97 | > $env:JAVA_HOME
98 | C:\CommandLineTools\Java\graalvm-jdk-21.0.2+13.1\
99 | > $env:GRAALVM_HOME
100 | C:\CommandLineTools\Java\graalvm-jdk-21.0.2+13.1\
101 | ```
102 |
103 | #### 此项目使用的 Oracle GraalVM 版本
104 |
105 | ```shell
106 | java 21.0.2 2024-01-16 LTS
107 | Java(TM) SE Runtime Environment Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30)
108 | Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30, mixed mode, sharing)
109 | ```
110 |
111 | ##### gradle
112 |
113 | ###### 构建与运行
114 |
115 | ```shell
116 | .\gradlew.bat nativeBuild
117 | .\gradlew.bat nativeRun
118 | ```
119 |
120 | ###### `Downloading JavaFX static libs...`网络问题
121 |
122 | Gluon下载`JavaFX static libs`
123 | 涉及到的相关源码[FileOps.java](https://github.com/gluonhq/substrate/blob/master/src/main/java/com/gluonhq/substrate/util/FileOps.java)
124 | ,因此可以通过配置`java`的一些系统属性(需要梯子)来加速,在项目根目录下新建文件`gradle.properties`
125 | 内容如下,配置项的有关文档[Java Networking and Proxies](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html)
126 | ,gradle相关文档[Accessing the web through a proxy](https://docs.gradle.org/current/userguide/build_environment.html#ex-configuring-an-http-proxy-using-gradle-properties)
127 |
128 | ```properties
129 | org.gradle.jvmargs=-Dfile.encoding=UTF-8
130 | systemProp.http.proxyHost=127.0.0.1
131 | systemProp.http.proxyPort=7890
132 | systemProp.https.proxyHost=127.0.0.1
133 | systemProp.https.proxyPort=7890
134 | ```
135 |
136 | 重新执行相关命令,可能会因为之前的下载缓存导致重新执行命令失败,这时可以去删除此文件夹`C:\Users\你的用户名\.gluon\substrate`
137 | 再重新运行,`JavaFX static libs`下载成功后可以注释掉相关代理配置以防其他平台梯子配置不同,如果不想改配置文件,也可以通过下述命令的方式来使用代理
138 |
139 | ```
140 | gradlew.bat nativeBuild -D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890
141 | ```
142 |
143 | ##### maven
144 |
145 | ###### 构建与运行
146 |
147 | ```shell
148 | mvn -Pwin gluonfx:build
149 | mvn -Pwin gluonfx:run
150 |
151 | mvn -Pmac gluonfx:build
152 | mvn -Pmac gluonfx:run
153 | ```
154 |
155 | ###### `Downloading JavaFX static libs...`网络问题
156 |
157 | ```shell
158 | mvn -Pwin gluonfx:build -D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890
159 | mvn -Pmac gluonfx:build -D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890
160 | ```
161 |
162 | ## 日志
163 |
164 | 添加了支持GraalVM的Logback作为日志框架,在你自己的项目,如果是第一次添加以及后续修改了Logback配置文件的时候,在执行GraalVM构建之前,先用
165 | `mvn -Pwin gluonfx:runagent`或`.\gradlew.bat nativeRunAgent`来为`native-image`生成一些必要的文件,它会自动保存到
166 | `src/main/resources/META-INF/native-image`,然后在执行GraalVM构建,否则生成的程序执行时不会生成日志文件,正常情况下,会在程序执行目录创建
167 | `logs`目录及日志文件
168 |
169 | ## 补充
170 |
171 | 有时候`mvn gluonfx:build`或者`.\gradlew.bat jpackageImage`发生了错误后,执行`.\gradlew.bat clean`提示文件被占用,这时候可以打开
172 | `任务管理器-性能-资源监视器-CPU`,在`关联的句柄`处输入`gluon`、`jpackage`等关键词来检索出卡住的相关进程并干掉他
173 |
174 | ## Windows安装包制作
175 | `jpackage`默认使用`WiX Toolset`来制作安装包文件,可以通过命令行参数修改`WiX Toolset`相关配置文件,若不想使用`jpackage`内置安装包的制作工具,也可以在构建出image后自行制作安装包
176 | - [NSISBuilder](https://github.com/icuxika/NSISBuilder)
177 | - [WiXToolset3Builder](https://github.com/icuxika/WiXToolset3Builder)
--------------------------------------------------------------------------------
/src/main/resources/META-INF/native-image/resource-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "resources":{
3 | "includes":[{
4 | "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E"
5 | }, {
6 | "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
7 | }, {
8 | "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
9 | }, {
10 | "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E"
11 | }, {
12 | "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
13 | }, {
14 | "pattern":"\\Qlogback-test.scmo\\E"
15 | }, {
16 | "pattern":"\\Qlogback-test.xml\\E"
17 | }, {
18 | "pattern":"\\Qlogback.scmo\\E"
19 | }, {
20 | "pattern":"\\Qlogback.xml\\E"
21 | }, {
22 | "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/ubidi.icu\\E"
23 | }, {
24 | "pattern":"javafx.controls:\\Qcom/sun/javafx/scene/control/skin/modena/modena.bss\\E"
25 | }, {
26 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-console-l1-1-0.dll\\E"
27 | }, {
28 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-console-l1-2-0.dll\\E"
29 | }, {
30 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-datetime-l1-1-0.dll\\E"
31 | }, {
32 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-debug-l1-1-0.dll\\E"
33 | }, {
34 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-errorhandling-l1-1-0.dll\\E"
35 | }, {
36 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-file-l1-1-0.dll\\E"
37 | }, {
38 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-file-l1-2-0.dll\\E"
39 | }, {
40 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-file-l2-1-0.dll\\E"
41 | }, {
42 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-handle-l1-1-0.dll\\E"
43 | }, {
44 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-heap-l1-1-0.dll\\E"
45 | }, {
46 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-interlocked-l1-1-0.dll\\E"
47 | }, {
48 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-libraryloader-l1-1-0.dll\\E"
49 | }, {
50 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-localization-l1-2-0.dll\\E"
51 | }, {
52 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-memory-l1-1-0.dll\\E"
53 | }, {
54 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-namedpipe-l1-1-0.dll\\E"
55 | }, {
56 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-processenvironment-l1-1-0.dll\\E"
57 | }, {
58 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-processthreads-l1-1-0.dll\\E"
59 | }, {
60 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-processthreads-l1-1-1.dll\\E"
61 | }, {
62 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-profile-l1-1-0.dll\\E"
63 | }, {
64 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-rtlsupport-l1-1-0.dll\\E"
65 | }, {
66 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-string-l1-1-0.dll\\E"
67 | }, {
68 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-synch-l1-1-0.dll\\E"
69 | }, {
70 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-synch-l1-2-0.dll\\E"
71 | }, {
72 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-sysinfo-l1-1-0.dll\\E"
73 | }, {
74 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-timezone-l1-1-0.dll\\E"
75 | }, {
76 | "pattern":"javafx.graphics:\\Qapi-ms-win-core-util-l1-1-0.dll\\E"
77 | }, {
78 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-conio-l1-1-0.dll\\E"
79 | }, {
80 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-convert-l1-1-0.dll\\E"
81 | }, {
82 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-environment-l1-1-0.dll\\E"
83 | }, {
84 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-filesystem-l1-1-0.dll\\E"
85 | }, {
86 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-heap-l1-1-0.dll\\E"
87 | }, {
88 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-locale-l1-1-0.dll\\E"
89 | }, {
90 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-math-l1-1-0.dll\\E"
91 | }, {
92 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-multibyte-l1-1-0.dll\\E"
93 | }, {
94 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-private-l1-1-0.dll\\E"
95 | }, {
96 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-process-l1-1-0.dll\\E"
97 | }, {
98 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-runtime-l1-1-0.dll\\E"
99 | }, {
100 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-stdio-l1-1-0.dll\\E"
101 | }, {
102 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-string-l1-1-0.dll\\E"
103 | }, {
104 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-time-l1-1-0.dll\\E"
105 | }, {
106 | "pattern":"javafx.graphics:\\Qapi-ms-win-crt-utility-l1-1-0.dll\\E"
107 | }, {
108 | "pattern":"javafx.graphics:\\Qcom/sun/glass/utils/NativeLibLoader.class\\E"
109 | }, {
110 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/FillCircle_Color.obj\\E"
111 | }, {
112 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/FillPgram_Color.obj\\E"
113 | }, {
114 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_Color.obj\\E"
115 | }, {
116 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_TextureFirstPassLCD.obj\\E"
117 | }, {
118 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_TextureRGB.obj\\E"
119 | }, {
120 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Solid_TextureSecondPassLCD.obj\\E"
121 | }, {
122 | "pattern":"javafx.graphics:\\Qcom/sun/prism/d3d/hlsl/Texture_Color.obj\\E"
123 | }, {
124 | "pattern":"javafx.graphics:\\Qglass.dll\\E"
125 | }, {
126 | "pattern":"javafx.graphics:\\Qjavafx_font.dll\\E"
127 | }, {
128 | "pattern":"javafx.graphics:\\Qmsvcp140.dll\\E"
129 | }, {
130 | "pattern":"javafx.graphics:\\Qmsvcp140_1.dll\\E"
131 | }, {
132 | "pattern":"javafx.graphics:\\Qmsvcp140_2.dll\\E"
133 | }, {
134 | "pattern":"javafx.graphics:\\Qprism_d3d.dll\\E"
135 | }, {
136 | "pattern":"javafx.graphics:\\Qucrtbase.dll\\E"
137 | }, {
138 | "pattern":"javafx.graphics:\\Qvcruntime140.dll\\E"
139 | }, {
140 | "pattern":"javafx.graphics:\\Qvcruntime140_1.dll\\E"
141 | }]},
142 | "bundles":[{
143 | "name":"LanguageResource",
144 | "locales":["", "en", "zh-CN"]
145 | }, {
146 | "name":"com.sun.javafx.tk.quantum.QuantumMessagesBundle",
147 | "locales":["", "zh-CN"]
148 | }, {
149 | "name":"com/sun/glass/ui/win/themes",
150 | "locales":["", "zh-CN"]
151 | }]
152 | }
153 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/Windows下手动构建的演示.md:
--------------------------------------------------------------------------------
1 | 使用`jpackage`命令在Windows平台手动构建可执行程序和安装程序
2 | ==========
3 |
4 | ## 生成项目`jar`包(无法简单的通过`java -jar`执行)
5 | `gradle` -> `.\build\libs\JavaFX-Package-Sample-1.0.0.jar`
6 | ```
7 | .\gradlew.bat clean
8 | .\gradlew.bat assemble
9 | ```
10 | `maven` -> `.\target\JavaFX-Package-Sample-1.0.0.jar`
11 | ```
12 | mvn clean
13 | mvn package
14 | ```
15 | > `mvn package`执行时会将项目依赖的jar包拷贝到`.\target\alternateLocation\`
16 |
17 | ## 通过`java`命令来运行应用程序
18 | `gradle`
19 |
20 | 先执行`.\gradlew.bat copyDependencies`将项目依赖的jar包拷贝到`.\build\modules\`
21 | ```shell
22 | C:\CommandLineTools\Java\jdk-21\bin\java.exe -p ".\build\libs\;.\build\modules\" -m "sample/com.icuxika.MainApp"
23 | ```
24 | `maven`
25 | ```shell
26 | C:\CommandLineTools\Java\jdk-21\bin\java.exe -p ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" -m "sample/com.icuxika.MainApp"
27 | ```
28 |
29 | ## 通过`jdeps`命令可以显示项目依赖的模块
30 | `gradle`
31 | ```shell
32 | C:\CommandLineTools\Java\jdk-21\bin\jdeps.exe --print-module-deps --ignore-missing-deps --module-path .\build\modules\ .\build\libs\JavaFX-Package-Sample-1.0.0.jar
33 | ```
34 | > 输出:MaterialFX,java.base
35 |
36 | `maven`
37 | ```shell
38 | C:\CommandLineTools\Java\jdk-21\bin\jdeps.exe --print-module-deps --ignore-missing-deps --module-path .\target\alternateLocation\ .\target\JavaFX-Package-Sample-1.0.0.jar
39 | ```
40 | > 输出:MaterialFX,java.base
41 |
42 | ## 通过`jlink`构建出`.bat`形式的可执行程序
43 | `gradle` -> `.\build\jlink-build-dir\bin\JavaFX-Package-Sample.bat`
44 | ```shell
45 | C:\CommandLineTools\Java\jdk-21\bin\jlink.exe --module-path ".\build\libs\;.\build\modules\" --add-modules java.base,sample --launcher JavaFX-Package-Sample=sample/com.icuxika.MainApp --compress=zip-9 --no-header-files --no-man-pages --strip-debug --output .\build\jlink-build-dir
46 | ```
47 |
48 | `maven` -> `.\target\jlink-build-dir\bin\JavaFX-Package-Sample.bat`
49 | ```shell
50 | C:\CommandLineTools\Java\jdk-21\bin\jlink.exe --module-path ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" --add-modules java.base,sample --launcher JavaFX-Package-Sample=sample/com.icuxika.MainApp --compress=zip-9 --no-header-files --no-man-pages --strip-debug --output .\target\jlink-build-dir
51 | ```
52 |
53 | > 通过`jlink`构建出的文件夹`jlink-build-dir`已经可以压缩一下发到其他电脑上运行了,`.bat`脚本也有许多方式能够直接转为`.exe`,目前`jlink-build-dir`目录的大小为`89.7 MB`
54 |
55 | ## jpackage 使用一:以`jlink`命令构建出的目录`jlink-build-dir`为基础
56 | `gradle` -> `.\build\jpackage-build-dir\JavaFXSample\JavaFXSample.exe`
57 | ```shell
58 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample -m "sample/com.icuxika.MainApp" --runtime-image .\build\jlink-build-dir\ --icon .\src\main\resources\application.ico --app-version 1.0.0 --dest .\build\jpackage-build-dir\
59 | ```
60 | `maven` -> `.\target\jpackage-build-dir\JavaFXSample\JavaFXSample.exe`
61 | ```shell
62 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample -m "sample/com.icuxika.MainApp" --runtime-image .\target\jlink-build-dir\ --icon .\src\main\resources\application.ico --app-version 1.0.0 --dest .\target\jpackage-build-dir\
63 | ```
64 |
65 | > 目前`jpackage-build-dir`大小为`90.1 MB`
66 |
67 | ## jpackage 使用二:不以`jlink`构建结果为基础
68 | `gradle` -> `.\build\jpackage-direct-build-dir\JavaFXSample\JavaFXSample.exe`
69 | ```shell
70 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample --module-path ".\build\libs\;.\build\modules\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\build\jpackage-direct-build-dir
71 | ```
72 | `maven` -> `.\target\jpackage-direct-build-dir\JavaFXSample\JavaFXSample.exe`
73 | ```shell
74 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type app-image -n JavaFXSample --module-path ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\target\jpackage-direct-build-dir
75 | ```
76 |
77 | > 目前`jpackage-direct-build-dir`大小为`182 MB`
78 |
79 | ## 以下命令是构建Windows上`msi`格式的安装程序,需要配置[WiX Toolset v3.11.2](https://github.com/wixtoolset/wix3/releases/tag/wix3112rtm)的`bin`目录到环境变量`PATH`中
80 |
81 | ## jpackage 使用三:以`jpackage 使用二`的构建目录`jpackage-direct-build-dir`为基础
82 | `gradle` -> `.\build\jpackage-installer-dir\JavaFXSample-1.0.msi`
83 | ```shell
84 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --app-image .\build\jpackage-direct-build-dir\JavaFXSample\ --dest .\build\jpackage-installer-dir --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut
85 | ```
86 | `maven` -> `.\target\jpackage-installer-dir\JavaFXSample-1.0.msi`
87 | ```shell
88 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --app-image .\target\jpackage-direct-build-dir\JavaFXSample\ --dest .\target\jpackage-installer-dir --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut
89 | ```
90 |
91 | > 目前`JavaFXSample-1.0.msi`的大小为`65.9 MB`
92 |
93 | > 在jdk19、jdk20等版本有时候构建会遇到构建出的安装包是英文版本或者`exited with 311 code`的错误,请切换到`java20`分支此文件查看相应的解决方法,当前jdk21没有遇到这些问题
94 |
95 | ## jpackage 使用四:不以`jpackage 使用二`的构建结果为基础
96 | `gradle` -> `.\build\jpackage-direct-installer-dir\JavaFXSample-1.0.0.msi`
97 | ```shell
98 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --module-path ".\build\libs\;.\build\modules\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\build\jpackage-direct-installer-dir\ --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut
99 | ```
100 | `maven` -> `.\target\jpackage-direct-installer-dir\JavaFXSample-1.0.0.msi`
101 | ```shell
102 | C:\CommandLineTools\Java\jdk-21\bin\jpackage.exe --type msi -n JavaFXSample --module-path ".\target\JavaFX-Package-Sample-1.0.0.jar;.\target\alternateLocation\" -m "sample/com.icuxika.MainApp" --icon ./src/main/resources/application.ico --app-version 1.0.0 --dest .\target\jpackage-direct-installer-dir\ --win-dir-chooser --win-menu --win-menu-group JavaFXSample --win-shortcut
103 | ```
104 |
105 | > 目前`JavaFXSample-1.0.msi`的大小为`65.9 MB`
106 |
107 |
108 | ## 最后
109 | `jlink`和`jpackage`两条命令最主要的使用方式和关键的参数都已经在上面了,不过本项目只演示了作为一个模块化项目时应用的方式,但是`jpackage`是支持为一个非模块化的可以通过`java -jar`运行的`jar`包生成对应的exe的。
110 | Java模块化尤其是这种纯命令行的方式难以处理很多情况比如非模块化的依赖,甚至同时支持模块化导入和兼容非模块化的依赖有时也会遇到问题,我已经有好长时间没写过`javafx`相关的代码,`jpackage`除了几个玩具项目几乎也没怎么用到过,以这点经验来说,还是找一个合适的插件为好,更简单的方式是在grdle中使用[The Badass Runtime Plugin](https://badass-runtime-plugin.beryx.org/releases/latest/)这个插件来对非模块化项目打包,这样的话项目中不用维护一个`module-info.java`文件,也不用考虑在引入新的依赖各种模块的冲突问题了
111 |
112 | 使用 mvn,配置一下阿里仓库的镜像就几乎不会有网络问题了,但是更好用的maven插件你可能需要多上上`Google`才能发现
113 |
114 | 使用 gradle,最大的问题就是你需要学会翻墙,并在IDEA中配置代理,命令行中 gradlew.bat 命令结尾添加`-D"https.proxyHost"=127.0.0.1 -D"https.proxyPort"=7890`
115 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/native-image/reflect-config.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder",
4 | "queryAllPublicMethods":true,
5 | "methods":[{"name":"","parameterTypes":[] }]
6 | },
7 | {
8 | "name":"ch.qos.logback.classic.filter.ThresholdFilter",
9 | "queryAllPublicMethods":true,
10 | "methods":[{"name":"","parameterTypes":[] }, {"name":"setLevel","parameterTypes":["java.lang.String"] }]
11 | },
12 | {
13 | "name":"ch.qos.logback.classic.joran.SerializedModelConfigurator",
14 | "methods":[{"name":"","parameterTypes":[] }]
15 | },
16 | {
17 | "name":"ch.qos.logback.classic.pattern.DateConverter",
18 | "methods":[{"name":"","parameterTypes":[] }]
19 | },
20 | {
21 | "name":"ch.qos.logback.classic.pattern.FileOfCallerConverter",
22 | "methods":[{"name":"","parameterTypes":[] }]
23 | },
24 | {
25 | "name":"ch.qos.logback.classic.pattern.LevelConverter",
26 | "methods":[{"name":"","parameterTypes":[] }]
27 | },
28 | {
29 | "name":"ch.qos.logback.classic.pattern.LineOfCallerConverter",
30 | "methods":[{"name":"","parameterTypes":[] }]
31 | },
32 | {
33 | "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter",
34 | "methods":[{"name":"","parameterTypes":[] }]
35 | },
36 | {
37 | "name":"ch.qos.logback.classic.pattern.MessageConverter",
38 | "methods":[{"name":"","parameterTypes":[] }]
39 | },
40 | {
41 | "name":"ch.qos.logback.classic.pattern.ThreadConverter",
42 | "methods":[{"name":"","parameterTypes":[] }]
43 | },
44 | {
45 | "name":"ch.qos.logback.classic.pattern.color.HighlightingCompositeConverter",
46 | "methods":[{"name":"","parameterTypes":[] }]
47 | },
48 | {
49 | "name":"ch.qos.logback.classic.util.DefaultJoranConfigurator",
50 | "methods":[{"name":"","parameterTypes":[] }]
51 | },
52 | {
53 | "name":"ch.qos.logback.core.ConsoleAppender",
54 | "queryAllPublicMethods":true,
55 | "methods":[{"name":"","parameterTypes":[] }]
56 | },
57 | {
58 | "name":"ch.qos.logback.core.FileAppender",
59 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
60 | },
61 | {
62 | "name":"ch.qos.logback.core.OutputStreamAppender",
63 | "methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }]
64 | },
65 | {
66 | "name":"ch.qos.logback.core.UnsynchronizedAppenderBase",
67 | "methods":[{"name":"addFilter","parameterTypes":["ch.qos.logback.core.filter.Filter"] }]
68 | },
69 | {
70 | "name":"ch.qos.logback.core.encoder.Encoder",
71 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
72 | },
73 | {
74 | "name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder",
75 | "methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }]
76 | },
77 | {
78 | "name":"ch.qos.logback.core.filter.Filter",
79 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
80 | },
81 | {
82 | "name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase",
83 | "methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }]
84 | },
85 | {
86 | "name":"ch.qos.logback.core.pattern.color.BoldGreenCompositeConverter",
87 | "methods":[{"name":"","parameterTypes":[] }]
88 | },
89 | {
90 | "name":"ch.qos.logback.core.pattern.color.BoldYellowCompositeConverter",
91 | "methods":[{"name":"","parameterTypes":[] }]
92 | },
93 | {
94 | "name":"ch.qos.logback.core.pattern.color.CyanCompositeConverter",
95 | "methods":[{"name":"","parameterTypes":[] }]
96 | },
97 | {
98 | "name":"ch.qos.logback.core.rolling.RollingFileAppender",
99 | "queryAllPublicMethods":true,
100 | "methods":[{"name":"","parameterTypes":[] }, {"name":"setFile","parameterTypes":["java.lang.String"] }, {"name":"setRollingPolicy","parameterTypes":["ch.qos.logback.core.rolling.RollingPolicy"] }]
101 | },
102 | {
103 | "name":"ch.qos.logback.core.rolling.RollingPolicy",
104 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
105 | },
106 | {
107 | "name":"ch.qos.logback.core.rolling.RollingPolicyBase",
108 | "methods":[{"name":"setFileNamePattern","parameterTypes":["java.lang.String"] }, {"name":"setParent","parameterTypes":["ch.qos.logback.core.FileAppender"] }]
109 | },
110 | {
111 | "name":"ch.qos.logback.core.rolling.TimeBasedRollingPolicy",
112 | "queryAllPublicMethods":true,
113 | "methods":[{"name":"","parameterTypes":[] }, {"name":"setMaxHistory","parameterTypes":["int"] }, {"name":"setTotalSizeCap","parameterTypes":["ch.qos.logback.core.util.FileSize"] }]
114 | },
115 | {
116 | "name":"ch.qos.logback.core.rolling.helper.DateTokenConverter",
117 | "methods":[{"name":"","parameterTypes":[] }]
118 | },
119 | {
120 | "name":"ch.qos.logback.core.spi.ContextAware",
121 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
122 | },
123 | {
124 | "name":"ch.qos.logback.core.util.FileSize",
125 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
126 | },
127 | {
128 | "name":"com.icuxika.MainApp",
129 | "methods":[{"name":"","parameterTypes":[] }, {"name":"main","parameterTypes":["java.lang.String[]"] }]
130 | },
131 | {
132 | "name":"com.sun.glass.ui.Screen"
133 | },
134 | {
135 | "name":"com.sun.javafx.font.directwrite.DWFactory",
136 | "methods":[{"name":"getFactory","parameterTypes":[] }]
137 | },
138 | {
139 | "name":"com.sun.javafx.logging.PrintLogger",
140 | "methods":[{"name":"createInstance","parameterTypes":[] }]
141 | },
142 | {
143 | "name":"com.sun.javafx.logging.jfr.JFRPulseLogger",
144 | "methods":[{"name":"createInstance","parameterTypes":[] }]
145 | },
146 | {
147 | "name":"com.sun.javafx.scene.control.skin.Utils",
148 | "methods":[{"name":"getResource","parameterTypes":["java.lang.String"] }]
149 | },
150 | {
151 | "name":"com.sun.javafx.tk.quantum.QuantumToolkit",
152 | "methods":[{"name":"","parameterTypes":[] }]
153 | },
154 | {
155 | "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl",
156 | "methods":[{"name":"","parameterTypes":[] }]
157 | },
158 | {
159 | "name":"com.sun.prism.GraphicsPipeline",
160 | "methods":[{"name":"getFontFactory","parameterTypes":[] }, {"name":"getPipeline","parameterTypes":[] }]
161 | },
162 | {
163 | "name":"com.sun.prism.shader.FillCircle_Color_Loader",
164 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }]
165 | },
166 | {
167 | "name":"com.sun.prism.shader.FillPgram_Color_Loader",
168 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }]
169 | },
170 | {
171 | "name":"com.sun.prism.shader.Solid_Color_Loader",
172 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }]
173 | },
174 | {
175 | "name":"com.sun.prism.shader.Solid_TextureFirstPassLCD_Loader",
176 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }]
177 | },
178 | {
179 | "name":"com.sun.prism.shader.Solid_TextureRGB_Loader",
180 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }]
181 | },
182 | {
183 | "name":"com.sun.prism.shader.Solid_TextureSecondPassLCD_Loader",
184 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }]
185 | },
186 | {
187 | "name":"com.sun.prism.shader.Texture_Color_Loader",
188 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }]
189 | },
190 | {
191 | "name":"java.io.FilePermission"
192 | },
193 | {
194 | "name":"java.lang.Character",
195 | "methods":[{"name":"isIdeographic","parameterTypes":["int"] }]
196 | },
197 | {
198 | "name":"java.lang.Class",
199 | "methods":[{"name":"forName","parameterTypes":["java.lang.Module","java.lang.String"] }, {"name":"getModule","parameterTypes":[] }]
200 | },
201 | {
202 | "name":"java.lang.Module",
203 | "methods":[{"name":"getDescriptor","parameterTypes":[] }, {"name":"getLayer","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }]
204 | },
205 | {
206 | "name":"java.lang.ModuleLayer",
207 | "methods":[{"name":"boot","parameterTypes":[] }, {"name":"findModule","parameterTypes":["java.lang.String"] }]
208 | },
209 | {
210 | "name":"java.lang.RuntimePermission"
211 | },
212 | {
213 | "name":"java.net.NetPermission"
214 | },
215 | {
216 | "name":"java.net.SocketPermission"
217 | },
218 | {
219 | "name":"java.net.URLPermission",
220 | "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }]
221 | },
222 | {
223 | "name":"java.nio.ByteBuffer",
224 | "methods":[{"name":"order","parameterTypes":["java.nio.ByteOrder"] }]
225 | },
226 | {
227 | "name":"java.nio.ByteOrder",
228 | "methods":[{"name":"nativeOrder","parameterTypes":[] }]
229 | },
230 | {
231 | "name":"java.security.AllPermission"
232 | },
233 | {
234 | "name":"java.security.SecurityPermission"
235 | },
236 | {
237 | "name":"java.util.PropertyPermission"
238 | },
239 | {
240 | "name":"java.util.concurrent.atomic.AtomicBoolean",
241 | "fields":[{"name":"value"}]
242 | },
243 | {
244 | "name":"java.util.concurrent.atomic.AtomicReference",
245 | "fields":[{"name":"value"}]
246 | },
247 | {
248 | "name":"javafx.animation.KeyValue"
249 | },
250 | {
251 | "name":"javafx.scene.Camera"
252 | },
253 | {
254 | "name":"javafx.scene.Node"
255 | },
256 | {
257 | "name":"javafx.scene.ParallelCamera"
258 | },
259 | {
260 | "name":"javafx.scene.Parent"
261 | },
262 | {
263 | "name":"javafx.scene.Scene"
264 | },
265 | {
266 | "name":"javafx.scene.control.Control"
267 | },
268 | {
269 | "name":"javafx.scene.image.Image"
270 | },
271 | {
272 | "name":"javafx.scene.layout.Pane"
273 | },
274 | {
275 | "name":"javafx.scene.layout.Region"
276 | },
277 | {
278 | "name":"javafx.scene.shape.Circle"
279 | },
280 | {
281 | "name":"javafx.scene.shape.Rectangle"
282 | },
283 | {
284 | "name":"javafx.scene.shape.Shape"
285 | },
286 | {
287 | "name":"javafx.scene.text.Font"
288 | },
289 | {
290 | "name":"javafx.scene.text.Text"
291 | },
292 | {
293 | "name":"javafx.scene.transform.Transform"
294 | },
295 | {
296 | "name":"javafx.stage.Stage"
297 | },
298 | {
299 | "name":"javafx.stage.Window"
300 | },
301 | {
302 | "name":"javax.smartcardio.CardPermission"
303 | },
304 | {
305 | "name":"sun.misc.Unsafe",
306 | "fields":[{"name":"theUnsafe"}]
307 | },
308 | {
309 | "name":"sun.security.provider.MD5",
310 | "methods":[{"name":"","parameterTypes":[] }]
311 | }
312 | ]
313 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.icuxika
8 | JavaFX-Package-Sample
9 | 1.0.0
10 |
11 |
12 | UTF-8
13 | 21
14 | 21
15 |
16 |
17 |
18 |
19 | org.openjfx
20 | javafx-base
21 | ${javafx.version}
22 |
23 |
24 | org.openjfx
25 | javafx-controls
26 | ${javafx.version}
27 |
28 |
29 | org.openjfx
30 | javafx-graphics
31 | ${javafx.version}
32 |
33 |
34 | org.openjfx
35 | javafx-fxml
36 | ${javafx.version}
37 |
38 |
39 | org.openjfx
40 | javafx-swing
41 | ${javafx.version}
42 |
43 |
44 | org.openjfx
45 | javafx-media
46 | ${javafx.version}
47 |
48 |
49 | org.openjfx
50 | javafx-web
51 | ${javafx.version}
52 |
53 |
54 | io.github.palexdev
55 | materialfx
56 | 11.16.1
57 |
58 |
59 | org.slf4j
60 | slf4j-api
61 | 2.0.12
62 |
63 |
64 | ch.qos.logback
65 | logback-classic
66 | 1.5.3
67 |
68 |
69 |
70 |
71 |
72 | org.apache.maven.plugins
73 | maven-compiler-plugin
74 | 3.10.1
75 |
76 | ${maven.compiler.release}
77 |
78 |
79 |
80 | org.apache.maven.plugins
81 | maven-dependency-plugin
82 | 3.1.2
83 |
84 |
85 | copy-dependencies
86 | package
87 |
88 | copy-dependencies
89 |
90 |
91 | ${project.build.directory}/alternateLocation
92 | false
93 | false
94 | true
95 |
96 |
97 |
98 |
99 |
100 | org.codehaus.mojo
101 | exec-maven-plugin
102 | 3.0.0
103 |
104 |
105 | image
106 |
107 | exec
108 |
109 |
110 | jpackage
111 | ${project.basedir}
112 |
113 | -n
114 | JavaFXSample
115 | -t
116 | app-image
117 | -p
118 |
119 | ${project.build.directory}/alternateLocation${path.separator}${project.build.directory}/${project.artifactId}-${project.version}.jar
120 |
121 | -d
122 | ${project.build.directory}/mvnPackage
123 | -m
124 | sample/com.icuxika.MainApp
125 | --icon
126 |
127 | ${project.basedir}/src/main/resources/application.ico
128 |
129 | --app-version
130 | 1.0.0
131 |
132 |
133 |
134 |
135 | installer
136 |
137 | exec
138 |
139 |
140 | jpackage
141 | ${project.basedir}
142 |
143 | -n
144 | JavaFXSample
145 | -t
146 | dmg
147 | -p
148 |
149 | ${project.build.directory}/alternateLocation${path.separator}${project.build.directory}/${project.artifactId}-${project.version}.jar
150 |
151 | -d
152 | ${project.build.directory}/mvnInstaller
153 | -m
154 | sample/com.icuxika.MainApp
155 | --icon
156 |
157 | ${project.basedir}/src/main/resources/application.ico
158 |
159 | --app-version
160 | 1.0.0
161 |
162 |
163 |
164 |
165 |
166 |
167 | org.openjfx
168 | javafx-maven-plugin
169 | 0.0.8
170 |
171 | true
172 | 2
173 | true
174 | true
175 | JavaFXSample
176 | build-link
177 | sample-zip
178 | sample/com.icuxika.MainApp
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 | win
187 |
188 |
189 |
190 | com.gluonhq
191 | gluonfx-maven-plugin
192 | 1.0.22
193 |
194 | com.icuxika.MainApp
195 |
196 | LanguageResource
197 | LanguageResource_en
198 | LanguageResource_zh_CN
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | mac
207 |
208 |
209 |
210 | com.gluonhq
211 | gluonfx-maven-plugin
212 | 1.0.22
213 |
214 | com.icuxika.MainApp
215 |
216 | LanguageResource
217 | LanguageResource_en
218 | LanguageResource_zh_CN
219 |
220 |
221 |
222 | -Dsvm.platform=org.graalvm.nativeimage.Platform$MACOS_AMD64
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
--------------------------------------------------------------------------------