改进 Gradle API 以减少配置时间
这篇文章介绍了一个新的 API,用于在构建脚本和插件中声明和配置 Gradle 任务。我们希望这个新的 API 最终能够取代现有的 API,因为它允许 Gradle 避免配置不必要的构建逻辑。使用新的 API 将很快成为默认推荐,但现有的 API 将经过我们通常的弃用流程,跨越多个主要版本。
我们要求早期采用者尝试新的 Gradle 任务 API,以解决任何问题并收集反馈。我们创建了一个 新的用户手册章节,以快速介绍该功能并解释一些将构建迁移到使用新 API 的指南。
我们欢迎您对该 API 的任何反馈。您可以在此 Gradle 问题 上留下反馈。
Gradle 4.9 中新 Gradle 任务 API 预览
现有 Gradle 任务 API 和新 API 之间的主要区别之一是 Gradle 是否会花费时间创建 Task
实例并运行配置代码。新 API 允许 Gradle 延迟或完全避免配置在构建中永远不会执行的任务。例如,在编译代码时,Gradle 不需要配置运行测试的任务。
创建和配置从未使用过的任务所花费的时间是 Gradle 总体配置时间的一个因素。配置时间会影响每次构建,因此使其更快对每个人都有好处。
现有 API 还会尽快地创建 Task
实例。这会使插件的排序更加脆弱。新 API 旨在通过传递 Task
的 Provider
来使 Task
与需要它的东西之间的关系更加明确。
我们还在一些构建中发现,配置特定任务非常昂贵,因为它们会访问 Web 服务,运行 git status
或需要解析某些内容。如果这些任务很少使用,每次构建都会为配置该任务付出代价。新 API 允许插件或构建作者声明昂贵的任务,以便只有在必要时才配置它们。
Gradle 的后续版本将在新 API 成为默认推荐后提供更多详细信息和示例。
对 Gradle 构建的衡量影响
在过去几个月里,我们一直在 gradle/gradle 构建、Gradle Enterprise 和一个典型的 大型项目 中试用新的 Gradle 任务 API。
在此期间,我们进行了一些更改以减少 Gradle 构建中的配置时间,从 1.7 秒减少到 1.4 秒,这是在我们 Linux 性能代理上测量的。这些改进的一部分来自新 API,Gradle 构建仍然会不必要地配置数百个任务,因此我们相信可以进一步减少这个数字。
下面,我们绘制了与新 API 直接相关的改进。我们在配备 2.6 GHz Intel Core i5 处理器、16GB 内存的 2014 年中期的 MacBook Pro 上对几个不同的项目进行了测量。
perf-enterprise-large 项目是我们用于性能测试的生成项目,它包含近 350 个 Java 模块。在使用现有 API 配置此构建时,Gradle 会创建和配置超过 10000 个任务。使用新 API,Gradle 只配置了 349 个任务(避免了 97% 的所有任务)。Gradle 未来版本的更改将使这个数字变为 1 个任务。从图表中可以看出,平均配置时间从 936 毫秒降至 703 毫秒(减少了 233 毫秒)。
第二组图表是关于 gradle/gradle 构建的。如果没有新的 API,Gradle 将创建和配置超过 6300 个任务。我们还有很多工作要做,但我们避免了创建和配置 90% 的所有任务。我们的平均配置时间从 1325 毫秒下降到 1117 毫秒(-208 毫秒)。
对于我们的闭源 Gradle Enterprise 项目,我们才刚刚开始将其转换为新的 API。我们只避免了大约 64% 的所有任务。我们的平均配置时间从 1636 毫秒下降到 1467 毫秒(-169 毫秒)。
我们预计其他构建在不需要时避免了许多任务的情况下,将看到类似的减少。如果构建规模更大(数百个子项目)或配置非常昂贵的任务,一些构建可能会看到更大的影响。我们正在与知名的插件作者合作使用新的 API,因此大多数构建将看到任务避免带来的好处。
为什么需要一个新的 API?
不幸的是,现有的 API 无法适应我们的新需求。
Task
实例不应该立即创建。许多现有的 API 返回Task
,我们无法破坏二进制兼容性。- 除非需要,否则不应该创建或配置任务。许多现有的 API *可以* 被改造成延迟创建或配置,但这会以难以诊断的方式默默地破坏许多构建。
新的 API 旨在与现有 API 协同工作,因此使用现有 API 创建的任务对新的 API 可见,反之亦然。构建可以逐渐迁移到新的 API,但混合使用现有 API 会抵消新的 API 的一些好处。任何需要 Task
实例的现有 API 将迫使使用新的 API 创建的任务像使用现有 API 创建一样被创建。
新的 API 也旨在与现有 API 足够相似,以便迁移变得容易。
从现有的 Gradle Tasks API 迁移到新的 API
我们的 用户手册章节 提供了将现有 API 映射到新 API 的参考。
作为快速简便的参考
tasks.create(...)
变为tasks.register(...)
tasks.withType(SomeType) { }
变为tasks.withType(SomeType).configureEach { }
tasks.all { }
变为tasks.configureEach { }
tasks.getByName(...)
变为tasks.named(...)
- 对于 Groovy Gradle DSL,没有使用新 API 替换
task foo(...) { }
。
请记住,与新 API 一起使用的配置块不保证始终执行。
在现有 API 中,如果您使用 tasks.withType(...)
plugins {
id "java"
}
tasks.withType(JavaCompile) { javaCompile ->
println "Hello, " + javaCompile.name
}
运行 gradle help
或 gradle build
或 gradle compileJava
将记录“Hello, compileJava”和“Hello, compileTestJava”。配置的任务不会根据需要执行的内容而改变。
如果我们改用新 API tasks.withType(...).configureEach
plugins {
id "java"
}
tasks.withType(JavaCompile).configureEach { javaCompile ->
println "Hello, " + javaCompile.name
}
我们会看到行为非常不同。Gradle 会根据执行所需的内容配置或多或少的构建。
- 运行
gradle help
不会记录任何“Hello”消息。Gradle 能够避免配置JavaCompile
任务,因为它们从未执行过。 - 运行
gradle build
将记录“Hello, compileJava”和“Hello, compileTestJava”。Gradle 必须配置JavaCompile
任务,因为它们都被执行了。 - 运行
gradle compileJava
将只记录“Hello, compileJava”。Gradle 只需配置compileJava
任务,因为它被执行了。任务compileTestJava
未执行,因此不会创建或配置。
我们希望收到您的反馈
包含在 Gradle Enterprise 2018.3 中,您可以查看 使用新任务 API 的进度 在您的构建扫描中。对于许多内置的 Gradle 插件,我们已经切换到使用新的 API。对于使用 Java 的构建,您可能会看到大量从未配置过的任务。
这个 Gradle 任务 API 仍在开发中,但我们相信它已经足够完善,可以替代所有场景中的现有 API。我们计划将任何反馈纳入下一个版本,其中将包含更多使用新 API 的文档和示例。我们的目标是尽快使该 API 投入生产。
请在您的插件和构建中试用此 API,并告诉我们您的想法。我们在用户手册中添加了一个部分,用于收集现有 API 和新 API 之间的性能比较数据。我们很乐意看到您的结果,并听取您对哪些功能有效、哪些功能令人困惑以及哪些功能缺失会阻止您使用新 API 而不是现有 API 的意见。您可以在此Gradle 问题上留下反馈。