Compose Multiplatform 桌面分发打包工具对比:内置 jlink/jpackage 任务与第三方方案的深度剖析
2026/9/13 21:31:41 网站建设 项目流程

Compose Multiplatform 桌面分发打包工具对比:内置 jlink/jpackage 任务与第三方方案的深度剖析

【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform

本文基于仓库中 packaging-tools-comparison.md 教程展开,对照 Compose Multiplatform Gradle 插件的源码实现,讲清桌面应用分发链路中两条路线的边界:一是插件内置的jlink/jpackage打包任务能覆盖哪些基本需求、受哪些 JDK 与平台限制,二是第三方工具 Conveyor 在自动更新、跨平台构建等场景下提供了哪些增量能力,帮助你在发布 Compose Desktop 应用时做出有依据的工具选型。

一、内置 Gradle 打包任务:覆盖基本分发需求

Compose Multiplatform 的 Compose Gradle 插件(源码位于 gradle-plugins/compose 目录)把桌面应用的打包构建直接挂到了 Gradle 任务体系上。官方教程 packaging-tools-comparison.md 对内置能力的概括是三点:

  • 为 Windows 生成 MSI 文件或 NSIS 安装器 EXE,为 macOS 生成签名的应用 bundle,为 Linux 生成 DEB/RPM 包;
  • 使用jlink捆绑 JVM;
  • 在每个操作系统上自定义打包选项。

下面结合源码逐项拆解这些能力在插件中的真实落地方式。

1.1 支持的打包格式与任务命名

所有可打包格式定义在 TargetFormat.kt 的枚举中,每个格式都绑定了目标操作系统,这直接决定了“格式与构建机 OS 必须匹配”的约束:

枚举值格式 ID目标 OS产物
AppImageapp-image构建机当前 OS目录形态的应用镜像(无文件扩展名)
DebdebLinuxDEB 包
RpmrpmLinuxRPM 包
DmgdmgmacOSDMG 镜像
PkgpkgmacOSPKG 安装器
ExeexeWindowsNSIS 安装器 EXE
MsimsiWindowsMSI 安装器

枚举中的isCompatibleWithCurrentOS属性按currentOS == targetOS判定兼容性。任务名遵循 JvmTasks.kt 中注释声明的[动作][构建类型][对象]模式,例如runDistributablerunReleaseDistributablepackageDmgpackageReleaseDmg——即在package前缀后拼上构建类型(如Release)与格式名。

1.2 用 jlink 捆绑 JVM

jlink环节由 AbstractJLinkTask.kt 实现,它从 JDK 安装目录调用jlink工具,组装出一个只含应用所需模块的精简 runtime。从源码看,其命令行参数的拼装逻辑(makeArgs方法)如下:

  • --add-modules:每个模块单独追加一次。模块集合来自两处——若includeAllModules为 true,则从JvmRuntimeProperties(由前置的 JDK 探测任务生成)中读取该 JDK 的全部可用模块;否则使用用户在 distributions DSL 中显式声明的modules列表;
  • --strip-debug--no-header-files--no-man-pages--strip-native-commands:这四项默认均为true,即默认剥离调试信息、头文件、man 页面和本地命令,以减小 runtime 体积;
  • --compress:对应可选的compressionLevel(取值定义见 RuntimeCompressionLevel.kt),用于在输出时压缩 runtime 中的 jar/class 文件;
  • --generate-cds-archive:可选地生成 JRE CDS 归档以加速启动,源码中明确校验了它与--strip-native-commands互斥(二者同时开启会直接抛错);
  • --output:指向任务的输出目录。

该任务标注了@DisableCachingByDefault,原因注释写明:它依赖平台特定的 JDK 工具,输出取决于本机的 JDK 安装情况,因此默认不参与跨构建缓存。

1.3 jpackage 的调用细节

安装器(以及 app image)由 AbstractJPackageTask.kt 调用jpackage生成。从源码的makeArgs方法可以看到完整的参数组织方式:

