The Gradle team is excited to announce Gradle 8.7.
Java 22 is now supported for compiling, testing, and running JVM-based projects.
Script compilation for the Groovy DSL can now be avoided thanks to the build cache.
Additionally, this release includes improvements to build authoring, error and warning messages, the configuration cache, and the Kotlin DSL.
See the full release notes below for details.
We would like to thank the following community members for their contributions to this release of Gradle: Aleksandr Postnov, Björn Kautler, Brice Dutheil, Denis Buzmakov, Federico La Penna, Gregor Dschung, Hal Deadman, Hélio Fernandes Sebastião, Ivan Gavrilovic, Jendrik Johannes, Jörgen Andersson, Marie, pandaninjas, Philip Wedemann, Ryan Schmitt, Steffen Yount, Tyler Kinkade, Zed Spencer-Milnes
Be sure to check out the public roadmap for insight into what's planned for future releases.
Switch your build to use Gradle 8.7 by updating your wrapper:
./gradlew wrapper --gradle-version=8.7
See the Gradle 8.x upgrade guide to learn about deprecations, breaking changes and other considerations when upgrading to Gradle 8.7.
For Java, Groovy, Kotlin, and Android compatibility, see the full compatibility notes.
Gradle now supports using Java 22 for compiling, testing, and starting other Java programs. Selecting a language version is done using toolchains.
You cannot run Gradle 8.7 itself with Java 22 because Groovy still needs to support JDK 22. However, future versions are expected to provide this support.
The Gradle build cache is a mechanism designed to save time by reusing local or remote outputs from previous builds.
In this release, Groovy build script compilation can benefit from the remote build cache, which, when enabled, reduces initial build times for developers by avoiding this step altogether.
While this feature has been available for Kotlin build script compilation since the introduction of the Kotlin DSL in Gradle 5.0, the Groovy DSL lacked this feature parity.
As a build grows in complexity, it can be challenging to determine when and where particular values are configured. Gradle provides an efficient way to manage this complexity using lazy configuration.
This release improves the API for lazy collection properties, a key element of Gradle lazy configuration. Before this release, the interaction of classical collection methods, the concept of convention, and the rules around empty providers have resulted in surprising behaviors for users in some scenarios. Based on the community feedback, this release of Gradle introduces alternative APIs for updating collections with a clearer contract:
HasMultipleValues.append*(...)
which are meant as more convenient replacements for HasMultipleValues.add*(...)
.MapProperty.insert*(...)
which are meant as more convenient replacements for MapProperty.put*(...)
.The new APIs provide the following benefits:
One common complaint was that adding values (using ListProperty.add(...)
, SetProperty.add(...)
or MapProperty.put(...)
on top of values from a convention would result in losing the values from the convention.
For example, in an applied plugin, a list property ListProperty<String>
is configured with a convention:
listProp.convention(listOf("one"))
In the build file, the build author adds elements to that list property:
listProp.add("two")
// listProp now only contains "two", that’s confusing
However, as explained in the snippet, the behavior is surprising. The newly introduced methods (such as ListProperty.append(...)
, SetProperty.append(...)
and MapProperty.insert(...)
) allow the user to express that the convention should be preserved:
listProp.append("two")
// listProp now contains ["one", "two"], as expected
Another common source of confusion is how empty providers are handled in collection properties. For instance, adding an empty provider to a collection property using add(...)
will cause the entire property to become void of any values as well.
listProp.add("one")
listProp.add(providers.environmentVariable("myEnvVar"))
// listProp will be empty if `myEnvVar` is not defined
In order to avoid that behavior, you can instead use the new update APIs introduced in this release (such as ListProperty.append(...)
, SetProperty.append(...)
and MapProperty.insert(...)
):
listProp.append("one")
listProp.append(providers.environmentVariable("myEnvVar"))
// listProp will still contain "one" if myEnvVar is not defined
Gradle provides a rich set of error and warning messages to help you understand and resolve problems in your build.
When applying a plugin that requires a higher version of Gradle (by specifying the org.gradle.plugin.api-version
attribute), the error message when dependency resolution fails will now clearly state the issue:
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring root project 'example'.
> Could not resolve all files for configuration ':classpath'.
> Could not resolve com.example:plugin:1.0.
Required by:
project : > com.example.greeting:com.example.greeting.gradle.plugin:1.0
> Plugin com.example:plugin:1.0 requires at least Gradle 8.0. This build uses Gradle 7.6.
* Try:
> Upgrade to at least Gradle 8.0. See the instructions at https://docs.gradle.org/8.7/userguide/upgrading_version_8.html#sub:updating-gradle.
> Downgrade plugin com.example:plugin:1.0 to an older version compatible with Gradle 7.6.
The failure’s suggested resolutions will include upgrading your version of Gradle or downgrading the version of the plugin. This replaces the previous low-level incompatibility message containing details about all the attributes involved in the plugin request.
When including a compressed archive in a Copy
task results in duplicate files and DuplicatesStrategy.Fail
is used, the error message will now clearly state the issue:
Cannot copy file <SOURCE_FILE> to <DESTINATION_DIR> because file <OTHER_SOURCE_FILE> has already been copied there.
When generating Gradle Module Metadata files, Gradle prevents your project from publishing broken metadata by looking for common configuration errors.
One of those errors is publishing metadata with dependencies that do not have versions. This validation error can now be suppressed as there are use cases where such metadata is valid:
tasks.withType(GenerateModuleMetadata).configureEach {
suppressedValidationErrors.add('dependencies-without-versions')
}
The configuration cache improves build time by caching the result of the configuration phase and reusing it for subsequent builds. This feature can significantly improve build performance.
The stack traces shown in the configuration cache report for forbidden API calls can be long and contain internal Gradle frames that do not always help to troubleshoot the issue. With this release, internal stack frames are collapsed by default to highlight the build logic that triggered the error:
The collapsed frames can still be expanded and examined if necessary.
The standard streams (System.in
, System.out
, and System.err
) can now be used as standardInput
, standardOutput
, and errorOutput
of Exec
and JavaExec
tasks without breaking configuration caching.
User created tasks with properties of types java.io.InputStream
and java.io.OutputStream
can also use the standard streams as property values. Setting up custom standard streams with System.setIn
, System.setOut
, and System.setErr
isn't supported.
The embedded Kotlin has been updated from 1.9.10 to Kotlin 1.9.22.
Javadocs generated from Java code now support a "since" section, indicating the Gradle version when the functionality was introduced.
The information comes from the @since
tags in the Javadoc, which haven’t been displayed until now. An example can be found at JavaToolchainSpec
.
Using the new --no-comments
option allows Gradle init to generate new projects that contain code without comments. The resulting build files and source files are smaller and less verbose.
gradle init --use-defaults --type kotlin-library --no-comments
You can permanently set this preference by configuring the org.gradle.buildinit.comments
property to false
in Gradle properties.
Gradle enables tasks to share state or resources, such as pre-computed values or external services, through build services, which are objects holding the state for task use. A build service can optionally take parameters, which Gradle injects into the service instance when creating it.
Shared build services that do not require additional configuration can now be registered without having to provide an empty configuration action using the updated registerIfAbsent()
method:
gradle.sharedServices.registerIfAbsent("counter", CountingService, voidAction) // Old method
gradle.sharedServices.registerIfAbsent("counter", CountingService) // New method
TestNG is a testing framework supported in Gradle. In TestNG, the threadPoolFactoryClass
attribute is used to specify a custom thread pool factory class, which details how TestNG manages threads for parallel test execution.
The threadPoolFactoryClass
parameter can now be configured on TestNGOptions
for TestNG versions that support it (i.e., TestNG 7.0.0 and above):
testing {
suites {
test {
useTestNG("7.5")
targets {
all {
testTask.configure {
options.threadPoolFactoryClass = "com.example.MyThreadPoolFactory"
}
}
}
}
}
}
An error will occur if this parameter is set for a version of TestNG that does not support it.
To mitigate the security risks and avoid integrating compromised dependencies in your project, Gradle supports dependency verification. Dependency verification is typically done using checksums or digital signatures. Gradle verifies that downloaded artifacts match the expected checksums or are signed with trusted keys.
Before this release, the ignored keys list in the metadata verification file was not properly sorted by Gradle so that the order would change depending on the execution order, impacting the reproducibility of the build and checksums for the file. In this release, the order is guaranteed to be preserved regardless of the execution order.
Gradle maintains a Virtual File System (VFS) to calculate what needs to be rebuilt on repeat builds of a project. By watching the file system, Gradle keeps the VFS current between builds, reducing the required I/O operations.
This version fixes a problem with detecting content changes indirectly referenced via symlinks, improving the build's correctness.
The strongly-typed dependencies block introduced in Gradle 7.6 did not support dependency constraints.
In this release, dependency constraints can now be added:
testing {
suites {
getByName<JvmTestSuite>("test") {
dependencies {
implementation(constraint("foo:bar:1.0"))
}
}
}
}
Providing separate strings or using named arguments for each part is not supported currently.
Known issues are problems that were discovered post release that are directly related to changes made in this release.
We love getting contributions from the Gradle community. For information on contributing, please see gradle.org/contribute.
If you find a problem with this release, please file a bug on GitHub Issues adhering to our issue guidelines. If you're not sure you're encountering a bug, please use the forum.
We hope you will build happiness with Gradle, and we look forward to your feedback via Twitter or on GitHub.