ComposeDesktop初体验之打包-创新互联

从 0 到 1 搞一个 Compose Desktop 版本的玩天气之打包

大家好,前两篇文章大概介绍了下上手Compose Desktop和自定义绘制时遇到的一些问题,项目的最终实现效果如下:

在友谊等地区,都构建了全面的区域性战略布局,加强发展的系统性、市场前瞻性、产品创新能力,以专注、极致的服务理念,为客户提供成都网站设计、成都网站制作、外贸网站建设 网站设计制作按需网站策划,公司网站建设,企业网站建设,成都品牌网站建设,成都全网营销推广,成都外贸网站制作,友谊网站建设费用合理。

视频

代码写好了,该弄的动画也弄了,该请求的网络数据也请求了,该实现的效果也都实现好了,但是!!!咱们得打包出来啊!不打包出来别人如何使用呢?难道说别人想用你开发的桌面应用,结果你给他说你先下载一个IntelliJ Idae,然后下载下我的源码,之后把环境配置好,最后运行就可以了!如果下次再想用的时候再运行一次就好了!

这说的是人话嘛😂,肯定不能这样,所以一定要打包!由于Compose Desktop不止可以运行在Mac中,还可以运行在WindowsLinux中,所以需要打多个包。那使用Compose Desktop应该如何打包呢?且听我慢慢道来!

基本用法

插件中的基本配置单元是一个applicationapplication是什么呢?在第一篇文章中也提到了,就是在build.gradle.kts文件中的代码,咱们再来看下:

compose.desktop {application {mainClass = "MainKt"
        nativeDistributions {targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "Demo"
            packageVersion = "1.0.0"
        }
    }
}

一个application定义了一组最终二进制文件的共享配置。换句话说,applicationDSL 允许将一堆文件连同 JDK 分发包打包成一组各种格式(.dmg.deb.msi.exe等)的压缩二进制安装程序。

该插件创建以下任务:

  • package(例如packageDmgpackageMsi)用于将应用程序打包成相应的格式。这块需要注意的是,目前没有交叉编译支持,因此只能使用特定操作系统构建格式(例如,要构建.dmg就必须使用 macOS)。默认情况下会跳过与当前操作系统不兼容的任务。
  • packageDistributionForCurrentOS是一个生命周期任务,聚合了应用程序的所有包任务。
  • packageUberJarForCurrentOS用于创建单个 jar 文件,其中包含当前操作系统的所有依赖项。
  • run用于在本地运行应用程序。需要定义一个mainClass包含该main函数的类。请注意,run将启动具有完整运行时的非打包 JVM 应用程序。这比创建具有最小运行时间的紧凑二进制映像更快、更容易调试。要运行最终的二进制图像,需要改用runDistributable
  • createDistributable用于在不创建安装程序的情况下创建预打包的应用程序映像和最终应用程序映像。
  • runDistributable用于运行预先打包的应用程序映像。
  • 只有在脚本中使用blockproperty时才会创建任务。

光这么说其实有点懵,来一张图大家就明白我说的是什么了!

在这里插入图片描述

是不是有点恍然大明白的感觉!直接点击IntelliJ IDEA右侧边栏的Gradle,就会出现这个侧边栏,然后点击Task中的compose desktop就会出现上面描述的那些任务。

打包配置

Compose Desktop打包有很多的配置项,下面来分别看下。

配置包含的 JDK 模块

Gradle 插件使用 jlink 通过仅包含必要的 JDK 模块来最小化可分发的大小。

此时,Gradle 插件不会自动确定必要的 JDK 模块。未能提供必要的模块不会导致编译问题,但会导致在运行时出现ClassNotFoundException的错误。

如果在运行打包的应用程序或任务时遇到ClassNotFoundException,可以使用DSL 方法runDistributable来配置包含额外的 JDK 模块,需要使用modules来配置。

可以通过手动或运行suggestModules任务来确定哪些模块是必需的。suggestModules使用 jdeps 静态分析工具来确定可能缺少的模块。