应用镜像相关参数(创建 app image、或不带--app-image的安装器时传入):

  • --input指向插件预先准备好的libs工作目录,--runtime-image指向 jlink 产物,--main-jar/--main-class指定启动入口;
  • --arguments透传应用启动参数,javaOption透传 JVM 参数;
  • 两个对 Compose 渲染至关重要的系统属性:-Dskiko.library.path=$APPDIR(告知 Skiko 原生库位置)以及应用资源目录属性。

安装器相关参数(由已有 app image 生成安装器时传入):

  • --app-image--install-dir(对应 DSL 中的installationPath)、--license-file
  • --file-associations:源码会把 DSL 里fileAssociation(mimeType, extension, description, iconFile)声明的每种文件关联,写成形如FA<ext>.properties的临时文件(内容为mime-type/extension/description/icon四个键值),再逐一传给 jpackage。这一机制在 AbstractPlatformSettings 中定义,三个平台的设置基类都继承自它。

平台分支参数(按currentOS分别追加,这也是“任务与构建机 OS 绑定”的直接证据):

  • Linux:--linux-shortcut--linux-package-name--linux-app-release--linux-app-category--linux-deb-maintainer--linux-menu-group--linux-rpm-license-type
  • Windows:--win-dir-chooser--win-per-user-install--win-shortcut--win-menu--win-menu-group--win-upgrade-uuid
  • macOS:--mac-package-name--mac-package-identifier(bundleID)、--mac-app-store--mac-app-category--mac-entitlements,以及签名相关的--mac-sign--mac-signing-key-user-name--mac-signing-keychain--mac-package-signing-prefix

所有格式统一的公共参数包括--type <格式id>--dest--name--icon--description--copyright--app-version--vendor

此外,源码还处理了两个工程细节:一是多模块项目中 jar 简单名冲突问题(mangleJarFilesNames默认为 true,会给拷贝进 libs 目录的 jar 名追加内容哈希,避免:data:utils:ui:utils这类同名片段互相覆盖);二是 Skiko 原生库的特殊处理——打包前会扫描并解出 Skiko AWT runtime jar 内的目标平台原生条目(stripAndUnpackSkikoNatives),把无关平台的.so/.dll/.dylib剔除,减小安装体积。

1.4 各平台的 DSL 配置项

各平台的可配置项定义在 PlatformSettings.kt,与 jpackage 参数一一对应,全部通过jvmApplication { nativeDistributions { ... } }下的windows { ... }linux { ... }macos { ... }块配置(入口见 JvmApplication.kt 中声明的nativeDistributions属性)。三平台共有:iconFilepackageVersioninstallationPathfileAssociation(...)

WindowsWindowsPlatformSettings):

  • console(默认 false):是否显示控制台窗口;
  • dirChooser(默认 true):安装时是否允许选择目录;
  • perUserInstall(默认 false):按用户安装而非按机器安装;
  • shortcut(默认 false)/menu(默认 false,设置menuGroup后自动置真)/menuGroup:桌面快捷方式与开始菜单项;
  • upgradeUuid:MSI 升级识别 UUID;
  • msiPackageVersion/exePackageVersion:分别为 MSI 与 EXE 指定包版本。

LinuxLinuxPlatformSettings):shortcut(默认 false)、packageNameappReleaseappCategorydebMaintainermenuGrouprpmLicenseTypedebPackageVersionrpmPackageVersion

macOSJvmMacOSPlatformSettings):除继承自AbstractMacOSPlatformSettingspackageNamepackageBuildVersiondmgPackageVersiondmgPackageBuildVersionappCategoryminimumSystemVersionbundleID(推荐反向 DNS 风格,如com.mycompany.myapp,只允许字母数字、连字符与点)外,JVM 应用还有dockNamesetDockNameSameAsPackageNameappStoreentitlementsFile/runtimeEntitlementsFilepkgPackageVersion/pkgPackageBuildVersionprovisioningProfile/runtimeProvisioningProfile,以及infoPlist { extraKeysRawXml }用于向自动生成的 Info.plist 注入额外 XML 键。

