JEP 392: 打包工具
总结
提供 jpackage
工具,用于打包自包含的 Java 应用程序。
历史
jpackage
工具由 JEP 343 作为孵化工具引入到 JDK 14 中。在 JDK 15 中它仍然是一个孵化工具,以便有时间收集更多反馈。现在它已经准备好从孵化阶段升级为生产就绪功能。在此过渡过程中,jpackage
模块的名称将从 jdk.incubator.jpackage
更改为 jdk.jpackage
。
相对于 JEP 343,唯一的实质性变化是我们将 --bind-services
选项替换为更通用的 --jlink-options
选项,如下所述。
目标
创建一个基于旧版 JavaFX javapackager
工具的打包工具,该工具:
-
支持原生打包格式,为最终用户提供自然的安装体验。这些格式包括在 Windows 上的
msi
和exe
,在 macOS 上的pkg
和dmg
,以及在 Linux 上的deb
和rpm
。 -
允许在打包时指定启动时参数。
-
可以直接调用,也可以通过命令行或编程方式通过
ToolProvider
API 调用。
非目标
-
传统的
javapackager
工具的以下功能不受支持:- Java Web Start 应用程序支持,
- JavaFX 特定功能,
- 使用
jdeps
确定所需的模块,以及 - Ant 插件。
-
该工具没有图形用户界面;命令行界面 (CLI) 就足够了。
-
不支持跨平台编译。例如,为了创建 Windows 安装包,必须在 Windows 上运行该工具。打包工具将依赖特定于平台的工具。
-
除了 JMOD 文件中已提供的内容外,没有任何特殊的法律文件支持。不会汇总单个许可证文件。
-
没有原生的启动画面支持。
-
没有自动更新机制。
动机
许多 Java 应用程序需要以一流的方式安装在本地平台上,而不是简单地放在类路径或模块路径上。应用程序开发者仅仅交付一个简单的 JAR 文件是不够的;他们必须提供一个适合本地平台的可安装包。这使得 Java 应用程序能够以用户熟悉的方式进行分发、安装和卸载。例如,在 Windows 上,用户期望能够双击一个包来安装他们的软件,然后使用控制面板删除该软件;在 macOS 上,用户期望能够双击一个 DMG 文件并将他们的应用程序拖到 Application 文件夹中。
jpackage 工具还可以帮助填补过去技术留下的空白,例如从 Oracle 的 JDK 11 中移除的 Java Web Start 和在 JDK 14 中移除的 pack200
(JEP 367)。开发者可以使用 jlink
将 JDK 精简到所需模块的最小集合,然后使用打包工具生成一个压缩的、可安装的镜像,并将其部署到目标机器上。
为了满足这些需求,以前 Oracle 的 JDK 8 中会附带一个名为 javapackager
的打包工具。但是,作为移除 JavaFX 的一部分,它从 Oracle 的 JDK 11 中被移除了。
描述
jpackage
工具将 Java 应用程序打包为包含所有必要依赖项的平台特定包。该应用程序可以作为普通 JAR 文件的集合提供,也可以作为模块的集合提供。支持的平台特定包格式包括:
- Linux:
deb
和rpm
- macOS:
pkg
和dmg
- Windows:
msi
和exe
默认情况下,jpackage
会以最适合运行该命令的系统的格式生成一个包。
基本用法:非模块化应用程序
假设你有一个由 JAR 文件组成的应用程序,所有文件都位于名为 lib
的目录中,并且 lib/main.jar
包含主类。那么命令如下:
$ jpackage --name myapp --input lib --main-jar main.jar
将会以本地系统的默认格式打包应用程序,并将生成的包文件留在当前目录中。如果 main.jar
中的 MANIFEST.MF
文件没有 Main-Class
属性,那么你必须显式指定主类:
$ jpackage --name myapp --input lib --main-jar main.jar \
--main-class myapp.Main
该包的名称将为 myapp
,尽管包文件本身的名称会更长,并以包类型结尾(例如,myapp.exe
)。该包将包括一个也称为 myapp
的应用程序启动器。要启动应用程序,启动器会将从输入目录复制的每个 JAR 文件放置在 JVM 的类路径中。
如果你希望生成默认格式以外的包,请使用 --type
选项。例如,在 macOS 上生成 pkg
文件而不是 dmg
文件:
$ jpackage --name myapp --input lib --main-jar main.jar --type pkg
基本用法:模块化应用程序
如果您的应用程序是由模块化的 JAR 文件和/或 JMOD 文件组成的模块化应用,并且这些文件位于 lib
目录中,主类在模块 myapp
中,那么可以使用以下命令:
$ jpackage --name myapp --module-path lib -m myapp
将会打包它。如果 myapp
模块没有识别出它的主类,那么,你必须显式地指定它:
$ jpackage --name myapp --module-path lib -m myapp/myapp.Main
(创建模块化 JAR 或 JMOD 文件时,可以使用 jar
和 jmod
工具的 --main-class
选项指定主类。)
包元数据
jpackage
工具允许你指定各种平台无关的元数据,例如名称和应用版本,以及每个平台的特定元数据。
所有 jpackage
选项的描述可以在 jpackage
手册页 中找到。
文件关联
你可以通过 --file-associations
选项为你的应用程序定义一个或多个文件类型关联,该选项可以多次使用。此选项的参数是一个属性文件,包含以下一个或多个键的值:
extension
指定要与应用程序关联的文件的扩展名,mime-type
指定要与应用程序关联的文件的 MIME 类型,icon
指定应用程序映像内用于此关联的图标,以及description
指定该关联的简短描述。
启动器
默认情况下,jpackage
工具会为您的应用程序创建一个简单的原生启动器。您可以通过以下选项自定义默认启动器:
--arguments <string>
— 如果启动器没有给出命令行参数,则传递给主类的命令行参数(此选项可以多次使用)--java-options <string>
— 传递给 JVM 的选项(此选项可以多次使用)
如果您的应用程序需要更多的启动器,那么您可以通过 --add-launcher
选项来添加它们:
--add-launcher <launcher-name>=<file>
命名的 <file>
应为一个属性文件,其中包含一个或多个键的值,这些键包括 app-version
、icon
、arguments
、java-options
、main-class
、main-jar
、module
或 win-console
。这些键的值将被解释为与同名选项的参数,但相对于正在创建的启动器,而不是默认启动器。--add-launcher
选项可以多次使用。
应用图像
jpackage
工具构建一个 应用程序映像 作为输入,提供给它在最后一步调用的特定平台打包工具。通常,这个映像是一个临时产物,但有时在打包之前需要对其进行自定义。因此,可以分两步运行 jpackage
工具。首先,使用特殊的包类型 app-image
创建初始的应用程序映像:
$ jpackage --name myapp --module-path lib -m myapp --type app-image
这将在 myapp
目录中生成一个应用镜像。根据需要自定义该镜像,然后通过 --app-image
选项创建最终的软件包:
$ jpackage --name myapp --app-image myapp
运行时镜像
应用镜像包含组成你的应用的文件以及将运行你的应用的 JDK 运行时镜像。默认情况下,jpackage
工具会调用 jlink
工具 来创建运行时镜像。镜像的内容取决于应用的类型:
-
对于由 JAR 文件组成的非模块化应用程序,运行时镜像包含与常规
java
启动器提供给类路径应用程序在未命名模块中的相同的一组 JDK 模块。 -
对于由模块化 JAR 文件和/或 JMOD 文件组成的模块化应用程序,运行时镜像包含应用程序的主模块及其所有依赖项的传递闭包。
jpackage
使用的 jlink
选项默认集合为
--strip-native-commands --strip-debug --no-man-pages --no-header-files
但可以通过 --jlink-options
选项进行更改。生成的镜像不会包含所有可用的服务提供者;如果希望绑定这些服务提供者,则使用 --jlink-options
并在 jlink
选项列表中包含 --bind-services
。
对于这两种情况,如果要将其他模块包含到运行时镜像中,可以使用 jpackage
工具的 --add-modules
选项。运行时镜像中的模块列表在镜像的 release 文件中可用。
jpackage
工具创建的运行时镜像不包含 src.zip
文件。
如果你希望进一步自定义运行时镜像,那么你可以自行调用 jlink
,并通过 --runtime-image
选项将生成的镜像传递给 jpackage
工具。例如,如果你使用了 jdeps
工具 确定你的非模块化应用程序仅需要 java.base
和 java.sql
模块,那么你可以显著减少包的大小:
$ jlink --add-modules java.base,java.sql --output myjre
$ jpackage --name myapp --input lib --main-jar main.jar --runtime-image myjre
应用镜像布局与内容
应用镜像的布局和内容是平台特定的。实际的镜像中包含了一些在下面布局中未显示的文件;这些文件是实现细节,可能会随时更改。
Linux
myapp/
bin/ // Application launcher(s)
myapp
lib/
app/
myapp.cfg // Configuration info, created by jpackage
myapp.jar // JAR files, copied from the --input directory
mylib.jar
...
runtime/ // JDK runtime image
Linux 上的默认安装目录是 /opt
。可以通过 --install-dir
选项覆盖该设置。
macOS
MyApp.app/
Contents/
Info.plist
MacOS/ // Application launcher(s)
MyApp
Resources/ // Icons, etc.
app/
MyApp.cfg // Configuration info, created by jpackage
myapp.jar // JAR files, copied from the --input directory
mylib.jar
...
runtime/ // JDK runtime image
macOS 上的默认安装目录是 /Applications
。可以通过 --install-dir
选项覆盖该默认设置。
Windows
MyApp/
MyApp.exe // Application launcher(s)
app/
MyApp.cfg // Configuration info, created by jpackage
myapp.jar // JAR files, copied from the --input directory
mylib.jar
...
runtime/ // JDK runtime image
Windows 上的默认安装目录是 C:\Program Files\
。可以通过 --install-dir
选项覆盖该设置。
提供 jpackage
jpackage
工具以名为 jdk.jpackage
的模块形式随 JDK 一起提供。
命令行界面符合 JEP 293(JDK 命令行工具选项指南)。
除了命令行界面外,jpackage
还可以通过 ToolProvider API(java.util.spi.ToolProvider
)以名称 "jpackage"
访问。
测试
大多数测试可以使用自动化脚本完成,但有一些需要注意的事项:
-
测试原生包可能需要安装一些可选工具;这些测试需要编写成在没有必要工具的系统上可以跳过的形式。
-
验证某些类型的原生包(例如,Windows 上的
exe
或 macOS 上的dmg
)可能需要进行一些手动测试。 -
我们需要确保原生包能够干净地安装和卸载,以便开发者可以在本地环境中进行测试,而无需担心污染他们的系统。
依赖
原生包是使用主机平台上可用的工具生成的。在 Windows 上,开发者必须安装第三方 “Wix” 工具,以便生成 msi
或 exe
包。