Compose Multiplatform 发布流程实战指南:从 Skia/Skiko 升级到 Maven 发布与 Docker 镜像构建
【免费下载链接】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
导读
本文以 Compose Multiplatform 仓库中的 ci/release.md 为骨架,完整讲解该项目的端到端发布流水线:从更新底层 Skia 渲染库、发布 Skiko(Skia 的 Kotlin 绑定层)、把新版本 Skiko 引入 Compose,到最终发布 Compose Desktop 组件与构建 CI 所用的 Docker 镜像。读完本文,你将掌握每一环节的操作步骤、关键配置参数(如dependencies.skia.windows/dependencies.skia.linux、SKIKO_VERSION)、验证命令(如./gradlew publishToMavenLocal)以及背后涉及的仓库源码与脚本佐证,可直接用于参与或复现 Compose Multiplatform 的发布过程。
发布链路全景:Compose Multiplatform 的三层依赖体系
Compose Multiplatform(CMP)的桌面渲染栈是一条典型的"自底向上"依赖链:Skia(C++ 图形库)→ Skija/Skiko(Skia 的 Kotlin/JVM 绑定)→ Compose Multiplatform(UI 框架)。
- Skia:Google 开源的 2D 图形引擎,负责所有实际的绘制、文本排版与光栅化工作。Compose 桌面端在渲染时并不直接调用 Skia,而是经由其绑定层间接使用。
- Skija/Skiko:JetBrains 维护的 Skia 绑定项目。Skija 侧重 JVM 侧原生绑定,Skiko 则是 Compose Multiplatform 真正依赖的渲染层(提供
SkiaLayer、Surface、PictureRecorder等 API)。 - Compose Multiplatform:本文所在的仓库本体,通过 Maven 坐标引用特定版本的 Skiko,从而获得跨 JVM/原生/Web 的渲染能力。
因此,每次发布新版 Compose 之前,通常都要先完成一次"渲染层升级",这也是 ci/release.md 中四大部分(更新 Skia、发布 Skiko、在 Compose 中更新 Skiko、发布 Compose Desktop)按顺序串成一条流水线的原因。此外,发布环节还依赖 TeamCity CI 上的发布配置(JetBrains 公共项目下 Compose/Skiko 相关的PublishRelease构建配置)以及内部 Docker 镜像,文中相应小节会一并说明。
第一步:更新 Skia(Skija 子模块升级)
Skia 的版本更新发生在Skija 仓库中,而不是 Compose Multiplatform 仓库本身。发布流程的第一步是在 Skija 中推进third_party/skia子模块:
升级子模块:在 skija 仓库中更新
third_party/skia子模块指向的提交。验证构建:运行
<skija_root>/script/build.sh,确认 Skija 构建没有被破坏。这一步是硬性门禁——只有本地三平台构建通过,才允许进入后续发布环节。同步分支与版本变量:如果使用的 Skia 分支发生变化(例如从
chrome/m85切换到chrome/m86),需要同步更新三个平台构建脚本中的VER变量:script/build_skia_linux.shscript/build_skia_windows.shscript/build_skia_macos.sh
VER变量直接决定 CI 拉取哪个 Skia 发行包,分支切换而版本号未更新会导致拉取到错误产物。部署到 Bintray:在 TeamCity 的
JetBrainsPublicProjects_Compose_Skia_PublishRelease构建配置中点击Deploy(或点击 "..." 自定义发布选项)把 Skia 产物发布出去。固定构建:发布完成后在 TeamCity 上Pin该次构建,以保留其产物不被清理策略回收,保证后续 Skiko 构建能够稳定引用。
在 Skiko 中同步版本:更新
skiko仓库skiko/gradle.properties中的以下参数:dependencies.skija.git.commit—— Skija(即 Skia 绑定)的 Git 提交;dependencies.skia.windows—— Windows 平台的 Skia 包版本;dependencies.skia.linux—— Linux 平台的 Skia 包版本;dependencies.skia.macos—— macOS 平台的 Skia 包版本(release.md 原文中该行笔误重复写为windows,按构建脚本与平台划分,第三个应为 macOS 对应的版本参数)。
验证方式:在
<skiko_root>/skiko目录执行./gradlew publishToMavenLocal,把 Skiko 发布到本地 Maven 仓库,确保基于新 Skia 的 Skiko 可以正常构建并被本地方案引用。跨平台复查:仅在本机验证往往不够(例如本地是 Linux,无法覆盖 Windows/macOS)。可在 TeamCity 的
JetBrainsPublicProjects_Compose_Skiko_BuildCheckManualTrigger构建配置中发起一次全平台构建检查:- 点击 "...";
- 方式一:在Changes标签页选择要验证的分支或提交;
- 方式二:在General标签页勾选 "run as a personal build" 上传自定义 patch,即可在提交正式代码前用补丁跑一遍全平台构建。
第二步:发布 Skiko
Skiko 是 Compose 依赖链上真正对外发布的一环,发布前必须先确认一切就绪:
- 发布就绪检查:
- 与团队成员确认所有必要变更均已合入并发布;
- 确定本次发布对应的 Git 提交(branch/commit);
- 验证 sample 项目可正常运行,例如:
cd skiko && ./gradlew publishToMavenLocal && cd samples/SkijaInjectSample && ./gradlew run - 由于个人通常无法覆盖全部平台,跨平台验证可以请对应平台的同事协助测试。
- 执行发布:在 TeamCity 的
JetBrainsPublicProjects_Compose_Skiko_PublishRelease构建配置中点击Deploy,并设置发布参数:- 在Parameters标签页把 "Skiko Release Version" 设置为新的发布版本号(例如
0.1.6); - 在Changes标签页选择要发布的分支/提交;
- 小技巧:如果时间紧张,可在General标签页勾选 "put the build to the queue top",把该构建插入到构建队列最前面,优先执行。
- 在Parameters标签页把 "Skiko Release Version" 设置为新的发布版本号(例如
- 确认产物:在 Skiko 的 GitHub Releases 页面检查新版本是否已正确发布。
值得注意的是,发布产物最终落到 JetBrains 的 Space Maven 仓库(https://packages.jetbrains.team/maven/p/ui/dev这类 dev 仓库),这是后续"在 Compose 中更新 Skiko"环节能够拉取到新版本的前提。
第三步:在 Compose 中更新 Skiko
新版本 Skiko 发布后,需要把它引入 Compose Multiplatform 的构建依赖。这一步通过 AOSP(Android Open Source Project)的importMavenArtifacts工具完成——Compose 的 Maven 依赖以 prebuilt 形式保存在 AOSP 的prebuilts/androidx/external仓库中:
- 使用
<androidx-dev-master>/frameworks/support/development/importMaven/import_maven_artifacts.py脚本下载 Maven 产物:# 如果本地 build.gradle.kts 的 repository 段还没有该仓库,需要先添加 # maven("https://packages.jetbrains.team/maven/p/ui/dev") export SKIKO_VERSION="0.1.6" import_maven_artifacts.py --name "org.jetbrains.skiko:skiko-jvm:$SKIKO_VERSION" import_maven_artifacts.py --name "org.jetbrains.skiko:skiko-jvm-runtime-linux:$SKIKO_VERSION" import_maven_artifacts.py --name "org.jetbrains.skiko:skiko-jvm-runtime-windows:$SKIKO_VERSION" import_maven_artifacts.py --name "org.jetbrains.skiko:skiko-jvm-runtime-macos:$SKIKO_VERSION"这里需要同时导入
skiko-jvm本体与三个平台的 runtime 构件(linux/windows/macos)。它们各自携带对应平台的原生库,是桌面端跨平台运行的基础。 - 将变更提交到
<androidx-dev-master>/prebuilts/androidx/external仓库,并上传 CL(change list)等待合入。
仓库内的自动化佐证:skikoAospCommit 脚本
仓库的 compose/scripts/skikoAospCommit 脚本把上述"导入 + 提交"过程完全自动化,可作为理解该环节内部细节的参考:
- 它要求通过环境变量
AOSP_COMPOSE_SOURCE指定 AOSP 源码根目录,并把 Skiko 版本作为命令行参数传入(如./updateSkikoInAosp 0.4.15); - 脚本会在
prebuilts/androidx/external与frameworks/support两个仓库中基于aosp/androidx-main各创建skiko<版本>分支; - 通过
sed更新frameworks/support/gradle/libs.versions.toml中的skiko = "..."版本号; - 随后删除旧的
org/jetbrains/skikoprebuilt 目录,并用 Gradle 的 importMaven 任务一次导入完整的 Skiko 构件清单,包括skiko、skiko-awt以及 linux/macos/windows 的 x64/arm64 runtime:org.jetbrains.skiko:skiko:$SKIKO_VERSION,\ org.jetbrains.skiko:skiko-awt:$SKIKO_VERSION,\ org.jetbrains.skiko:skiko-awt-runtime-linux-x64:$SKIKO_VERSION,\ org.jetbrains.skiko:skiko-awt-runtime-linux-arm64:$SKIKO_VERSION,\ org.jetbrains.skiko:skiko-awt-runtime-macos-x64:$SKIKO_VERSION,\ org.jetbrains.skiko:skiko-awt-runtime-macos-arm64:$SKIKO_VERSION,\ org.jetbrains.skiko:skiko-awt-runtime-windows-x64:$SKIKO_VERSION - 最后分别在两个仓库中
git commit(frameworks/support的提交信息为 "Update Skiko to $SKIKO_VERSION",并附上验证命令./gradlew jvmTest desktopTest -Pandroidx.compose.multiplatformEnabled=true)。
这与 release.md 手写流程一一对应,可作为发布时参考的完整实现。注意该脚本会写 AOSP 侧仓库,仅供维护者使用;本文仅作原理讲解。
第四步:发布 Compose Desktop
当 Compose 侧已经切换到新版本 Skiko、所有测试通过后,即可发布 Compose Desktop 组件本身:
- 在 TeamCity 的
JetBrainsPublicProjects_Skija_JetpackComposeMpp_Dev(即 Compose 构建配置)中启动一次新构建。
这一构建会产出 Compose Multiplatform 桌面端的所有 artifacts,并发布到 JetBrains 的 Maven 仓库。后续开发者只需在build.gradle.kts中声明对应版本号即可使用。
本地验证与发布辅助工具
仓库中还提供了与发布相关的本地工具链,可用于发布前的验证:
- 本地发布验证:与 Skiko 一样,Compose 自身也可以通过
publishToMavenLocal在本地验证。仓库提供了封装脚本 compose/scripts/publishComponentsToMavenLocal,其核心命令为:./gradlew publishToMavenLocal -Pcompose.version="$COMPOSE_CUSTOM_VERSION" -Pcompose.useMavenLocal=true通过
-Pcompose.version指定自定义版本号、-Pcompose.useMavenLocal=true让依赖解析优先使用本地仓库,即可在完全离线的本地环境验证组件可发布性。 - 发布辅助库:ci/build-helpers/README.md 说明存在一个专门帮助 CMP 项目及其依赖向 Maven Central 发布 artifacts 的辅助库,例如对 Skiko 执行
./gradlew publish即可发布;它会在 CMP 源码发生变更时由 CI 任务自动发布到https://packages.jetbrains.team/maven/p/cmp/dev。本地使用时既可以./gradlew publishToMavenLocal发布到本地,也可以直接从源码以参数化方式执行:./gradlew -p=cli reuploadArtifactsToMavenCentral -Pmaven.central.sign=true \ -Pmaven.central.coordinates=org.jetbrains.compose*:*:%version.COMPOSE%,org.jetbrains.compose.material:material-navigation*:%version.COMPOSE_MATERIAL_NAVIGATION% \ -Pmaven.central.stage=org.jetbrains.compose \ -Pmaven.central.description="Compose %version.COMPOSE% and associated libs" \ -Pmaven.central.staging.close.after.upload=true
发布后的清理:从 Space 仓库删除旧包
发布过程中,JetBrains 的 Space Maven 仓库会积累大量历史版本(尤其是各种-preview-*快照)。仓库提供了专门的清理工具 ci/delete-packages-from-space/README.md,其流程如下:
- 环境要求:JDK 9+;
- 生成个人 token:在 Space 中创建带
ReadRepository、WriteRepository、ViewProject权限的 token; - 创建配置:
cp template.local.properties local.properties,然后设置space.server.url与space.auth.token; - 查询 ID:运行
./gradlew listProjectsAndPackageRepositories找出space.project.id与space.repo.id并填入配置; - 生成删除清单:运行
./gradlew generateListOfPackagesToDelete -Pspace.package.version=0.4.0-preview-*,按版本通配符生成待删包列表(写入build/packages-to-delete.txt); - 人工确认:取消注释要删除的包,再运行
./gradlew deletePackages执行删除。
第五步:构建与维护 Docker 镜像
CI 构建离不开容器环境。release.md 中 Docker 镜像部分明确了两类操作:
本地构建
- Linux 镜像的本地构建说明见 docker/linux/README.md 对应的文档(release.md 原文指向
docker/linux/README.md与docker/windows/README.md;当前仓库中可确认存在 Linux 测试镜像的 Dockerfile); - Windows 镜像说明对应仓库中的
docker/windows/README.md(历史路径,当前仓库已不再包含该目录,说明 Windows 镜像构建已从本仓库移除)。
更新内部镜像
当需要更新 JetBrains 内部 Docker registry 上的镜像时,在对应 TeamCity 构建配置中发起构建:
- Linux:
JetBrainsPublicProjects_Compose_Docker_Linux - Windows:
JetBrainsPublicProjects_Compose_Docker_Windows
镜像内容参考:Linux 测试镜像 Dockerfile
ci/docker/linux-tests/Dockerfile 展示了 CMP CI 测试环境的完整依赖栈,可以帮助理解"为什么 CI 需要这些基础环境":
- 基础系统:
ubuntu:24.04,安装binutils、curl、fakeroot、libgl-dev、maven、python3、unzip、wget、xvfb(虚拟 X server,无头环境跑 GUI 测试必需)、git、xz-utils等; - JDK:OpenJDK 21(
openjdk-${JAVA_VERSION}-jdk),并设置JAVA_HOME; - Node.js:Node 22(供 Web/JS 目标与前端工具链使用);
- UTF-8 环境:
LANG=en_US.UTF-8、JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8,避免跨平台构建出现编码问题; - JetBrains Runtime(JBR):安装
jbr_jcef发行版(内含 JCEF,用于 Compose 桌面端嵌入 Chromium 的 WebView 场景); - Android SDK:通过 commandline-tools 安装
android-37.0平台; - 浏览器与驱动:安装固定版本的 Google Chrome + ChromeDriver 与 Firefox + GeckoDriver(供 Web 目标的端到端/UI 测试使用)。
这套镜像内容与 release.md 中"构建 Docker 镜像"的环节互相印证:CI 上的 Compose 构建需要同时覆盖 JVM 桌面、Android 与 Web 目标,因此镜像必须提前备齐 JDK、Android SDK、浏览器驱动与虚拟显示环境。
发布链路中的源码佐证:Skiko 渲染 API 在 Compose 中的使用
release.md 反复强调"更新 Skia/Skiko 后必须验证构建与渲染没有回归"。那么 Skiko 的 API 到底在 Compose 中扮演什么角色?以基准测试模块为例,benchmarks/multiplatform/benchmarks/src/skikoMain/kotlin/MeasureComposable.skiko.kt 直接展示了 Compose 与 Skiko/Skia 的交互方式:
- 代码直接 import
org.jetbrains.skia.Surface、org.jetbrains.skia.Color、org.jetbrains.skia.PictureRecorder、org.jetbrains.skia.Rect,说明 Compose 的 Skiko 目标在渲染层直接对接 Skia 对象; GraphicsContext接口要求实现类提供surface(width, height): Surface(创建 Skia Surface)与awaitGPUCompletion()(等待 GPU 完成,用于 GPU 时间测量);mimicSkikoRender方法按 SkiaLayer 的渲染逻辑模拟一帧渲染:用PictureRecorder录制 Compose 场景绘制命令为Picture,清空画布后drawPicture到 Surface,再flushAndSubmit提交给 GPU——其注释还说明,如果跳过中间 Picture 录制,基准测试结果可能产生 ±10% 的偏差;- 文件顶部注释明确该方法复刻自 Skiko 的
SkiaLayer.awt.kt渲染逻辑,并提示"如果新版 Skiko 不再使用 picture,这里也需要同步删除"。
这段代码生动地说明:Skia/Skiko 的任何一个渲染行为变化(Surface 语义、Picture 录制、GPU 提交方式)都会直接影响 Compose 的渲染正确性与性能,因此 release.md 中"升级 Skia → 重建 Skiko → 全平台验证"的门禁流程绝非可有可无。
总结:一次完整发布需要执行的关键动作清单
| 阶段 | 核心动作 | 关键参数/命令 | 验证手段 |
|---|---|---|---|
| 更新 Skia | 升级 skija 的third_party/skia子模块 | VER(三个平台构建脚本) | script/build.sh |
| 发布 Skia | TeamCity PublishRelease 配置点 Deploy,并 Pin 构建 | — | 保留产物可被后续引用 |
| 同步 Skiko 依赖 | 更新skiko/gradle.properties | dependencies.skija.git.commit、dependencies.skia.windows/linux/macos | ./gradlew publishToMavenLocal+ BuildCheck 全平台构建 |
| 发布 Skiko | TeamCity PublishRelease 点 Deploy | "Skiko Release Version" 设置为新版本号 | sample 项目./gradlew run |
| Compose 引入新 Skiko | import_maven_artifacts.py导入 artifacts 并提交 prebuilts | SKIKO_VERSION(如0.1.6) | 可参考 skikoAospCommit 自动化脚本 |
| 发布 Compose Desktop | TeamCity Compose 配置发起新构建 | — | 本地可用 publishComponentsToMavenLocal 预验证 |
| 清理旧包(可选) | Space 仓库删除历史版本 | space.package.version通配符 | 见 delete-packages-from-space |
| Docker 镜像 | 本地构建或 TeamCity 更新内部镜像 | Linux/Windows 对应配置 | 见 linux-tests/Dockerfile |
整体来看,Compose Multiplatform 的发布是一条"渲染层先行、平台全量验证、CI 统一发布"的严谨流水线:任何一层的版本推进都必须以构建与渲染回归验证为前置条件。对想要参与 Compose 生态贡献、或者在自己的项目中维护基于 Skia/Skiko 渲染栈的开发者而言,理解这条链路,就等于掌握了整个桌面 UI 生态最关键的一根"升级主动脉"。
【免费下载链接】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),仅供参考