Gradle Release Notes
The Gradle team is excited to announce Gradle 8.8.
Gradle now supports
Java 22
.
This release introduces a preview feature to configure the Gradle daemon JVM
using toolchains
and
improved IDE performance
with large projects.
Additionally, this release includes many notable improvements to
build authoring
,
error and warning messages
, the
build cache
, and the
configuration cache
.
We would like to thank the following community members for their contributions to this release of Gradle:
Björn Kautler
,
Denes Daniel
,
Fabian Windheuser
,
Hélio Fernandes Sebastião
,
Jay Wei
,
jhrom
,
jwp345
,
Jörgen Andersson
,
Kirill Gavrilov
,
MajesticMagikarpKing
,
Maksim Lazeba
,
Philip Wedemann
,
Robert Elliot
,
Róbert Papp
,
Stefan M.
,
Tibor Vyletel
,
Tony Robalik
,
Valentin Kulesh
,
Yanming Zhou
,
김용후
Be sure to check out the
public roadmap
for insight into what's planned for future releases.
Table Of Contents
Upgrade instructions
New features and usability improvements
Full Java 22 support
Gradle daemon JVM configurable via toolchain
Improved IDE performance for large projects
Build authoring improvements
Error and warning reporting improvements
IDE Integration improvements
Build cache changes
Configuration cache improvements
Other improvements
Promoted features
File Permissions API is now stable
Fixed issues
Known issues
External contributions
Reporting problems
Upgrade instructions
Switch your build to use Gradle 8.8 by updating your wrapper:
./gradlew wrapper --gradle-version=8.8
See the
Gradle 8.x upgrade guide
to learn about deprecations, breaking changes, and other considerations when upgrading to Gradle 8.8.
For Java, Groovy, Kotlin, and Android compatibility, see the
full compatibility notes
.
New features and usability improvements
Full Java 22 support
With this release, Gradle supports running on
Java 22
. This means you can now use Java 22 for the
daemon
in addition to
toolchains
.
For details, see the full
compatibility documentation
.
Previously, Gradle did not support capturing the JVM requirements of the build, which could lead to build failures when using the wrong JVM version. This made such builds harder to import into IDEs or run locally.
With this release, users can
configure the JVM
used to run a Gradle build. This feature is built on top of
Java toolchains
and works similarly to how the
Gradle wrapper
captures Gradle version requirements.
This is an incubating feature that will change in future releases.
When invoking large builds from an IDE, the
Tooling API's
execution of extensive task graphs suffered from a performance penalty caused by transferring unnecessary information.
Eliminating this transfer results in performance improvements of up to 12% in large up-to-date builds with over 15,000 tasks in their task graph.
We want to thank a
community member
for identifying and fixing this issue.
Updating your Gradle version will immediately benefit Android Studio, IntelliJ IDEA, Eclipse, and other
Tooling API clients
.
Build authoring improvements
Gradle provides rich APIs for plugin authors and build engineers to develop custom build logic.
Allow version catalog plugin aliases without a version
Previously, a
version catalog plugin alias
could be defined without a version, but attempting to use it would result in an exception. It is now explicitly allowed to have a plugin alias with no version, and no exception will be thrown when using it:
# In libs.versions.toml
[plugins]
myPlugin = { id = "my.plugin.id" }
// In build.gradle(.kts)
plugins {
alias(libs.plugins.myPlugin)
Accessors for Settings
extensions in Kotlin DSL
Previously, extensions registered in Plugin<Settings>
weren't available in settings.gradle.kts
. Now, type-safe accessors for these extensions are generated.
interface MySettingsExtension {
val myProperty: Property<Int>
Assuming you register the extension above as mySettingsExtension
, then you can access it directly:
// In settings.gradle.kts
// accessor function
mySettingsExtension {
myProperty = 42
// accessor property
println(mySettingsExtension.myProperty)
This fixes a long-standing issue.
Ability to set conventions on file collections
Plugin-provided tasks often expose file collections that build engineers need to customize, such as the classpath for the JavaCompile task. Until now, plugin authors defined default values for these collections by setting initial values.
With this release, Gradle introduces a more flexible approach using conventions. Conventions allow plugin authors to recommend default values, which users can then accept, extend, or completely replace.
This release introduces a pair of convention(...)
methods on ConfigurableFileCollection
that define the default value of a file collection if no explicit value is previously set via setFrom(...)
or from(...)
:
val files = objects.fileCollection().convention("dir1")
files.from("dir2")
println(files.elements.get()) // [.../dir1, .../dir2]
#from(...)
will honor the convention if one is configured when invoked, so the order of operations will matter.
To forcefully override or prevent a convention (i.e., regardless of the order of those operations), use `#setFrom():
val files = objects.fileCollection().convention("dir1")
files.setFrom("dir2")
println(files.elements.get()) // [.../dir2]
This is analogous to the convention(...)
methods that have been available on lazy properties since Gradle 5.1.
New Gradle lifecycle callbacks
This release introduces a new GradleLifecycle
API, accessible via gradle.lifecycle
, which plugin authors and build engineers can use to register actions to be executed at certain points in the build lifecycle.
Actions registered as GradleLifecycle
callbacks (currently, beforeProject
and afterProject
) are isolated, running in an isolated context that is private to every project. This will allow Gradle to perform additional performance optimizations and will be required in the future to take advantage of parallelism during the build configuration phase.
While the existing callbacks continue to work, we encourage everyone to adopt the new API and provide us with early feedback.
The example below shows how this new API could be used in a settings script or settings plugins to apply configuration to all projects, while avoiding cross-project configuration:
// settings.gradle.kts
include("sub1")
include("sub2")
gradle.lifecycle.beforeProject {
apply(plugin = "base")
repositories {
mavenCentral()
Isolated project views
There is now support for obtaining an isolated view of a project as an IsolatedProject
via Project.getIsolated()
.
The view exposes only those properties that are safe to access across project boundaries when running the build configuration phase in parallel (to be supported in a future release).
The example below shows how the API could be used from a Project
configuration callback to query the root project directory in a parallel-safe way:
gradle.lifecycle.beforeProject {
val rootDir = project.isolated.rootProject.projectDirectory
println("The root project directory is $rootDir")
Error and warning reporting improvements
Gradle provides a rich set of error and warning messages to help you understand and resolve problems in your build.
This update improves how Gradle handles errors when downloading Java toolchains from configured resolvers.
Previously, if a resolver threw an exception while mapping toolchain specs to download URLs or during the auto-provisioning process, Gradle did not try other configured resolvers. Gradle will now handle these errors better by attempting to use other resolvers in such cases.
Improved JVM version mismatch error reporting
When depending on a library that requires a higher version of the JVM runtime than is requested via the automatically supplied TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE
attribute, or when applying a plugin that requires a higher version or the JVM runtime than the current JVM supplies, dependency resolution will fail.
The error message in this situation will now clearly state the issue:
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring root project 'example'.
> Could not determine the dependencies of task ':consumer:compileJava'.
> Could not resolve all task dependencies for configuration ':consumer:compileClasspath'.
> Could not resolve project :producer.
Required by:
project :consumer
> project :producer requires at least a Java 18 JVM. This build uses a Java 17 JVM.
* Try:
> Run this build using a Java 18 JVM (or newer).
> Change the dependency on 'project :producer' to an earlier version that supports JVM runtime version 17.
The failure’s suggested resolutions will include upgrading your JVM or downgrading the version of the dependency.
This replaces the previous low-level incompatibility message, which was difficult to understand without knowledge of Gradle internals. This new error message can be especially helpful for users upgrading the Spring Boot dependencies or the Spring Boot Gradle plugin to version 3+, which requires Java 17 or later.
Fixed error message when buildscript dependencies fail to resolve
When a build script fails to resolve dependencies on its classpath, the error message will now more clearly state the issue:
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring root project 'unified-prototype'.
> Could not resolve all dependencies for configuration ':classpath'.
> Could not resolve project :unified-plugin:plugin-android.
Previously, the error message contained a null
and a possibly misleading reference to "task dependencies":
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring root project 'unified-prototype'.
> Could not determine the dependencies of null.
> Could not resolve all task dependencies for configuration ':classpath'.
> Could not resolve project :unified-plugin:plugin-android.
Fixed error reporting when repositories are disabled
When Gradle determines that a particular repository is unavailable when requesting a dependency, it will stop trying to resolve any dependencies from that repository. This can prevent other dependencies from resolving successfully.
The new error message will now print the underlying cause of the issue:
* What went wrong:
A problem occurred configuring root project 'fevi6'.
> Could not resolve all artifacts for configuration ':classpath'.
> Could not resolve group:a:1.0.
Required by:
project :
> Could not resolve group:a:1.0.
> Could not get resource 'http://127.0.0.1:49179/repo/group/a/1.0/a-1.0.pom'.
> Could not GET 'http://127.0.0.1:49179/repo/group/a/1.0/a-1.0.pom'.
> Read timed out
Previously, this could result in an error message that only mentioned that dependencies failed to resolve due to "Skipped to earlier error", without printing the error itself:
* What went wrong:
A problem occurred configuring root project 'fevi6'.
> Could not resolve all artifacts for configuration ':classpath'.
> Could not resolve group:a:1.0.
Required by:
project :
> Skipped due to earlier error
> Could not resolve group:b:1.0.
Required by:
project :
> Skipped due to earlier error
> Could not resolve group:c:1.0.
Required by:
project :
> Skipped due to earlier error
Suppressed duplicate error reporting when multiple failures have the same cause
When multiple failures have the same cause, Gradle will now only print the first failure, which includes all the necessary details.
Any additional failures stemming from the same cause will be summarized at the end of the message, indicating how many were found:
* What went wrong:
Execution failed for task ':resolve'.
> Could not resolve all files for configuration ':deps'.
> Could not resolve group:a:1.0.
Required by:
project : > group:d:1.0
> <SOME FAILURE CAUSE>
> There are 2 more failures with identical causes.
IDE Integration improvements
Gradle is integrated into many IDEs using the Tooling API.
The following improvements are for IDE integrators. They will become available to end-users in future IDE releases once IDE vendors adopt them.
IDEs and other tools leverage the tooling API to access information about tests executed by Gradle. Each test event sent via the tooling API includes a test descriptor containing metadata such as a human-readable name, class name, and method name.
Previously, the test display name could only be obtained by parsing the operation display name, which was not always reliable. A new method to the TestOperationDescriptor
interface called getTestDisplayName
provides the test display name.
For JUnit5 and Spock, we updated the test descriptor for dynamic and parameterized tests to include information about the class name and method name containing the test. These enhancements enable IDEs to offer improved navigation and reporting capabilities for dynamic and parameterized tests.
Build cache changes
The Gradle build cache is a mechanism designed to save time by reusing local or remote outputs from previous builds.
Improved control of local build cache cleanup
With this release, local build cache cleanup is configurable via the standard init-script mechanism, providing improved control and consistency.
Specifying Cleanup.DISABLED
or Cleanup.ALWAYS
will now prevent or force the cleanup of the local build cache
Build cache entry retention is now configured via Settings.caches.buildCache.setRemoveUnusedEntriesAfterDays()
//init.gradle.kts
beforeSettings {
caches {
cleanup = Cleanup.ALWAYS
buildCache.setRemoveUnusedEntriesAfterDays(30)
Previously, the retention period was configured via the DirectoryBuildCache.removeUnusedEntriesAfterDays
setting. See the upgrade guide for details on how to adopt the new API.
Groovy build script compilation build cache support is disabled
In Gradle 8.7, we introduced support for using the remote build cache with Groovy build script compilation. However, after receiving reports of slower compile times with the remote cache, we conducted further investigation. Our findings showed that the cache was not delivering the expected performance improvements.
As a result, we have disabled the remote build cache for Groovy build script compilation in this release.
Configuration cache improvements
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.
Support for Java Record classes and Externalizable
instances
The configuration cache now supports:
Java Record classes
java.io.Externalizable instances
Other improvements
Allow specifying source encoding for Jacoco report tasks
For the JaCoCo Plugin, the source encoding of JacocoReport
tasks may now be specified via the sourceEncoding
property:
jacocoTestReport {
sourceEncoding = 'UTF-8'
Filter standard output and error output in XML test reports
When testing Java projects using common frameworks, reports are typically produced in XML format. The new includeSystemOutLog
and includeSystemErrLog
options control whether output written to standard output and standard error output during testing is included in those XML test reports. This report format is used by JUnit 4, JUnit Jupiter, and TestNG, despite the name of the report format, and can be configured when using any of these test frameworks.
Disabling these options can be useful when running a test task, as they result in a large amount of standard output or standard error data that is irrelevant to testing. It is also useful for preserving disk space when running jobs on CI.
You can set these options by configuring the JUnitXmlReport options block:
tasks.test {
reports.junitXml {
includeSystemOutLog = false
includeSystemErrLog = true
Setting custom POM values in Maven publications for plugins
The Maven-publish plugin provides a way to set custom POM values for a Gradle plugin publication, as demonstrated in the following example:
gradlePlugin {
plugins {
register("some.plugin") {
name = "SomePluginName"
description = "SomePluginDesc"
publishing {
publications.withType<MavenPublication> {
pom {
name = "CustomPublicationName"
description = "CustomPublicationDesc"
Previously, this was not working as expected, and the published POM would contain the name and description values configured via the plugins
block.
Now, any values configured in the pom
block will take precedence if present and be written to the published POM for that publication:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<name>CustomPublicationName</name>
<description>CustomPublicationDesc</description>
</project>
This fixes a long-standing issue.
Custom dependencies
blocks
Since Gradle 8.7, it's been possible for plugins to define their own dependencies-like block. A custom dependencies block allows users to declare dependencies in a type-safe and context-aware way:
// ExamplePlugin.java
public class ExamplePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
ExampleExtension example = project.getExtensions().create("example", ExampleExtension.class);
// build.gradle.kts
example {
dependencies {
implementation("junit:junit:4.13")
This is currently used by the JVM test suite plugin.
See the user manual to learn more about using this incubating feature.
Promoted features are features that were incubating in previous versions of Gradle but are now supported and subject to backward compatibility. See the User Manual section on the “Feature Lifecycle” for more information.
The following are the features that have been promoted in this Gradle release.
File Permissions API is now stable
The File Permissions API for defining file permissions using UNIX style values (added in Gradle 8.3) is now stable; see:
FilePermissions
ConfigurableFilePermissions
CopyProcessingSpec.getFilePermissions()
CopyProcessingSpec.filePermissions(Action)
CopyProcessingSpec.getDirPermissions()
CopyProcessingSpec.dirPermissions(Action)
FileCopyDetails.permissions(Action)
FileCopyDetails.setPermissions(FilePermissions)
FileSystemOperations.filePermissions(Action)
FileSystemOperations.directoryPermissions(Action)
FileSystemOperations.permissions(int)
FileSystemOperations.permissions(String)
FileSystemOperations.permissions(Provider)
FileTreeElement.getPermissions()
Fixed issues
Known issues
Known issues are problems that were discovered post-release that are directly related to changes made in this release.
External contributions
We love getting contributions from the Gradle community. For information on contributing, please see gradle.org/contribute.
Reporting problems