使用NSIS进行JavaFX程序打包

  • 软件版本
  • IDEA不支持JDK11+平台上打包JavaFX程序
  • 解决方案
  • 1. 创建JAR包
  • 2. 生成JRE运行环境
  • 3. 打包生成EXE
  • 附件


软件版本

编辑器:IDEA 2020.2
JDK: OpenJDK 14.02
JavaFX: OpenJFX 11.02
NSIS: nsis-3.06.1

IDEA不支持JDK11+平台上打包JavaFX程序

由于JDK11+平台上没有JavaFX packager和javapackager,IDEA不支持通过fx:deploy打包JavaFX程序生成exe。

在IDEA 2020.2中使用Artifacts打包JavaFX程序(File->Project Structure->Artifacts->±>JavaFX Application->From module ‘HelloFX’),出现错误:
Java FX Packager: Can’t build artifact - fx:deploy is not available in this JDK。

解决方案

1. 创建JAR包

通过顺次点击File->Project Structure->Artifacts->±>JAR->From module with dependencies!打开创建JAR包对话框:

javafx怎么发送图片 javafx怎么打包_javafx


javafx怎么发送图片 javafx怎么打包_javafx_02


依次设置Main Class 和META-INF目录,点击OK:

javafx怎么发送图片 javafx怎么打包_jar_03


依次点击Build->Build Artifacts->HelloFX:jar->Build就可以生成相应的JAR包了:

javafx怎么发送图片 javafx怎么打包_exe_04

2. 生成JRE运行环境

转到HelloFX\out\artifacts\HelloFX_jar路径下。注意生成的JAR包在*_jar路径之下。

2.1 获取依赖项
使用jdeps将依赖项导入到depends.txt中:

jdeps --list-deps --ignore-missing-deps --module-path "D:\Program Files\java\openjfx-11_windows-x64_bin-sdk\javafx-jmods-11;D:\Program Files\java\openjfx-11_windows-x64_bin-sdk\javafx-sdk-11\lib;D:\Program Files\java\openjdk-14.0.2_windows-x64_bin\jdk-14.0.2\jmods" --add-modules javafx.controls,javafx.fxml  HelloFX.jar > depends.txt

其中的路径包括jdk和javafx库路径和jmods路径,注意需要添加javafx.controls等modules。

生成的depends.txt内容为:

java.base
   java.desktop
   java.scripting
   java.xml
   javafx.base/com.sun.javafx
   javafx.base/com.sun.javafx.beans
   javafx.base/com.sun.javafx.binding
   javafx.base/com.sun.javafx.collections
   javafx.base/com.sun.javafx.event
   javafx.base/com.sun.javafx.logging
   javafx.base/com.sun.javafx.property
   javafx.base/com.sun.javafx.reflect
   javafx.controls/com.sun.javafx.scene.control
   javafx.controls/com.sun.javafx.scene.control.behavior
   javafx.controls/com.sun.javafx.scene.control.inputmap
   javafx.controls/com.sun.javafx.scene.control.skin
   javafx.graphics/com.sun.glass.ui
   javafx.graphics/com.sun.glass.utils
   javafx.graphics/com.sun.javafx.application
   javafx.graphics/com.sun.javafx.css
   javafx.graphics/com.sun.javafx.font
   javafx.graphics/com.sun.javafx.geom
   javafx.graphics/com.sun.javafx.geom.transform
   javafx.graphics/com.sun.javafx.iio
   javafx.graphics/com.sun.javafx.menu
   javafx.graphics/com.sun.javafx.scene
   javafx.graphics/com.sun.javafx.scene.input
   javafx.graphics/com.sun.javafx.scene.layout
   javafx.graphics/com.sun.javafx.scene.text
   javafx.graphics/com.sun.javafx.scene.traversal
   javafx.graphics/com.sun.javafx.sg.prism
   javafx.graphics/com.sun.javafx.stage
   javafx.graphics/com.sun.javafx.text
   javafx.graphics/com.sun.javafx.tk
   javafx.graphics/com.sun.javafx.util
   javafx.graphics/com.sun.prism
   javafx.graphics/com.sun.prism.image
   javafx.graphics/com.sun.prism.paint
   javafx.graphics/com.sun.scenario.effect
   javafx.graphics/com.sun.scenario.effect.impl
   javafx.graphics/com.sun.scenario.effect.impl.prism
   javafx.media/com.sun.javafx.media
   javafx.media/com.sun.media.jfxmedia
   javafx.media/com.sun.media.jfxmedia.control
   javafx.media/com.sun.media.jfxmedia.events
   javafx.media/com.sun.media.jfxmedia.locator
   javafx.media/com.sun.media.jfxmedia.track
   jdk.jsobject
   jdk.xml.dom

