Gradle Kotlin增量编译“看起来开了”却没生效,根本原因是Kotlin编译器与Gradle在源码变更捕获、execution.strategy未启用或buildSrc/gradlePlugin模块禁用增量三个环节脱节。

Gradle Kotlin增量编译为什么“看起来开了”却没生效?
根本原因不是配置写错了,而是 Kotlin 编译器和 Gradle 的增量机制在三个关键环节脱节:源码变更未被正确捕获、kotlin.compiler.execution.strategy 未启用、或 buildSrc / gradlePlugin 模块本身禁用了增量。Kotlin 1.9+ 默认开启增量编译,但前提是项目结构干净、无跨模块强耦合、且没有手动关闭 incremental=true(它早已不是必需配置项)。很多团队在升级 Kotlin 后发现“改一行代码还是全量重编”,其实是旧版 build.gradle.kts 里残留了显式设为 false 的逻辑,或者用了过时的 kotlinOptions { freeCompilerArgs += "-Xno-inline" } 这类干扰 AST 分析的参数。
如何验证当前是否真正在用增量编译?
别信 IDE 状态栏的小图标,要查真实日志:
- 执行
./gradlew compileKotlin --info,搜索输出中是否出现Using incremental compilation—— 如果只有Compiling with Kotlin compiler version而没这句,说明没走增量路径 - 检查
build/kotlin-classes/下是否有按文件粒度生成的.kotlin_built_class文件,而不是整个main.jar被整体重建 - 改一个
.kt文件后再次执行compileKotlin,观察耗时是否明显低于首次(比如从 8s 降到 0.3s),否则大概率 fallback 到全量
常见破坏增量的配置与依赖模式
Kotlin 增量编译依赖精确的依赖边界推断,以下情况会直接退化为全量:
-
implementation(project(":common"))中的:common模块如果启用了@OptIn(KotlinInternalApi::class)或使用了internal修饰符暴露 API,Kotlin 编译器无法安全判断影响范围,强制全量 - 在
buildSrc里定义了自定义SourceSet但没声明dependsOn关系,导致 Kotlin 插件无法追踪变更传播链 - 使用了
kotlinx.coroutines1.7+ 的@FlowPreview等不稳定 API,且未在kotlinOptions中添加allWarningsAsErrors = false,警告触发全量重编 - Gradle 配置缓存(
org.gradle.configuration-cache=true)开启时,某些旧版 Kotlin 插件(如 1.8.20 之前)不兼容,静默禁用增量
修复步骤:从最可能的点开始动手
优先检查并操作这三项,80% 的案例可当场解决:
- 删掉所有
kotlinOptions { incremental = false }或freeCompilerArgs += "-Xno-incremental"—— Kotlin 1.9+ 已移除该 flag,保留反而报错 - 在根
gradle.properties中确认有org.gradle.caching=true和org.gradle.configuration-cache=false(除非你明确需要配置缓存且已升级到 Kotlin 1.9.20+) - 对多模块项目,在每个子模块的
build.gradle.kts中显式加上:tasks.withType<org.jetbrains.kotlin.gradle.tasks.kotlincompile>().configureEach { kotlinOptions.freeCompilerArgs += "-Xskip-prerelease-check" }</org.jetbrains.kotlin.gradle.tasks.kotlincompile>—— 避免因预发布注解(如@ExperimentalTime)触发保守全量策略
真正棘手的是跨模块 internal API 暴露和 buildSrc 的隐式依赖,这类问题不会报错,只会让增量“悄悄失效”。遇到这种情况,得打开 --debug 日志,盯住 KotlinCompilerArgumentsBuilder 的输出,看它最终传给编译器的 -Xuse-fir 和 -Xenable-k2 是否一致——不一致就说明 FIR/K2 混用,这是当前最隐蔽的增量杀手。











