Gradle 构建缓存 Beta 版介绍
引言
已在 Gradle 3.5 中引入,用于减少构建时间。
它做什么? #
构建缓存会在本地重用 Gradle 任务的输出,并在机器之间共享任务输出。在许多情况下,这将加速平均构建时间。
构建缓存是对 Gradle 增量构建功能的补充,增量构建功能可以优化本地更改(尚未构建)的构建性能。许多 Gradle 任务被设计为可增量的,因此如果任务的输入和输出没有改变,Gradle 可以跳过该任务。即使任务的输入已更改,某些任务也可以只重新构建已更改的部分。当然,这些技术只有在先前本地构建已有输出的情况下才有效。过去,在全新 checkout 后构建或执行“clean”构建需要从头开始重新构建一切,即使这些构建的结果已经在本地或另一台机器(如持续集成服务器)上创建过。
现在,Gradle 使用任务的输入作为键来唯一标识任务的输出。启用构建缓存功能后,如果 Gradle 在构建缓存中找到该键,Gradle 将会跳过任务执行,直接将输出从缓存复制到构建目录。这比再次执行任务要快得多。
特别是,如果您正在使用持续集成服务器,您可以配置 Gradle 将任务输出推送到共享构建缓存。当开发人员进行构建时,CI 上已构建的任务输出将复制到开发人员的机器上。这可以大大改善开发人员的本地构建体验。
当使用本地构建缓存时,每当您切换分支时,Gradle 可以跳过任务执行并从本地缓存中拉取之前的输出,而不是重新构建项目的大部分内容。
它是如何工作的? #
可缓存的 Gradle 任务被设计为将所有影响任务输出的内容声明为输入。Gradle 通过对任务的所有输入进行哈希运算来计算构建缓存键。该构建缓存键唯一标识任务的输出。这是每个任务实现的一个可选功能,因此并非所有任务都是可缓存的或需要可缓存。Gradle 的几个内置任务(JavaCompile
、Test
、Checkstyle
)已启用缓存以加速典型的 Java 项目。
任务的构建缓存键会考虑
- 任务通过注解(例如
@InputFiles
)或运行时 TaskInputs API 定义的所有输入的值。 - 任何文件输入的 contents(和相对路径)。
- 任务的 classpath,包括任何插件和使用的 Gradle 版本。
- 任何任务操作的 classpath,其中可能包括构建脚本。
当启用构建缓存功能时,Gradle 将在任务未过期时检查配置的任何构建缓存是否包含与任务的构建缓存键匹配的项。如果 Gradle 未找到匹配项,任务将按正常方式执行。执行后,Gradle 将收集任务的所有输出,并在配置的情况下将其推送到构建缓存。如果 Gradle 找到匹配项,则任务的输出将被删除,并将之前的输出复制到输出目录中。
它能提供帮助吗? #
自 2016 年 11 月以来,我们一直在 Gradle CI 构建中使用构建缓存。我们还有一些合作伙伴在他们的构建中试用了构建缓存。我们无法直接分享他们的数据,但他们在 CI 和开发者构建中看到了与我们类似的改进。平均而言,我们看到每次提交的构建总时间减少了 25%,但某些提交的改进甚至更好(减少了 80%),中位数构建的减少了 15%。
这是 Gradle 的缓存和非缓存构建之间花费时间(分钟)的另一张图。您可以看到减少量如何为我们节省了大约 90 分钟的 360 分钟构建时间。
构建缓存是一项通用的功能,可以避免在可能的情况下重新执行任务,因此大小项目都能以某种方式从中受益。您的项目结构将影响您整体的收益。如果您的项目由一个单一的模块组成,Gradle 还有其他功能可能也有帮助,例如增量编译或复合构建。我们将在未来的博客文章和 Gradle Summit 中提供更多关于如何充分利用构建缓存的信息。
立即提升您的构建速度 #
Gradle 3.5 版本是第一个包含构建缓存功能的版本。
我们预计构建缓存功能将在下一个版本中普遍可用,但我们希望每个项目都能尝试一下构建缓存 Beta 版。为此,我们希望您为我们尝试 3 件事。
1) 在一个简单的项目中进行尝试 #
升级到 3.5 后,选择一个简单的 Java 项目并运行
gradle --build-cache clean assemble
gradle --build-cache clean assemble
第二次构建应该会更快,因为一些任务输出将从第一次构建中重用。这些输出将从您的本地构建缓存中拉取,该缓存位于 GRADLE_USER_HOME 的一个目录中。
2) 尝试在多台机器之间共享构建输出 #
要使用共享的远程缓存,我们提供了一个推荐的配置,该配置使用您的持续集成构建来填充共享构建缓存,并允许所有开发人员从该构建缓存中拉取。
您将需要一个远程构建缓存后端供开发人员共享。我们提供了一个build cache node Docker 镜像,它作为一个远程 Gradle 构建缓存运行,并可以连接到 Gradle Enterprise 进行集中管理。缓存节点也可以在没有 Gradle Enterprise 安装的情况下使用,但功能受限。
3) 向我们提供反馈 #
如果您有反馈,我们很乐意倾听。如果您有可以分享的构建扫描,那就更好了。
我们很高兴能在 Gradle 3.5 中推出 Gradle 构建缓存功能以收集反馈,但我们也知道还有很多工作要做才能使构建缓存稳定且高性能。在在 GitHub 上提出新问题之前,您应该检查一些已知问题。
目前,我们不建议您在生产构建中启用构建缓存而不了解风险。存在已知问题可能导致您的构建失败或产生不正确输出,但您对问题类型或成功的反馈对于成熟构建缓存功能非常有价值。您可以通过将 org.gradle.caching=true
设置或运行 --build-cache
来配置您的构建中的构建缓存并试用启用它,而不会影响所有构建。
为了对 Gradle 进行构建缓存的自测,我们使用了一个单独的 CI 作业来运行启用构建缓存的构建。这使我们能够比较相同更改集下启用和未启用构建缓存的构建时间。
致谢和路线图 #
在尝试构建缓存后,您可能会想知道为什么您的构建中有更多的部分无法缓存。无论您使用的是哪种构建缓存后端,Gradle Enterprise 2017.2 都附带了功能,可以通过收集数据来了解构建缓存的使用情况和行为,无论构建缓存是否启用。构建扫描会跟踪任务未缓存的原因。如果任务存在特定问题,例如没有输出或未启用其缓存功能,则该任务可能未被缓存。您可以在构建扫描时间线上搜索这些原因。
在 Gradle 和 Gradle Enterprise 的未来版本中,我们将收集更多与构建缓存和任务缓存能力相关的信息,以便更容易诊断构建失败或构建缓存性能不佳的问题。
对于下一个版本 Gradle 4.0,我们打算专注于使构建缓存能够安全地用于所有行为良好的构建,并提供关于 Gradle 无法安全缓存任务输出的情况的反馈。这也意味着我们将提供一个行为良好的本地构建缓存和几个验证检查。
对于后续版本,我们打算花时间扩展我们的文档和 Gradle 指南,以便您更容易缓存更多任务并开发适合缓存的任务。
感谢您持续的帮助和支持。请考虑通过我们上面概述的三步法,使用构建缓存让您的构建更快。