如果安装包的大小不重要的话,可以使用includeAllModulesDSL 属性简单地包括所有运行时模块作为替代。

compose.desktop {application {nativeDistributions {modules("java.sql")
            // alternatively: includeAllModules = true
        }
    }
}

这块在我打包的时候搜了好久!最后在Issue中找到了解决方案!

可用格式

以下格式可用于支持的操作系统:

  • macOS —.dmg(TargetFormat.Dmg)、.pkg(TargetFormat.Pkg)
  • Windows —.exe(TargetFormat.Exe)、.msi(TargetFormat.Msi)
  • Linux —.deb(TargetFormat.Deb)、.rpm(TargetFormat.Rpm)
指定包版本

由于可以打多种不同的包,也有可能需要区分不同的版本,所以可以指定包的版本。如何指定的呢?来看代码:

compose.desktop {application {nativeDistributions {packageVersion = "1.0.0" 
            
            linux {  packageVersion = "1.0.0" 
              debPackageVersion = "1.0.0" 
              rpmPackageVersion = "1.0.0" 
            }
            macOS {  packageVersion = "1.1.0"
              dmgPackageVersion = "1.1.0" 
              pkgPackageVersion = "1.1.0" 
              
              packageBuildVersion = "1.1.0"
              dmgPackageBuildVersion = "1.1.0" 
              pkgPackageBuildVersion = "1.1.0" 
            }
            windows {  packageVersion = "1.2.0"  
              msiPackageVersion = "1.2.0"
              exePackageVersion = "1.2.0" 
            }
        }
    }
}

必须为本机分发包指定包版本,还可以使用以下 DSL 属性(按优先级降序排列):

  • nativeDistributions..PackageVersion指定单个包格式的版本;
  • nativeDistributions..packageVersion指定单个目标操作系统的版本;
  • nativeDistributions.packageVersion指定所有包的版本;

对于 macOS,还可以使用以下 DSL 属性指定构建版本(按优先级降序排列):

  • nativeDistributions.macOS.PackageBuildVersion指定单一包格式的构建版本;
  • nativeDistributions.macOS.packageBuildVersion为所有 macOS 包指定构建版本。

需要注意的是,版本必须遵循以下规则:

  • dmgpkg:格式为 MAJOR.MINOR.PATCH

    其中:MAJOR是一个 >0 的整数;MINOR是一个可选的非负整数;PATCH是一个可选的非负整数;

  • msiexe:格式为 MAJOR.MINOR.BUILD

    其中:MAJOR是一个非负整数,大值为255;MINOR是一个非负整数,大值为255;BUILD是一个非负整数,大值为65535;

  • rpm:版本不得包含-(破折号)字符。

  • deb:格式为 EPOCH:UPSTREAM_VERSION-DEBIAN_REVISION

    其中:EPOCH是一个可选的非负整数;UPSTREAM_VERSION只包含字母数字和字符.,+,-,~,必须以数字开头;DEBIAN_REVISION是可选的,可能只包含字母数字和字符.,+,~

自定义 JDK 版本

由于该插件使用jpackage,所以最低得使用 JDK 15。

  • JAVA_HOME环境变量指向兼容的 JDK 版本。
  • javaHome通过 DSL 设置:
compose.desktop {
    application {
        javaHome = System.getenv("JDK_15")
    }
}
自定义输出目录

Compose Desktop默认的打包路径在 /build/compose/binaries/main/app 中,如果想修改下打包路径的话,需要修改下配置:

compose.desktop {application {nativeDistributions {outputBaseDir.set(project.buildDir.resolve("customOutputDir"))
        }
    }
}
自定义基本数据

DSL 块中提供以下属性nativeDistributions

  • packageName— 应用程序名称(默认值:Gradle 项目名称);
  • version— 应用程序的版本(默认值:Gradle 项目的版本);
  • description— 应用程序的描述(默认值:无);
  • copyright— 应用程序的版权(默认值:无);
  • vendor— 应用程序的供应商(默认值:无);
  • licenseFile— 应用程序的许可证(默认值:无)。