1.5 Windows 平台的前置依赖:WiX Toolset

jpackage 生成 MSI 依赖 WiX Toolset。插件对这一依赖做了自动化处理,逻辑在 wixToolset.kt 中:

  1. 若设置了WIX_PATH环境变量并指向有效目录,则直接使用本地的 WiX 安装;
  2. 否则注册downloadWix/unzipWix任务,自动下载 WiX 3.11 的二进制发行包并解压到构建目录,Windows 打包任务会自动依赖它;
  3. 若设置 Gradle 属性compose.desktop.application.downloadWix=false,则跳过自动下载(此时需自行准备 WiX 环境)。

1.6 JDK 要求与运行环境校验

打包前的 JDK 校验由 AbstractCheckNativeDistributionRuntime.kt 完成,关键约束有三条:

  • 最低版本MIN_JAVA_RUNTIME_VERSION = 17,构建 JDK 主版本低于 17 会直接报“minimum required JDK version is '17'”错误;
  • 工具完整性:校验 JDK 的bin目录下同时存在javajlinkjpackage三个可执行文件,缺失即报“Failed to check JDK distribution”;
  • 发行商检查:在 macOS 上若探测到 Homebrew 的 JDK,会因已知的打包问题而报错,建议改用其他发行商的 JDK(如 Amazon Corretto),或在gradle.properties中加compose.desktop.application.checkJdkVendor=false自担风险继续。

该任务同时会运行java --list-modules收集全部模块名并写入JvmRuntimeProperties,供后续includeAllModules的 jlink 任务读取。

1.7 macOS 签名与公证

macOS 平台的签名/公证通过 DSL 暴露:signing { sign, identity, keychain, prefix }(定义见 MacOSSigningSettings.kt,默认值可被compose.desktop.application.macSign*等 Gradle 属性覆盖)以及notarization { ... }。从 AbstractJPackageTask 的macSigner逻辑可以看出行为边界:

  • 仅当构建机是 macOS 时才会创建签名器;启用sign时使用配置了证书身份的MacSignerImpl,否则使用NoCertificateSigner(无证书签名);
  • 打包完成后,modifyRuntimeOnMacOsIfNeeded会对 app image 内的 runtime 逐个重签所有可执行文件与 dylib,再签 runtime 与整个.app,并把 provisioning profile 写入Contents/runtime/Contents/embedded.provisionprofile
  • 生成安装器时,签名信息会翻译为--mac-sign等 jpackage 参数,让安装器内容在生成阶段就带签名。

1.8 为什么内置任务无法跨平台构建

教程中提到“内置任务必须从每个目标 OS 上运行”,这一点在源码中得到印证:

  • AbstractJPackageTask.makeArgs中的平台参数全部由currentOS分支决定(Linux/Windows/macOS 各一组);
  • WiX 配置函数开头就有check(currentOS == OS.Windows),非 Windows 环境直接报错;
  • macOS 签名器在非 macOS 环境下恒为null

也就是说,插件的任务体系把“格式—构建机 OS”强绑定在了一起:在 macOS 上跑打包只会产出 macOS 格式,在 Windows 上只产出 Windows 格式。若要同时分发多平台,就得为每个目标平台各准备一台构建机(或 CI runner),这正是第三方方案切入的痛点。

二、第三方方案:Conveyor 提供的增量能力

