# jpackage-maven-plugin **Repository Path**: huahill/jpackage-maven-plugin ## Basic Information - **Project Name**: jpackage-maven-plugin - **Description**: 用于使用 JDK jlink 和 jpackage 工具打包桌面 Java 应用程序的 Maven 插件,并可选支持 Project Leyden AOT 缓存训练工作流。 - **Primary Language**: Java - **License**: UPL-1.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-19 - **Last Updated**: 2026-10-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # JPackage Maven Plugin 中文 | [English](README.md) --- 这是一个用于打包桌面 Java 应用的 Maven 插件。它封装 JDK 自带的 `jlink` 和 `jpackage` 工具,并提供可选的 Project Leyden AOT 缓存训练流程。 ## 坐标 Maven Central: https://central.sonatype.com/artifact/io.github.huahill/jpackage-maven-plugin ```xml io.github.huahill jpackage-maven-plugin 0.1.1 ``` 当前插件构建基线是 Java 25 和 Maven 4。 ## Java 兼容性 这里需要区分两个 Java 版本: - 用来构建和运行该 Maven 插件的 Java 版本。 - `javaHome` 选择的 JDK 版本,插件会调用这个 JDK 下的 `jlink` 和 `jpackage` 工具。 | 范围 | 要求 | 说明 | |---|---|---| | 插件构建/运行 | Java 25、Maven 4 | 插件当前使用 `maven.compiler.release=25` 编译。 | | `jlink` goal | 带有 `jlink` 的 JDK | 具体支持哪些选项取决于所选 JDK。当前结构化选项按 JDK 25 对齐。 | | `jpackage` goal | 带有 `jpackage` 的 JDK | `jpackage` 不是 JDK 8 时代的工具,请使用现代 JDK。当前结构化选项按 JDK 25 对齐。 | | `leyden` goal | JDK 25 或更新版本 | 该 goal 使用 `-XX:AOTCacheOutput`,也就是 JDK 25+ 的 Leyden AOT 缓存流程。若 `javaHome` 低于 JDK 25,会提前失败。 | 为了得到可预测的结果,建议 Maven 和 `javaHome` 使用同一个现代 JDK。如果给 `jlink` 或 `jpackage` 选择较老的 JDK,该 JDK 不支持的选项会在底层 JDK 工具中失败。 ## Goals | Goal | 用途 | |---|---| | `jlink` | 直接调用 JDK `jlink` 工具。 | | `jpackage` | 直接调用 JDK `jpackage` 工具。 | | `leyden` | 暂存项目产物,构建 `jlink` 运行时,创建 `jpackage` app image,训练 Leyden AOT 缓存,重写 launcher 配置,并创建最终安装包。 | ## 快速开始 直接从 Maven Central 使用已发布插件,不需要配置额外 Maven 仓库: ```xml io.github.huahill jpackage-maven-plugin 0.1.1 ``` 完整配置可以参考本仓库中的示例项目。如果是在开发插件本身,需要先把当前源码安装到 本地 Maven 仓库,再运行示例: ```bash sdk env ./mvnw install ./mvnw -f samples/classpath-swing/pom.xml package ``` 示例项目会把打包产物写入自己的 `target/` 目录。 ## 支持的应用形态 | 形态 | 状态 | |---|---| | 使用 `mainJar` 和 `mainClass` 的 classpath 桌面应用 | 已实现,并在命令生成层面有单元测试。 | | 使用 `module` 的 JPMS 桌面应用 | 已实现,并通过 `samples/jpms-swing` 的 macOS app image 验证。 | | Swing/AWT | 由 `samples/classpath-swing` 和 `samples/jpms-swing` 覆盖;macOS app image 已验证。 | | JavaFX | 由 `samples/classpath-javafx` 覆盖;macOS app image 已验证。 | | 多 launcher | 通过 `addLaunchers` properties 文件支持。 | | Linux/Windows 安装包 | 命令生成和路径推导已有单元测试;真实主机上的 `.deb`、`.rpm`、`.msi`、`.exe` 仍待验证。 | ## Classpath ### Swing ```xml io.github.huahill jpackage-maven-plugin 0.1.1 package-app package jpackage Swing Sample app-image ${project.build.directory}/jpackage ${project.build.directory}/jpackage-input ${project.build.finalName}.jar com.acme.sample.SwingSampleApp java.desktop ``` 消费方项目需要确保主 jar 和运行时依赖已经放到 `input` 目录中。完整示例见 `samples/classpath-swing/pom.xml`,该示例会把 jar 直接写入 `target/jpackage-input`。 ### JavaFX ```xml io.github.huahill jpackage-maven-plugin 0.1.1 package-app package jpackage JavaFX Sample app-image ${project.build.directory}/jpackage ${project.build.directory}/jpackage-input ${project.build.finalName}.jar com.acme.sample.JavaFxSampleApp ${project.build.directory}/jpackage-input javafx.controls ``` 对于 classpath JavaFX 应用,需要把 JavaFX 运行时依赖复制到 `input` 目录, 并把该目录加入 `modulePath`,这样 `jpackage` 才能把 JavaFX 模块链接进运行时镜像。 ## JPMS Module ```xml io.github.huahill jpackage-maven-plugin 0.1.1 package-app package jpackage JPMS Swing Sample app-image ${project.build.directory}/jpackage ${project.build.directory}/jpackage-input com.acme.modulesample/com.acme.modulesample.ModuleSwingSampleApp ``` 对于 JPMS 应用,需要把模块化应用 jar 和模块化依赖放到 `modulePath` 上, 并使用 `module`,而不是 `mainJar`/`mainClass`。 ## Leyden ```xml io.github.huahill jpackage-maven-plugin 0.1.1 leyden-package package leyden Swing Sample ${project.build.finalName}.jar com.acme.sample.SwingSampleApp java.desktop jdk.jfr sample.leyden.training ``` 应用必须在 `-D=true` 存在时自行退出。插件会在 Maven 构建期间 直接启动生成出来的 app image,并等待该进程结束。 JPMS Leyden 启动通过 `module` 配置: ```xml JPMS Swing Sample com.acme.modulesample/com.acme.modulesample.ModuleSwingSampleApp com.acme.modulesample java.desktop ``` ## `jlink` 参数 | 参数 | 工具选项 | 说明 | |---|---|---| | `modulePath` | `--module-path` | 该 goal 必填。 | | `modules` | `--add-modules` | 该 goal 必填。 | | `output` | `--output` | 该 goal 必填。 | | `noHeaderFiles` | `--no-header-files` | 布尔值。 | | `noManPages` | `--no-man-pages` | 布尔值。 | | `stripDebug` | `--strip-debug` | 布尔值。 | | `compress` | `--compress` | 字符串值,例如 `zip-6`。 | | `bindServices` | `--bind-services` | 布尔值。 | | `extraOptions` | 透传 | 追加到已建模选项之后。 | | `javaHome` | 工具位置 | 默认 `${java.home}`。 | ## `jpackage` 参数 通用选项: | 参数 | 工具选项 | |---|---| | `optionFiles` | `@file` | | `name` | `--name` | | `packageType` | `--type` | | `packageVersion` | `--app-version` | | `copyright` | `--copyright` | | `description` | `--description` | | `destination` | `--dest` | | `help` | `--help` | | `icon` | `--icon` | | `temp` | `--temp` | | `toolVersion` | `--version` | | `vendor` | `--vendor` | | `verbose` | `--verbose` | 运行时镜像选项: | 参数 | 工具选项 | |---|---| | `modules` | `--add-modules` | | `jlinkOptions` | `--jlink-options` | | `modulePath` | `--module-path` | | `runtimeImage` | `--runtime-image` | 应用镜像选项: | 参数 | 工具选项 | |---|---| | `appContent` | `--app-content` | | `input` | `--input` | Launcher 选项: | 参数 | 工具选项 | |---|---| | `addLaunchers` | `--add-launcher` | | `arguments` | `--arguments` | | `javaOptions` | `--java-options` | | `mainClass` | `--main-class` | | `mainJar` | `--main-jar` | | `module` | `--module` | 安装包选项: | 参数 | 工具选项 | |---|---| | `aboutUrl` | `--about-url` | | `appImage` | `--app-image` | | `fileAssociations` | `--file-associations` | | `installDir` | `--install-dir` | | `launcherAsService` | `--launcher-as-service` | | `licenseFile` | `--license-file` | | `resourceDir` | `--resource-dir` | macOS 选项: | 参数 | 工具选项 | |---|---| | `macAppCategory` | `--mac-app-category` | | `macAppImageSignIdentity` | `--mac-app-image-sign-identity` | | `macAppStore` | `--mac-app-store` | | `macDmgContent` | `--mac-dmg-content` | | `macEntitlements` | `--mac-entitlements` | | `macInstallerSignIdentity` | `--mac-installer-sign-identity` | | `macPackageIdentifier` | `--mac-package-identifier` | | `macPackageName` | `--mac-package-name` | | `macPackageSigningPrefix` | `--mac-package-signing-prefix` | | `macSign` | `--mac-sign` | | `macSigningKeyUserName` | `--mac-signing-key-user-name` | | `macSigningKeychain` | `--mac-signing-keychain` | Linux 选项: | 参数 | 工具选项 | |---|---| | `linuxAppCategory` | `--linux-app-category` | | `linuxAppRelease` | `--linux-app-release` | | `linuxDebMaintainer` | `--linux-deb-maintainer` | | `linuxMenuGroup` | `--linux-menu-group` | | `linuxPackageDeps` | `--linux-package-deps` | | `linuxPackageName` | `--linux-package-name` | | `linuxRpmLicenseType` | `--linux-rpm-license-type` | | `linuxShortcut` | `--linux-shortcut` | Windows 选项: | 参数 | 工具选项 | |---|---| | `winConsole` | `--win-console` | | `winDirChooser` | `--win-dir-chooser` | | `winHelpUrl` | `--win-help-url` | | `winMenu` | `--win-menu` | | `winMenuGroup` | `--win-menu-group` | | `winPerUserInstall` | `--win-per-user-install` | | `winShortcut` | `--win-shortcut` | | `winShortcutPrompt` | `--win-shortcut-prompt` | | `winUpdateUrl` | `--win-update-url` | | `winUpgradeUuid` | `--win-upgrade-uuid` | 未来 JDK 新增但尚未结构化建模的选项,可以先通过 `extraOptions` 追加。 ## `leyden` 参数 `leyden` 接受与 `jpackage` 相同的应用、launcher、安装包和平台选项,并额外支持: | 参数 | 默认值 | 说明 | |---|---|---| | `trainingProperty` | `leyden.training` | 用于让应用在训练后退出的系统属性。 | | `aotCacheName` | `startup.aot` | app image 内部的 AOT 缓存文件名。 | | `inputDirectory` | `${project.build.directory}/jpackage-input` | 暂存项目产物和运行时依赖。 | | `runtimeDirectory` | `${project.build.directory}/jlink-runtime` | 生成的运行时镜像。 | | `appImageDirectory` | `${project.build.directory}/jpackage-app-image` | 训练用 app image 输出目录。 | | `packageDirectory` | `${project.build.directory}/jpackage` | 最终安装包输出目录。 | | `runtimeModules` | 无 | 必填。传给内部 `jlink --add-modules`。 | | `modulePath` | 空 | 提供给内部 `jlink` 的额外模块或 JMOD 位置。 | | `includeInputDirectoryOnModulePath` | `true` | 把暂存 jar 加入内部 `jlink` 的模块路径。 | | `noHeaderFiles` | `true` | 内部 `jlink` 选项。 | | `noManPages` | `true` | 内部 `jlink` 选项。 | | `stripDebug` | `true` | 内部 `jlink` 选项。 | | `compress` | 无 | 内部 `jlink --compress` 值。 | | `bindServices` | `false` | 内部 `jlink --bind-services`。 | | `jlinkOptions` | 空 | 额外内部 `jlink` 选项。 | `leyden` goal 会在打包前检查所选 `javaHome`,要求 JDK 25 或更新版本。 ## 当前限制 - 插件目前处于 alpha 阶段。 - 当前源码树包含命令生成、descriptor 生成、路径推导、暂存和 launcher 配置重写的单元测试。 - 示例项目已经在 macOS 上手工验证过 `app-image` 输出,但还没有接入根项目的 `verify` 生命周期。 - Linux 和 Windows 安装包创建尚未在真实 Linux/Windows 主机上验证。 - JPMS module launch 已支持,并已验证直接 `jpackage` app-image 打包;JPMS Leyden 打包仍需要专门的运行时验证。 - GraalVM native-image 打包不在该插件范围内。 - Apple notarization 不在范围内。Notarization 是 `jpackage` 之后的发布流程步骤, 不是 JDK `jpackage` 选项。插件支持 `jpackage` 暴露的 JDK macOS 签名选项。 ## 开发 ```bash sdk env ./mvnw verify ``` 当前构建验证的是 Maven 插件本身。示例项目在加入集成测试前仍需要手工运行。 ## 发布流程 发布流程见 `docs/releasing.md`。 匹配 `v*` 的 tag 会触发 GitHub Actions 发布到 Maven Central。workflow 会校验 tag 与 `project.version` 一致,拒绝发布 `-SNAPSHOT` 版本,使用 GPG 签名构件, 并通过 Sonatype Central Publisher Portal 发布。发布成功后,workflow 会把 `main` 自动提升到下一个 patch `-SNAPSHOT` 版本。 ## 许可证 本项目使用 Universal Permissive License, Version 1.0。