第4条:Gradle配置缓存陷阱
Publié le 26 September 2025
引言
在我们创建插件的历程中`site-baker`, 我们遵循了严格的TDD方法。每个功能都进行了测试、验证,并且我们充满信心地前进。然后有一天,意外到来。构建开始表现得反复无常。插件逻辑或配置文件的修改似乎被忽略,而我们之前可靠的功能测试莫名其妙地失败了。
这种问题可能会令人极度沮丧。它质疑了工具的可靠性和我们代码的有效性。经过一次激烈的调试会话后,罪魁祸首被确定为:Gradle 配置缓存.
�症状:幽灵构建
问题以多种方式表现出来:
-
我在一项任务中修改字符串`println`, 但旧的字符串在运行时仍然显示。
-
我当时正在更改我的文件中的一个值`managed-jbake-context.yml`, 但插件的行为就像文件没有被修改一样。
-
功能测试会即时创建测试项目,但它们失败了,因为插件似乎没有检测到新创建的配置文件。
一切都好像 Gradle 在执行我们构建的一个“幻影版本”,忽略了我们最近的更改。
调查:配置缓存是什么?
配置缓存是 Gradle 相对现代且功能极其强大的特性,在新版本中默认启用。其目标是让构建更快。
-
在第一次执行时,Gradle 执行阶段配置( 读取 的)
build.gradle.kts, 创建任务,解析依赖) 并构建任务图。 -
在此阶段结束时,Gradle序列化此任务图并将其缓存。
-
在随后的执行中,如果没有任何更改(构建脚本`gradle.properties`, etc.), Gradle完全跳过配置阶段并重新使用缓存的任务图。
在大型项目中,时间节约效果惊人。然而,这种性能有代价:它对插件的编写方式提出了严格的要求。
问题的原因:不合规的插件
我们的插件`site-baker`不知不觉地违反了配置缓存的若干规则。 为了使任务图可序列化,任务不得包含对复杂对象(如对象)的引用`Project`或以任意方式读取文件,在执行阶段。
我们的主要错误是在任务执行逻辑内直接读取 YAML 文件的内容,使用我们扩展中存储的路径引用。这种方法与缓存不兼容,因为如果这种读取未被建模为一个,Gradle 无法知道文件内容是否已更改。任务条目(任务输入).
临时解决方案:禁用缓存
为了使我们摆脱困境并恢复可预测的构建行为,最快的解决方案是禁用配置缓存。只需在文件中添加以下行即可。gradle.properties`使用该插件的项目(或者在我们的情况下,是测试项目)`site-baker).
# site-baker/gradle.properties
org.gradle.configuration-cache=false
瞬间,构建恢复了正常行为。每次执行都重新启动了配置阶段,我们的更改被考虑在内。
然而,这是一个变通的解决方案,而不是一个持久的解决方案。它牺牲了性能,也没有解决我们插件的根本问题。
真正的解决方案:使插件兼容
为了让插件成为现代 Gradle 生态系统的良好公民,它必须与配置缓存兼容。这意味着我们需要重新思考数据如何流向我们的任务。
关键是使用提供方 APIGradle的。 而不是直接传递值(如一个)String ou un File) 对于我们的任务,我们必须花费`Property<T>`或一些`Provider<T>`。
-
声明任务条目:解析 YAML 文件的任务必须将该文件声明为输入。为此,我们使用该注解。
@InputFile。
[source,kotlin] ---- @get:InputFile abstract val configFile: RegularFileProperty ----