避免编译

目录

介绍

我们最近注意到社区中有人讨论通过忽略不影响依赖项 ABI 的更改来加速 JVM 上的 Gradle 编译。真是个好主意!事实上,Gradle 已经开箱即用地为 Java 使用 ABI 来实现这一点,无需任何额外配置 自 3.4 版本以来。我们将此功能称为 避免编译。这篇文章解释了基于 ABI 的编译对平均工作流程意味着什么。剧透:利用避免编译是任何构建的最佳性能增强措施之一。

什么是应用程序二进制接口? #

应用程序二进制接口 (ABI) 是编译软件生成的接口,它定义了内部和外部交互。ABI 代表了在编译时对使用者可见的内容。在编译项目时,其任何依赖项的 ABI 中是否存在更改决定了编译是否是最新的,或者是否需要重新编译。这些 ABI 包含关于依赖项的所有公共信息,这些信息对使用者项目可见,例如

  • 任何公共方法及其参数类型和返回语句
  • 任何公共属性和字段
  • 用于针对 ABI 编译的任何依赖项。

当人们在源代码中访问库时,他们使用库的 API。当机器访问已编译的二进制文件时,它使用 ABI。

为什么 ABI 与构建性能相关? #

现代构建系统在编译代码时会考虑 ABI 兼容性,以在编译代码库的增量更改时尽可能避免编译。

对内部实现细节的更改是 ABI 兼容的:它们不会更改公共接口。实际上,项目的内部实现细节比公共组件更频繁地更改。当公共信息不更改时,任何下游项目都不需要重新编译。跳过额外的步骤可以对大型项目(本地和 CI)的构建性能产生巨大影响。

对公共接口的更改是 ABI 不兼容的,因为它们更改了公共接口。ABI 不兼容的更改需要重新编译所有下游依赖项。必须重新编译 ABI 不兼容更改的所有下游依赖项可能会大大增加构建时间。

什么是避免编译? #

当发生 ABI 兼容的更改时,Gradle 会优化构建。我们将此优化称为 避免编译

为了了解这是如何工作的,假设有两个项目。:app 依赖于 :lib。以下是一些我们通常可以对 :lib 执行的操作,这些操作是 ABI 兼容的,并且不会导致 :app(或任何依赖于 :app 的内容)需要重新编译

  • 对方法体进行任何更改
  • 添加、删除或更改私有方法、字段或内部类
  • 重命名参数
  • 更改注释
  • 更改类路径中 jar 或目录的名称
  • 添加、删除或更改资源

:lib 中发生 ABI 兼容的更改,并且运行依赖于其类的 ':app' 中的任务时,Gradle 不会重新编译项目 :app 或任何依赖于 :app 的项目。在大型多项目构建中,这可以节省大量时间。

避免编译与增量编译有何不同? #

并非所有更改都符合上述要求。在某些情况下,您需要更改 :lib 的公共 ABI,这会导致需要编译 :app。幸运的是,另一个开箱即用的功能称为 增量编译。这将智能地重新编译 :lib 中发生更改的类,以便下游 :app 的编译速度仍然比完全编译快。

增量编译与避免编译不同,但非常互补。

“避免编译”是指 *完全避免为给定项目调用编译器*。

另一方面,“增量编译” *确实* 意味着调用编译器,但这样做是为了 *减少需要重新编译的代码量*。这是通过跟踪类之间的引用作为正常编译的一部分来实现的,并且仅重新编译受给定更改影响的内容。

通过增量编译,我们查看所有与避免编译相比发生更改的类,避免编译查看的是项目。避免编译跨依赖项目工作,而不仅仅像增量编译那样在一个项目内部工作。也就是说,增量编译优化了项目内各个类的编译。增量编译发生在单个项目内,但避免编译着眼于多个项目之间的关系。增量编译仍然可以节省跨项目的时间,因为它减少了需要重新编译的数量。

我正在使用增量编译还是避免编译? #

总结一下,如果您进行 ABI 兼容的更改,那么您正在使用增量编译和避免编译:对您进行更改的源文件上的编译任务进行增量编译,对下游项目进行避免编译。

或者,如果您的更改与 ABI 不兼容,您只能从增量编译中受益。

ABI JAR 呢? #

一些构建系统生成 ABI JAR 以实现避免编译。有时称为头 JAR,它们具有整体接口,但没有内部细节。ABI JAR 仅包含公共方法、字段、常量和嵌套类型,删除了所有方法体,可用于评估任何更改是否表明需要重新编译。使用 Gradle,我们不需要 ABI JAR,因为当存在编译器任务时,我们会规范化其输入并生成 ABI 的唯一哈希值。然后,Gradle 使用此哈希值来检查是否有任何更改。

不同语言的注意事项 #

Groovy 有一个 选择加入的实验性功能

Kotlin 有一个 实验性功能,由 JetBrains 开发,作为 Kotlin Gradle 插件 的一部分。

如何使用? #

Gradle 自动使用避免编译。多年来,对于使用 Gradle 构建的 Java 项目,默认情况下已启用增量编译和避免编译。因此,下次当您害怕在编辑后重新编译代码时,请放心,Gradle 会自动为您提供性能提升。

不确定您是否正在使用增量编译或避免编译,或者您可能认为您有一个应该有效但不适用的用例?在 Slack 上找到我们,我们很乐意看到用例。

讨论