Gradle 6.0 的新功能

Gradle 6.0 自 5.0 版本以来的新功能。插件开发、依赖管理、Groovy 增量编译。

Gradle 6.0 是数年来创新改进的结晶。观看我们关于 Gradle 6.0 新功能的网络研讨会录像

Gradle 6.0 中的依赖管理秉承了这样一种理念:软件组成和重用不仅仅是将一组 Jar 文件放到类路径中。Gradle 现在提供了一种新的元数据格式,可以丰富地定义软件组件,这些组件通常由多个文件、不同的变体和对依赖项的特定约束组成。

Gradle 6.0 将通过更新 Java、Groovy 和 Scala 生态系统插件来帮助提高您的整体开发者生产力。插件作者使用的 API 持续发展,以便更容易创建可并行化、增量且易于使用的插件和任务。

com.gradle.build-scan 插件在 Gradle 6.0 中进行了重大更新。scans.gradle.com 的用户和Develocity 用户需要切换到 com.gradle.enterprise 插件。这不仅仅是相同的插件,只是 ID 和应用方式发生了变化。com.gradle.enterprise 插件可靠地捕获 buildSrc 构建事件和失败。

如果您有兴趣关注 Gradle 新功能的开发,我们会在有新功能预览时,例如Swift 支持或关于避免依赖地狱的建议时,在我们的博客上发布更新。我们还会发布过去网络研讨会的视频,内容涵盖顶级 Android 构建问题、依赖管理技术和发布策略。

以下是 Gradle 5.0 到 6.0 的主要改进

使用 Gradle 模块元数据进行依赖管理

现在,当使用 maven-publishivy-publish 插件时,发布 Gradle 模块元数据是默认行为。这使得下面描述的许多创新性依赖管理功能能够在项目边界之间工作。

使用平台在项目间共享依赖版本

Gradle 提供了一种简单的方法,可以在项目之间推荐和共享版本,这被称为*平台*。

通过 Gradle 平台,可以获得更多关于版本声明的上下文,可以推荐版本并强制执行严格的版本。为了实现互操作性,构建还可以利用与 Maven BOM 的集成

处理互斥依赖

Gradle 使用能力来允许插件和构建检测和解决互斥依赖之间的实现冲突。JVM 世界中一个众所周知的例子是相互竞争的日志实现。能力允许构建配置选择哪个依赖,而不是将相互竞争的依赖添加到类路径中。

升级传递性依赖的版本

依赖管理的问题通常与处理传递性依赖有关。通常,传递性依赖问题通过添加直接依赖来错误地解决。为了避免这种情况,Gradle 提供了依赖约束的概念来影响传递性依赖的版本

在多个依赖之间对齐版本

依赖版本对齐允许构建表达不同的模块属于同一个逻辑组(如平台),并且需要在依赖图中具有相同的(也称为*对齐*的)版本。JVM 世界中一个众所周知的例子是 Jackson 库。

用上下文表达意图

在声明依赖时,构建可以向 Gradle 提供更多关于其版本的信息,包括范围内的版本偏好、严格的版本要求或被拒绝的版本。开发者还可以提供人类可读的描述,说明为什么使用或需要某个依赖。

调整已发布的元数据

Gradle 允许构建使用以前无法发布的额外信息(例如依赖约束、富版本、能力和变体)来修复或丰富传统元数据。这些被称为*组件元数据规则*。组件元数据规则还使得将附加已发布构件映射到新的 Gradle 变体成为可能。

建模特性变体和可选依赖

Gradle 提供了建模库可选功能的能力。每个功能变体都可以有自己的一组依赖项,并且可以单独使用。

为 Java、Groovy 和 Scala 开发者提供更好的构建体验

通过升级到 Gradle 6.0,您可以为 Java、Groovy 或 Scala 构建带来改进。

更快的 Java 增量编译

在分析更改类影响时,增量编译器现在可以排除作为另一个类实现细节的类。这限制了需要重新编译的类数量。对于深度依赖链,这大大减少了需要重新编译的文件数量。

例如,更改一个源文件后编译一个包含 5000 个源文件的项目

Gradle 5.6 ( 21s)
Gradle 5.6 编译需要 21 秒。
Gradle 6.0 (0.5s)
Gradle 6.0 编译相同的更改仅需 0.5 秒。

更快的 Groovy 编译

Gradle 6.0 通过两种策略加快 Groovy 的编译速度。

如果依赖项目没有发生会影响其编译输出的更改,Gradle 会使用编译规避来跳过重新编译这些项目。

Gradle 还使用增量 Groovy 编译。如果只有一小部分 Groovy 源文件被更改,则只会重新编译受影响的源文件。例如,如果您只更改一个 Groovy 测试类,Gradle 不需要重新编译所有 Groovy 测试源文件。Gradle 将只重新编译更改的类以及受其影响的类。

