Skip to content

JEP 343: Packaging Tool (Incubator) | 打包工具(孵化器)

摘要

创建一个用于打包自包含 Java 应用程序的工具。

目标

基于 JavaFX 的 javapackager 工具,创建一个简单的打包工具,该工具:

  • 支持原生打包格式,为最终用户提供自然的安装体验。这些格式包括 Windows 上的 msiexe,macOS 上的 pkgdmg,以及 Linux 上的 debrpm

  • 允许在打包时指定启动参数。

  • 可以直接从命令行或通过 ToolProvider API 以编程方式调用。

非目标

  • 以下 javapackager 工具的功能将不受支持:
    • Java Web Start 应用程序支持,
    • JavaFX 特定功能,
    • 使用 jdeps 确定所需的模块,以及
    • Ant 插件。
  • 该工具将没有图形用户界面 (GUI);命令行界面 (CLI) 已足够。
  • 将不支持交叉编译。例如,为了创建 Windows 软件包,必须在 Windows 上运行该工具。打包工具将依赖于特定于平台的工具。
  • 除了 JMOD 文件中已经提供的内容外,将不会对法律文件提供特殊支持。将不会聚合单个许可证文件。
  • 将不支持原生启动画面。
  • 将没有自动更新机制。
  • 该工具将不在 Solaris 上可用。

动机

许多 Java 应用程序需要以一流的方式安装在本地平台上,而不仅仅是放在类路径或模块路径上。仅仅提供一个简单的 JAR 文件对于应用程序开发者来说是不够的;他们必须提供一个适合本地平台的可安装包。这使得 Java 应用程序能够以用户熟悉的方式进行分发、安装和卸载。例如,在 Windows 上,用户期望能够双击一个安装包来安装他们的软件,然后使用控制面板来卸载软件;在 macOS 上,用户期望能够双击一个 DMG 文件并将他们的应用程序拖到应用程序文件夹中。

一个打包工具还可以帮助填补其他技术留下的空白,如 Java Web Start(已在 Oracle 的 JDK 11 中被移除)和 pack200(在 JDK 11 中被弃用,并将在未来的版本中移除)。开发人员可以使用 jlink 将 JDK 缩减到所需的最小模块集,然后使用打包工具生成一个压缩的、可安装的映像,以便部署到目标机器上。

为了应对这些需求,之前分发了一个名为 javapackager 的打包工具,它与 Oracle 的 JDK 8 一起提供。然而,随着 JavaFX 的移除,它也在 Oracle 的 JDK 11 中被移除了。

描述

jpackage 工具将 Java 应用程序打包成包含所有必要依赖项的平台特定包。应用程序可以作为一组普通的 JAR 文件或一组模块提供。支持的平台特定包格式包括:

  • Linux:debrpm
  • macOS:pkgdmg
  • Windows:msiexe

默认情况下,jpackage 会产生与运行它的系统最匹配的格式。

基本用法:非模块化应用程序

假设你有一个由 JAR 文件组成的应用程序,所有这些文件都位于名为 lib 的目录中,并且 lib/main.jar 包含主类。然后,你可以使用以下命令

bash
$ jpackage --name myapp --input lib --main-jar main.jar

来将应用程序打包成本地系统的默认格式,并将生成的包文件留在当前目录中。如果 main.jar 中的 MANIFEST.MF 文件没有 Main-Class 属性,那么你必须明确指定主类:

bash
$ jpackage --name myapp --input lib --main-jar main.jar \
  --main-class myapp.Main

包的名称将是 myapp,但包文件本身的名称会更长,并以包类型结尾(例如,myapp.exe)。该包将包含一个用于应用程序的启动器,也称为 myapp。要启动应用程序,启动器会将从输入目录复制的每个 JAR 文件放在 JVM 的类路径上。

如果你希望生成除默认格式以外的包格式,请使用 --type 选项。例如,在 macOS 上生成 pkg 文件而不是 dmg 文件:

bash
$ jpackage --name myapp --input lib --main-jar main.jar --type pkg

基本用法:模块化应用程序

如果你有一个模块化应用程序,它由位于 lib 目录中的模块化 JAR 文件和 / 或 JMOD 文件组成,主类位于 myapp 模块中,那么可以使用以下命令进行打包:

bash
$ jpackage --name myapp --module-path lib -m myapp

如果 myapp 模块没有指定其主类,那么,同样地,你必须明确指定它:

bash
$ jpackage --name myapp --module-path lib -m myapp/myapp.Main

