Skip to content

JEP 392: Packaging Tool | 打包工具

摘要

提供 jpackage 工具,用于打包自包含的 Java 应用程序。

历史

jpackage 工具作为孵化工具在 JDK 14 中通过 JEP 343 引入。在 JDK 15 中,它继续作为孵化工具存在,以便有更多时间收集反馈。现在,它已准备好从孵化阶段提升到生产就绪特性。由于这一转变,jpackage 模块的名称将从 jdk.incubator.jpackage 更改为 jdk.jpackage

与 JEP 343 相比,唯一的实质性变化是我们用更通用的 --jlink-options 选项替换了 --bind-services 选项,具体描述见下文

目标

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

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

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

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

非目标

  • 以下传统 javapackager 工具的功能不受支持:

    • Java Web Start 应用程序支持,
    • JavaFX 特定功能,
    • 使用 jdeps 确定所需模块,以及
    • Ant 插件。
  • 该工具没有图形用户界面;命令行界面(CLI)已足够。

  • 不支持交叉编译。例如,为了创建 Windows 包,必须在 Windows 上运行该工具。打包工具将依赖于特定于平台的工具。

  • 除了 JMOD 文件中已提供的内容外,对法律文件没有特殊支持。不会聚合单独的许可证文件。

  • 不支持原生启动画面。

  • 没有自动更新机制。

动机

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

jpackage 工具还可以帮助填补过去技术留下的空白,如 Oracle JDK 11 中移除的 Java Web Start 和 JDK 14 中移除的 pack200JEP 367)。开发人员可以使用 jlink 将 JDK 精简到所需的最小模块集,然后使用打包工具生成一个压缩的、可安装的映像,该映像可以部署到目标机器上。

为了满足这些需求,Oracle 的 JDK 8 附带了一个名为 javapackager 的打包工具。然而,随着 JavaFX 的移除,它也被从 Oracle 的 JDK 11 中移除了。

描述

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

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

默认情况下,jpackage 会在其运行的系统上生成最适合该系统的包格式。

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

假设你有一个由 JAR 文件组成的应用程序,所有 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 工具允许你指定各种与平台无关的元数据,如名称和应用程序版本,以及每个平台的特定元数据。

所有 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-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 使用的默认 jlink 选项集是

bash
--strip-native-commands --strip-debug --no-man-pages --no-header-files

但可以通过 --jlink-options 选项进行更改。生成的映像将不包括所有可用的服务提供者;如果您希望绑定这些服务提供者,请使用 --jlink-options 并在 jlink 选项列表中包括 --bind-services

在任何情况下,如果您希望将其他模块包含在运行时映像中,可以使用 jpackage 工具的 --add-modules 选项。运行时映像中的模块列表可在映像的发布文件中找到。

jpackage 工具创建的运行时映像不包含 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

应用程序映像的布局和内容

应用程序映像的布局和内容是特定于平台的。实际的映像包含一些在以下布局中未显示的文件;这些文件是实现细节,随时可能更改。

Linux

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

Linux 上的默认安装目录是 /opt。这可以通过 --install-dir 选项进行覆盖。

macOS

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

macOS 上的默认安装目录是 /Applications。这可以通过 --install-dir 选项进行覆盖。

Windows

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

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

提供 jpackage

jpackage 工具在名为 jdk.jpackage 的模块中以 JDK 的形式提供。

命令行接口符合 JEP 293(JDK 命令行工具选项指南)

除了命令行接口外,还可以通过名为 "jpackage"ToolProvider APIjava.util.spi.ToolProvider)访问 jpackage

测试

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

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

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

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

依赖项

原生包是使用主机平台上可用的工具生成的。在 Windows 上,开发人员必须安装第三方“Wix”工具才能生成 msiexe 包。