将构建从 Gradle 8.x 升级到最新版本
本章提供了将您的 Gradle 8.x 构建迁移到最新 Gradle 版本所需的信息。对于从 Gradle 4.x, 5.x, 6.x 或 7.x 迁移,请先参阅旧版迁移指南。
我们建议所有用户遵循以下步骤
-
尝试运行
gradle help --scan
并查看生成的构建扫描的弃用功能视图。这可以让您看到适用于您构建的任何弃用警告。
或者,您可以运行
gradle help --warning-mode=all
在控制台中查看弃用信息,但这可能不会报告那么多详细信息。 -
更新您的插件。
某些插件在此新版本的 Gradle 中将无法工作,因为它们使用了已移除或更改的内部 API。上一步将通过在插件尝试使用已弃用的 API 部分时发出弃用警告来帮助您识别潜在问题。
-
运行
gradle wrapper --gradle-version 8.14
将项目更新到 8.14。 -
尝试运行项目,并使用故障排除指南调试任何错误。
从 8.13 及更早版本升级
潜在的破坏性变更
Gradle Wrapper 现在是可执行 JAR
Gradle Wrapper JAR 已转换为可执行 JAR。这意味着它现在包含一个 Main-Class
属性,允许使用 -jar
选项启动它,而不是手动指定 classpath 和主类。
当您使用 gradle wrapper
或 ./gradlew wrapper
命令更新 wrapper 脚本时,wrapper JAR 将自动更新以反映此更改。
Settings
默认值的更改
在 Gradle 8.10 中引入的孵化中的 Settings.getDefaults()
方法已被移除。请改用 Settings.defaults(Action<SharedModelDefaults>)
方法,该方法接受一个 lambda。
此更改允许在单个项目的上下文中解释默认值,而不是在 Settings
级别解释。
升级到 Guava 33.4.6
Guava 已从版本 32.1.2 更新到 33.4.6。此版本弃用了几个核心功能,包括 Charsets
。有关完整详细信息,请参阅Guava 版本说明。
EclipseClasspath.baseSourceOutputDir
现在是 DirectoryProperty
孵化中的 EclipseClasspath.baseSourceOutputDir
以前被声明为 Property<File>
。现在已正确更新为 DirectoryProperty
以反映预期的类型。
升级到 JaCoCo 0.8.13
JaCoCo 已更新到0.8.13。
弃用
使用 null
键查找属性已被弃用
现在明确弃用将 null
传递给 getAttribute(Attribute)
。
之前,这会静默返回 null
。现在,会发出弃用警告。在 AttributeContainer
中不应需要使用 null
键执行查找。
Groovy 字符串到枚举的属性类型强制转换已被弃用
Groovy 支持字符串到枚举的强制转换。现在弃用将 String
赋值给 Property<T>
,其中 T
是枚举类型。这将在 Gradle 10.0 中变为错误。
此弃用仅影响使用 Groovy DSL 编写的 Groovy 插件。
Groovydoc.getAntGroovydoc()
和 org.gradle.api.internal.tasks.AntGroovydoc
已被弃用
这些内部 API 被无意中暴露,现已弃用。它们将在 Gradle 9.0 中移除。
GradlePluginDevelopmentExtension
中的已弃用方法
GradlePluginDevelopmentExtension
的构造函数及其 pluginSourceSet
方法现已弃用。
这些方法不应直接使用,它们 intended to be configured solely by the Gradle Plugin Development plugin. 仅支持主源集进行插件开发。
这些方法将在 Gradle 9.0 中移除。
IdeaModule
中的已弃用集合现在发出警告
org.gradle.plugins.ide.idea.model.IdeaModule
中的 testResourcesDirs
和 testSourcesDirs
属性在 Gradle 7.6 中被标记为 @Deprecated
,但直到现在才发出警告。
Gradle 现在在使用这些属性时发出弃用警告。它们将在 Gradle 9.0 中移除。
ForkOptions.getJavaHome()
和 ForkOptions.setJavaHome()
方法不再弃用
这些方法在 Gradle 8.11 中被弃用,但由于它们尚未有稳定的替代方案,因此不再弃用。
弃用的 StartParameter.isConfigurationCacheRequested
现在发出警告
StartParameter
中的 isConfigurationCacheRequested
属性在 Gradle 8.5 中被标记为 @Deprecated
,但直到现在才发出警告。
Gradle 现在在使用此属性时发出弃用警告。它将在 Gradle 10.0 中移除。
从 Gradle 8.5 开始,可以通过 BuildFeatures
服务使用 configurationCache.requested
属性获取相同的信息。
弃用的配置用法不再弃用
从 8.0 开始,将 artifact 添加到既不可解析也不可消费的配置中被弃用。此弃用范围过广,也包含了某些有效的用法。它已在 Gradle 8.14 中移除。
从 8.12 及更早版本升级
潜在的破坏性变更
JvmTestSuite 的更改
testType
属性已从 JvmTestSuite 中移除,同时 TestSuiteTargetName
和 TestSuiteType
属性也已移除。现在可以通过指定目标项目中要聚合的测试套件的名称来在项目之间聚合测试报告和 JaCoCo 报告。
更多详细信息见下文。
测试报告聚合和 JaCoCo 聚合的更改
孵化中的测试报告聚合和JaCoCo 报告聚合插件进行了一些更改。
现在,这些插件为每个测试套件创建一个单一的测试结果 variant,其中包含整个套件的所有测试结果,而不是为每个测试目标创建一个 variant。此更改允许聚合插件聚合具有多个目标的测试套件,而之前这会导致模糊的 variant 选择错误。
将来,随着我们继续开发这些插件,我们计划再次为每个测试套件目标创建一个结果 variant,从而允许明确聚合来自某些目标的测试结果。
JacocoCoverageReport 和 AggregateTestReport 上的 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
接口上的急切任务创建方法已被标记为 @Deprecated
,在构建脚本或插件代码中使用时会生成编译器和 IDE 警告。目前尚未发出针对其使用的 Gradle 弃用警告。
但是,如果构建配置为在 Kotlin 脚本或插件代码编译期间遇到警告时失败,则此更改可能导致构建失败。
在未来版本中这些方法完全弃用时,使用时将打印标准的 Gradle 弃用警告。
弃用
在延迟 provider 中递归查询 AttributeContainer
在 Gradle 9.0 中,从同一容器的属性值 provider 内部查询 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
它们分别被 languageVersion
和 vendor
替换。这使得 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
属性看起来是一个输入。
有两种方法可以解决此问题:
-
引入一个以
get
开头而不是is
的新方法,该方法具有相同的行为。旧方法不需要移除(为了保持二进制兼容性),但可能需要按照以下指示进行调整。-
建议弃用
is-
方法,并在未来的主要版本中将其移除。
-
-
将属性的类型(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
接口上的急切任务创建方法已被标记为 @Deprecated
,在构建脚本或插件代码中使用时会生成编译器和 IDE 警告。目前尚未发出针对其使用的 Gradle 弃用警告。
但是,如果构建配置为在 Kotlin 脚本或插件代码编译期间遇到警告时失败,则此行为可能导致构建失败。
在未来版本中这些方法完全弃用时,使用时将打印标准的 Gradle 弃用警告。
弃用
已弃用的模糊转换链
之前,当至少存在两个等长的Artifact Transform 链,它们都能生成兼容的 variant 并满足解析请求时,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 无法知道应该使用两个(或更多)可能的转换链中的哪一个。选择任意链可能导致性能低下或在修改看似无关的构建部分时出现意外行为变化。这是一个潜在的非常复杂的情况,现在消息会通过按顺序打印所有已注册的 transforms,以及每个候选链的源(输入)variants,来充分解释这种情况。
遇到此类故障时,构建作者应采取以下措施:
-
在注册链中存在的 transforms 时,添加额外的、有区别的 attributes,以确保只有一个链可供选择以满足请求
-
请求额外的 attributes 以消除歧义,明确选择哪个链(如果它们导致非完全相同的最终 attributes)
-
从构建中移除不必要的已注册 transforms
这将在 Gradle 9.0 中变为错误。
init
必须单独运行
init
任务必须单独运行。此任务不应在单个 Gradle 调用中与其他任务结合使用。
在与其它任务相同的调用中运行 init
将在 Gradle 9.0 中变为错误。
例如,这将不允许:
> gradlew init tasks
在任务 action 中调用 Task.getProject()
在执行时从任务 action 调用 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 10.0 中移除。届时,除非定义了显式方法 propertyName
,否则 propertyName value
和 propertyName(value)
都将停止工作。请改用显式赋值 propertyName = value
。
对于显式方法,为了清晰起见,考虑使用 propertyName(value)
语法代替 propertyName value
。例如,对于 Test
任务,jvmArgs "some", "arg"
可以替换为 jvmArgs("some", "arg")
或 jvmArgs = ["some", "arg"]
。
如果您的项目很大,例如可以使用以下 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.10和Kotlin 2.0.0的发布说明。
JVM测试套件中默认的kotlin-test
版本也已升级到2.0.20。
为了向后兼容性,Kotlin DSL脚本仍将以Kotlin语言版本1.8进行编译。
通过工具链配置Gradle daemon JVM
属性UpdateDaemonJvm.jvmVersion
的类型现在是Property<JavaLanguageVersion>
。
如果您在构建脚本中配置了该任务,则需要将
jvmVersion = JavaVersion.VERSION_17
替换为
jvmVersion = JavaLanguageVersion.of(17)
使用命令行选项配置Gradle Daemon使用的JVM版本不会产生影响。
名称匹配变更
名称匹配逻辑已更新,将数字视为驼峰式命名中的单词边界。以前,诸如unique
的请求会同时匹配uniqueA
和unique1
。现在,由于歧义,此类请求将失败。为避免问题,请使用确切的名称而不是缩短的版本。
此变更影响
-
任务选择
-
项目选择
-
依赖报告任务中的配置选择
弃用
ForkOptions的JavaHome属性已弃用
ForkOptions
类型的JavaHome属性已弃用,并将在Gradle 9.0中移除。
请改为使用JVM工具链,或executable属性。
此弃用后来被取消,对于Gradle 8.14及更高版本,这些方法将不再抛出弃用警告。 |
改变构建脚本配置已弃用
从Gradle 9.0开始,在脚本的buildscript块中改变配置将导致错误。这适用于项目脚本、设置脚本、初始化脚本和独立脚本。
buildscript配置块仅用于控制构建脚本的classpath解析。
考虑以下脚本,该脚本在设置脚本中创建一个新的buildscript配置并对其进行解析
buildscript {
configurations {
create("myConfig")
}
dependencies {
"myConfig"("org:foo:1.0")
}
}
val files = buildscript.configurations["myConfig"].files
这种模式有时用于在设置中解析依赖项,因为没有其他方法可以获取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开始,手动将配置实例添加到配置容器将导致错误。应仅通过急切或惰性工厂方法将配置添加到容器中。分离配置和复制的配置不应添加到容器中。
调用ConfigurationContainer上的以下方法将被禁止: - add(Configuration) - addAll(Collection) - addLater(Provider) - addAllLater(Provider)
ProjectDependency#getDependencyProject()
已弃用
ProjectDependency#getDependencyProject()
方法已弃用,并将在Gradle 9.0中移除。
应避免访问其他项目的可变项目实例。
要发现关于解析中包含的所有项目的详细信息,请检查完整的ResolutionResult。项目依赖项在DependencyResult中公开。有关此API的更多详细信息,请参阅用户指南中关于编程方式依赖项解析的部分。这是查找解析中使用的所有项目的唯一可靠方法。仅检查声明的ProjectDependency
可能会遗漏传递性或被替换的项目依赖项。
要获取目标项目的身份,请使用新的Isolated Projects安全项目路径方法: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中指定的属性来替换它。
此外,依赖于define
的CompileOptions#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#exec
和 Project#javaexec
已弃用
Project#exec(Closure)、Project#exec(Action)、Project#javaexec(Closure)、Project#javaexec(Action) 方法已弃用,并将在Gradle 9.0中移除。
这些方法计划移除,作为使编写配置缓存兼容代码更轻松的持续努力的一部分。使用这些方法无法不违反配置缓存要求,因此建议迁移到兼容的替代方案。适合您的用例的替代方法取决于之前调用该方法的上下文。
在执行时,例如在@TaskAction
或doFirst
/doLast
回调中,当配置缓存启用时,不允许使用Project
实例。要运行外部进程,任务应使用注入的 ExecOperation
服务,该服务具有相同的API,并且可以作为直接替代品。标准的Java/Groovy/Kotlin进程API,如java.lang.ProcessBuilder
,也可以使用。
在配置时,当配置缓存启用时,只能使用特殊的基于Provider的API来运行外部进程。您可以使用ProviderFactory.exec
和ProviderFactory.javaexec
来获取进程的输出。对于更复杂的场景,可以使用自定义的ValueSource
实现。配置缓存指南中有一个使用这些API的更详细示例。
分离配置不应使用extendsFrom
分离配置不应使用extendsFrom
扩展其他配置。
此行为已弃用,并将在Gradle 9.0中成为错误。
要创建配置之间的扩展关系,您应该改用通过项目的ConfigurationContainer
中提供的其他工厂方法创建的非分离配置。
自定义Gradle日志记录已弃用
Gradle#useLogger(Object)方法已弃用,并将在Gradle 9.0中移除。
此方法最初旨在自定义Gradle打印的日志。但是,它只允许拦截日志的子集,并且无法与配置缓存一起工作。我们不计划为此功能引入替代方案。
编译选项和文档任务中不必要的选项已弃用
Gradle的API允许某些表示嵌套属性组的属性通过setter方法被完全替换。这样做很笨拙且不寻常,有时还需要使用内部API。为了简化API并确保一致的行为,这些属性的setter将在Gradle 9.0中移除。应改为通过调用getter并直接配置对象,或使用方便的配置方法来配置这些属性。例如,在CompileOptions
中,您可以调用getForkOptions()
或forkOptions(Action)
,而不是调用setForkOptions
setter。
受影响的属性包括
Javadoc.isVerbose()
和 Javadoc.setVerbose(boolean)
已弃用
Javadoc上的这些方法已弃用,并将在Gradle 9.0中移除。
-
调用 setVerbose(boolean) 并传入
true
由 getOptions().verbose() 替换 -
调用
setVerbose(false)
不起作用。
从8.9及更早版本升级
潜在的破坏性变更
即使不需要编译,使用JRE时JavaCompile
任务也可能失败
使用JRE而非JDK时,JavaCompile
任务有时可能会失败。这是由于工具链解析代码的更改,当请求编译器时,它会强制要求编译器的存在。当未设置sourceCompatibility
/targetCompatibility
或release
时,java-base
插件使用其创建的JavaCompile
任务来确定默认的source和target兼容性。新的强制要求导致在仅提供JRE时(即使不需要编译,例如在没有源代码的项目中)缺少编译器会失败。
这可以通过在java
扩展中显式设置sourceCompatibility
/targetCompatibility
,或在相关任务中设置sourceCompatibility
/targetCompatibility
或release
来解决。
升级到 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将编译以 target JVM 17 字节码为目标。
Gradle仍将支持编译Java代码以 target JVM 6 或更高版本为目标。编译代码的目标JVM版本可以与用于运行Gradle的JVM版本分开配置。
所有Gradle客户端(wrapper、launcher、Tooling API和TestKit)将保持与JVM 8兼容,并将编译以 target JVM 8 字节码为目标。只有Gradle daemon需要JVM 17或更高版本。可以将这些客户端配置为使用与运行客户端不同的JVM版本来运行Gradle构建
-
使用Daemon JVM标准(一个孵化中的功能)
-
设置
org.gradle.java.home
Gradle属性 -
在Tooling API上使用ConfigurableLauncher#setJavaHome方法
另外,可以将JAVA_HOME
环境变量设置为JVM 17或更高版本,这将使用相同版本的JVM运行客户端和daemon。
使用--no-daemon运行Gradle构建或在测试中使用ProjectBuilder将需要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。
改变daemon日志文件编码
在Gradle的先前版本中,位于$GRADLE_USER_HOME/daemon/8.14/
的daemon日志文件使用默认的JVM编码。现在此文件始终以UTF-8编码,以防止使用不同默认编码的客户端错误读取数据。此变更可能影响尝试读取此文件的第三方工具。
针对Gradle实现classpath进行编译
在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中,配置一旦被观察到,对其进行的任何更改都将成为错误。任何类型的配置一旦被观察到,就应被视为不可变。此验证涵盖配置的以下属性
-
解析策略
-
依赖项
-
约束
-
排除规则
-
Artifacts
-
角色 (可消费, 可解析, 依赖范围)
-
层级结构 (
extendsFrom
) -
其他 (传递性, 可见性)
从Gradle 8.8开始,对于以前不是错误的情况,将发出弃用警告。通常,此弃用是由于在beforeResolve
hook中改变配置引起的。此hook仅在配置完全解析后执行,而在为计算任务依赖项进行部分解析时不会执行。
考虑以下展示已弃用行为的代码
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
hook时,请考虑这些替代方案
-
添加依赖项:使用DependencyFactory并在DependencySet上使用
addLater
或addAllLater
。 -
更改依赖项版本:使用首选版本约束。
-
添加排除项:使用组件元数据规则调整依赖项级别的排除项,或使用withDependencies向配置添加排除项。
-
角色:配置角色应在创建时设置,之后不应更改。
-
层级结构:配置层级结构(
extendsFrom
)应在创建时设置。在解析之前改变层级结构非常不推荐,但在withDependencies hook中允许。 -
解析策略:在
beforeResolve
hook中仍然允许改变配置的解析策略;但是,不推荐这样做。
Filtered Configuration的file
和fileCollection
方法已弃用
作为简化Gradle API的持续努力的一部分,以下支持基于声明的依赖项进行过滤的方法已弃用
-
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
方法来选择配置的artifact子集
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"))
}
}
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
不考虑被过滤组件的传递性依赖项。这允许对选择哪些artifact进行更精细的控制。
Task
和 Configuration
的 Namer
已弃用
Task
和Configuration
有一个Namer
内部类(也称为Namer
),可以用作检索任务或配置名称的常用方法。现在这些类型实现了Named
,这些类不再必要,并已弃用。它们将在Gradle 9.0中移除。请改为使用Named.Namer.INSTANCE
。
超接口Namer
未被弃用。
弃用直接在本地构建缓存上设置保留期
在先前版本中,本地构建缓存条目的清理每24小时运行一次,且此间隔无法配置。保留期使用buildCache.local.removeUnusedEntriesAfterDays
进行配置。
在Gradle 8.0中,添加了一种新机制来配置Gradle用户主目录中各种资源的清理和保留期。在Gradle 8.8中,此机制得到扩展,允许配置本地构建缓存条目的保留,从而提供了改进的控制和一致性。
-
指定
Cleanup.DISABLED
或Cleanup.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)中,您可以在plugins块中使用gradle-enterprise
来应用与gradle --scan
版本相同的Gradle Enterprise插件。
plugins {
`gradle-enterprise`
}
在settings.gradle
(Groovy DSL)中没有等效项。
Gradle Enterprise 已更名为 Develocity,com.gradle.enterprise
插件已更名为 com.gradle.develocity
。因此,gradle-enterprise
插件块扩展已被弃用,并将在 Gradle 9.0 中移除。
Develocity 插件必须使用明确的插件 ID 和版本来应用。plugins 块中没有可用的 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.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。
这包括重塑 Gradle 为 SSH 操作配置 JGit 的方式,从 JSch 迁移到 Apache SSHD。
升级到 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。
弃用
插件约定的注册被弃用
自 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 中移除。不再使用 "name"()
引用任务或领域对象,而是使用 named("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,例如以下示例所示
原始输入 | 新输入 | 原因 |
---|---|---|
|
|
|
|
|
没有 scheme 时,路径原样使用,不进行解码。 |
|
空格在 URL 中无效。 |
|
在 URL 中, |
||
file::somepath |
somepath |
URI 应该是分层的。 |
弃用 SelfResolvingDependency
SelfResolvingDependency
接口已被弃用,并将在 Gradle 9.0 中移除。此类型可追溯到 Gradle 的最初版本,当时某些依赖项可以独立解析。现在,所有依赖项都应作为依赖图的一部分,使用 Configuration
进行解析。
目前,ProjectDependency
和 FileCollectionDependency
实现了此接口。在 Gradle 9.0 中,这些类型将不再实现 SelfResolvingDependency
。相反,它们都将直接实现 Dependency
。
因此,ProjectDependency
和 FileCollectionDependency
的以下方法将不再可用
-
resolve
-
resolve(boolean)
-
getBuildDependencies
请考虑以下展示已弃用接口及其替代方案的脚本
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"))
}
}
从 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
源集时,会创建新的 implementation
、compileOnly
、runtimeOnly
、api
和 compileOnlyApi
配置,并且 main
源集的编译和运行时类路径被配置为扩展这些配置。
从 Gradle 9.0 开始,main
源集将被视为任何其他源集。应用了 java-library
插件(或任何应用 java
插件的其他插件)时,使用 main
源集调用 usingSourceSet
将抛出异常。这是因为 java
插件已经配置了一个 main
特性。只有在未应用 java
插件的情况下,才允许在使用 usingSourceSet
时使用 main
源集。
当前使用 main 源集注册特性的代码,例如
plugins {
id("java-library")
}
java {
registerFeature("feature") {
usingSourceSet(sourceSets["main"])
}
}
plugins {
id("java-library")
}
java {
registerFeature("feature") {
usingSourceSet(sourceSets.main)
}
}
应该改为为该特性创建一个单独的源集,并使用该源集注册特性。
plugins {
id("java-library")
}
sourceSets {
create("feature")
}
java {
registerFeature("feature") {
usingSourceSet(sourceSets["feature"])
}
}
plugins {
id("java-library")
}
sourceSets {
feature
}
java {
registerFeature("feature") {
usingSourceSet(sourceSets.feature)
}
}
已弃用将具有显式名称的构件依赖项发布到 Maven 仓库
已弃用将具有显式构件(名称与依赖项的 artifactId
不同)的依赖项发布到 Maven 仓库。发布到 Ivy 仓库时仍然允许此行为。在 Gradle 9.0 中,这将导致错误。
发布到 Maven 仓库时,Gradle 将把下面的依赖项解释为使用坐标 org:notfoo:1.0
声明的依赖项。
dependencies {
implementation("org:foo:1.0") {
artifact {
name = "notfoo"
}
}
}
dependencies {
implementation("org:foo:1.0") {
artifact {
name = "notfoo"
}
}
}
相反,此依赖项应声明为
dependencies {
implementation("org:notfoo:1.0")
}
dependencies {
implementation("org:notfoo:1.0")
}
弃用 ArtifactIdentifier
ArtifactIdentifier
类已被弃用,并将在 Gradle 9.0 中移除。
观察后修改 DependencyCollector
依赖项已弃用
从 Gradle 9.0 开始,在观察了来自 DependencyCollector 的依赖项后修改这些依赖项将导致错误。DependencyCollector
接口用于在测试套件 DSL 中声明依赖项。
考虑以下示例,其中在观察了测试套件的依赖项后修改了它
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 版本约束。
dependencies {
constraints {
testImplementation("com:foo") {
version {
prefer("2.0")
}
}
}
}
从 8.4 及更早版本升级
潜在的重大更改
升级到 Kotlin 1.9.20
内嵌的 Kotlin 已更新到 Kotlin 1.9.20。
Groovy 任务约定的更改
groovy-base
插件现在负责配置所有 GroovyCompile
任务的源和目标兼容性版本约定。
如果您未应用 grooy-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 将不会运行构建。如果您打算动态合成项目,请确保也为其创建目录。
include("project-without-directory")
project(":project-without-directory").projectDir.mkdirs()
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 中使用的 XML 解析器以兼容 Gradle 8.4。
如果您无法升级来自构建逻辑依赖项的 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{}
自定义描述符,那么出于安全原因,构建现在将失败。
plugins {
id("ear")
}
ear {
deploymentDescriptor {
version = "1.3"
withXml {
asElement()
}
}
}
plugins {
id("ear")
}
ear {
deploymentDescriptor {
version = "1.3"
withXml {
asElement()
}
}
}
如果您碰巧使用 asNode()
而不是 asElement()
,则没有任何变化,因为 asNode()
会简单地忽略外部 DTD。
您可以通过将 javax.xml.accessExternalDTD
系统属性设置为 http
来运行构建以解决此问题。
在命令行上,将其添加到您的 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
将报告构建失败,而 TestLauncher
不会。最初,这是一个设计决策,目的是继续执行并运行所有测试任务中的测试,而不是在第一次失败时停止。同时,此行为可能让用户感到困惑,因为他们可能在成功的构建中遇到失败的测试。为了使这两个 API 更加一致,我们让 TestLauncher
也使构建失败,这是一个潜在的重大更改。Tooling API 客户端应显式向构建传递 --continue
,即使测试任务失败,也要继续执行测试。
修复了 ArtifactView
和 ArtifactCollection
的变体选择行为
用于选择不同构件或文件的依赖项解析 API(Configuration.getIncoming().artifactView { }
和 Configuration.getIncoming().getArtifacts()
)会捕获底层 `Configuration` 属性的不可变副本,用于变体选择。如果在调用这些方法后更改了 `Configuration` 的属性,则这些方法选择的构件可能会出乎意料。
考虑在创建 ArtifactView
后更改 Configuration
上的属性集的情况
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
}
}
}
myTask
的 inputFiles
属性使用构件视图从配置 classpath
中选择不同类型的构件。由于构件视图是在向配置添加属性之前创建的,Gradle 无法选择正确的构件。
一些构建可能通过也将附加属性放入构件视图来解决此问题。现在不再需要这样做。
升级到 Kotlin 1.9.0
内嵌的 Kotlin 已从 1.8.20 更新到 Kotlin 1.9.0。为了向后兼容,Kotlin DSL 的 Kotlin 语言和 API 级别仍设置为 1.8。请参阅 Kotlin 1.8.22 和 Kotlin 1.8.21 的发行说明。
Kotlin 1.9 放弃了对 Kotlin 语言和 API 级别 1.3 的支持。如果您使用此版本的 Gradle 构建用 Kotlin 编写的 Gradle 插件,并且需要支持 Gradle <7.0,则需要继续使用 Kotlin Gradle Plugin <1.9.0 并将 Kotlin 语言和 API 级别配置为 1.3。有关其他版本的详细信息,请参阅兼容性矩阵。
Configuration
属性的急切求值
Gradle 8.3 更新了 JVM Configuration 的 org.gradle.libraryelements
和 org.gradle.jvm.version
属性,使其在创建时就存在,而不是像以前那样,只有在 Configuration 被解析或消费后才存在。特别是,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 Configuration 的属性以创建具有相同属性的新 Configuration 时,可能会出现这种情况。以前,此逻辑会完全忽略上述两个属性,而现在,相同的逻辑将复制属性并最终确定项目的 Java 工具链。为了避免工具链过早最终确定,应更新属性复制逻辑,以延迟查询源 Configuration 的属性。
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)
}
}
}
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,如果在构建逻辑中读取其值然后再修改,则存在排序问题。这可能导致输出文件最终位于不同的位置。
它已被 Project.layout.buildDirectory
中的 DirectoryProperty
替换。有关详细信息,请参阅 ProjectLayout
接口。
请注意,在此阶段,如果您仍然使用 Project.buildDir
,Gradle 不会打印弃用警告。我们知道这是一个重大变化,我们希望给予主要插件的作者时间停止使用它。
从 File
切换到 DirectoryProperty
需要在构建逻辑中进行调整。主要影响是您不能在 String
中使用该属性来展开它。相反,您应该利用 dir
和 file
方法来计算您所需的位置。
以下是一个创建文件的示例,其中以下内容
// Returns a java.io.File
file("$buildDir/myOutput.txt")
// Returns a java.io.File
file("$buildDir/myOutput.txt")
应该替换为
// 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 }
// 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 }
以下是创建目录的另一个示例,其中以下内容
// Returns a java.io.File
file("$buildDir/outputLocation")
// Returns a java.io.File
file("$buildDir/outputLocation")
应该替换为
// 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 }
// 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 中移除。
客户端模块依赖项最初旨在允许构建通过在本地定义元数据来覆盖外部依赖项的不正确或缺失的组件元数据。此功能现已被组件元数据规则取代。
考虑以下客户端模块依赖项示例
dependencies {
implementation(module("org:foo:1.0") {
dependency("org:bar:1.0")
module("org:baz:1.0") {
dependency("com:example:1.0")
}
})
}
dependencies {
implementation module("org:foo:1.0") {
dependency "org:bar:1.0"
module("org:baz:1.0") {
dependency "com:example:1.0"
}
}
}
这可以用以下组件元数据规则替换
@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)
}
}
}
}
}
}
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")
}
@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)
}
}
}
}
}
}
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。
升级到 CodeNarc 3.2.0
CodeNarc 的默认版本已更新到 CodeNarc 3.2.0。
升级到 JaCoCo 0.8.9
JaCoCo 已更新到 0.8.9。
插件兼容性更改
使用 Kotlin DSL 函数 Project.the<T>()
、Project.the(KClass)
或 Project.configure<T> {}
且用 Gradle >= 8.2 编译的插件不能在 Gradle ⇐ 6.1 上运行。
延迟或避免某些任务的配置
执行依赖解析时,Gradle 会创建一个可用 Configuration 的内部表示。这需要检查所有配置和 artifact。处理由任务创建的 artifact 会导致这些任务被实例化和配置。
现在更懒惰地创建此内部表示,这可能会改变任务的配置顺序。某些任务可能永远不会被配置。
此更改可能导致依赖特定顺序的代码路径不再起作用,例如基于某些属性的存在有条件地向配置添加属性。
这影响了 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()
方法用于检索配置及其父配置上所有已声明的依赖,它不会被限制用于任何特定用法。
废弃的插件约定访问
约定的概念已过时,已被 扩展(extensions)取代,用于提供自定义 DSL。
为了在 Gradle API 中体现这一点,以下元素已废弃:
-
org.gradle.api.internal.HasConvention
Gradle 核心插件为了向后兼容,除了注册扩展外,仍然注册其约定。
访问任何这些约定及其属性已被废弃。现在这样做会发出废弃警告。这将在 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 中移除。更多背景信息,请参阅关于插件约定废弃的部分。
约定已被由 BasePluginExtension 支持的 base { }
配置块取代。旧的约定对象定义了带有简单 getter 和 setter 方法的 distsDirName,
libsDirName
和 archivesBaseName
属性。这些方法仅在扩展中可用以保持向后兼容性。构建脚本应仅使用 Property
类型的属性。
plugins {
base
}
base {
archivesName.set("gradle")
distsDirectory.set(layout.buildDirectory.dir("custom-dist"))
libsDirectory.set(layout.buildDirectory.dir("custom-libs"))
}
plugins {
id 'base'
}
base {
archivesName = "gradle"
distsDirectory = layout.buildDirectory.dir('custom-dist')
libsDirectory = layout.buildDirectory.dir('custom-libs')
}
废弃的 application
插件约定
由 application
插件贡献的约定属性已被废弃,并计划在 Gradle 9.0 中移除。更多背景信息,请参阅关于插件约定废弃的部分。
以下代码现在会发出废弃警告:
plugins {
application
}
applicationDefaultJvmArgs = listOf("-Dgreeting.language=en") // Accessing a convention
plugins {
id 'application'
}
applicationDefaultJvmArgs = ['-Dgreeting.language=en'] // Accessing a convention
这应改为使用由 JavaApplication 支持的 application { }
配置块:
plugins {
application
}
application {
applicationDefaultJvmArgs = listOf("-Dgreeting.language=en")
}
plugins {
id 'application'
}
application {
applicationDefaultJvmArgs = ['-Dgreeting.language=en']
}
废弃的 java
插件约定
由 java
插件贡献的约定属性已被废弃,并计划在 Gradle 9.0 中移除。更多背景信息,请参阅关于插件约定废弃的部分。
以下代码现在会发出废弃警告:
plugins {
id("java")
}
configure<JavaPluginConvention> { // Accessing a convention
sourceCompatibility = JavaVersion.VERSION_18
}
plugins {
id 'java'
}
sourceCompatibility = 18 // Accessing a convention
这应改为使用由 JavaPluginExtension 支持的 java { }
配置块:
plugins {
id("java")
}
java {
sourceCompatibility = JavaVersion.VERSION_18
}
plugins {
id 'java'
}
java {
sourceCompatibility = JavaVersion.VERSION_18
}
废弃的 war
插件约定
由 war
插件贡献的约定属性已被废弃,并计划在 Gradle 9.0 中移除。更多背景信息,请参阅关于插件约定废弃的部分。
以下代码现在会发出废弃警告:
plugins {
id("war")
}
configure<WarPluginConvention> { // Accessing a convention
webAppDirName = "src/main/webapp"
}
plugins {
id 'war'
}
webAppDirName = 'src/main/webapp' // Accessing a convention
客户端应直接配置 war
任务。此外,可以使用 tasks.withType(War.class).configureEach(…) 来配置 War
类型的每个任务。
plugins {
id("war")
}
tasks.war {
webAppDirectory.set(file("src/main/webapp"))
}
plugins {
id 'war'
}
war {
webAppDirectory = file('src/main/webapp')
}
废弃的 ear
插件约定
由 ear
插件贡献的约定属性已被废弃,并计划在 Gradle 9.0 中移除。更多背景信息,请参阅关于插件约定废弃的部分。
以下代码现在会发出废弃警告:
plugins {
id("ear")
}
configure<EarPluginConvention> { // Accessing a convention
appDirName = "src/main/app"
}
plugins {
id 'ear'
}
appDirName = 'src/main/app' // Accessing a convention
客户端应直接配置 ear
任务。此外,可以使用 tasks.withType(Ear.class).configureEach(…) 来配置 Ear
类型的每个任务。
plugins {
id("ear")
}
tasks.ear {
appDirectory.set(file("src/main/app"))
}
plugins {
id 'ear'
}
ear {
appDirectory = file('src/main/app') // use application metadata found in this folder
}
废弃的 project-report
插件约定
由 project-reports
插件贡献的约定属性已被废弃,并计划在 Gradle 9.0 中移除。更多背景信息,请参阅关于插件约定废弃的部分。
以下代码现在会发出废弃警告:
plugins {
`project-report`
}
configure<ProjectReportsPluginConvention> {
projectReportDirName = "custom" // Accessing a convention
}
plugins {
id 'project-report'
}
projectReportDirName = "custom" // Accessing a convention
请转而配置您的报告任务:
plugins {
`project-report`
}
tasks.withType<HtmlDependencyReportTask>() {
projectReportDirectory.set(project.layout.buildDirectory.dir("reports/custom"))
}
plugins {
id 'project-report'
}
tasks.withType(HtmlDependencyReportTask) {
projectReportDirectory = project.layout.buildDirectory.dir("reports/custom")
}
依赖自动测试框架实现依赖
在某些情况下,Gradle 会从 Gradle 发行版加载 JVM 测试框架依赖以执行测试。此现有行为可能导致测试类路径上的测试框架依赖版本冲突。为了避免这些冲突,此行为已被废弃,并将于 Gradle 9.0 中移除。使用 TestNG 的测试不受影响。
为了应对此行为变更,您可以显式声明所需依赖,或迁移到 Test Suites,其中这些依赖会自动管理。
Test Suites
使用测试套件的构建不会受到此更改的影响。测试套件会自动管理测试框架依赖,并且不需要显式声明依赖。有关迁移到测试套件的更多信息,请参阅用户手册。
手动声明依赖
在没有测试套件的情况下,必须在测试运行时类路径上手动声明依赖:
-
如果使用 JUnit 5,除了对测试引擎的现有
implementation
依赖外,还需要显式runtimeOnly
依赖于junit-platform-launcher
。 -
如果使用 JUnit 4,仅需要对 JUnit 4 的现有
implementation
依赖。 -
如果使用 JUnit 3,除了对 JUnit 3 的
compileOnly
依赖外,还需要对 JUnit 4 的测试runtimeOnly
依赖。
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")
}
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'
}
BuildIdentifier
和 ProjectComponentSelector
方法废弃
BuildIdentifier
上的以下方法已废弃:
-
getName()
-
isCurrentBuild()
您可以使用这些方法区分具有相同名称但来自不同构建的不同项目组件。然而,对于某些复合构建设置,这些方法提供的信息不足以保证唯一性。
这些方法的当前用法应迁移到 BuildIdentifier.getBuildPath()
。
类似地,ProjectComponentSelector.getBuildName()
方法已废弃。请转而使用 ProjectComponentSelector.getBuildPath()
。
从 8.0 及更早版本升级
在全局缓存目录中创建 CACHEDIR.TAG 文件
Gradle 现在会在一些全局缓存目录中发出 CACHEDIR.TAG
文件,如 directory_layout.html 中指定。
这可能导致某些工具不再搜索或备份这些目录。要禁用它,请在 Gradle User Home 中的 init script 中使用以下代码:
beforeSettings {
caches {
// Disable cache marking for all caches
markingStrategy.set(MarkingStrategy.NONE)
}
}
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…
属性在此版本中仍然有效,如果使用它们,将不会产生警告,但它们将在未来版本中被废弃和移除。
潜在的破坏性变更
Kotlin DSL 脚本发出编译警告
Kotlin DSL 脚本的编译警告会打印到控制台输出。例如,在 Kotlin DSL 中使用废弃的 API 将在每次编译脚本时发出警告。
如果您正在消费 Gradle 构建的控制台输出,这可能是一个潜在的破坏性更改。
应用了 kotlin-dsl
插件时配置 Kotlin 编译器选项
如果您在应用了 kotlin-dsl 插件的项目中配置自定义 Kotlin 编译器选项,可能会遇到破坏性更改。
在之前的 Gradle 版本中,kotlin-dsl
插件会在 afterEvaluate {} 中添加所需的编译器参数。现在,Kotlin Gradle 插件提供了 延迟配置 属性,我们的 kotlin-dsl
插件已切换为直接向延迟属性添加所需的编译器参数。因此,如果您设置了 freeCompilerArgs
,现在 kotlin-dsl
插件会导致构建失败,因为其所需的编译器参数被您的配置覆盖了。
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
插件所需的参数后面:
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 起,在配置时使用 Project.exec
、Project.javaexec
以及标准 Java 和 Groovy API 运行外部进程仅在 特性预览 STABLE_CONFIGURATION_CACHE
启用时才被视为错误。随着配置缓存在 Gradle 8.1 中提升为稳定特性,无论特性预览状态如何,都会检测到此错误。配置缓存章节提供了更多详细信息,以帮助迁移到新的基于 provider 的 API 在配置时执行外部进程。
不使用配置缓存或仅在执行时启动外部进程的构建不受此更改影响。
废弃
修改核心插件配置用法
配置的允许用法在创建后应是不可变的。修改由 Gradle 核心插件创建的配置的允许用法已被废弃。这包括调用以下任何 Configuration
方法:
-
setCanBeConsumed(boolean)
-
setCanBeResolved(boolean)
这些方法现在会在这些配置上发出废弃警告,某些特殊情况除外,这些特殊情况允许了流行插件的现有行为。此规则尚不适用于 detached configurations 或在构建脚本和第三方插件中创建的配置。调用 apiElements
或 runtimeElements
上的 setCanBeConsumed(false)
尚未废弃,以避免在使用某些流行的第三方插件时否则会发出的警告。
此更改是正在进行的一项更广泛努力的一部分,旨在使配置的预期行为更加一致和可预测,并在此领域解锁进一步的速度和内存改进。
在创建后更改配置的允许用法的能力将在 Gradle 9.0 中移除。
保留的配置名称
配置名称 "detachedConfiguration" 和 "detachedConfigurationX"(其中 X 是任何整数)保留用于在创建 detached configurations 时的内部使用。
使用这些名称创建 non-detached configurations 的能力将在 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
任务的 testClassesDirs
和 classpath
具有相同的约定。除非另有更改,默认行为是通过使用来自默认 test
TestSuite
的 classpath
和 testClassesDirs
配置任务来执行测试。此行为将在 Gradle 9.0 中移除。
虽然此现有默认行为对于在不同环境下执行默认单元测试套件的用例是正确的,但它不支持执行完全独立的测试集的用例。
如果您希望继续包含这些测试,请使用以下代码避免 8.1 中的废弃警告,并为 9.0 中的行为变更做好准备。或者,考虑迁移到测试套件。
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 })
}
tasks.myTestTask {
testClassesDirs = testing.suites.test.sources.output.classesDirs
classpath = testing.suites.test.sources.runtimeClasspath
}
在填充 publication 后修改 Gradle Module Metadata
在从其组件填充 Maven 或 Ivy publication 之后修改 GMM(例如,更改组件配置变体)现在已被废弃。此功能将在 Gradle 9.0 中移除。
如果调用以下方法,可能会发生 publication 的即时填充:
-
Maven
-
Ivy
以前,以下代码不会生成警告,但它会在已发布的 artifact 之间创建不一致性:
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() }
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 publication 将包含项目的主要 JAR artifact,而 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 插件 < 1.8.0 一起应用
将 kotlin-dsl
与 Kotlin Gradle 插件 < 1.8.0 一起应用已被废弃。请移除构建逻辑中任何显式的 kotlin-dsl
版本约束,让 Gradle 控制 kotlin-dsl
的版本。这将使 kotlin-dsl
插件决定使用哪个版本的 Kotlin Gradle 插件。如果您显式声明了用于构建逻辑的 Kotlin Gradle 插件版本,请将其更新到 >= 1.8.0。
在 Kotlin 脚本的 plugins {}
块中访问依赖版本目录中的 libraries
或 bundles
在 Kotlin 脚本的 plugins {}
块中访问依赖版本目录中的 libraries
或 bundles
已被废弃。请在 plugins {}
块中仅使用依赖版本目录中的 versions
或 plugins
。
在没有 Java 工具链的情况下使用 ValidatePlugins
任务
在使用 ValidatePlugins 类型的任务时没有应用 Java 工具链插件已被废弃,并将成为 Gradle 9.0 中的错误。
为了避免此警告,请将插件应用于您的项目:
plugins {
id("jvm-toolchains")
}
plugins {
id 'jvm-toolchains'
}
Java 工具链插件由 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
,以避免警告和在下一个主要版本发布中的潜在错误。
在 StartParameter
和 GradleBuild
上设置自定义构建布局
继 Gradle 7.1 中相关先前废弃的行为之后,现在使用相关的 StartParameter 和 GradleBuild 属性也已被废弃。这些属性将在 Gradle 9.0 中移除。
在 GradleBuild 任务中使用 buildFile 属性设置自定义构建文件已被废弃。
请转而使用 dir 属性指定嵌套构建的根目录。或者,考虑使用 GradleBuild 任务推荐的替代方案之一。
使用 StartParameter 方法 setBuildFile(File) 和 setSettingsFile(File) 以及对应的 getter getBuildFile() 和 getSettingsFile() 设置自定义构建布局已被废弃。
请使用 settings 文件和构建文件的标准位置:
-
settings 文件位于构建根目录
-
构建文件位于每个子项目的根目录
废弃的 org.gradle.cache.cleanup 属性
Gradle User Home 下 gradle.properties
文件中的 org.gradle.cache.cleanup
属性已被废弃。请转而使用 cache cleanup DSL 禁用或修改清理配置。
由于旧版本的 Gradle 可能仍然需要 org.gradle.cache.cleanup
属性,只要同时通过 DSL 配置,该属性可能仍然存在并且不会打印废弃警告。DSL 的值将始终优先于 org.gradle.cache.cleanup
属性。如果期望的配置是为旧版本 Gradle 禁用清理(使用 org.gradle.cache.cleanup
),但为 Gradle 8 及以上版本启用默认值的清理,则应将清理配置为使用 Cleanup.DEFAULT。
if (GradleVersion.current() >= GradleVersion.version('8.0')) {
apply from: "gradle8/cache-settings.gradle"
}
if (GradleVersion.current() >= GradleVersion.version("8.0")) {
apply(from = "gradle8/cache-settings.gradle")
}
beforeSettings { settings ->
settings.caches {
cleanup = Cleanup.DEFAULT
}
}
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 {}
块中显示的误报错误的解决方法已不再需要
在 IntelliJ IDEA 和 Android Studio Kotlin 脚本编辑器中,plugins {}
块中用于插件别名的版本目录访问器不再显示为错误。
如果您之前使用 @Suppress("DSL_SCOPE_VIOLATION")
注解作为解决方法,现在可以将其移除。
如果您之前使用了 IntelliJ IDEA 插件 Gradle Libs Error Suppressor,现在可以将其卸载。
将 Gradle 升级到 8.1 后,您需要清除 IDE 缓存并重新启动。