(在打包模块化 JAR 或 JMOD 文件时,你可以使用 jarjmod 工具的 --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-versioniconargumentsjava-optionsmain-classmain-jarmodulewin-console 等键的一个或多个值。这些键的值将被解释为与相同名称选项的参数,但它们是针对正在创建的启动器而不是默认启动器。--add-launcher 选项可以多次使用。

应用程序映像

jpackage 工具在其最后一步中调用特定于平台的打包工具时,会构造一个 应用程序映像 作为输入。通常,这个映像是临时产物,但有时你需要在它被打包之前进行自定义。因此,你可以将 jpackage 工具分两步运行。首先,使用特殊的包类型 app-image 创建初始的应用程序映像:

bash
$ jpackage --name myapp --module-path lib -m myapp --type app-image

这将在 myapp 目录中生成一个应用程序映像。根据需要自定义该映像,然后通过 --app-image 选项创建最终的包:

bash
$ jpackage --name myapp --app-image myapp

运行时映像

应用程序映像既包含组成你的应用程序的文件,也包含将运行你的应用程序的 JDK运行时映像。默认情况下,jpackage 工具会调用 jlink 工具 来创建运行时映像。映像的内容取决于应用程序的类型:

  • 对于由 JAR 文件组成的非模块化应用程序,运行时映像包含与常规 java 启动器为未命名模块中的类路径应用程序提供的相同的 JDK 模块集。

  • 对于由模块化 JAR 文件和 / 或 JMOD 文件组成的模块化应用程序,运行时映像包含应用程序的主模块及其所有依赖项的传递闭包。它不会包含所有可用的服务提供者;如果你想要绑定这些服务提供者,那么请向 jpackage 工具指定 --bind-services 选项。

在任一情况下,如果你希望将额外的模块添加到运行时映像中,你可以使用 jpackage 工具的 --add-modules 选项。运行时映像中的模块列表在映像的 release 文件中可用。

jpackage 工具创建的运行时映像不包含调试符号、通常的 JDK 命令、手册页或 src.zip 文件。

如果你想进一步自定义运行时映像,那么你可以自己调用 jlink,并通过 --runtime-image 选项将生成的映像传递给 jpackage 工具。例如,如果你已经使用 jdeps 工具 确定你的非模块化应用程序只需要 java.basejava.sql 模块,你可以显著减小你的包的大小:

bash
$ 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
txt
myapp/
  bin/              // 应用程序启动器
    myapp
  lib/
    app/
      myapp.cfg     // 由 jpackage 创建的配置信息
      myapp.jar     // JAR 文件,从--input 目录复制
      mylib.jar
      ...
    runtime/        // JDK 运行时映像

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
txt
MyApp.app/
  Contents/
    Info.plist
    MacOS/          // 应用程序启动器
      MyApp
    Resources/      // 图标等
    app/
      MyApp.cfg     // 配置信息,由 jpackage 创建
      myapp.jar     // JAR 文件,从--input 目录复制
      mylib.jar
      ...
    runtime/        // JDK 运行时映像

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
txt
MyApp/
  MyApp.exe         // 应用程序启动器
  app/
    MyApp.cfg     // 配置信息,由 jpackage 创建
    myapp.jar     // JAR 文件,从--input 目录复制
    mylib.jar
    ...
  runtime/        // JDK 运行时映像

Windows 上的默认安装目录是 C:/Program Files/。这可以通过 --install-dir 选项进行覆盖。

Windows 特定选项:

  • --win-console — 为应用程序创建控制台启动器(应为需要控制台交互的应用程序指定)
  • --win-dir-chooser — 添加一个对话框,使用户能够选择在其中安装应用程序的目录
  • --win-menu — 将应用程序添加到系统菜单
  • --win-menu-group <菜单组名> — 开始菜单中的组,将应用程序放置在此组中
  • --win-per-user-install — 基于每个用户安装应用程序
  • --win-shortcut — 为应用程序创建桌面快捷方式
  • --win-upgrade-uuid <字符串> — 与此包升级相关联的 UUID

交付 jpackage

jpackage 工具将以名为 jdk.incubator.jpackage孵化器模块 的形式在 JDK 中交付。作为在孵化器模块中交付的功能,jpackage 工具的命令行选项、应用程序布局和其他导出的接口不保证稳定,并可能在未来的版本中修订。从命令行运行时,该工具将显示警告。默认情况下,jdk.incubator.jpackage 模块不会被解析,并且在解析时将显示警告。

jpackage 工具基于 javapackager 工具,移除了与 Java Web Start 和 JavaFX 相关的所有功能。命令行接口(CLI)遵循 JEP 293(JDK 命令行工具选项的指南)。除了命令行接口外,jpackage 还可以通过名为 "jpackage"ToolProvider API (java.util.spi.ToolProvider) 访问。

测试

大多数测试可以使用自动化脚本来完成,但需要注意以下几点:

  • 测试原生包可能需要安装可选工具;这些测试需要编写成在缺少必要工具的系统上被跳过的形式。

  • 验证某些类型的原生包(例如,Windows 上的 exe 或 macOS 上的 dmg)可能需要一些手动测试。

  • 我们需要确保原生包可以干净地安装和卸载,以便开发人员可以在其本地环境中进行测试,而无需担心污染他们的系统。

依赖项

原生包将使用目标平台上的工具来生成。对于 Windows,如果开发人员想要生成原生包,他们需要安装一个额外的工具:

  • Wix 是一个第三方工具,用于生成 msiexe

当前正在进行的工作是增强 jlink 以在未来的 JDK 版本中生成原生启动器。jlinkjpackage 之间可能需要一定程度的协调。