compose.desktop {application {nativeDistributions {packageName = "PlayWeather"
            version = "1.1.0"
            description = "PlayWeather"
            copyright = "© 2022 My Name. All rights reserved."
            vendor = "Example vendor"
            licenseFile.set(project.file("LICENSE.txt"))
        }
    }
}

这块大家可以根据需求来定义这些数据,如不需要不写即可。

特定平台选项

需要使用相应的 DSL 块设置特定于平台的选项,使用方法就是上面maxOSwindowslinux,不同平台可配置的选项都不太一样!

  • 所有平台

    • iconFile.set(File("PATH_TO_ICON"))— 应用程序特定于平台的图标的路径。

    • packageVersion = "1.0.0"— 特定于平台的包版本。

    • installationPath = "PATH_TO_INSTALL_DIR"默认安装目录的绝对或相对路径;在 Windows 上dirChooser = true,可用于启用在安装过程中自定义路径。

  • Linux

    • packageName = "custom-package-name"覆盖默认的应用程序名称;

    • debMaintainer = "maintainer@example.com"— deb 包维护者的电子邮件;

    • menuGroup = "my-example-menu-group"— 应用程序的菜单组;

    • appRelease = "1"— rpm 包的发布值,或 deb 包的修订值;

    • appCategory = "CATEGORY"— rpm 包的组值,或 deb 包的部分值;

    • rpmLicenseType = "TYPE_OF_LICENSE"— rpm 包的一种许可证;

    • debPackageVersion = "DEB_VERSION"``Specifying package version— 特定于 deb 的包版本;

    • rpmPackageVersion = "RPM_VERSION"``Specifying package version— 特定于 rpm 的软件包版本;

  • MacOS

    • bundleID — 唯一的应用标识符;

      • 只能包含字母数字字符 (A-Z,a-z,0-9)、连字符 (-) 和句点 (.) 字符;
      • com.mycompany.myapp建议使用反向 DNS 表示法(例如);
    • packageName— 应用名称;

    • dockName— 显示在菜单栏、“关于”菜单项、停靠栏等中的应用程序名称。packageName等于默认情况下的名称;

    • signing,notarization,provisioningProfile, 和runtimeProvisioningProfile— 详见相应教程;

    • appStore = true— 为 Apple App Store 构建和签名。至少需要 JDK 17;

    • appCategory— Apple App Store 的应用类别。默认值是public.app-category.utilities

    • entitlementsFile.set(File("PATH_TO_ENTITLEMENTS"))— 包含签名时使用的权利的文件路径;

    • runtimeEntitlementsFile.set(File("PATH_TO_RUNTIME_ENTITLEMENTS"))— 包含在签署 JVM 运行时时使用的权利的文件路径;

    • dmgPackageVersion = "DMG_VERSION"— 一个特定于 dmg 的包版本(详见参考资料部分);

    • pkgPackageVersion = "PKG_VERSION"— 特定于 pkg 的包版本(详情请参阅参考资料部分);

    • packageBuildVersion = "DMG_VERSION"— 包构建版本(详见参考资料部分);

    • dmgPackageBuildVersion = "DMG_VERSION"— 特定于 dmg 的软件包构建版本(详情请参阅参考资料部分);

    • pkgPackageBuildVersion = "PKG_VERSION"— 特定于 pkg 的包构建版本;

    • infoPlist— 链接到别的程序。

  • Linux

    • console = true为应用程序添加一个控制台启动器;
    • dirChooser = true允许在安装过程中自定义安装路径;
    • perUserInstall = true允许在每个用户的基础上安装应用程序
    • menuGroup = "start-menu-group"将应用程序添加到指定的开始菜单组;
    • upgradeUuid = "UUID"— 一个唯一的 ID,当更新的版本比安装的版本更新时,它使用户能够通过安装程序更新应用程序。对于单个应用程序,该值必须保持不变;
    • msiPackageVersion = "MSI_VERSION"— 特定于 msi 的软件包版本;
    • exePackageVersion = "EXE_VERSION"— 特定于 pkg 的包版本