2.2 使用jlink生成jre
将depends.txt中的依赖项以及原有的javafx依赖项(javafx.fxml)放在jlink命令中,生成jre-14.0.2运行环境文件夹:

jlink --module-path "D:\Program Files\java\openjfx-11_windows-x64_bin-sdk\javafx-jmods-11;D:/Program Files/java/openjfx-14.0.2.1_windows-x64_bin-sdk/javafx-sdk-14.0.2.1/lib;D:\Program Files\java\openjdk-14.0.2_windows-x64_bin\jdk-14.0.2\jmods" --add-modules java.base,java.desktop,java.scripting,java.xml,javafx.base,javafx.controls,javafx.graphics,javafx.media,jdk.jsobject,jdk.xml.dom,javafx.fxml --output jre-14.0.2 --strip-debug --compress 2 --no-header-files --no-man-pages

在命令行中输入以下命令,如果能打开程序说明生成的jre可用:

"jre-14.0.2/bin/java.exe" -jar HelloFX.jar

3. 打包生成EXE

根据Creating a Windows Executable Bundled with Java OpenJDK上面的做法,制作NSIS脚本:

Unicode true

!include WinMessages.nsh
!include FileFunc.nsh


!addplugindir Plugins 
 
SilentInstall silent
RequestExecutionLevel user
ShowInstDetails hide
 
OutFile "HelloFX.exe"
Icon "wind4.ico"
VIProductVersion 2.67.0.00000
VIAddVersionKey ProductName "HelloFX"
VIAddVersionKey LegalCopyright "Copyright (c) 2011-2018 Chris Mason"
VIAddVersionKey FileDescription "Dynamic Templating Tool"
VIAddVersionKey FileVersion 2.67.0.00000
VIAddVersionKey ProductVersion "2.67 / OpenJRE 14.0.2 (x64)"
VIAddVersionKey InternalName "HelloFX"
VIAddVersionKey OriginalFilename "HelloFX.exe"
 
Section
  SetOverwrite off
 
  SetOutPath "$TEMP\jre-14.0.2-HelloFX"
  File /r "jre-14.0.2\*"
 
  InitPluginsDir
  SetOutPath $PluginsDir
  File "HelloFX.jar"
  SetOutPath $TEMP
  ${GetParameters} $R0

  nsExec::Exec '"$TEMP\jre-14.0.2-HelloFX\bin\javaw.exe" -jar "$PluginsDir\HelloFX.jar" $R0'
  RMDir /r $PluginsDir
SectionEnd

选择NSIS脚本,右键运行Compile NSIS Script就可以生成exe了。

javafx怎么发送图片 javafx怎么打包_jar_05


最终运行效果如下:

javafx怎么发送图片 javafx怎么打包_exe_06

注意:

  • 使用javaw.exe运行jar不会出现控制台;
  • NSIS打包生成的EXE会在运行时解压到C:\Users\dell\AppData\Local\Temp中,本例中生成jre-14.02路径。如果两次打包生成的jre不同,NSIS不会删除原有的jre可能造成软件运行出错。
  • 为了区分不同软件及版本,可在$TEMP\jre-14.0.2后加入标识字符串,如本例中设置为$TEMP\jre-14.0.2-HelloFX
  • 我在使用nsExec::Exec打包时发现窗口不显示,于是修改了nsis-3.06.1-src\Contrib\nsExec\nsExec.c的代码,生成了新的nsExec.dll,并将其放到NSIS插件路径NSIS\Plugins\x86-unicode下。
GetStartupInfo(&si); // Why?
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = TRUE; // 修改SW_HIDE为TRUE,否则不显示窗口了
si.hStdInput = newstdin;
si.hStdOutput = newstdout;
si.hStdError = newstdout;

为了生成nsExec.dll需要下载zlib开发环境,并编译NSIS:

javafx怎么发送图片 javafx怎么打包_exe_07


设置zlib环境:

set ZLIB_W32=E:\programming\c++\zlib

编译NSIS

scons UNICODE=yes SKIPUTILS="NSIS Menu"

修改nsExec.c代码后,生成nsExec.dll

scons SKIPSTUBS=all SKIPUTILS=all SKIPMISC=all UNICODE=yes