本章提供将您的 Gradle 8.x 构建迁移到最新 Gradle 版本所需的信息。要从 Gradle 4.x、5.x、6.x 或 7.x 迁移,请首先参阅较旧的迁移指南

我们建议所有用户执行以下步骤

  1. 尝试运行 gradle help --scan 并查看生成的构建扫描的弃用视图

    Deprecations View of a Gradle Build Scan

    这使您可以查看适用于您构建的任何弃用警告。

    或者,您可以运行 gradle help --warning-mode=all 以在控制台中查看弃用,但这可能不会报告那么多详细信息。

  2. 更新您的插件。

    某些插件会因 Gradle 的新版本而崩溃,因为它们使用了已删除或更改的内部 API。上一步将通过在插件尝试使用已弃用的 API 部分时发出弃用警告,帮助您识别潜在问题。

  3. 运行 gradle wrapper --gradle-version 8.13 以将项目更新到 8.13。

  4. 尝试运行项目并使用问题排查指南调试任何错误。

从 8.12 及更早版本升级

潜在的破坏性变更

JvmTestSuite 的变更

testType 属性已从JvmTestSuite 中删除,并且 TestSuiteTargetNameTestSuiteType 属性均已删除。现在可以通过在要聚合的目标项目中指定测试套件的名称,在项目之间聚合测试报告和 JaCoCo 报告。

请参阅下文了解更多详细信息。

测试报告聚合和 Jacoco 聚合的变更

对孵化中的测试报告聚合JaCoCo 报告聚合插件进行了一些更改。

插件现在为每个测试套件创建一个单一的测试结果变体,其中包含整个套件的所有测试结果,而不是每个测试目标一个变体。此更改允许聚合插件聚合具有多个目标的测试套件,而以前这会导致歧义的变体选择错误。

将来,随着我们继续开发这些插件,我们计划再次为每个测试套件目标创建一个结果变体,从而允许显式聚合来自某些目标的测试结果。

JacocoCoverageReportAggregateTestReport 上的 testType 属性已被删除,并替换为新的 testSuiteName 属性

之前

reporting {
    reports {
        val testCodeCoverageReport by creating(JacocoCoverageReport::class) {
            testType = TestSuiteType.UNIT_TEST
        }
    }
}

现在

reporting {
    reports {
        val testCodeCoverageReport by creating(JacocoCoverageReport::class) {
            testSuiteName = "test"
        }
    }
}

调用 BuildLauncher.addJvmArguments 时的行为已更改

