JEP 343: 打包工具(孵化器)
概述
创建一个用于打包自包含 Java 应用程序的工具。
目标
创建一个简单的打包工具,基于 JavaFX 的 javapackager 工具,该工具:
-
支持原生打包格式,为最终用户提供自然的安装体验。这些格式包括 Windows 上的
msi和exe、macOS 上的pkg和dmg以及 Linux 上的deb和rpm。 -
允许在打包时指定启动时参数。
-
可以直接调用,也可以通过命令行或通过
ToolProviderAPI 以编程方式调用。
非目标
javapackager工具的以下功能将不再被支持:- Java Web Start 应用支持,
- JavaFX 特定功能,
- 使用
jdeps来确定所需的模块,以及 - Ant 插件。
- 该工具将不提供图形用户界面(GUI);命令行界面(CLI)已足够。
- 不会支持跨平台编译。例如,为了创建 Windows 的安装包,必须在 Windows 上运行该工具。打包工具将依赖特定平台的工具。
- 除了 JMOD 文件中已提供的内容外,不会有针对法律文件的特殊支持。不会汇总单个许可证文件。
- 不支持本地启动画面。
- 没有自动更新机制。
- 该工具将不适用于 Solaris。
动机
许多 Java 应用程序需要以一流的方式安装在原生平台上,而不是简单地放在类路径或模块路径上。应用程序开发者仅仅提供一个简单的 JAR 文件是不够的;他们必须提供一个适合原生平台的可安装包。这使得 Java 应用程序能够以用户熟悉的方式进行分发、安装和卸载。例如,在 Windows 上,用户期望能够双击一个包来安装他们的软件,然后使用控制面板删除该软件;在 macOS 上,用户期望能够双击一个 DMG 文件并将他们的应用程序拖到 Applications 文件夹中。
一种打包工具还可以帮助填补其他技术留下的空白,例如 Oracle 的 JDK 11 中移除的 Java Web Start,以及在 JDK 11 中已被弃用、未来版本中将被移除的 pack200。开发者可以使用 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 模块没有标识其主类,那么同样,你必须显式指定:
jlink --module-path $JAVA_HOME/jmods:mlib --add-modules myapp --output myapp-runtime --launcher myapp=myapp/com.example.Main
$ jpackage --name myapp --module-path lib -m myapp/myapp.Main
(当打包模块化的 JAR 或 JMOD 文件时,可以使用 jar 和 jmod 工具的 --main-class 选项来指定主类。)
包元数据
jpackage 工具允许你为你的软件包指定各种元数据。所有平台通用的选项包括:
--app-version <version>--copyright <string>--description <string>--license-file <file>--name <string>--vendor <string>
该工具会根据软件包的类型,以适当的方式使用提供给这些选项的参数。平台特定的软件包元数据选项描述见下方。
文件关联
你可以通过 --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 模块集相同的 JDK 模块集。 -
对于由模块化的 JAR 文件和/或 JMOD 文件组成的模块化应用程序,运行时镜像包含应用程序的主模块及其所有依赖项的传递闭包。它不会包含所有可用的服务提供者;如果需要绑定这些服务提供者,请在使用
jpackage工具时指定--bind-services选项。
你可以在两种情况下使用 jpackage 工具的 --add-modules 选项,将额外的模块添加到运行时镜像中。运行时镜像中的模块列表可以在镜像的 release 文件中找到。
jpackage 工具创建的运行时镜像不包含调试符号、常见的 JDK 命令、手册页或 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
特定平台的详细信息
本节描述了 jpackage 工具的平台相关方面,包括应用程序映像布局和特定于平台的选项。命令 jpackage --help 将打印所有选项的摘要。
jpackage 工具创建的应用程序镜像包含一些在以下布局中未显示的文件;这些文件应被视为可能会更改的实现细节。
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 选项覆盖此设置。
特定于 Linux 的选项:
--linux-package-name <package name>— Linux 包的名称,默认为应用程序名称--linux-deb-maintainer <email address>— DEB 包的维护者--linux-menu-group <menu-group-name>— 此应用程序所属的菜单组--linux-package-deps <deps>— 应用程序所需的包或功能--linux-rpm-license-type <type string>— 许可证的类型(RPM.spec文件中的License: <value>)--linux-app-release <release value>— RPM<name>.spec文件的发布值,或者是 DEB 控制文件的 Debian 修订值--linux-app-category <category value>— RPM<name>.spec文件的组值,或者是 DEB 控制文件的 Section 值--linux-shortcut为应用程序创建快捷方式
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 选项覆盖此设置。
macOS 特定选项:
--mac-package-identifier <string>— 用于唯一标识 macOS 应用程序的标识符(默认为主类名;仅限字母数字、连字符和句点字符)--mac-package-name <string>— 菜单栏中显示的应用程序名称(默认为应用程序名称;长度必须小于 16 个字符,并且适合显示在菜单栏和应用程序信息窗口中)--mac-package-signing-prefix <string>— 在签署应用程序包时,为需要签名但没有现有包标识符的所有组件添加的前缀值--mac-sign— 请求对包进行签名--mac-signing-keychain <file>— 搜索签名身份的钥匙串路径(默认为标准钥匙串)--mac-signing-key-user-name <team name>— Apple 签名身份的团队名称部分(例如,“Developer ID Application: ”)
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 选项覆盖此设置。
Windows 特定选项:
--win-console— 为应用程序创建一个控制台启动器(应为需要控制台交互的应用程序指定)--win-dir-chooser— 添加一个对话框,使用户可以选择安装应用程序的目录--win-menu— 将应用程序添加到系统菜单--win-menu-group <menu-group-name>— 放置此应用程序的开始菜单组--win-per-user-install— 以每个用户为基础安装应用程序--win-shortcut— 为应用程序创建桌面快捷方式--win-upgrade-uuid <string>— 与此包的升级相关联的 UUID
交付 jpackage
jpackage 工具将作为 孵化模块 提供在 JDK 中,模块名称为 jdk.incubator.jpackage。作为一个在孵化模块中提供的功能,jpackage 工具的命令行选项、应用程序布局以及其他导出的接口并不保证稳定,可能会在未来的版本中进行修订。该工具在命令行运行时会显示警告信息。jdk.incubator.jpackage 模块默认不会被解析,并且在解析时会触发警告显示。
jpackage 工具基于 javapackager 工具,删除了与 Java Web Start 和 JavaFX 相关的所有功能。命令行界面 (CLI) 遵循 JEP 293(JDK 命令行工具选项指南)。除了命令行界面外,jpackage 还可以通过 ToolProvider API (java.util.spi.ToolProvider) 使用,名称为 "jpackage"。
测试
大多数测试可以使用自动化脚本完成,但有一些需要注意的事项:
-
测试原生包可能需要安装一些可选工具;这些测试需要编写成在没有必要工具的系统上能够跳过。
-
验证某些类型的原生包(例如,Windows 上的
exe或 macOS 上的dmg)可能需要进行一些手动测试。 -
我们需要确保原生包可以干净地安装和卸载,以便开发者能够在本地环境中进行测试,而无需担心污染他们的系统。
依赖
将使用目标平台上的工具生成原生包。对于 Windows,如果开发者想要生成原生包,则需要安装一个额外的工具:
- 需要使用第三方工具 Wix 来生成
msi或exe包装文件
正在努力增强 jlink,以便在将来版本的 JDK 中生成原生启动器。jlink 和 jpackage 之间可能需要某种程度的协调。