packaging-tools-comparison.md 介绍的第二条路线是 Hydraulic 公司的Conveyor工具。它与 Compose Gradle 插件集成,针对内置任务覆盖不到的场景提供了一组能力,教程原文列出的九项如下:

  1. 在线更新(Online updates):插件生成的包需要用户手动重装来更新;Conveyor 生成的包在 Windows/macOS 上可以后台静默自更新,在 Linux 上则走 apt 等系统包管理通道;
  2. 跨构建(Cross-building):从任意 OS(开发笔记本或 CI 机器)为所有支持的目标生成、签名、公证包,而内置任务必须逐平台各开一台构建机;
  3. 自签名包(Self-signed packages):不需要购买签名证书,但代价是用户安装时要复制/粘贴终端命令;
  4. 下载页(Download pages):生成静态 HTML,自动探测用户的操作系统与 CPU 架构并给出对应下载项;
  5. 图标转换(Icon conversion):内置任务要求开发者手工把图标转成各平台格式(如 Windows 的.ico、macOS 的.icns),Conveyor 代劳这一步;
  6. 体积优化(Size optimization):使用jdeps剔除未使用的 JDK 模块以缩小下载体积(内置 jlink 方案通过模块列表实现类似目标,jdeps 是从依赖分析角度补强);
  7. 可访问性(Accessibility):通过 Java Accessibility Bridge 自动加入屏幕阅读器支持;
  8. 对企业 IT 部门友好:Windows 侧采用 MSIX——微软 Windows 10/11 的当代打包系统,与 Windows 网络管理工具深度集成;
  9. CLI 支持:包内可附带附属命令行工具。

教程还特别提示:以上并非完整功能清单,且 Licensing 模型为开源项目免费,商业项目度过引入期后需要购买许可证。选型时应以其官方文档与定价页为准确认最新功能与费用条款。

三、如何选型:内置任务 vs Conveyor

结合教程与源码证据,可以把两条路线的适用边界归纳如下:

优先使用内置 Gradle 任务,当你的场景满足:

  • 只需基础分发产物(MSI/EXE、签名 app bundle、DEB/RPM),且接受“每个目标平台各备一台构建机”(本地或 CI 均可);
  • 需要精细控制打包参数:jlink 的压缩级别与 CDS 归档、jpackage 的各平台安装器选项、fileAssociation文件关联、Info.plist 注入(infoPlist { extraKeysRawXml })等,插件 DSL 已完整暴露(PlatformSettings.kt);
  • 希望零外部商业依赖,构建链路完全由 JDK 17+ 的jlink/jpackage与插件任务组成。

考虑引入 Conveyor,当你的产品形态需要:

  • 静默在线更新能力,或面向 Linux 的 apt 分发通道;
  • 单一构建环境(如一台 CI 机器)产出全部平台、签名与公证产物;
  • 不想维护签名证书(自签名路线),或需要开箱即用的下载页、图标转换、MSIX 打包;
  • 需要附带 CLI 工具或企业级 Windows 部署(MSIX + 网络管理工具集成)。

需要注意的是,无论选择哪条路线,JVM 捆绑、图标、版本号、安装路径这些基础输入都是共通的——内置任务把这些暴露为 DSL 属性与 jpackage 参数,Conveyor 则以工具自身的工作流承接。两者并非互斥替代关系:Conveyor 同样以“与 Compose Gradle 插件集成”的方式工作。

四、小结与延伸阅读

本文以 tutorials/Native_distributions_and_local_execution/packaging-tools-comparison.md 为主线,对照 Compose Multiplatform 仓库源码核实了内置打包链路的关键实现:

  • 格式与 OS 绑定关系见 TargetFormat.kt,任务命名规则见 JvmTasks.kt;
  • jlink参数组装见 AbstractJLinkTask.kt,jpackage参数组装、Skiko 原生库瘦身、macOS 重签逻辑见 AbstractJPackageTask.kt;
  • Windows WiX 自动下载见 wixToolset.kt,JDK 17+ 与jlink/jpackage工具校验、Homebrew JDK 告警见 AbstractCheckNativeDistributionRuntime.kt;
  • 三平台 DSL 配置项(含 macOS 签名/公证入口)见 PlatformSettings.kt 与 MacOSSigningSettings.kt。

掌握上述内容后,你可以先跑通内置的package<构建类型><格式>任务完成基础分发,再依据是否涉及自动更新、跨平台 CI 与 MSIX 等企业需求,评估是否引入 Conveyor 补齐能力短板。

【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询