这篇文章介绍了一个新的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进行内测。
在这段时间里,我们做了一些改变,将Gradle构建中的配置时间从1.7s减少到Linux性能代理上的1.4s。这些改进部分来自新的API,Gradle构建仍然不必要地配置了几百个任务,所以我们相信我们可以进一步减少这个数字。
下面,我们绘制了与新API直接相关的改进。我们在2014年中期的MacBook Pro(2.6GHz英特尔酷睿i5,16GB内存)上测量了几个不同的项目。
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 help 或gradle build 或gradle 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问题上留下反馈。