使 Java 增量编译更快的相同分析改进也适用于 Groovy 编译。

此功能需要您对构建进行更改才能使用。有关更多详细信息,请参阅Groovy 编译规避Groovy 增量编译

最新的 Zinc 编译器集成

Gradle 6.0 使用最新的 Zinc 增量 Scala 编译器。Zinc 编译器在 Zinc 1.0 中性能得到了大幅提升。

默认情况下,Gradle 使用 Zinc 1.3.0。

支持多种 JDK

Gradle 6.0 现在支持 Java 13

Gradle 支持在 JDK8 到 JDK13 上运行。JDK6 和 JDK7 仍可用于编译和测试。

添加 Javadoc 和源代码 Jar

您现在可以为 Java 库或 Java 项目激活 Javadoc 和源发布

java {
    withJavadocJar()
    withSourcesJar()
}

使用 maven-publishivy-publish 插件,这不仅会自动创建和发布 -javadoc.jar 和 -sources.jar,还会发布它们作为 Gradle 模块元数据中变体存在的信息。这意味着您可以查询模块的 Javadoc 或源变体,并检索其依赖项的 Javadoc(或源)。

如果激活,Java 和 Java 库项目会自动提供 javadocJar 和 sourcesJar 任务。

在项目之间共享测试夹具

Gradle 6.0 允许一个项目的 Java 测试夹具与构建中的其他项目共享。Gradle 会自动执行必要的连接,以便测试编译依赖于测试夹具。java-test-fixtures 插件需要与 java-library 插件一起应用。它还可以与 Groovy 或 Scala 等其他 JVM 语言插件结合使用,以这些语言编写测试夹具。

例如,这会将“my-lib”的测试夹具添加到*此*项目中测试的编译类路径中。

dependencies {
   testImplementation(testFixtures(project(":my-lib")))
}

组织构建逻辑

Gradle 6.0 帮助您更好地控制构建。

通过自定义分发 gradle.properties 实现组织范围的 Gradle 属性

Gradle 现在会在构建使用的 Gradle 分发中查找 gradle.properties 文件。该文件的优先级最低,其他位置定义的属性会覆盖此处定义的值。

通过将 gradle.properties 文件放置在自定义 Gradle 分发中,组织可以为整个组织添加默认属性,或者通过 org.gradle.jvmargs 调整默认的 Gradle 守护进程内存参数。

从 settings.gradle 集中管理插件版本

Gradle 6.0 让管理构建使用的插件版本变得更容易。通过在新的 pluginManagement.plugins {} 块中配置 settings 脚本中的所有插件版本,构建脚本可以通过 plugins {} 块应用插件,而无需指定版本。

pluginManagement {
    plugins {
        id 'org.my.plugin' version '1.1'
    }
}

以这种方式管理插件版本的一个好处是,插件版本可以从 gradle.properties 中加载或以编程方式定义。

使用复合构建进行插件开发

构建脚本中的 plugins { } 块现在可以用来引用包含构建中定义的插件。在 Gradle 以前的版本中,这是可能的,但需要在 settings 文件中添加一些额外的样板代码。现在不再需要这些样板代码。

此更改使得为 Gradle 插件添加测试构建变得异常简单,并简化了实现 Gradle 插件的过程。您还可以使用此功能方便地同时处理插件更改和使用该插件的构建,以实现一个既发布又被同一源仓库中的项目使用的插件,或者将复杂构建分解为多个插件。

使用 plugins { } 块也使得 Gradle Kotlin DSL 的使用更加方便。

您可以在用户手册中了解更多关于复合构建的信息。

用于更好插件的 API

Gradle 6.0 使您更容易构建快速且行为正确的插件。

最小化任务所需完成的工作量

使用 Gradle,实现一个当其所有输入和输出都是最新时跳过的任务非常简单。这允许 Gradle 执行*增量构建*,其中只执行需要执行的任务。

对于某些任务,即使任务需要执行,也可以完成较少的工作。如果自上次执行以来只有少数输入文件发生更改,则任务只需要重新处理已更改的文件。Gradle 将这些类型的任务称为*增量任务*。要实现增量任务,请使用InputChanges API

使工作并行安全

Gradle 6.0 提供了新的 API,任务可以使用它来提交并行执行的工作单元。选择此 API 的任务为 Gradle 提供了更大的灵活性,可以并行启动其他任务。

使用此 API 的插件作者可以将任务的操作移动到单独的类加载器甚至单独的进程中。单独的进程在可能的情况下会被重用,如果它们消耗过多资源则会过期。单独的类加载器允许插件在控制任务操作使用的类路径方面具有很大的灵活性。

工作单元的参数是类型安全且隔离的,因此它们不能与工作单元的操作同时修改。参数可以是可选的 (null) 或由复杂类型组成(例如,文件集合)。有几种服务可以注入到操作中以执行常见功能,如复制和删除文件或 fork 其他进程。