修复了问题(#31426),该问题导致 BuildLauncher.addJvmArguments 覆盖来自 org.gradle.jvmargs 系统属性的标志。升级到 Gradle 8.13 时,请确保您没有依赖此行为。如果需要覆盖系统属性,则应改用 BuildLauncher.setJvmArguments

val buildLauncher: BuildLauncher = connector.connect().newBuild()
buildLauncher.setJvmArguments("-Xmx2048m", "-Dmy.custom.property=value")

升级到 ASM 9.7.1

ASM 从 9.6 升级到 9.7.1,以确保更早地兼容 Java 24。

Project.task 方法的源代码级别弃用

Project 接口上的 Eager 任务创建方法已标记为 @Deprecated,并且在构建脚本或插件代码中使用时将生成编译器和 IDE 警告。尚未使用 Gradle 弃用警告来指示其使用。

但是,如果构建配置为在 Kotlin 脚本或插件代码编译期间警告失败,则此更改可能会导致构建失败。

当这些方法在未来版本中完全弃用时,将在使用时打印标准的 Gradle 弃用警告。

弃用

在延迟提供器中递归查询 AttributeContainer

在 Gradle 9.0 中,从同一容器的属性值提供器内部查询 AttributeContainer 的内容将成为错误。

以下示例展示了禁止的行为

AttributeContainer container = getAttributeContainer();
Attribute<String> firstAttribute = Attribute.of("first", String.class);
Attribute<String> secondAttribute = Attribute.of("second", String.class);
container.attributeProvider(firstAttribute, project.getProviders().provider(() -> {
    // Querying the contents of the container within an attribute value provider
    // will become an error.
    container.getAttribute(secondAttribute);
    return "first";
}));

已弃用的 org.gradle.api.artifacts.transform.VariantTransformConfigurationException

此异常没有好的公共用例,并且不打算由用户抛出。在 Gradle 9.0 中,它将被 org.gradle.api.internal.artifacts.transform.VariantTransformConfigurationException 取代,仅供内部使用。

孵化中 UpdateDaemonJvm 中已弃用的属性

UpdateDaemonJvm 的以下属性现已弃用

  • jvmVersion

  • jvmVendor

它们分别被 languageVersionvendor 取代。这允许 Java 工具链规范的配置和 UpdateDaemonJvm 任务可互换。

请注意,由于 vendor 属性的类型更改,使用 jvmVendor 属性执行 updateDaemonJvm 将导致任务失败。有关新的配置选项,请参阅文档

使用 is 前缀和 Boolean 类型声明布尔属性

Gradle 属性名称是通过遵循 Java Bean 规范(有一个例外)派生的。Gradle 将具有 Boolean 返回类型和 is 前缀的方法识别为布尔属性。这是最初从 Groovy 继承的行为。Groovy 4 更密切地遵循 Java Bean 规范,并且 不再支持此例外

当 Gradle 检测到布尔属性是从具有 Boolean 返回类型和 is 前缀的方法派生时,它将发出弃用警告。在 Gradle 9.0 中,这些方法将不再被视为定义 Gradle 属性。这可能会导致任务被认为是最新的,即使 Boolean 属性似乎是输入。

有两种选项可以解决此问题

  1. 引入一个新的方法,该方法以 get 而不是 is 开头,并且具有相同的行为。旧方法不需要删除(为了保持二进制兼容性),但可能需要进行如下所示的调整。

    • 建议弃用 is- 方法,然后在未来的主要版本中将其删除。

  2. 将属性的类型(get 和 set)更改为 boolean这是一个破坏性更改。

对于使用第一个选项的任务输入属性,您还应该使用 @Deprecated@ReplacedBy 注释旧的 is- 方法,以确保 Gradle 不会使用它。例如,以下代码

class MyValue {
    private Boolean property = Boolean.TRUE;

    @Input
    Boolean isProperty() { return property; }
}

应替换为以下代码

class MyValue {
    private Boolean property = Boolean.TRUE;

    @Deprecated
    @ReplacedBy("getProperty")
    Boolean isProperty() { return property; }

    @Input
    Boolean getProperty() { return property; }
}

从 8.11 及更早版本升级

潜在的破坏性变更

升级到 Kotlin 2.0.21

嵌入式 Kotlin 已从 2.0.20 更新到 Kotlin 2.0.21

升级到 Ant 1.10.15

Ant 已更新到 Ant 1.10.15

升级到 Zinc 1.10.4

Zinc 已更新到 1.10.4

Swift SDK 发现

为了确定 Swift 的 Mac OS X SDK 的位置,Gradle 现在将 --sdk macosx 参数传递给 xcrun。这是必要的,因为如果没有此参数,SDK 在不同环境中可能会以不一致的方式被发现。

TaskContainer.create 方法的源代码级别弃用

TaskContainer 接口上的 Eager 任务创建方法已标记为 @Deprecated,并且在构建脚本或插件代码中使用时将生成编译器和 IDE 警告。尚未使用 Gradle 弃用警告来指示其使用。

但是,如果构建配置为在 Kotlin 脚本或插件代码编译期间警告失败,则此行为可能会导致构建失败。

当这些方法在未来版本中完全弃用时,将在使用时打印标准的 Gradle 弃用警告。

弃用

已弃用的歧义转换链

以前,当至少有两个等长的工件转换链可用,并且它们将生成可以满足解析请求的兼容变体时,Gradle 将任意且静默地选择一个。

现在,Gradle 发出弃用警告,解释了这种情况

There are multiple distinct artifact transformation chains of the same length that would satisfy this request. This behavior has been deprecated. This will fail with an error in Gradle 9.0.
Found multiple transformation chains that produce a variant of 'root project :' with requested attributes:
  - color 'red'
  - texture 'smooth'
Found the following transformation chains:
  - From configuration ':squareBlueSmoothElements':
      - With source attributes:
          - artifactType 'txt'
          - color 'blue'
          - shape 'square'
          - texture 'smooth'
      - Candidate transformation chains:
          - Transformation chain: 'ColorTransform':
              - 'BrokenColorTransform':
                  - Converts from attributes:
                      - color 'blue'
                      - texture 'smooth'
                  - To attributes:
                      - color 'red'
          - Transformation chain: 'ColorTransform2':
              - 'BrokenColorTransform2':
                  - Converts from attributes:
                      - color 'blue'
                      - texture 'smooth'
                  - To attributes:
                      - color 'red'
 Remove one or more registered transforms, or add additional attributes to them to ensure only a single valid transformation chain exists.

在这种情况下,Gradle 无法知道应该使用两个(或更多)可能的转换链中的哪一个。选择任意链可能会导致性能低下,或者在修改构建的看似无关的部分时导致意外的行为更改。这可能是一个非常复杂的情况,并且该消息现在通过打印所有已注册的转换(按顺序排列)以及每个候选链的源(输入)变体来完整地解释这种情况。

当遇到此类故障时,构建作者应执行以下操作之一

  1. 在注册链中存在的转换时添加其他可区分的属性,以确保仅选择单个链来满足请求

  2. 请求其他属性以消除歧义,确定选择哪个链(如果它们导致非相同的最终属性)

  3. 从构建中删除不必要的已注册转换

这将在 Gradle 9.0 中成为错误。

init 必须单独运行

init 任务必须单独运行。此任务不应与单个 Gradle 调用中的其他任务组合在一起。

在与其他任务相同的调用中运行 init 将在 Gradle 9.0 中成为错误。

例如,这被允许

> gradlew init tasks

从任务操作中调用 Task.getProject()

现在已弃用在执行时从任务操作中调用 Task.getProject(),并且将在 Gradle 10.0 中成为错误。此方法仍然可以在配置时使用。

仅当启用配置缓存时,才会发出弃用。当启用配置缓存时,对 Task.getProject() 的调用将报告为配置缓存问题。

此弃用最初在 Gradle 7.4 中引入,但仅在启用 STABLE_CONFIGURATION_CACHE 功能标志时才会发出。该功能标志不再控制此弃用。这是朝着使用户远离与配置缓存不兼容的习惯用法的又一步,配置缓存将在未来的版本中成为 Gradle 支持的唯一模式。

请参阅配置缓存文档,了解在执行时调用 Task.getProject() 的替代方案,这些方案与配置缓存兼容。

Groovy “空格赋值” 语法

当前,有多种使用 Groovy DSL 语法设置属性的方法

---
propertyName = value
setPropertyName(value)
setPropertyName value
propertyName(value)
propertyName value
---

后者“空格赋值”是 Gradle 特有的功能,不是 Groovy 语言的一部分。在常规 Groovy 中,这只是一个方法调用:propertyName(value),如果 Gradle 运行时中尚不存在 propertyName 方法,则 Gradle 会生成该方法。此功能可能会引起混淆(尤其是对于新用户),并为用户和 Gradle 代码库增加了一层额外的复杂性,而没有提供任何显着的价值。有时,类声明的方法名称相同,甚至可能具有与纯赋值不同的语义。

这些生成的方法现在已弃用,将在 Gradle 10.0 中删除,并且 propertyName valuepropertyName(value) 都将停止工作,除非定义了显式方法 propertyName。请改用显式赋值 propertyName = value

对于显式方法,请考虑使用 propertyName(value) 语法而不是 propertyName value 以提高清晰度。例如,jvmArgs "some", "arg" 可以替换为 jvmArgs("some", "arg")jvmArgs = ["some", "arg"] 用于 Test 任务。

如果您有一个大型项目,要替换空格赋值语法的出现,可以使用例如以下 sed 命令

---
find . -name 'build.gradle' -type f -exec sed -i.bak -E 's/([^A-Za-z]|^)(replaceme)[ \t]*([^= \t{])/\1\2 = \3/g' {} +
---

您应该将 replaceme 替换为一个或多个要替换的属性名称,以 | 分隔,例如 (url|group)

DependencyInsightReportTask.getDependencySpec

该方法已弃用,因为它不打算在构建脚本中公开使用。

ReportingExtension.baseDir

ReportingExtension.getBaseDir()`ReportingExtension.setBaseDir(File)ReportingExtension.setBaseDir(Object) 已弃用。它们应替换为 ReportingExtension.getBaseDirectory() 属性。

从 8.10 及更早版本升级

潜在的破坏性变更

升级到 Kotlin 2.0.20

嵌入式 Kotlin 已从 1.9.24 更新到 Kotlin 2.0.20。另请参阅 Kotlin 2.0.10Kotlin 2.0.0 发行说明。

JVM 测试套件中的默认 kotlin-test 版本也已升级到 2.0.20。

Kotlin DSL 脚本仍然使用 Kotlin 语言版本设置为 1.8 进行编译,以实现向后兼容性。

通过工具链配置 Gradle 守护进程 JVM

属性 UpdateDaemonJvm.jvmVersion 的类型现在是 Property<JavaLanguageVersion>

如果您在构建脚本中配置了任务,则需要替换

jvmVersion = JavaVersion.VERSION_17

替换为

jvmVersion = JavaLanguageVersion.of(17)

使用 CLI 选项配置 Gradle 守护进程要使用的 JVM 版本没有影响。

名称匹配更改

名称匹配逻辑已更新,将数字视为驼峰式名称的单词边界。以前,像 unique 这样的请求会同时匹配 uniqueAunique1。此类请求现在将因歧义而失败。为避免问题,请使用确切的名称而不是缩短的版本。

此更改影响

  • 任务选择

  • 项目选择

  • 依赖报告任务中的配置选择

弃用

ForkOptions 的已弃用 JavaHome 属性

ForkOptions 类型的 JavaHome 属性已被弃用,将在 Gradle 9.0 中删除。

请改用 JVM 工具链,或 executable 属性。

已弃用更改 buildscript 配置

从 Gradle 9.0 开始,在脚本的 buildscript 块中更改配置将导致错误。这适用于项目、设置、初始化和独立脚本。

buildscript 配置块仅用于控制 buildscript 类路径解析。

考虑以下脚本,该脚本在 Settings 脚本中创建新的 buildscript 配置并解析它

buildscript {
    configurations {
        create("myConfig")
    }
    dependencies {
        "myConfig"("org:foo:1.0")
    }
}

val files = buildscript.configurations["myConfig"].files

此模式有时用于解析 Settings 中的依赖项,因为没有其他方法可以获取 Configuration。不建议在此上下文中解析依赖项。使用分离的配置是一种可能的但并不推荐的替代方案。

上面的示例可以修改为使用分离的配置

val myConfig = buildscript.configurations.detachedConfiguration(
    buildscript.dependencies.create("org:foo:1.0")
)

val files = myConfig.files

按配置名称选择 Maven 变体

从 Gradle 9.0 开始,将禁止从非 Ivy 外部组件按名称选择变体。

仍然允许从本地组件按名称选择变体;但是,不鼓励使用此模式。对于本地组件,应优先使用变体感知依赖项解析,而不是按名称选择变体。

当以非 Ivy 外部组件为目标时,以下依赖项将无法解析

dependencies {
    implementation(group: "com.example", name: "example", version: "1.0", configuration: "conf")
    implementation("com.example:example:1.0") {
        targetConfiguration = "conf"
    }
}

已弃用手动添加到配置容器

从 Gradle 9.0 开始,手动将配置实例添加到配置容器将导致错误。配置应仅通过 eager 或 lazy 工厂方法添加到容器。分离的配置和复制的配置不应添加到容器。

将禁止在 ConfigurationContainer 上调用以下方法:- add(Configuration) - addAll(Collection) - addLater(Provider) - addAllLater(Provider)

已弃用 ProjectDependency#getDependencyProject()

ProjectDependency#getDependencyProject() 方法已弃用,将在 Gradle 9.0 中删除。

应避免访问其他项目的可变项目实例。

要发现有关解析中包含的所有项目的详细信息,请检查完整的 ResolutionResult。项目依赖项在 DependencyResult 中公开。有关此 API 的更多详细信息,请参阅用户指南中关于程序化依赖项解析的部分。这是查找解析中使用的所有项目的唯一可靠方法。仅检查声明的 `ProjectDependency` 可能会遗漏传递或替换的项目依赖项。

要获取目标项目的标识,请使用新的隔离项目安全项目路径方法:ProjectDependency#getPath()

要访问或配置目标项目,请考虑以下直接替换

val projectDependency: ProjectDependency = getSomeProjectDependency()

// Old way:
val someProject = projectDependency.dependencyProject

// New way:
val someProject = project.project(projectDependency.path)

此方法不会从不同的构建中获取项目实例。

已弃用 ResolvedConfiguration.getFiles()LenientConfiguration.getFiles()

ResolvedConfiguration.getFiles()LenientConfiguration.getFiles() 方法已弃用,将在 Gradle 9.0 中删除。

这些已弃用的方法不跟踪任务依赖项,这与它们的替代方法不同。

val deprecated: Set<File> = conf.resolvedConfiguration.files
val replacement: FileCollection = conf.incoming.files

val lenientDeprecated: Set<File> = conf.resolvedConfiguration.lenientConfiguration.files
val lenientReplacement: FileCollection = conf.incoming.artifactView {
    isLenient = true
}.files

已弃用 AbstractOptions

AbstractOptions 类已弃用,将在 Gradle 9.0 中删除。所有扩展 AbstractOptions 的类将不再扩展它。

因此,AbstractOptions#define(Map) 方法将不再存在。此方法公开了一个非类型安全 API,并且不必要地依赖于反射。它可以替换为直接设置 map 中指定的属性。

此外,依赖于 defineCompileOptions#fork(Map)CompileOptions#debug(Map)GroovyCompileOptions#fork(Map) 也已弃用,将在 Gradle 9.0 中删除。

考虑以下已弃用行为的示例及其替换

tasks.withType(JavaCompile) {
    // Deprecated behavior
    options.define(encoding: 'UTF-8')
    options.fork(memoryMaximumSize: '1G')
    options.debug(debugLevel: 'lines')

    // Can be replaced by
    options.encoding = 'UTF-8'

    options.fork = true
    options.forkOptions.memoryMaximumSize = '1G'

    options.debug = true
    options.debugOptions.debugLevel = 'lines'
}

已弃用 Dependency#contentEquals(Dependency)

Dependency#contentEquals(Dependency) 方法已弃用,将在 Gradle 9.0 中删除。

该方法最初旨在根据依赖项的实际目标组件进行比较,而不管它们是否属于不同的依赖项类型。现有方法的行为与其 Javadoc 指定的行为不符,我们不计划引入替代方法。

可能的迁移包括直接使用 Object.equals(Object),或手动比较依赖项的字段。

已弃用 Project#execProject#javaexec

Project#exec(Closure)Project#exec(Action)Project#javaexec(Closure)Project#javaexec(Action) 方法已弃用,将在 Gradle 9.0 中删除。

计划删除这些方法,作为使编写与配置缓存兼容的代码更容易的持续工作的一部分。在不违反配置缓存要求的情况下,无法使用这些方法,因此建议迁移到兼容的替代方案。您的用例的适当替代方案取决于先前调用该方法的上下文。

在执行时,例如在 @TaskActiondoFirst/doLast 回调中,启用配置缓存时不允许使用 Project 实例。要运行外部进程,任务应使用注入的 ExecOperation 服务,该服务具有相同的 API,并且可以充当直接替换。标准 Java/Groovy/Kotlin 进程 API,如 java.lang.ProcessBuilder 也可以使用。

在配置时,仅当启用配置缓存时,才必须使用特殊的基于 Provider 的 API 来运行外部进程。您可以使用 ProviderFactory.execProviderFactory.javaexec 来获取进程的输出。自定义 ValueSource 实现可用于更复杂的场景。配置缓存指南提供了一个更详细的使用这些 API 的示例。

分离的配置不应使用 extendsFrom

分离的配置不应使用 extendsFrom 扩展其他配置。

此行为已被弃用,将在 Gradle 9.0 中成为错误。

要创建配置之间的扩展关系,您应该改为使用通过项目 ConfigurationContainer 中提供的其他工厂方法创建的非分离配置。

已弃用自定义 Gradle 日志记录

Gradle#useLogger(Object) 方法已弃用,将在 Gradle 9.0 中移除。

此方法最初旨在自定义 Gradle 打印的日志。但是,它只允许拦截一部分日志,并且无法与配置缓存一起使用。我们不计划为此功能引入替代方案。

编译选项和文档任务上不必要的选项已被弃用

Gradle 的 API 允许使用 setter 方法整体替换某些表示嵌套属性组的属性。这样做既笨拙又不常见,有时还需要使用内部 API。这些属性的 setter 将在 Gradle 9.0 中移除,以简化 API 并确保行为一致。应该通过调用 getter 并直接配置对象,或使用便捷的配置方法来配置这些属性,而不是使用 setter 方法。例如,在 CompileOptions 中,您可以调用 getForkOptions()forkOptions(Action),而不是调用 setForkOptions setter。

受影响的属性包括

已弃用 Javadoc.isVerbose()Javadoc.setVerbose(boolean)

Javadoc 上的这些方法已弃用,将在 Gradle 9.0 中移除。

从 8.9 及更早版本升级

潜在的破坏性变更

即使不需要编译,使用 JRE 时 JavaCompile 任务也可能失败

当使用 JRE 而不是 JDK 时,JavaCompile 任务有时可能会失败。这是由于工具链解析代码中的更改,该代码在请求编译器时强制要求编译器的存在。当未设置 sourceCompatibility/targetCompatibilityrelease 时,java-base 插件使用它创建的 JavaCompile 任务来确定默认的源和目标兼容性。由于新的强制执行,即使不需要编译(例如,在没有源文件的项目中),仅提供 JRE 时,缺少编译器也会导致此操作失败。

可以通过在 java 扩展中显式设置 sourceCompatibility/targetCompatibility,或者在相关的任务中设置 sourceCompatibility/targetCompatibilityrelease 来解决此问题。

升级到 Kotlin 1.9.24

嵌入式 Kotlin 已从 1.9.23 更新到 Kotlin 1.9.24

升级到 Ant 1.10.14

Ant 已更新到 Ant 1.10.14

升级到 JaCoCo 0.8.12

JaCoCo 已更新到 0.8.12

升级到 Groovy 3.0.22

Groovy 已更新到 Groovy 3.0.22

弃用

在较旧的 JVM 上运行 Gradle

从 Gradle 9.0 开始,Gradle 将需要 JVM 17 或更高版本才能运行。大多数 Gradle API 将被编译为目标 JVM 17 字节码。

Gradle 仍将支持将 Java 代码编译为目标 JVM 版本 6 或更高版本。编译代码的目标 JVM 版本可以与用于运行 Gradle 的 JVM 版本分开配置。

所有 Gradle 客户端(wrapper、launcher、Tooling API 和 TestKit)将继续与 JVM 8 兼容,并将被编译为目标 JVM 8 字节码。只有 Gradle 守护程序将需要 JVM 17 或更高版本。这些客户端可以配置为使用与运行客户端的 JVM 版本不同的 JVM 版本来运行 Gradle 构建

或者,可以将 JAVA_HOME 环境变量设置为 JVM 17 或更高版本,这将使用相同版本的 JVM 运行客户端和守护程序。

使用 --no-daemon 或在测试中使用 ProjectBuilder 运行 Gradle 构建将需要 JVM 版本 17 或更高版本。worker API 将继续与 JVM 8 兼容,而运行 JVM 测试将需要 JVM 8。

我们决定升级 Java 运行时的最低版本,原因如下

  • 依赖项开始放弃对旧版本的支持,并且可能不会发布安全补丁。

  • 如果不升级,则无法使用 Java 8 和 Java 17 之间显着的语言改进。

  • 一些最流行的插件已经需要 JVM 17 或更高版本。

  • Gradle 发行版的下载指标表明 JVM 17 已被广泛使用。

已弃用从 Ivy 使用不可消费的配置

在 Gradle 的早期版本中,可以使用已发布的 Ivy 元数据来使用项目的不可消费配置。Ivy 依赖项有时可以替换为项目依赖项,可以通过 DependencySubstitutions API 显式替换,也可以通过包含的构建隐式替换。发生这种情况时,可以选择替换项目中标记为不可消费的配置。

以这种方式使用不可消费的配置已被弃用,并且会在 Gradle 9.0 中导致错误。

已弃用在同一项目中扩展配置

在 Gradle 的早期版本中,可以扩展不同项目中的配置。

项目的配置层次结构不应受其他项目中配置的影响。当以配置所有者不希望的方式扩展配置时,跨项目层次结构可能会导致意外行为。

项目也不应访问另一个项目的可变状态。由于配置是可变的,因此跨项目边界扩展配置会限制 Gradle 可以应用的并行性。

在不同项目中扩展配置已被弃用,并且会在 Gradle 9.0 中导致错误。

从 8.8 及更早版本升级

潜在的破坏性变更

工具链配置的变更

在 Gradle 的早期版本中,工具链配置可能会将部分配置的工具链留在原位,并带有一个标记文件,指示工具链已完全配置。这可能会导致工具链出现奇怪的行为。在 Gradle 8.9 中,工具链在写入标记文件之前已完全配置。但是,为了不检测潜在的损坏工具链,使用了不同的标记文件 (.ready)。这意味着您现有的所有工具链将在您首次在 Gradle 8.9 中使用它们时重新配置。Gradle 8.9 还会写入旧的标记文件 (provisioned.ok),以指示工具链已完全配置。这意味着,如果您返回到旧版本的 Gradle,则 8.9 配置的工具链将不会重新配置。

升级到 Kotlin 1.9.23

嵌入式 Kotlin 已从 1.9.22 更新到 Kotlin 1.9.23

更改守护程序日志文件的编码

在 Gradle 的早期版本中,守护程序日志文件(位于 $GRADLE_USER_HOME/daemon/8.13/)使用默认的 JVM 编码进行编码。此文件现在始终使用 UTF-8 编码,以防止可能使用不同默认编码的客户端错误地读取数据。此更改可能会影响尝试读取此文件的第三方工具。

针对 Gradle 实现类路径进行编译

在 Gradle 的早期版本中,没有声明依赖项的 Java 项目可以隐式地针对 Gradle 的运行时类进行编译。这意味着,即使某些项目引用了 Gradle 运行时类,它们也能够在没有任何声明依赖项的情况下进行编译。这种情况不太可能在项目中发生,因为 IDE 集成和测试执行将受到影响。但是,如果您需要使用 Gradle API,请声明 gradleApi 依赖项或应用 java-gradle-plugin 插件。

配置缓存实现包现在位于 org.gradle.internal

应避免引用不属于公共 API 的 Gradle 类型,因为不支持直接使用它们。Gradle 内部实现类可能会发生破坏性变更(或被重命名或移除),恕不另行通知。

用户需要区分 Gradle 代码库的 API 部分和内部部分。这通常通过在实现包名称中包含 internal 来实现。但是,在此版本之前,配置缓存子系统未遵循此模式。

为了解决此问题,最初位于 org.gradle.configurationcache* 包下的所有代码都已移至新的内部包 (org.gradle.internal.*)。

macOS 11 (Big Sur) 及更早版本上的文件系统监视已禁用

自 Gradle 8.8 以来,文件系统监视仅在 macOS 12 (Monterey) 及更高版本上受支持。我们添加了一项检查,以在 macOS 11 (Big Sur) 及更早版本上自动禁用文件系统监视。

使用注解处理器时,基于 JDK8 的编译器输出可能发生更改

Java 编译基础结构已更新为使用 Problems API。此更改将为 Tooling API 客户端提供关于编译问题的结构化、丰富的信息。

此功能不应对通常的构建输出产生任何可见的影响,JDK8 除外。当编译器中使用注解处理器时,输出消息与之前的消息略有不同。

此更改主要体现在打印的类型名称中。例如,Java 标准类型(如 java.lang.String)将报告为 java.lang.String 而不是 String

从 8.7 及更早版本升级

弃用

弃用在观察后修改配置

为了确保依赖项解析的准确性,Gradle 会检查在用作依赖项图的一部分后是否修改了配置。

  • 可解析的配置在解析后不应修改其解析策略、依赖项、层次结构等。

  • 可消费的配置在发布或作为变体消费后不应修改其依赖项、层次结构、属性等。

  • 依赖项范围配置在观察到从中扩展的配置后,不应修改其依赖项、约束等。

在 Gradle 的早期版本中,许多这些情况被检测到并通过使构建失败来处理。但是,有些情况未被检测到或未触发构建失败。在 Gradle 9.0 中,一旦观察到对配置的所有更改,都将变为错误。观察到任何类型的配置后,都应将其视为不可变的。此验证涵盖配置的以下属性

  • 解析策略

  • 依赖项

  • 约束

  • 排除规则

  • 工件

  • 角色(可消费、可解析、依赖项范围)

  • 层次结构 (extendsFrom)

  • 其他(传递性、可见性)

从 Gradle 8.8 开始,在尚未出错的情况下,将发出弃用警告。通常,此弃用是由在 beforeResolve 挂钩中修改配置引起的。此挂钩仅在配置完全解析后执行,而不是在部分解析配置以计算任务依赖项时执行。

请考虑以下代码,该代码展示了已弃用的行为

build.gradle.kts
plugins {
    id("java-library")
}

configurations.runtimeClasspath {
    // `beforeResolve` is not called before the configuration is partially resolved for
    // build dependencies, but only before a full graph resolution.
    // Configurations should not be mutated in this hook
    incoming.beforeResolve {
        // Add a dependency on `com:foo` if not already present
        if (allDependencies.none { it.group == "com" && it.name == "foo" }) {
            configurations.implementation.get().dependencies.add(project.dependencies.create("com:foo:1.0"))
        }
    }
}

tasks.register("resolve") {
    val conf: FileCollection = configurations["runtimeClasspath"]

    // Wire build dependencies
    dependsOn(conf)

    // Resolve dependencies
    doLast {
        assert(conf.files.map { it.name } == listOf("foo-1.0.jar"))
    }
}

对于以下用例,请考虑在替换 beforeResolve 挂钩时使用以下替代方案

  • 添加依赖项:在 DependencySet 上使用 DependencyFactoryaddLateraddAllLater

  • 更改依赖项版本:使用首选版本约束

  • 添加排除项:使用 组件元数据规则来调整依赖项级别的排除项,或使用 withDependencies 向配置添加排除项。

  • 角色:配置角色应在创建时设置,之后不得更改。

  • 层次结构:配置层次结构 (extendsFrom) 应在创建时设置。强烈建议不要在解析之前修改层次结构,但在 withDependencies 挂钩中允许修改。

  • 解析策略:仍然允许在 beforeResolve 挂钩中修改配置的 ResolutionStrategy;但是,不建议这样做。

已弃用过滤配置 filefileCollection 方法

为了持续简化 Gradle API,以下支持基于声明的依赖项进行过滤的方法已被弃用

Configuration

  • files(Dependency…​)

  • files(Spec)

  • files(Closure)

  • fileCollection(Dependency…​)

  • fileCollection(Spec)

  • fileCollection(Closure)

  • getFiles(Spec)

  • getFirstLevelModuleDependencies(Spec)

  • getFirstLevelModuleDependencies(Spec)

  • getFiles(Spec)

  • getArtifacts(Spec)

为了缓解此弃用,请考虑以下示例,该示例利用 ArtifactView API 以及 componentFilter 方法来选择配置工件的子集

build.gradle.kts
val conf by configurations.creating

dependencies {
    conf("com.thing:foo:1.0")
    conf("org.example:bar:1.0")
}

tasks.register("filterDependencies") {
    val files: FileCollection = conf.incoming.artifactView {
        componentFilter {
            when(it) {
                is ModuleComponentIdentifier ->
                    it.group == "com.thing" && it.module == "foo"
                else -> false
            }
        }
    }.files

    doLast {
        assert(files.map { it.name } == listOf("foo-1.0.jar"))
    }
}
build.gradle
configurations {
    conf
}

dependencies {
    conf "com.thing:foo:1.0"
    conf "org.example:bar:1.0"
}

tasks.register("filterDependencies") {
    FileCollection files = configurations.conf.incoming.artifactView {
        componentFilter {
            it instanceof ModuleComponentIdentifier
                && it.group == "com.thing"
                && it.module == "foo"
        }
    }.files

    doLast {
        assert files*.name == ["foo-1.0.jar"]
    }
}

与已弃用的 Dependency 过滤方法相反,componentFilter 不考虑被过滤组件的传递依赖项。这允许对选择哪些工件进行更精细的控制。

已弃用 TaskConfigurationNamer

TaskConfiguration 具有一个 Namer 内部类(也称为 Namer),可以用作检索任务或配置名称的通用方法。现在这些类型实现了 Named,这些类不再是必需的,并且已被弃用。它们将在 Gradle 9.0 中移除。请改用 Named.Namer.INSTANCE

超级接口 Namer 被弃用。

已弃用直接在本地构建缓存上设置保留期

在之前的版本中,本地构建缓存条目的清理每 24 小时运行一次,并且无法配置此间隔。保留期是使用 buildCache.local.removeUnusedEntriesAfterDays 配置的。

在 Gradle 8.0 中,添加了一种新机制来配置 Gradle 用户主目录中各种资源的清理和保留期。在 Gradle 8.8 中,此机制已扩展为允许本地构建缓存条目的保留配置,从而提供改进的控制和一致性。

  • 指定 Cleanup.DISABLEDCleanup.ALWAYS 现在将阻止或强制清理本地构建缓存

  • 构建缓存条目保留现在通过 init-script 进行配置,方式与其他缓存相同

如果您希望将构建缓存条目保留 30 天,请移除对已弃用方法的所有调用

buildCache {
    local {
        // Remove this line
        removeUnusedEntriesAfterDays = 30
    }
}

~/.gradle/init.d 中添加如下文件

beforeSettings {
    caches {
        buildCache.setRemoveUnusedEntriesAfterDays(30)
    }
}

调用 buildCache.local.removeUnusedEntriesAfterDays 已弃用,此方法将在 Gradle 9.0 中移除。如果设置为非默认值,则此已弃用的设置将优先于 Settings.caches.buildCache.setRemoveUnusedEntriesAfterDays()

已弃用 Kotlin DSL gradle-enterprise 插件块扩展

settings.gradle.kts (Kotlin DSL) 中,您可以使用插件块中的 gradle-enterprise 来应用 Gradle Enterprise 插件,其版本与 gradle --scan 相同。

plugins {
    `gradle-enterprise`
}

settings.gradle (Groovy DSL) 中没有等效项。

Gradle Enterprise 已重命名为 Develocity,com.gradle.enterprise 插件已重命名为 com.gradle.develocity。因此,gradle-enterprise 插件块扩展已被弃用,将在 Gradle 9.0 中移除。

Develocity 插件必须使用显式的插件 ID 和版本应用。插件块中没有可用的 develocity 简写

plugins {
    id("com.gradle.develocity") version "3.17.3"
}

如果您想继续使用 Gradle Enterprise 插件,可以指定已弃用的插件 ID

plugins {
    id("com.gradle.enterprise") version "3.17.3"
}

我们建议您使用最新发布的 Develocity 插件版本,即使在使用旧版本的 Gradle 时也是如此。

潜在的破坏性变更

Problems API 中的变更

我们已经实现了 Problems API 的多项重构,包括问题定义和上下文信息的处理方式的重大变更。完整的设计规范可以在此处找到。

在实现此规范时,我们对 ProblemSpec 接口引入了以下破坏性变更

  • label(String)description(String) 方法已被 id(String, String) 方法及其重载变体替换。

集合属性的变更

已移除 8.7 中引入的以下孵化 API

  • MapProperty.insert*(…​)

  • HasMultipleValues.append*(…​)

正在考虑使用更好地处理约定的替代方案,以用于未来的 8.x 版本。

升级到 Groovy 3.0.21

Groovy 已更新到 Groovy 3.0.21

由于之前的版本是 3.0.17,因此也包括 3.0.183.0.19 以及 3.0.20 中的变更。

静态类型检查中的一些更改导致了源代码不兼容。从 3.0.18 开始,如果您将闭包强制转换为没有泛型的 Action,则闭包参数将是 Object,而不是任何指定的显式类型。可以通过向强制转换添加适当的类型来解决此问题,并且可以移除冗余的参数声明

// Before
tasks.create("foo", { Task it -> it.description = "Foo task" } as Action)

// Fixed
tasks.create("foo", { it.description = "Foo task" } as Action<Task>)

升级到 ASM 9.7

ASM 从 9.6 升级到 9.7,以确保更早地兼容 Java 23。

从 8.6 及更早版本升级

潜在的破坏性变更

升级到 Kotlin 1.9.22

嵌入式 Kotlin 已从 1.9.10 更新到 Kotlin 1.9.22

升级到 Apache SSHD 2.10.0

Apache SSHD 已从 2.0.0 更新到 2.10.0

替换和升级 JSch

JSch 已被 com.github.mwiede:jsch 替换,并从 0.1.55 更新到 0.2.16

升级到 Eclipse JGit 5.13.3

Eclipse JGit 已从 5.7.0 更新到 5.13.3

这包括通过从 JSch 迁移到 Apache SSHD,重新设计 Gradle 为 SSH 操作配置 JGit 的方式。

升级到 Apache Commons Compress 1.25.0

Apache Commons Compress 已从 1.21 更新到 1.25.0。此更改可能会影响生成的 jar、zip 和其他归档类型的校验和,因为生成的工件的元数据可能有所不同。

升级到 ASM 9.6

ASM 从 9.5 升级到 9.6,以更好地支持多版本 jar。

版本目录解析器的升级

版本目录解析器已升级,现在符合 TOML 规范的 1.0.0 版本

这不应影响使用推荐语法或由 Gradle 生成以进行发布的目录。

弃用

已弃用插件约定的注册

自 Gradle 8.2 以来,使用插件约定一直在发出警告。现在,注册插件约定也会触发弃用警告。有关更多信息,请参阅关于插件约定弃用的部分

在 Kotlin DSL 中通过 "name"() 引用任务和域对象

在 Kotlin DSL 中,可以使用 "name"() 表示法通过名称引用任务或其他域对象。

有几种方法可以通过名称查找容器中的元素

tasks {
    "wrapper"() // 1 - returns TaskProvider<Task>
    "wrapper"(Wrapper::class) // 2 - returns TaskProvider<Wrapper>
    "wrapper"(Wrapper::class) { // 3 - configures a task named wrapper of type Wrapper
    }
    "wrapper" { // 4 - configures a task named wrapper of type Task
    }
}

第一个表示法已被弃用,将在 Gradle 9.0 中移除。请使用 named("name") 或其他受支持的表示法之一,而不是使用 "name"() 引用任务或域对象。

上面的示例将写为

tasks {
    named("wrapper") // returns TaskProvider<Task>
}

Gradle API 和 Groovy 构建脚本不受此影响。

已弃用无效的 URL 解码行为

在 Gradle 8.3 之前,Gradle 会使用一种算法解码提供给 Project.uri(Object)CharSequence,该算法接受无效的 URL 并错误地解码其他 URL。Gradle 现在使用 URI 类来解析和解码 URL,但在发生错误时会回退到旧的行为。

从 Gradle 9.0 开始,回退将被移除,并且会改为抛出错误。

要修复弃用警告,需要旧行为的无效 URL 应重新编码为有效的 URL,如下例所示

表 1. 旧 URL 转换
原始输入 新输入 理由

file:relative/path

relative/path

file 方案不支持相对路径。

file:relative/path%21

relative/path!

如果没有方案,则路径按原样获取,无需解码。

https://example.com/my folder/

https://example.com/my%20folder/

空格在 URL 中无效。

https://example.com/my%%badly%encoded%path

https://example.com/my%25%25badly%25encoded%25path

% 必须在 URL 中编码为 %25,并且不应有无效的 % 转义符。

file::somepath

somepath

URI 应为分层结构。

已弃用 SelfResolvingDependency

SelfResolvingDependency 接口已被弃用,将在 Gradle 9.0 中移除。此类型可以追溯到 Gradle 的第一个版本,在这些版本中,某些依赖项可以独立解析。现在,所有依赖项都应使用 Configuration 作为依赖项图的一部分进行解析。

目前,ProjectDependencyFileCollectionDependency 实现了此接口。在 Gradle 9.0 中,这些类型将不再实现 SelfResolvingDependency。相反,它们都将直接实现 Dependency

因此,ProjectDependencyFileCollectionDependency 的以下方法将不再可用

  • resolve

  • resolve(boolean)

  • getBuildDependencies

请考虑以下脚本,这些脚本展示了已弃用的接口及其替代方案

build.gradle.kts
plugins {
    id("java-library")
}

dependencies {
    implementation(files("bar.txt"))
    implementation(project(":foo"))
}

tasks.register("resolveDeprecated") {
    // Wire build dependencies (calls getBuildDependencies)
    dependsOn(configurations["implementation"].dependencies.toSet())

    // Resolve dependencies
    doLast {
        configurations["implementation"].dependencies.withType<FileCollectionDependency>() {
            assert(resolve().map { it.name } == listOf("bar.txt"))
            assert(resolve(true).map { it.name } == listOf("bar.txt"))
        }
        configurations["implementation"].dependencies.withType<ProjectDependency>() {
            // These methods do not even work properly.
            assert(resolve().map { it.name } == listOf<String>())
            assert(resolve(true).map { it.name } == listOf<String>())
        }
    }
}

tasks.register("resolveReplacement") {
    val conf = configurations["runtimeClasspath"]

    // Wire build dependencies
    dependsOn(conf)

    // Resolve dependencies
    val files = conf.files
    doLast {
        assert(files.map { it.name } == listOf("bar.txt", "foo.jar"))
    }
}

org.gradle.util 包的已弃用成员现在报告其弃用

这些成员将在 Gradle 9.0 中移除。

  • Collection.stringize(Collection)

从 8.5 及更早版本升级

潜在的破坏性变更

升级到 JaCoCo 0.8.11

JaCoCo 已更新到 0.8.11

DependencyAdder 重命名为 DependencyCollector

孵化中的 DependencyAdder 接口已重命名为 DependencyCollector。接口中添加了一个 getDependencies 方法,该方法返回所有声明的依赖项。

弃用

已弃用使用 main 源集调用 registerFeature

java 扩展上使用 main 源集调用 registerFeature 已弃用,并且将在 Gradle 9.0 中更改行为。

目前,在使用 main 源集调用 usingSourceSet 时创建的功能与在使用任何其他源集调用 usingSourceSet 时创建的功能的初始化方式不同。以前,当使用 main 源集时,会创建新的 implementationcompileOnlyruntimeOnlyapicompileOnlyApi 配置,并且 main 源集的编译和运行时类路径被配置为扩展这些配置。

从 Gradle 9.0 开始,main 源集将像任何其他源集一样对待。应用 java-library 插件(或任何其他应用 java 插件的插件)后,使用 main 源集调用 usingSourceSet 将抛出异常。这是因为 java 插件已经配置了 main 功能。仅当未应用 java 插件时,才允许在调用 usingSourceSet 时使用 main 源集。

代码目前向主源码集注册功能,例如

build.gradle.kts
plugins {
    id("java-library")
}

java {
    registerFeature("feature") {
        usingSourceSet(sourceSets["main"])
    }
}
build.gradle
plugins {
    id("java-library")
}

java {
    registerFeature("feature") {
        usingSourceSet(sourceSets.main)
    }
}

应该改为为该功能创建一个单独的源码集,并将该功能注册到该源码集

build.gradle.kts
plugins {
    id("java-library")
}

sourceSets {
    create("feature")
}

java {
    registerFeature("feature") {
        usingSourceSet(sourceSets["feature"])
    }
}
build.gradle
plugins {
    id("java-library")
}

sourceSets {
    feature
}

java {
    registerFeature("feature") {
        usingSourceSet(sourceSets.feature)
    }
}

已弃用:发布具有与 artifactId 不同的显式名称的构件依赖项到 Maven 仓库

发布具有与依赖项的 artifactId 不同的显式构件名称的依赖项到 Maven 仓库已被弃用。当发布到 Ivy 仓库时,此行为仍然允许。在 Gradle 9.0 中,这将导致错误。

当发布到 Maven 仓库时,Gradle 将把下面的依赖项解释为使用坐标 org:notfoo:1.0 声明的依赖项

build.gradle.kts
dependencies {
    implementation("org:foo:1.0") {
        artifact {
            name = "notfoo"
        }
    }
}
build.gradle
dependencies {
    implementation("org:foo:1.0") {
        artifact {
            name = "notfoo"
        }
    }
}

相反,此依赖项应声明为

build.gradle.kts
dependencies {
    implementation("org:notfoo:1.0")
}
build.gradle
dependencies {
    implementation("org:notfoo:1.0")
}

已弃用 ArtifactIdentifier

ArtifactIdentifier 类已被弃用,将在 Gradle 9.0 中移除。

弃用:在观察后修改 DependencyCollector 依赖项

从 Gradle 9.0 开始,在从 DependencyCollector 获取的依赖项被观察后对其进行修改将导致错误。DependencyCollector 接口用于在测试套件 DSL 中声明依赖项。

考虑以下示例,其中测试套件的依赖项在被观察后被修改

build.gradle.kts
plugins {
    id("java-library")
}

testing.suites {
    named<JvmTestSuite>("test") {
        dependencies {
            // Dependency is declared on a `DependencyCollector`
            implementation("com:foo")
        }
    }
}

configurations.testImplementation {
    // Calling `all` here realizes/observes all lazy sources, including the `DependencyCollector`
    // from the test suite block. Operations like resolving a configuration similarly realize lazy sources.
    dependencies.all {
        if (this is ExternalDependency && group == "com" && name == "foo" && version == null) {
            // Dependency is mutated after observation
            version {
                require("2.0")
            }
        }
    }
}

在上面的示例中,构建逻辑使用迭代和修改来尝试为特定依赖项设置默认版本(如果尚未设置版本)。像上面示例这样的构建逻辑在解析声明的依赖项时会产生挑战,因为报告工具会将此依赖项显示为用户声明了版本 "2.0",即使他们从未这样做过。相反,构建逻辑可以通过在依赖项的坐标上声明 preferred 版本约束来避免迭代和修改。这允许依赖项管理引擎在没有声明其他版本的情况下使用约束上声明的版本。

考虑以下示例,该示例用不加区分的 preferred 版本约束替换了上面的迭代

build.gradle.kts
dependencies {
    constraints {
        testImplementation("com:foo") {
            version {
                prefer("2.0")
            }
        }
    }
}

从 8.4 及更早版本升级

潜在的破坏性更改

升级到 Kotlin 1.9.20

嵌入式 Kotlin 已更新至 Kotlin 1.9.20

Groovy 任务约定的更改

groovy-base 插件现在负责在所有 GroovyCompile 任务上配置源和目标兼容性版本约定。

如果您在未应用 groovy-base 的情况下使用此任务,则必须手动在这些任务上设置兼容性版本。通常,在处理 Groovy 语言任务时,应始终应用 groovy-base 插件。

Provider.filter

传递给 Provider.filter 的参数类型从 Predicate 更改为 Spec,以便 API 更加一致。此更改不应影响任何使用带有 lambda 表达式的 Provider.filter 的用户。但是,如果插件作者不使用 SAM 转换来创建 lambda,则可能会受到影响。

弃用

org.gradle.util 包中已弃用的成员现在报告其弃用

这些成员将在 Gradle 9.0 中移除

  • VersionNumber.parse(String)

  • VersionNumber.compareTo(VersionNumber)

已弃用:依赖于已解析的配置

当解析 Configuration 时,有时可以选择将同一配置作为变体。配置应仅用于一个目的(解析、消费或依赖项声明),因此这种情况仅在配置标记为既可消费又可解析时才会发生。

这可能会导致循环依赖关系图,因为已解析的配置用于两个目的。

为了避免此问题,插件应将所有可解析配置标记为 canBeConsumed=false,或者在使用配置工厂方法 resolvable(String) 创建用于解析的配置时使用该方法。

在 Gradle 9.0 中,将不再允许以这种方式消费配置,并将导致错误。

包含没有现有目录的项目

如果将项目添加到构建中,但关联的 projectDir 不存在或不可写,Gradle 将发出警告。从 9.0 版本开始,如果项目目录丢失或只读,Gradle 将不会运行构建。如果您打算动态合成项目,请确保也为其创建目录

settings.gradle.kts
include("project-without-directory")
project(":project-without-directory").projectDir.mkdirs()
settings.gradle
include 'project-without-directory'
project(":project-without-directory").projectDir.mkdirs()

从 8.3 及更早版本升级

潜在的破坏性更改

升级到 Kotlin 1.9.10

嵌入式 Kotlin 已更新至 Kotlin 1.9.10

XML 解析现在需要最新的解析器

Gradle 8.4 现在配置了启用安全功能的 XML 解析器。如果您的构建逻辑依赖于不支持安全解析的旧 XML 解析器,则您的构建可能会失败。如果遇到故障,请检查并更新或删除对旧 XML 解析器的任何依赖项。

如果您是 Android 用户,请将您的 AGP 版本升级到 8.3.0 或更高版本,以修复 AGP 本身导致的问题。有关更多详细信息,请参阅 更新 AGP 中用于 Gradle 8.4 兼容性的 XML 解析器

如果您无法升级来自构建逻辑依赖项的 XML 解析器,您可以强制使用 JVM 内置的 XML 解析器。例如,在 OpenJDK 中,可以通过将以下内容添加到 gradle.properties 来完成此操作

systemProp.javax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
systemProp.javax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
systemProp.javax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

有关更多详细信息以及在以前的 Gradle 版本上启用安全 XML 处理的方法,请参阅 CVE-2023-42445 公告。

具有自定义 JEE 1.3 描述符的 EAR 插件

Gradle 8.4 禁止在解析 XML 文档时使用外部 XML 实体。如果您使用 EAR 插件并通过 EAR 插件的 DSL 配置 application.xml 描述符,并使用 withXml {} 自定义描述符,并在自定义块中使用 asElement{},则构建现在将因安全原因而失败。

build.gradle.kts
plugins {
    id("ear")
}
ear {
    deploymentDescriptor {
        version = "1.3"
        withXml {
            asElement()
        }
    }
}
build.gradle
plugins {
    id("ear")
}
ear {
    deploymentDescriptor {
        version = "1.3"
        withXml {
            asElement()
        }
    }
}

如果您碰巧使用 asNode() 而不是 asElement(),那么什么也不会改变,因为 asNode() 只是简单地忽略外部 DTD。

您可以通过使用设置为 httpjavax.xml.accessExternalDTD 系统属性运行构建来解决此问题。

在命令行上,将此添加到您的 Gradle 调用中

-Djavax.xml.accessExternalDTD=http

要使此解决方法持久生效,请将以下行添加到您的 gradle.properties

systemProp.javax.xml.accessExternalDTD=http

请注意,这将为整个构建 JVM 启用对外部 DTD 的 HTTP 访问。有关更多详细信息,请参阅 JAXP 文档

弃用

已弃用 GenerateMavenPom 方法

GenerateMavenPom 上的以下方法已被弃用,将在 Gradle 9.0 中移除。它们从未打算作为公共 API。

  • getVersionRangeMapper

  • withCompileScopeAttributes

  • withRuntimeScopeAttributes

从 8.2 及更早版本升级

潜在的破坏性更改

已弃用:Project.buildDir 可能导致脚本编译失败

随着 Project.buildDir 的弃用,如果使用了已弃用的字段,则使用将警告视为错误的编译脚本可能会失败。

有关详细信息,请参阅 弃用条目

TestLauncher API 不再忽略构建失败

TestLauncher 接口是 Tooling API 的一部分,专门用于运行测试。它是 BuildLauncher 的逻辑扩展,BuildLauncher 只能启动任务。已报告它们行为上的差异:如果执行相同的失败测试,BuildLauncher 将报告构建失败,但 TestLauncher 不会。最初,这是一个设计决策,目的是继续执行并运行所有测试任务中的测试,而不是在第一次失败时停止。同时,此行为可能会让用户感到困惑,因为他们可能会在成功的构建中遇到失败的测试。为了使两个 API 更加统一,我们使 TestLauncher 也导致构建失败,这是一个潜在的破坏性更改。Tooling API 客户端应显式传递 --continue 给构建,即使测试任务失败,也继续执行测试。

修复了 ArtifactViewArtifactCollection 的变体选择行为

用于选择不同构件或文件的依赖项解析 API (Configuration.getIncoming().artifactView { }Configuration.getIncoming().getArtifacts()) 捕获了底层 `Configuration` 的属性的不可变副本,用于变体选择。如果在调用这些方法后更改了 `Configuration` 的属性,则这些方法选择的构件可能会出乎意料。

考虑在创建 ArtifactView 后更改 Configuration 上的一组属性的情况

build.gradle.kts
tasks {
    myTask {
        inputFiles.from(configurations.classpath.incoming.artifactView {
            attributes {
                // Add attributes to select a different type of artifact
            }
        }.files)
    }
}

configurations {
    classpath {
        attributes {
            // Add more attributes to the configuration
        }
    }
}

myTaskinputFiles 属性使用构件视图从配置 classpath 中选择不同类型的构件。由于构件视图是在属性添加到配置之前创建的,因此 Gradle 无法选择正确的构件。

一些构建可能通过也将其他属性放入构件视图来解决此问题。现在不再需要这样做。

升级到 Kotlin 1.9.0

嵌入式 Kotlin 已从 1.8.20 更新到 Kotlin 1.9.0。Kotlin DSL 的 Kotlin 语言和 API 级别仍设置为 1.8 以实现向后兼容性。请参阅 Kotlin 1.8.22Kotlin 1.8.21 的发行说明。

Kotlin 1.9 放弃了对 Kotlin 语言和 API 级别 1.3 的支持。如果您使用此版本的 Gradle 构建用 Kotlin 编写的 Gradle 插件,并且需要支持 Gradle <7.0,则需要坚持使用 Kotlin Gradle 插件 <1.9.0,并将 Kotlin 语言和 API 级别配置为 1.3。有关其他版本的详细信息,请参阅 兼容性矩阵

Configuration 属性的急切求值

Gradle 8.3 更新了 JVM 配置的 org.gradle.libraryelementsorg.gradle.jvm.version 属性,使其在创建时就存在,而不是像以前那样,仅在配置已解析或消费后才存在。特别是,org.gradle.jvm.version 的值依赖于项目的配置工具链,这意味着查询此属性的值将最终确定项目 Java 工具链的值。

插件或构建逻辑如果急切地查询 JVM 配置的属性,现在可能会导致项目的 Java 工具链比以前更早地最终确定。尝试在工具链最终确定后修改工具链将导致类似于以下的错误消息

The value for property 'implementation' is final and cannot be changed any further.
The value for property 'languageVersion' is final and cannot be changed any further.
The value for property 'vendor' is final and cannot be changed any further.

当插件或构建逻辑急切地查询现有 JVM 配置的属性以创建具有相同属性的新配置时,可能会出现这种情况。以前,此逻辑将完全省略上述两个属性,而现在,相同的逻辑将复制属性并最终确定项目的 Java 工具链。为了避免过早的工具链最终确定,应更新属性复制逻辑以延迟查询源配置的属性

build.gradle.kts
fun <T> copyAttribute(attribute: Attribute<T>, from: AttributeContainer, to: AttributeContainer) =
    to.attributeProvider<T>(attribute, provider { from.getAttribute(attribute)!! })

val source = configurations["runtimeClasspath"].attributes
configurations {
    create("customRuntimeClasspath") {
        source.keySet().forEach { key ->
            copyAttribute(key, source, attributes)
        }
    }
}
build.gradle
def source = configurations.runtimeClasspath.attributes
configurations {
    customRuntimeClasspath {
        source.keySet().each { key ->
            attributes.attributeProvider(key, provider { source.getAttribute(key) })
        }
    }
}

弃用

已弃用:Project.buildDir 将被 Project.layout.buildDirectory 替换

Project.buildDir 属性已弃用。它使用急切 API,并且如果在构建逻辑中读取该值,然后稍后修改,则会存在排序问题。这可能会导致输出最终出现在不同的位置。

它被 DirectoryProperty 替换,该属性位于 Project.layout.buildDirectory。有关详细信息,请参阅 ProjectLayout 接口。

请注意,在此阶段,如果您仍然使用 Project.buildDir,Gradle 不会打印弃用警告。我们知道这是一个重大更改,我们希望给主要插件的作者时间来停止使用它。

File 切换到 DirectoryProperty 需要在构建逻辑中进行调整。主要影响是您不能在 String 中使用该属性来扩展它。相反,您应该利用 dirfile 方法来计算您想要的位置。

以下是一个创建文件的示例

build.gradle.kts
// Returns a java.io.File
file("$buildDir/myOutput.txt")
build.gradle
// Returns a java.io.File
file("$buildDir/myOutput.txt")

应替换为

build.gradle.kts
// Compatible with a number of Gradle lazy APIs that accept also java.io.File
val output: Provider<RegularFile> = layout.buildDirectory.file("myOutput.txt")

// If you really need the java.io.File for a non lazy API
output.get().asFile

// Or a path for a lazy String based API
output.map { it.asFile.path }
build.gradle
// Compatible with a number of Gradle lazy APIs that accept also java.io.File
Provider<RegularFile> output = layout.buildDirectory.file("myOutput.txt")

// If you really need the java.io.File for a non lazy API
output.get().asFile

// Or a path for a lazy String based API
output.map { it.asFile.path }

以下是另一个创建目录的示例

build.gradle.kts
// Returns a java.io.File
file("$buildDir/outputLocation")
build.gradle
// Returns a java.io.File
file("$buildDir/outputLocation")

应替换为

build.gradle.kts
// Compatible with a number of Gradle APIs that accept a java.io.File
val output: Provider<Directory> = layout.buildDirectory.dir("outputLocation")

// If you really need the java.io.File for a non lazy API
output.get().asFile

// Or a path for a lazy String based API
output.map { it.asFile.path }
build.gradle
// Compatible with a number of Gradle APIs that accept a java.io.File
Provider<Directory> output = layout.buildDirectory.dir("outputLocation")

// If you really need the java.io.File for a non lazy API
output.get().asFile

// Or a path for a lazy String based API
output.map { it.asFile.path }

已弃用:声明 ClientModule 依赖项

ClientModule 依赖项已弃用,将在 Gradle 9.0 中移除。

客户端模块依赖项最初旨在允许构建通过本地定义元数据来覆盖外部依赖项的不正确或缺失的组件元数据。此功能自那时以来已被 组件元数据规则 替换。

考虑以下客户端模块依赖项示例

build.gradle.kts
dependencies {
    implementation(module("org:foo:1.0") {
        dependency("org:bar:1.0")
        module("org:baz:1.0") {
            dependency("com:example:1.0")
        }
    })
}
build.gradle
dependencies {
    implementation module("org:foo:1.0") {
        dependency "org:bar:1.0"
        module("org:baz:1.0") {
            dependency "com:example:1.0"
        }
    }
}

可以用以下组件元数据规则替换

build-logic/src/main/kotlin/my-plugin.gradle.kts
@CacheableRule
abstract class AddDependenciesRule @Inject constructor(val dependencies: List<String>) : ComponentMetadataRule {
    override fun execute(context: ComponentMetadataContext) {
        listOf("compile", "runtime").forEach { base ->
            context.details.withVariant(base) {
                withDependencies {
                    dependencies.forEach {
                        add(it)
                    }
                }
            }
        }
    }
}
build.gradle.kts
dependencies {
    components {
        withModule<AddDependenciesRule>("org:foo") {
            params(listOf(
                "org:bar:1.0",
                "org:baz:1.0"
            ))
        }
        withModule<AddDependenciesRule>("org:baz") {
            params(listOf("com:example:1.0"))
        }
    }

    implementation("org:foo:1.0")
}
build-logic/src/main/groovy/my-plugin.gradle
@CacheableRule
abstract class AddDependenciesRule implements ComponentMetadataRule {

    List<String> dependencies

    @Inject
    AddDependenciesRule(List<String> dependencies) {
        this.dependencies = dependencies
    }

    @Override
    void execute(ComponentMetadataContext context) {
        ["compile", "runtime"].each { base ->
            context.details.withVariant(base) {
                withDependencies {
                    dependencies.each {
                        add(it)
                    }
                }
            }
        }
    }
}
build.gradle
dependencies {
    components {
        withModule("org:foo", AddDependenciesRule) {
            params([
                "org:bar:1.0",
                "org:baz:1.0"
            ])
        }
        withModule("org:baz", AddDependenciesRule) {
            params(["com:example:1.0"])
        }
    }

    implementation "org:foo:1.0"
}

最早支持的 Develocity 插件版本为 3.13.1

从 Gradle 9.0 开始,最早支持的 Develocity 插件版本为 3.13.1。应用时将忽略 3.0 到 3.13 的插件版本。

升级到 Develocity 插件的 3.13.1 或更高版本。您可以在 Gradle 插件门户上找到最新可用版本。有关兼容性的更多信息,请参见此处

从 8.1 及更早版本升级

潜在的破坏性更改

升级到 Kotlin 1.8.20

嵌入式 Kotlin 已更新至 Kotlin 1.8.20。有关更多信息,请参阅 Kotlin 1.8.20 中的新增功能

请注意,Kotlin 编译避免存在一个已知问题,如果编译类路径包含非常大的 JAR 文件,可能会导致 compileKotlin 任务中出现 OutOfMemory 异常。这适用于应用 Kotlin 插件 v1.8.20 或 kotlin-dsl 插件的构建。

您可以通过在 gradle.properties 文件中禁用 Kotlin 编译避免来解决此问题

kotlin.incremental.useClasspathSnapshot=false

有关更多信息,请参阅 KT-57757

升级到 Groovy 3.0.17

Groovy 已更新至 Groovy 3.0.17

由于之前的版本是 3.0.15,因此也包含了 3.0.16 的更改。

升级到 Ant 1.10.13

Ant 已更新至 Ant 1.10.13

由于之前的版本是 1.10.11,因此也包含了 1.10.12 的更改。

升级到 CodeNarc 3.2.0

CodeNarc 的默认版本已更新至 CodeNarc 3.2.0

升级到 PMD 6.55.0

PMD 已更新至 PMD 6.55.0

由于之前的版本是 6.48.0,因此包含了此后的所有更改。

升级到 JaCoCo 0.8.9

JaCoCo 已更新至 0.8.9

插件兼容性变更

使用 Gradle >= 8.2 编译的插件,如果使用了 Kotlin DSL 函数 Project.the<T>()Project.the(KClass)Project.configure<T> {},则无法在 Gradle ⇐ 6.1 上运行。

某些任务的延迟或避免配置

当执行依赖项解析时,Gradle 会创建可用 Configuration 的内部表示。这需要检查所有配置和构件。处理由任务创建的构件会导致这些任务被实现和配置。

现在,这种内部表示创建得更加延迟,这可能会更改任务的配置顺序。某些任务可能永远不会被配置。

此更改可能会导致依赖于特定顺序的代码路径不再起作用,例如基于某些属性的存在有条件地向配置添加属性。

这影响了 bnd 插件和 JUnit5 构建

我们建议不要从其他可能未配置的域对象的配置块中修改域对象(配置、源码集、任务等)。

例如,避免执行如下操作

    configurations {
        val myConfig = create("myConfig")
    }

    tasks.register("myTask") {
            // This is not safe, as the execution of this block may not occur, or may not occur in the order expected
          configurations["myConfig"].attributes {
              attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME))
          }
    }

弃用

CompileOptions 方法弃用

CompileOptions 上的以下方法已被弃用

  • getAnnotationProcessorGeneratedSourcesDirectory()

  • setAnnotationProcessorGeneratedSourcesDirectory(File)

  • setAnnotationProcessorGeneratedSourcesDirectory(Provider<File>)

当前对这些方法的使用应迁移到 DirectoryProperty getGeneratedSourceOutputDirectory()

不正确地使用配置

Configuration 的方法与其配置的预期用途不一致地调用时,Gradle 现在会在运行时发出警告。

此更改是使配置的预期行为更一致和可预测,并解锁进一步的速度和内存改进的更大持续努力的一部分。

目前,以下方法应仅与这些列出的允许用途一起调用

  • resolve() - 仅限 RESOLVABLE 配置

  • files(Closure)files(Spec)files(Dependency…)fileCollection(Spec)fileCollection(Closure)fileCollection(Dependency…) - 仅限 RESOLVABLE 配置

  • getResolvedConfigurations() - 仅限 RESOLVABLE 配置

  • defaultDependencies(Action) - 仅限 DECLARABLE 配置

  • shouldResolveConsistentlyWith(Configuration) - 仅限 RESOLVABLE 配置

  • disableConsistentResolution() - 仅限 RESOLVABLE 配置

  • getDependencyConstraints() - 仅限 DECLARABLE 配置

  • copy()copy(Spec)copy(Closure)copyRecursive()copyRecursive(Spec)copyRecursive(Closure) - 仅限 RESOLVABLE 配置

预期用途在 Configuration 接口的 Javadoc 中注明。此列表可能会在未来的版本中增长。

从 Gradle 9.0 开始,不允许不一致地使用与其预期用途不符的配置,这将受到禁止。

另请注意,尽管目前未受到限制,但 getDependencies() 方法仅适用于 DECLARABLE 配置。getAllDependencies() 方法(检索配置及其任何超配置上的所有声明的依赖项)将不受任何特定用途的限制。

已弃用:访问插件约定

约定的概念已过时,并且已被 扩展 取代,以提供自定义 DSL。

为了在 Gradle API 中反映这一点,以下元素已被弃用

Gradle Core 插件仍然注册其约定以及其扩展,以实现向后兼容性。

访问任何这些约定及其属性已被弃用。这样做现在将发出弃用警告。这将在 Gradle 9.0 中变为错误。您应该首选访问扩展及其属性。

有关具体示例,请参阅下一节。

著名的社区插件已经迁移到使用扩展来提供自定义 DSL。其中一些插件仍然注册约定以实现向后兼容性。注册约定尚不会发出弃用警告,以提供迁移窗口。未来的 Gradle 版本将会这样做。

另请注意,使用 Gradle ⇐ 8.1 编译的插件,如果使用了 Kotlin DSL 函数 Project.the<T>()Project.the(KClass)Project.configure<T> {},则在 Gradle >= 8.2 上运行时将发出弃用警告。要解决此问题,应使用 Gradle >= 8.2 重新编译这些插件,或更改为直接使用 extensions.getByType<T>() 访问扩展。

已弃用:base 插件约定

base 插件贡献的约定属性已被弃用,并计划在 Gradle 9.0 中移除。有关更多上下文,请参阅 关于插件约定弃用的部分

这些约定已由 base { } 配置块替换,该配置块由 BasePluginExtension 支持。旧的约定对象定义了具有简单 getter 和 setter 方法的 distsDirName, libsDirNamearchivesBaseName 属性。这些方法仅在扩展中可用,以保持向后兼容性。构建脚本应仅使用类型为 Property 的属性

build.gradle.kts
plugins {
    base
}

base {
    archivesName.set("gradle")
    distsDirectory.set(layout.buildDirectory.dir("custom-dist"))
    libsDirectory.set(layout.buildDirectory.dir("custom-libs"))
}
build.gradle
plugins {
    id 'base'
}

base {
    archivesName = "gradle"
    distsDirectory = layout.buildDirectory.dir('custom-dist')
    libsDirectory = layout.buildDirectory.dir('custom-libs')
}

已弃用:application 插件约定

application 插件贡献的约定属性已被弃用,并计划在 Gradle 9.0 中移除。有关更多上下文,请参阅 关于插件约定弃用的部分

以下代码现在将发出弃用警告

build.gradle.kts
plugins {
    application
}

applicationDefaultJvmArgs = listOf("-Dgreeting.language=en") // Accessing a convention
build.gradle
plugins {
    id 'application'
}

applicationDefaultJvmArgs = ['-Dgreeting.language=en'] // Accessing a convention

应将其更改为使用 application { } 配置块,该配置块由 JavaApplication 支持,取而代之

build.gradle.kts
plugins {
    application
}

application {
    applicationDefaultJvmArgs = listOf("-Dgreeting.language=en")
}
build.gradle
plugins {
    id 'application'
}

application {
    applicationDefaultJvmArgs = ['-Dgreeting.language=en']
}

已弃用:java 插件约定

java 插件贡献的约定属性已被弃用,并计划在 Gradle 9.0 中移除。有关更多上下文,请参阅 关于插件约定弃用的部分

以下代码现在将发出弃用警告

build.gradle.kts
plugins {
    id("java")
}

configure<JavaPluginConvention> { // Accessing a convention
    sourceCompatibility = JavaVersion.VERSION_18
}
build.gradle
plugins {
    id 'java'
}

sourceCompatibility = 18 // Accessing a convention

应将其更改为使用 java { } 配置块,该配置块由 JavaPluginExtension 支持,取而代之

build.gradle.kts
plugins {
    id("java")
}

java {
    sourceCompatibility = JavaVersion.VERSION_18
}
build.gradle
plugins {
    id 'java'
}

java {
    sourceCompatibility = JavaVersion.VERSION_18
}

已弃用:war 插件约定

war 插件贡献的约定属性已被弃用,并计划在 Gradle 9.0 中移除。有关更多上下文,请参阅 关于插件约定弃用的部分

以下代码现在将发出弃用警告

build.gradle.kts
plugins {
    id("war")
}

configure<WarPluginConvention> { // Accessing a convention
    webAppDirName = "src/main/webapp"
}
build.gradle
plugins {
    id 'war'
}

webAppDirName = 'src/main/webapp' // Accessing a convention

客户端应直接配置 war 任务。此外,tasks.withType(War.class).configureEach(…​) 可用于配置 War 类型的每个任务。

build.gradle.kts
plugins {
    id("war")
}

tasks.war {
    webAppDirectory.set(file("src/main/webapp"))
}
build.gradle
plugins {
    id 'war'
}

war {
    webAppDirectory = file('src/main/webapp')
}

已弃用:ear 插件约定

ear 插件贡献的约定属性已被弃用,并计划在 Gradle 9.0 中移除。有关更多上下文,请参阅 关于插件约定弃用的部分

以下代码现在将发出弃用警告

build.gradle.kts
plugins {
    id("ear")
}

configure<EarPluginConvention> { // Accessing a convention
    appDirName = "src/main/app"
}
build.gradle
plugins {
    id 'ear'
}

appDirName = 'src/main/app' // Accessing a convention

客户端应直接配置 ear 任务。此外,tasks.withType(Ear.class).configureEach(…​) 可用于配置 Ear 类型的每个任务。

build.gradle.kts
plugins {
    id("ear")
}

tasks.ear {
    appDirectory.set(file("src/main/app"))
}
build.gradle
plugins {
    id 'ear'
}

ear {
    appDirectory = file('src/main/app')  // use application metadata found in this folder
}

已弃用:project-report 插件约定

project-reports 插件贡献的约定属性已被弃用,并计划在 Gradle 9.0 中移除。有关更多上下文,请参阅 关于插件约定弃用的部分

以下代码现在将发出弃用警告

build.gradle.kts
plugins {
    `project-report`
}

configure<ProjectReportsPluginConvention> {
    projectReportDirName = "custom" // Accessing a convention
}
build.gradle
plugins {
    id 'project-report'
}

projectReportDirName = "custom" // Accessing a convention

请改为配置您的报告任务

build.gradle.kts
plugins {
    `project-report`
}

tasks.withType<HtmlDependencyReportTask>() {
    projectReportDirectory.set(project.layout.buildDirectory.dir("reports/custom"))
}
build.gradle
plugins {
    id 'project-report'
}

tasks.withType(HtmlDependencyReportTask) {
    projectReportDirectory = project.layout.buildDirectory.dir("reports/custom")
}

Configuration 方法弃用

Configuration 上的以下方法已被弃用,将被移除

  • getAll()

请改为从项目的 configurations 容器中获取所有配置的集合。

依赖于自动测试框架实现依赖项

在某些情况下,Gradle 将从 Gradle 发行版加载 JVM 测试框架依赖项以执行测试。这种现有行为可能导致测试类路径上的测试框架依赖项版本冲突。为了避免这些冲突,此行为已被弃用,将在 Gradle 9.0 中移除。使用 TestNG 的测试不受影响。

为了为此行为更改做好准备,请显式声明所需的依赖项,或迁移到 测试套件,在测试套件中,这些依赖项会自动管理。

测试套件

使用测试套件的构建不会受到此更改的影响。测试套件自动管理测试框架依赖项,不需要显式声明依赖项。有关迁移到测试套件的更多信息,请参阅 用户手册

手动声明依赖项

在没有测试套件的情况下,必须在测试运行时类路径上手动声明依赖项

  • 如果使用 JUnit 5,除了现有的 implementation 对测试引擎的依赖项之外,还需要显式 runtimeOnlyjunit-platform-launcher 的依赖项。

  • 如果使用 JUnit 4,则仅需要现有的 implementationjunit 4 的依赖项。

  • 如果使用 JUnit 3,除了 compileOnlyjunit 3 的依赖项之外,还需要测试 runtimeOnlyjunit 4 的依赖项。

build.gradle.kts
dependencies {
    // If using JUnit Jupiter
    testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")

    // If using JUnit Vintage
    testCompileOnly("junit:junit:4.13.2")
    testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.9.2")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")

    // If using JUnit 4
    testImplementation("junit:junit:4.13.2")

    // If using JUnit 3
    testCompileOnly("junit:junit:3.8.2")
    testRuntimeOnly("junit:junit:4.13.2")
}
build.gradle
dependencies {
    // If using JUnit Jupiter
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // If using JUnit Vintage
    testCompileOnly 'junit:junit:4.13.2'
    testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.9.2'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // If using JUnit 4
    testImplementation 'junit:junit:4.13.2'

    // If using JUnit 3
    testCompileOnly 'junit:junit:3.8.2'
    testRuntimeOnly 'junit:junit:4.13.2'
}

BuildIdentifierProjectComponentSelector 方法弃用

BuildIdentifier 上的以下方法已被弃用

  • getName()

  • isCurrentBuild()

您可以使用这些方法来区分来自不同构建但名称相同的不同项目组件。但是,对于某些复合构建设置,这些方法无法提供足够的信息来保证唯一性。

当前对这些方法的使用应迁移到 BuildIdentifier.getBuildPath()

类似地,方法 ProjectComponentSelector.getBuildName() 已被弃用。请改为使用 ProjectComponentSelector.getBuildPath()

从 8.0 及更早版本升级

CACHEDIR.TAG 文件在全局缓存目录中创建

Gradle 现在在某些全局缓存目录中发出 CACHEDIR.TAG 文件,如 directory_layout.html 中指定的那样。

这可能会导致某些工具不再搜索或备份这些目录。要禁用它,请在 Gradle 用户主目录的 init 脚本中使用以下代码

init.gradle.kts
beforeSettings {
    caches {
        // Disable cache marking for all caches
        markingStrategy.set(MarkingStrategy.NONE)
    }
}
init.gradle
beforeSettings { settings ->
    settings.caches {
        // Disable cache marking for all caches
        markingStrategy = MarkingStrategy.NONE
    }
}

配置缓存选项已重命名

在此版本中,配置缓存功能已从孵化阶段提升为稳定阶段。因此,最初在功能文档中提到的所有属性(其名称中都有 unsafe 部分,例如 org.gradle.unsafe.configuration-cache)都已重命名,在某些情况下,通过删除名称的 unsafe 部分。

孵化属性 最终属性

org.gradle.unsafe.configuration-cache

org.gradle.configuration-cache

org.gradle.unsafe.configuration-cache-problems

org.gradle.configuration-cache.problems*

org.gradle.unsafe.configuration-cache.max-problems

org.gradle.configuration-cache.max-problems

请注意,此版本仍然支持原始的 org.gradle.unsafe.configuration-cache…​ 属性,并且如果使用它们不会产生警告,但这些属性将在未来的版本中被弃用并移除。

潜在的破坏性变更

Kotlin DSL 脚本发出编译警告

来自 Kotlin DSL 脚本的编译警告会打印到控制台输出。例如,在 Kotlin DSL 中使用已弃用的 API 每次编译脚本时都会发出警告。

如果您正在使用 Gradle 构建的控制台输出,则这是一个潜在的破坏性变更。

使用已应用的 kotlin-dsl 插件配置 Kotlin 编译器选项

如果您在应用了 kotlin-dsl 插件的项目上配置自定义 Kotlin 编译器选项,您可能会遇到破坏性变更。

在之前的 Gradle 版本中,kotlin-dsl 插件在 afterEvaluate {} 上添加必需的编译器参数。现在 Kotlin Gradle 插件提供了延迟配置属性,我们的 kotlin-dsl 插件已切换为直接将必需的编译器参数添加到延迟属性。因此,如果您设置了 freeCompilerArgskotlin-dsl 插件现在会构建失败,因为其必需的编译器参数被您的配置覆盖。

build.gradle.kts
plugins {
    `kotlin-dsl`
}

tasks.withType(KotlinCompile::class).configureEach {
    kotlinOptions { // Deprecated non-lazy configuration options
        freeCompilerArgs = listOf("-Xcontext-receivers")
    }
}

使用上述配置,您将收到以下构建失败

* What went wrong
Execution failed for task ':compileKotlin'.
> Kotlin compiler arguments of task ':compileKotlin' do not work for the `kotlin-dsl` plugin. The 'freeCompilerArgs' property has been reassigned. It must instead be appended to. Please use 'freeCompilerArgs.addAll(\"your\", \"args\")' to fix this.

您必须将其更改为将您的自定义编译器参数添加到 Kotlin Gradle 插件的延迟配置属性中,以便将它们附加到 kotlin-dsl 插件所需的参数之后

build.gradle.kts
plugins {
    `kotlin-dsl`
}

tasks.withType(KotlinCompile::class).configureEach {
    compilerOptions { // New lazy configuration options
        freeCompilerArgs.addAll("-Xcontext-receivers")
    }
}

如果您已经添加到 freeCompilerArgs 而不是设置其值,则不应遇到构建失败。

引入的新 API 可能会与现有的 Gradle DSL 代码冲突

当向 Gradle DSL 中现有类型添加新的属性或方法时,它可能会与用户代码中已使用的名称冲突。

当发生名称冲突时,一种解决方案是重命名用户代码中的元素。

这是 8.1 中可能导致与现有用户代码名称冲突的 API 添加的不完全列表。

启用配置缓存后,不再允许在配置时使用不支持的 API 启动外部进程

自 Gradle 7.5 起,如果启用 功能预览 STABLE_CONFIGURATION_CACHE,则在配置时使用 Project.execProject.javaexec 以及标准 Java 和 Groovy API 运行外部进程才会被视为错误。随着配置缓存升级为 Gradle 8.1 中的稳定功能,无论功能预览状态如何,都会检测到此错误。配置缓存章节提供了更多详细信息,以帮助迁移到新的基于提供程序的 API,以便在配置时执行外部进程。

不使用配置缓存或仅在执行时启动外部进程的构建不受此更改的影响。

弃用

修改核心插件配置的允许用法

配置的允许用法应在创建后保持不变。修改 Gradle 核心插件创建的配置的允许用法已被弃用。这包括调用以下任何 Configuration 方法

  • setCanBeConsumed(boolean)

  • setCanBeResolved(boolean)

这些方法现在在这些配置上发出弃用警告,但某些特殊情况除外,这些情况允许流行的插件的现有行为。此规则尚不适用于分离的配置或在构建脚本和第三方插件中创建的配置。为了避免在使用某些流行的第三方插件时发出警告,在 apiElementsruntimeElements 上调用 setCanBeConsumed(false) 尚未弃用。

此更改是更大规模的持续努力的一部分,旨在使配置的预期行为更加一致和可预测,并在此 Gradle 领域解锁进一步的速度和内存改进。

在 Gradle 9.0 中,将移除在创建后更改配置的允许用法的能力。

保留的配置名称

配置名称 "detachedConfiguration" 和 "detachedConfigurationX"(其中 X 是任何整数)保留供内部使用,用于创建分离的配置。

在 Gradle 9.0 中,将移除使用这些名称创建非分离配置的能力。

在不存在 java 组件的情况下调用 JavaPluginExtension 上的某些方法

从 Gradle 8.1 开始,在不存在默认 java 组件的情况下,调用 JavaPluginExtension 上的以下任何方法都已被弃用

  • withJavadocJar()

  • withSourcesJar()

  • consistentResolution(Action)

java 组件由 JavaPlugin 添加,JavaPlugin 由任何 Gradle JVM 插件应用,包括

  • java-library

  • application

  • groovy

  • scala

从 Gradle 9.0 开始,在不存在默认 java 组件的情况下调用上述任何方法都将成为错误。

WarPlugin#configureConfiguration(ConfigurationContainer)

从 Gradle 8.1 开始,调用 WarPlugin#configureConfiguration(ConfigurationContainer) 已被弃用。此方法旨在供内部使用,从未打算用作公共接口的一部分。

从 Gradle 9.0 开始,此方法将被移除,不提供替代方法。

依赖自定义 Test 任务的约定

默认情况下,当应用 java 插件时,所有 Test 任务的 testClassesDirsclasspath 都具有相同的约定。除非另有更改,否则默认行为是通过使用来自 test 套件的 classpathtestClassesDirs 配置任务来执行默认 test TestSuite 中的测试。此行为将在 Gradle 9.0 中移除。

虽然现有的默认行为对于在不同环境下执行默认单元测试套件的用例是正确的,但它不支持执行完全独立的测试集的使用场景。

如果您希望继续包含这些测试,请使用以下代码以避免 8.1 中的弃用警告,并为 9.0 中的行为更改做好准备。或者,考虑迁移到测试套件。

build.gradle.kts
val test by testing.suites.existing(JvmTestSuite::class)
tasks.named<Test>("myTestTask") {
    testClassesDirs = files(test.map { it.sources.output.classesDirs })
    classpath = files(test.map { it.sources.runtimeClasspath })
}
build.gradle
tasks.myTestTask {
    testClassesDirs = testing.suites.test.sources.output.classesDirs
    classpath = testing.suites.test.sources.runtimeClasspath
}

在发布填充后修改 Gradle 模块元数据

在从组件填充 Maven 或 Ivy 发布之后,更改 GMM(例如,更改组件配置变体)现在已被弃用。此功能将在 Gradle 9.0 中移除。

如果调用以下方法,可能会发生发布的预先填充

以前,以下代码不会生成警告,但它会在已发布的工件之间创建不一致性

build.gradle.kts
publishing {
    publications {
        create<MavenPublication>("maven") {
            from(components["java"])
        }
        create<IvyPublication>("ivy") {
            from(components["java"])
        }
    }
}

// These calls eagerly populate the Maven and Ivy publications

(publishing.publications["maven"] as MavenPublication).artifacts
(publishing.publications["ivy"] as IvyPublication).artifacts

val javaComponent = components["java"] as AdhocComponentWithVariants
javaComponent.withVariantsFromConfiguration(configurations["apiElements"]) { skip() }
javaComponent.withVariantsFromConfiguration(configurations["runtimeElements"]) { skip() }
build.gradle
publishing {
    publications {
        maven(MavenPublication) {
            from components.java
        }
        ivy(IvyPublication) {
            from components.java
        }
    }
}

// These calls eagerly populate the Maven and Ivy publications

publishing.publications.maven.artifacts
publishing.publications.ivy.artifacts

components.java.withVariantsFromConfiguration(configurations.apiElements) { skip() }
components.java.withVariantsFromConfiguration(configurations.runtimeElements) { skip() }

在此示例中,Maven 和 Ivy 发布将包含项目的主要 JAR 工件,而 GMM 模块文件 将省略它们。

在 JVM 版本 6 和 7 上运行测试

在低于 8 的 JVM 版本上运行 JVM 测试已被弃用。在这些版本上进行测试将在 Gradle 9.0 中成为错误

应用使用 Gradle < 6.0 发布的 Kotlin DSL 预编译脚本

应用使用 Gradle < 6.0 发布的 Kotlin DSL 预编译脚本已被弃用。请使用使用 Gradle >= 6.0 发布的插件版本。

kotlin-dsl 与 Kotlin Gradle Plugin < 1.8.0 一起应用

kotlin-dsl 与 Kotlin Gradle Plugin < 1.8.0 一起应用已被弃用。请删除构建逻辑中任何显式的 kotlin-dsl 版本约束,让 Gradle 控制 kotlin-dsl 的版本。这将允许 kotlin-dsl 插件决定使用哪个版本的 Kotlin Gradle Plugin。如果您显式声明要用于构建逻辑的 Kotlin Gradle Plugin 版本,请将其更新为 >= 1.8.0。

在 Kotlin 脚本的 plugins {} 代码块中访问依赖版本目录中的 librariesbundles

在 Kotlin 脚本的 plugins {} 代码块中访问依赖版本目录中的 librariesbundles 已被弃用。请仅在 plugins {} 代码块中使用依赖版本目录中的 versionsplugins

在没有 Java Toolchain 的情况下使用 ValidatePlugins 任务

在没有应用 Java Toolchains 插件的情况下使用 ValidatePlugins 类型的任务已被弃用,并且将在 Gradle 9.0 中成为错误。

要避免此警告,请将该插件应用到您的项目

build.gradle.kts
plugins {
    id("jvm-toolchains")
}
build.gradle
plugins {
    id 'jvm-toolchains'
}

Java Toolchains 插件由 Java library plugin 或其他 JVM 插件自动应用。因此,您可以将它们中的任何一个应用于您的项目,这将修复警告。

org.gradle.util 包中已弃用的成员现在报告其弃用

这些成员将在 Gradle 9.0 中移除。

  • WrapUtil.toDomainObjectSet(…​)

  • GUtil.toCamelCase(…​)

  • GUtil.toLowerCase(…​)

  • ConfigureUtil

已弃用的 JVM 供应商 IBM Semeru

枚举常量 JvmVendorSpec.IBM_SEMERU 现在已被弃用,将在 Gradle 9.0 中移除。

请将其替换为其等效项 JvmVendorSpec.IBM,以避免在下一个主要版本发布中出现警告和潜在错误。

StartParameterGradleBuild 上设置自定义构建布局

继 Gradle 7.1 中 相关的先前弃用行为之后,现在也弃用了使用相关的 StartParameterGradleBuild 属性。这些属性将在 Gradle 9.0 中移除。

GradleBuild 任务中,使用 buildFile 属性设置自定义构建文件已被弃用。

请改用 dir 属性来指定嵌套构建的根目录。或者,考虑使用 GradleBuild 任务的推荐替代方案之一。

使用 StartParameter 方法 setBuildFile(File)setSettingsFile(File) 以及对应的 getter getBuildFile()getSettingsFile() 设置自定义构建布局已被弃用。

请使用设置文件和构建文件的标准位置

  • 构建根目录中的设置文件

  • 每个子项目根目录中的构建文件

已弃用的 org.gradle.cache.cleanup 属性

Gradle 用户主目录下的 gradle.properties 文件中的 org.gradle.cache.cleanup 属性已被弃用。请改用 缓存清理 DSL 来禁用或修改清理配置。

由于旧版本的 Gradle 可能仍需要 org.gradle.cache.cleanup 属性,因此只要也通过 DSL 配置了此属性,该属性仍可能存在,并且不会打印弃用警告。DSL 值将始终优先于 org.gradle.cache.cleanup 属性。如果所需的配置是为旧版本的 Gradle(使用 org.gradle.cache.cleanup)禁用清理,但为 Gradle 8 或更高版本启用具有默认值的清理,则应将清理配置为使用 Cleanup.DEFAULT

cache-settings.gradle
if (GradleVersion.current() >= GradleVersion.version('8.0')) {
    apply from: "gradle8/cache-settings.gradle"
}
cache-settings.gradle.kts
if (GradleVersion.current() >= GradleVersion.version("8.0")) {
    apply(from = "gradle8/cache-settings.gradle")
}
gradle8/cache-settings.gradle
beforeSettings { settings ->
    settings.caches {
        cleanup = Cleanup.DEFAULT
    }
}
gradle8/cache-settings.gradle.kts
beforeSettings {
    caches {
        cleanup.set(Cleanup.DEFAULT)
    }
}

已弃用使用相对路径来指定 Java 可执行文件

现在已弃用使用相对文件路径来指向 Java 可执行文件,并且将在 Gradle 9 中成为错误。这样做是为了减少关于此类相对路径应相对于什么解析的困惑。

从任务动作中调用 Task.getConvention()Task.getExtensions()

现在已弃用在执行时从任务动作中调用 Task.getConvention()Task.getExtensions(),并且将在 Gradle 9.0 中使其成为错误。

有关如何将这些用法迁移到配置缓存支持的 API 的详细信息,请参阅配置缓存章节

已弃用在未执行任何测试时成功运行测试任务

现在已弃用在未执行任何测试时成功运行 Test 任务,并且将在 Gradle 9 中成为错误。请注意,当不存在测试源时,这不是错误,在这种情况下,test 任务只是被跳过。只有当存在测试源但未选择任何测试执行时,这才是错误。更改此项是为了避免由于错误的配置而意外成功运行测试。

IDE 集成方面的变更

Kotlin DSL plugins {} 代码块中使用版本目录显示的误报错误的解决方法不再需要

用于 plugins {} 代码块中插件别名的版本目录访问器不再在 IntelliJ IDEA 和 Android Studio Kotlin 脚本编辑器中显示为错误。

如果您之前使用 @Suppress("DSL_SCOPE_VIOLATION") 注解作为解决方法,您现在可以将其删除。

如果您之前使用 Gradle Libs Error Suppressor IntelliJ IDEA 插件,您现在可以卸载它。

将 Gradle 升级到 8.1 后,您需要清除 IDE 缓存并重新启动。