buildSrc 中使用 afterEvaluate 会禁用 Gradle 配置缓存,导致冷启动式慢构建;应改用声明式任务注册和配置,避免动态逻辑,并优先采用 Version Catalogs 或预编译脚本替代。

buildSrc 里用了 afterEvaluate 就别想快
Gradle 的 buildSrc 是一个隐式子项目,它的构建脚本(比如 build.gradle.kts)会在每次构建主项目前先执行一次。如果里面写了 project.afterEvaluate、gradle.taskGraph.whenReady 或者任何依赖运行时 project 状态的逻辑,Gradle 就无法启用配置缓存(configuration cache),导致每次构建都得重新解析整个脚本树——这不是“慢一点”,是直接退回到 Gradle 3.x 时代的冷启动体验。
常见错误写法包括:
afterEvaluate { tasks.named("assemble") { dependsOn("myCustomTask") } }-
tasks.withType<javacompile>().configureEach { options.fork = true }</javacompile>放在afterEvaluate块里 - 用
System.getenv("CI")或file("$rootDir/secrets.properties")动态读取外部值
正确做法是:把所有逻辑移到任务注册阶段,用声明式方式绑定。例如:
tasks.withType<JavaCompile>().configureEach {
// ✅ 直接配置,不等 afterEvaluate
options.fork = true
}
tasks.register<MyCustomTask>("myCustomTask") {
// ✅ 依赖关系也在这里声明
dependsOn(tasks.named("assemble"))
}
buildSrc 编译本身成了瓶颈?检查 Kotlin DSL 和插件版本
如果你的 buildSrc 是 Kotlin 写的(build.gradle.kts),且 Gradle 版本 ≥ 8.9,但没开 Kotlin 编译缓存,那么每次改一行代码,Kotlin 编译器都要重跑整个 buildSrc 模块——这比主项目编译还慢。这不是 Gradle 的问题,是 Kotlin 编译器没被复用。
必须确认以下三点:
-
buildSrc/build.gradle.kts中启用了 Kotlin 编译器缓存:kotlin { compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } },并确保org.gradle.configuration-cache=true已生效 -
buildSrc的gradle.properties(如果有)里没覆盖掉根项目的org.gradle.jvmargs,否则它可能用默认 512m 堆内存跑 Kotlin 编译,频繁 GC - 避免在
buildSrc里引入大而全的插件,比如com.github.ben-manes.versions这类扫描全依赖图的插件——它只在 CI 上有用,开发时纯属拖慢本地构建
buildSrc 修改后缓存全失效?别手动删 .gradle
很多人一发现 build 变慢,第一反应是 ./gradlew clean 或手动删 buildSrc/.gradle。这是最伤效率的操作:Gradle 的构建缓存(org.gradle.caching=true)和配置缓存(org.gradle.configuration-cache=true)都是基于输入哈希的,只要 buildSrc 内容变了,缓存自然失效——你删不删,它都不复用。但手动清目录反而会干掉 JVM 类加载缓存、Kotlin 编译器的增量缓存、甚至 daemon 的热方法 JIT 信息。
更稳妥的做法是:
- 只改
buildSrc时,用./gradlew --no-daemon --configuration-cache --scan buildSrc:build单独验证,避免污染主项目 daemon 状态 - 确认
buildSrc的变更是否真的必要:比如把一个常量从val VERSION = "1.2.0"改成"1.2.1",完全没必要动buildSrc,应该挪到gradle.properties或version catalogs里 - 如果只是调试用的临时逻辑,加个
if (System.getProperty("debug.buildsrc") == "true")包裹,避免每次构建都走分支判断
替代 buildSrc 的轻量方案:Version Catalogs + Precompiled Scripts
不是所有逻辑都值得塞进 buildSrc。它本质是个编译期强依赖,一旦引入,就锁死了 Gradle 插件 API 兼容性、Kotlin 版本、甚至 JVM 字节码级别。很多团队用 buildSrc 只是为了共享几个函数或常量,结果换来的是构建链路变重、CI 构建不可复现、IDE 同步卡顿。
更现代、更轻的替代路径是:
- 版本管理统一用
gradle/libs.versions.toml(Version Catalogs),它不参与编译,纯文本,Git diff 友好,且被 Gradle 原生缓存 - 简单逻辑抽成
.gradle/precompiled-libs/xxx.gradle.kts预编译脚本,通过apply(from = "...")引入,不触发buildSrc编译流程 - 复杂逻辑才考虑独立插件工程(
my-build-plugin),发布到本地 Maven,用plugins { id("my-build-plugin") version "1.0" }声明式引用,构建时只下载 jar,不编译源码
真正难的从来不是“怎么让 buildSrc 快起来”,而是判断“它是不是本就不该存在”。很多项目删掉 buildSrc 后,构建时间没降多少,但 CI 稳定性、IDE 同步速度、新成员上手成本反而明显改善——因为构建逻辑从“隐式编译时行为”变成了“显式声明式配置”。

