验证任务配置

定义输入或输出不正确的任务在运行增量构建或使用构建缓存时可能会导致问题。作为持续努力揭示这些问题的一部分,Gradle 6.0 现在会在构建期间将这些问题报告为弃用警告

当检测到问题时,使用 --warning-mode=all 运行时 Gradle 会显示警告

> Task :myTask
Property 'inputDirectory' is declared without normalization specified. 
Properties of cacheable work must declare their normalization via @PathSensitive, @Classpath or @CompileClasspath. 
Defaulting to PathSensitivity.ABSOLUTE. This behaviour has been deprecated and is scheduled to be removed in Gradle 7.0.

Property 'outputFile' is not annotated with an input or output annotation. This behaviour has been deprecated and is scheduled to be removed in Gradle 7.0.

无论使用何种命令行参数,弃用警告都将始终显示在 Build Scan™ 中。

转换依赖项中的构件

依赖项的构件可以有多种形式——JAR、AAR、ZIP 和其他格式。您甚至可能需要一种您正在使用的库未发布的格式。

举例来说,一个 Java 模块发布了一个普通的 JAR 文件。作为消费者,您希望使用普通的 JAR 进行开发,但生产构建需要使用混淆过的 JAR。

由于混淆过的 JAR 在任何仓库中都不可用,您必须使用普通的 JAR 并自行混淆它。

Gradle 提供了一个 API,用于注册构件转换,该转换会挂接到依赖管理解析引擎中。构件转换可以指定,每当请求混淆过的 JAR 但找不到时,Gradle 应该运行一个转换来创建混淆过的 JAR,并使其显示为可用构件。

您可能错过的其他功能

自 Gradle 5.0 以来,发生了许多变化。以下是 Gradle 6.0 中您可以享受的其他一些功能。

使用 gradle init 引导新项目

Gradle 6.0 可以通过 gradle init 和一些参数来引导项目。

您可以选择以下项目之一

  • 用 Java、Groovy、Kotlin、C++ 或 Swift 编写的应用程序
  • 用 Java、Groovy、Scala、Kotlin、C++ 或 Swift 编写的库
  • 用 Java、Groovy 或 Kotlin 编写的 Gradle 插件
  • 一个基本的空项目

通过按组筛选快速找到要运行的任务

Gradle 6.0 允许您通过仅显示属于特定组的任务来快速查找任务。这使得在具有许多可用任务的构建中更容易找到任务。

使用 gradle tasks --group="Build Setup" 试用。

通过在弃用警告时失败来保持更新

warning-mode 命令行选项有一个 fail 选项,它将显示所有弃用警告,如果构建期间发现任何弃用警告,则会使构建失败。

增量 PMD 分析

Gradle 6.0 的 PMD 插件支持使用 PMD 的增量分析缓存,以在文件在构建之间未更改时提高性能。要启用增量分析,请将以下内容添加到您的 PMD 配置中

pmd {
    incrementalAnalysis = true
}

执行具有长类路径的 Java 应用程序

当 Gradle 检测到 Java 进程命令行将超出 Windows 32,768 字符限制时,Gradle 将尝试通过“类路径 Jar”传递 Java 应用程序的类路径来缩短命令行。

类路径 jar 包含一个清单,其中包含应用程序的完整类路径。Gradle 只会将生成的 jar 作为命令行参数传递给应用程序。如果命令行仍然太长,Java 进程将像以前一样无法启动。

如果命令行不需要缩短,Gradle 将不会更改 Java 进程的命令行参数。

构件签名

Gradle 6.0 有几项与签名相关的改进

  • 签名插件使用 SHA512 而不是 SHA1
  • 支持内存子密钥

如果您使用 maven-publishivy-publish 插件,Gradle 除了传统的但安全性较低的 MD5 和 SHA1 签名外,还会自动上传 SHA256 和 SHA512 签名。

已弃用的 maven 插件不支持 SHA256 和 SHA512 文件的发布,但适用于 Ivy 仓库的旧版 uploadArchives 任务。

如何升级

我们提供了一份文档,帮助您从 Gradle 5.x 升级到 Gradle 6.0。如果您使用的是早于 Gradle 5.0 的版本,您可能需要先查看Gradle 5.0 中的所有新功能

升级前,我们建议您

  • 使用 Gradle wrapper 升级到 Gradle 5.6.4。gradle wrapper --gradle-version=5.6.4
  • 运行 gradle help --scan 以列出所有使用已弃用 Gradle API 的位置。
  • 更新您的 Gradle 插件,尤其是那些在 Build Scan™ 的弃用报告中列出的插件。
  • 如果遇到问题,请参阅故障排除指南或在社区论坛上寻求帮助。

您可以通过 Twitter 上的 @gradle 与 Gradle 团队分享反馈。去构建快乐吧!