构建日志
工具链

从 ext 迁到 Version Catalog:47 个模块的实操记录

buildSrc 里那份 Dependencies.kt 维护了四年,改一个版本号触发全量重编译。迁到 libs.versions.toml 之后,依赖变更不再让 buildSrc 失效。

·581 字 · 约 2 分钟

目录 · 5 节

我们的依赖版本一直放在 buildSrc/src/main/kotlin/Dependencies.kt 里。这个方案在 2022 年是最佳实践,现在它最大的问题是:buildSrc 的任何改动都会让整个构建的配置缓存和编译缓存失效。改一个 androidx.core 的小版本号,等于全量重来。

Version Catalog(gradle/libs.versions.toml)没有这个问题——它是纯数据文件,不参与编译。

迁移前后对比

buildSrc/Dependencies.kt libs.versions.toml
改版本号的代价 buildSrc 重编译 + 全量配置 只失效相关模块
IDE 补全 有(Kotlin 代码) 有(Gradle 生成访问器)
跨工程共享 需要发布 artifact from(files(...)) 直接引
依赖冲突检查 bundles + 版本对齐
学习成本 低,就是 Kotlin 中,TOML 命名有约束

命名规则先定死

TOML 的 key 会被转成 Gradle 访问器,-_. 都会变成 .。所以 androidx-core-ktxandroidx.core.ktx 生成的访问器是同一个,会直接报冲突。

我们定的规则:

  • 库的 key 一律用 - 分段,形如 <group 简写>-<artifact>androidx-core-ktxsquare-okhttp
  • 版本的 key 用 - 连接同一族:androidx-lifecyclekotlin
  • 插件的 key 加 plugin- 前缀,避免和同名库撞车
gradle/libs.versions.toml
[versions]
kotlin = "2.2.20"
agp = "8.7.3"
androidx-lifecycle = "2.9.1"
[libraries]
androidx-core-ktx = { module = "androidx.core:core-ktx", version = "1.15.0" }
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
[bundles]
lifecycle = ["androidx-lifecycle-runtime", "androidx-lifecycle-viewmodel"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

模块里用起来:

feature/profile/build.gradle.kts
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.bundles.lifecycle) // 一次引一组
}

迁移脚本

47 个模块手改不现实。我写了个一次性脚本做机械替换,剩下的手工收尾:

scripts/migrate-catalog.sh
#!/usr/bin/env bash
set -euo pipefail
# Deps.androidxCoreKtx -> libs.androidx.core.ktx
find . -name "build.gradle.kts" -not -path "./buildSrc/*" -print0 |
xargs -0 sed -i -E \
-e 's/Deps\.androidxCoreKtx/libs.androidx.core.ktx/g' \
-e 's/Deps\.okhttp/libs.square.okhttp/g' \
-e 's/Versions\.kotlin/libs.versions.kotlin.get()/g'
./gradlew help --configuration-cache

最后那行 help 是有意的:它几乎不做实际工作,但会完整走一遍配置阶段,能立刻暴露替换错的地方,比等 assembleDebug 快得多。

踩到的三处

其一version.ref 指向不存在的 version key 时,报错信息是 Invalid catalog definition,但不告诉你是哪一条。只能二分注释。

其二bundles 里的库如果有一个写错,整个 bundle 都不可用,同样不指名。

其三,Renovate / Dependabot 对 TOML 的支持要显式配置 enabledManagers,默认不扫。迁完之后我们有三周没收到任何依赖更新 PR,一直以为是上游没发版。

收益

改一个版本号之后的增量构建:

迁移前:BUILD SUCCESSFUL in 3m 51s (buildSrc 重编译 + 全量配置)
迁移后:BUILD SUCCESSFUL in 24s (只重编译受影响模块)

这个收益只在「改依赖版本」这个场景成立,日常改业务代码没有差别。但对我们这种每周升一次依赖的节奏,一个月能省下不少时间。

评论

由自建 Waline 驱动

输入关键词开始搜索