改进Gradle API以减少配置时间的使用指南

264 阅读7分钟

这篇文章介绍了一个新的API,用于在构建脚本和插件中声明和配置Gradle Tasks。我们打算让这个新的API最终取代现有的API,因为它允许Gradle避免配置不必要的构建逻辑。使用新的API将很快成为默认的建议,但现有的API将在几个主要版本中经历我们通常的废弃过程。

我们要求早期采用者试用新的Gradle Tasks API,以解决任何问题并收集反馈。我们已经创建了一个新的用户手册章节,对该功能进行了快速介绍,并解释了迁移构建以使用新API的一些指南。

我们欢迎你对这个API有任何反馈。你可以在这个Gradle问题上留下反馈。

预览Gradle 4.9中新的Gradle Tasks API

现有的和新的Gradle Tasks API之间的主要区别之一是Gradle是否花费时间来创建Task 实例和运行配置代码。新的API允许Gradle延迟或完全避免配置那些永远不会在构建中执行的任务。例如,在编译代码时,Gradle不需要配置运行测试的任务。

创建和配置那些从不使用的任务所花费的时间是整个Gradle配置时间的一个因素。配置时间会影响到每个构建,所以让它更快对大家都有好处。

现有的API还急于尽快创建Task实例。这可能使插件的排序更加脆弱。新的API是为了使Task 和需要它的东西之间的关系更加明确,通过传递一个 Provider为一个Task

我们还在一些构建中看到,配置特定的任务是非常昂贵的,因为它们接触到了网络服务,它们运行git status ,或者它们需要解析一些东西。如果这些任务很少使用,那么每个构建都要为配置该任务付出代价。新的API允许插件或构建者声明这些昂贵的任务,以便它们只在必要时被配置。

一旦新的API成为默认建议,以后的Gradle版本将提供更多细节和样本。

对Gradle构建的实测影响

在过去的几个月里,我们一直在gradle/gradle构建、Gradle Enterprise和一个典型的大型项目中对新的Gradle Tasks API进行内测。

image.png

在这段时间里,我们做了一些改变,将Gradle构建中的配置时间从1.7s减少到Linux性能代理上的1.4s。这些改进部分来自新的API,Gradle构建仍然不必要地配置了几百个任务,所以我们相信我们可以进一步减少这个数字。

下面,我们绘制了与新API直接相关的改进。我们在2014年中期的MacBook Pro(2.6GHz英特尔酷睿i5,16GB内存)上测量了几个不同的项目。

image.png

perf-enterprise-large项目是我们在性能测试中使用的一个生成项目,它有近350个Java模块。当用现有的API配置这个构建时,Gradle创建并配置了超过10000个任务。使用新的API,Gradle只配置了349个(97%的任务被避免了)。未来版本的Gradle的变化将使这1个任务。从图中,我们可以看到,平均配置时间从936ms到703ms(-233ms)。

第二组图是关于gradle/gradle构建的。如果没有新的API,Gradle将创建和配置6300多个任务。我们有更多的工作要做,但我们避免了90%的任务的创建和配置。 我们的平均配置时间从1325ms下降到1117ms(-208ms)。

对于我们的闭源Gradle Enterprise项目,我们才刚刚开始将其转换为新的API。我们只避免了所有任务的64%。我们的平均配置时间从1636ms下降到1467ms(-169ms)。

我们期望其他构建也能看到类似的减少,当许多任务在不需要的时候被避开。如果一些构建的规模大得多(数百个子项目)或配置非常昂贵的任务,可能会看到更大的影响。我们正在与著名的插件作者合作,使用新的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 helpgradle buildgradle compileJava 会记录 "Hello, compileJava "和 "Hello, compileTestJava"。配置的任务并不因需要执行的内容而改变。

如果我们改用新的APItasks.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而不是现有的。你可以在这个Gradle问题上留下反馈。