修改应用图标

这块为什么要单独拿出一大块来说呢?因为这个问题中困扰了我好久。。。所以才。。。。

还记得之前文章中说了Window可组合项中可以设定Icon么,但当时说的时候专门说了此Icon并非应用程序的图标!因为应用程序图标需要以特定于操作系统的格式提供:

  • .icns对于 macOS
  • .ico适用于 Windows
  • .png对于 Linux

看下代码吧:

compose.desktop {application {nativeDistributions {macOS {iconFile.set(project.file("icon.icns"))
            }
            windows {iconFile.set(project.file("icon.ico"))
            }
            linux {iconFile.set(project.file("icon.png"))
            }
        }
    }
}

Linux中的png格式的图片我们很常见,但是MacWindows中的格式是什么鬼。。。没见过啊!

如果知道这两种文件格式的话大家直接跳过后面的部分即可,这里还需要注意的是这里的文件路径指的是项目根目录。

ICNS 文件
  1. 什么是.icns?

.icns是苹果的 macOS 操作系统的 App 图标文件的扩展名,大家在 macOS 的Desktop 桌面Finder 访达Dock 程序坞等看到应用程序的外观就是由一个内置在此 App 内部的扩展名为.icns的文件实现的。

可以通过鼠标“右键”点击 App - “显示包内容” - 进入Contents目录 - 进入Resources目录,然后在目录下可以找到名为Appicon.icns或其他后缀为.icns的一个图标文件。

  1. 如何创建.icns扩展名的图标文件?

a. 准备一张图片,重命名为icon.png,其他大小尺寸可以通过终端命令生成;

b. 通过鼠标右键或者命令,创建一个名为icons.iconset的文件夹

mkdir icons.iconset

c. 通过”终端“来快速创建各种不同尺寸要求的图片文件

sips -z 512 512 icon.png -o icons.iconset/icon_512x512.png

d. ”终端“中运行下面的命令,就可以获得名为icon.icns的图标文件了

iconutil -c icns icons.iconset -o icon.icns

注意:icon.png图片文件和icons.iconset文件夹要保存在同一级目录下,”终端“启动后切换到相同目录。

ICO 文件

ico文件是Windows系统的应用图标格式,我也不会制作,但找到了一个制作ico的网站:

https://www.butterpig.top/icopro/

需要的话可以进去制作。

混淆

Compose Desktop1.2 版本开始,Compose Gradle插件支持开箱即用的 ProGuard。ProGuard是一个众所周知的用于缩小和混淆的开源工具Guardsquare。

Gradle插件为每个对应的默认打包任务提供了发布任务:

默认任务(没有 ProGuard)发布任务(带 ProGuard)描述
createDistributablecreateReleaseDistributable使用捆绑的 JDK 和资源创建应用程序映像
runDistributablerunReleaseDistributable使用捆绑的 JDK 和资源运行应用程序映像
runrunReleasejar使用 Gradle JDK运行非打包应用程序
packagepackageRelease将应用程序映像打包到文件中
packageForCurrentOSpackageReleaseForCurrentOS将应用程序映像打包成与当前操作系统兼容的格式
notarizenotarizeRelease上传用于公证的应用程序图像(仅限 macOS)
checkNotarizationStatuscheckReleaseNotarizationStatus检查公证是否成功(仅限 macOS)

默认配置添加了一些 ProGuard 规则:

  • 缩小应用程序图像,即删除未使用的类;
  • compose.desktop.application.mainClass用作入口点;
  • keep避免破坏Compose运行时的一些规则。

在许多情况下,获得缩小的Compose应用程序不需要任何额外的配置。但是,有时ProGuard可能无法跟踪字节码中的某些用法(例如,如果通过反射使用类,则可能会发生这种情况)。如果遇到仅在ProGuard处理后才会发生的问题,可能需要添加自定义规则。为此,通过 DSL 指定配置文件:

compose.desktop {
    application {
        buildTypes.release.proguard {
            configurationFiles.from(project.file("compose-desktop.pro"))
        }
    }
}

可以参考 Guardsquare 关于 ProGuard 规则和配置选项的综合手册。

默认情况下禁用混淆。要启用它,请通过 Gradle DSL 设置以下属性:

compose.desktop {
    application {
        buildTypes.release.proguard {
            obfuscate.set(true)
        }
    }
}

混淆需要根据需要使用了,如果需要控制包体积的话尽量还是打开,可以减小包体积,还会减小代码泄漏的可能性;反之打不打开都行!

实际操作

上面都是理论知识,咱们得实操啊!有基础知识之后实操就很简单了,先来看下build.gradle.kts中的配置项吧:

compose.desktop {application {mainClass = "MainKt"
        nativeDistributions {targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "PlayWeather"
            packageVersion = "1.0.0"
            description = "Play Weather App"
            copyright = "© 2022 My Name. All rights reserved."
            vendor = "Lenovo"
            licenseFile.set(project.file("LICENSE.txt"))
            modules("java.instrument", "java.management", "java.naming", "java.sql", "jdk.unsupported")

            linux {packageVersion = "1.0.0"
                debPackageVersion = "1.0.0"
                rpmPackageVersion = "1.0.0"
                // 设置图标
                iconFile.set(project.file("launcher/icon.png"))
            }
            macOS {packageVersion = "1.1.0"
                dmgPackageVersion = "1.1.0"
                pkgPackageVersion = "1.1.0"
                dockName = "PlayWeather"

                packageBuildVersion = "1.1.0"
                dmgPackageBuildVersion = "1.1.0"
                pkgPackageBuildVersion = "1.1.0"
                // 设置图标
                iconFile.set(project.file("launcher/icon.icns"))
            }
            windows {packageVersion = "1.2.0"
                msiPackageVersion = "1.2.0"
                exePackageVersion = "1.2.0"
                // 设置图标
                iconFile.set(project.file("launcher/icon.ico"))
            }
        }
        buildTypes.release.proguard {obfuscate.set(false)
            configurationFiles.from(project.file("proguard-rules.pro"))
        }
    }
}

这里的配置项就不多说了,上面都有过介绍,下面来打包吧!

在这里插入图片描述

点击上图中蓝色箭头标注的进行打包,上面说过了,不能跨系统打包,Mac只能打Mac中使用的包。。双击执行packageDmg任务:

在这里插入图片描述

没问题的话大概会出现上图的样子,由于没有配置自定义包路径,所以还在默认文件中,按照上面所描述的路径进行查看:

在这里插入图片描述

复制文件路径,在访达中打开:

在这里插入图片描述

双击进行安装即可:

在这里插入图片描述

然后在资源库中找到应用双击打开应该会遇到下面的错误:

在这里插入图片描述

这时点击取消,然后打开设置 ->隐私与安全性,往下滑:

在这里插入图片描述

点击箭头标注的“仍要打开”按钮,会让你输入电脑的密码,输入完成后会弹出下面的对话框:

在这里插入图片描述

点击打开,这时应用就能正常使用了。

在这里插入图片描述

苹果端就不打release包了,还需要苹果的开发者账号那一大堆。。。目前先能正常在Mac中运行吧!

总结

本文大概总结了下使用Compose Desktop如何进行打包,此项目所有代码都放到了Github中。

Github地址:https://github.com/zhujiang521/PlayWeather/tree/desktop

如果文中写的有误,欢迎在评论区提出,咱们一起探讨。

文章如果能帮助到大家,哪怕是一点,我也非常高兴,先这样。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


当前文章:ComposeDesktop初体验之打包-创新互联
标题来源:http://cdiso.cn/article/dhdjpo.html

其他资讯