# Gradle Release Notes

The Gradle team is excited to announce Gradle 9.0.0, a new major release (released 2025-07-31). For an overview of the changes between Gradle 8.0 and 9.0.0, along with demos and videos, see What’s New in Gradle 9.

This release makes Configuration Cache the preferred execution mode and introduces several enhancements to improve its behavior.

Gradle 9.0.0 requires Java 17+ to run, uses Kotlin 2 and Groovy 4, and adopts Semantic Versioning (SemVer) with version numbers in the format `MAJOR.MINOR.PATCH`.

It also introduces several improvements for build authors, including much better Kotlin DSL script compilation avoidance, updates to the Gradle API, reproducible archive outputs, and a new dependency graph root type that allows detached configurations to resolve project dependencies.

Gradle 9.0.0 includes numerous bug fixes and general improvements. As a major release, it also introduces changes to deprecated APIs and behaviors. For details on what has been removed or updated, refer to the Gradle 9.0.0 upgrade guide.

We would like to thank the following community members for their contributions to this release of Gradle: Aaron Matthis, Adam E, Adam S, Björn Kautler, Daniel Lacasse, Eng Zer Jun, EunHyunsu, FlorianMichael, Francisco Prieto, Gaëtan Muller, Jake Wharton, Kengo TODA, Kent Kaseda, Madalin Valceleanu, Marc Philipp, Mark S. Lewis, Matthew Haughton, Mycroft Wong, Na Minhyeok, Nelson Osacky, Olivier "Oli" Dagenais, ploober, Radai Rosenblatt, Róbert Papp, Sebastian Schuberth, Victor Merkulov.

Be sure to check out the public roadmap for insight into what's planned for future releases.

## Table Of Contents

## Upgrade instructions

Switch your build to use Gradle 9.0.0 by updating the wrapper in your project:

    ./gradlew wrapper --gradle-version=9.0.0 && ./gradlew wrapper

See the Gradle 9.0.0 upgrade guide to learn about deprecations, breaking changes, and other considerations when upgrading to Gradle 9.0.0.

For Java, Groovy, Kotlin, and Android compatibility, see the full compatibility notes.

## New features and usability improvements

### Configuration Cache improvements

Gradle's Configuration Cache improves build performance by caching and reusing the result of the configuration phase.

#### Configuration Cache as the preferred execution mode

The Configuration Cache is the preferred mode of execution. While not yet required, Gradle encourages adoption by prompting users and gradually phasing out incompatible APIs to prepare for a future where it becomes the only supported mode.

#### Prompt to enable Configuration Cache

If your build has no known Configuration Cache incompatibilities but doesn't yet have the Configuration Cache enabled, Gradle will suggest enabling it at the end of the build:

Some issues can only be detected when the Configuration Cache is active, so additional refinements may still be needed to fully adopt it.

If you're not ready to invest time in this yet, you can suppress the suggestion by explicitly disabling the feature in your `gradle.properties` file:

    org.gradle.configuration-cache=false

#### Graceful fallback from Configuration Cache mode

Gradle falls back to non-Configuration Cache mode automatically when encountering unsupported features, instead of failing the build.

This includes:

- Core plugins with limited support (such as Maven Publish and Ivy Publish)
- Unsupported or incompatible IDE plugins (such as Eclipse and IDEA)
- Features not yet supported (such as Source Dependencies)

After running a build, the reason for the fallback can be found in the Configuration Cache report.

#### Other notable Configuration Cache updates

Additional updates for the Configuration Cache include:

- Tasks marked as incompatible prevent cache hits in warning mode, ensuring correctness. Warning mode should only be used during migration or troubleshooting. Marking tasks as incompatible remains a recommended strategy for gradual adoption.
- Task execution will abort immediately when a Configuration Cache problem is encountered, avoiding undefined behavior and ensuring affected tasks are neither marked up-to-date nor cached.
- The Configuration Cache report includes more detailed errors, such as serialization issues, unsafe concurrent access, and Groovy DSL closures capturing script state.

### Gradle requires Java Virtual Machine (JVM) version 17 or higher to run

Gradle requires a Java Virtual Machine (JVM) version 17 or higher to start the Gradle daemon.

If you need to build with older JVM versions, you can specify a separate JDK toolchain in the build definition by using toolchains.
Gradle still supports compiling, testing and running other JVM-based tools with Java 8 and higher.

See the Compatibility Matrix for more information.

### Update to Kotlin 2

Gradle embeds the latest stable release of Kotlin 2.2.x runtime and uses Kotlin language version 2.2. This marks a shift from Gradle 8.x, which embedded Kotlin 2.0 starting in 8.11 but continued to use Kotlin language version 1.8 for compatibility.

For a comprehensive overview of what’s new, see the Kotlin 2.2.0, Kotlin 2.1.0 and Kotlin 2.0.0 release notes.

Gradle uses Kotlin for build logic, which includes:

- Build scripts written in the Kotlin DSL (`.gradle.kts` files)
- Plugins

As a result, some behavior has changed, most notably the new K2 compiler and nullability annotations on APIs. If you're upgrading, review the Gradle 9.0.0 upgrade guide for migration details.

### Update to Groovy 4

Gradle embeds the latest stable release of Groovy 4.0, a major upgrade from the Groovy 3.0 version used in Gradle 7 and 8.

This update introduces a range of new features and improvements to the Groovy language. For a comprehensive overview of what’s new, see the Groovy 4.0 release notes for full details.

Gradle uses Groovy for build logic, which includes: Build scripts written in the Groovy DSL (`.gradle` files) Ant integration Plugins

Some behavior has changed between Groovy 3.0 and 4.0. If you're upgrading, review the Gradle 9.0.0 upgrade guide for migration details.

### Semantic Versioning for Gradle releases

Starting with Gradle 9, all Gradle releases follow the Semantic Versioning (SemVer) specification.

Version numbers are expressed as `MAJOR.MINOR.PATCH`, whereas previous minor releases omitted the patch segment (e.g., `8.5` instead of `8.5.0`).

This change only applies to new releases and does not retroactively affect older versions or backports. Additionally, internal code and features marked with `@Incubating` are not considered part of the public API and may change in minor releases.

### Build authoring improvements

Gradle provides rich APIs for plugin authors and build engineers to develop custom build logic.

#### Kotlin build script compilation avoidance

Gradle speeds up feedback loops when editing build logic by avoiding unnecessary recompilation of Kotlin DSL (`.kts`) build scripts. This reduces build times and improves developer productivity.

The improvement comes from significantly better detection of ABI (Application Binary Interface) changes, made possible by using Kotlin’s built-in ABI fingerprinting instead of Gradle’s previous internal mechanism. This brings major performance benefits, especially in builds that use inline functions, which were not handled efficiently before.

For example, in the Gradle build itself, non-ABI changes to build logic result in up to a 60% reduction in configuration time by avoiding unnecessary script recompilation.

#### Gradle API uses JSpecify Nullability annotations

Since Gradle 5.0 we've been using annotations from JSR-305 to make the nullness of type usages explicit for the Gradle API. Starting with Gradle 9, the Gradle API is annotated using JSpecify instead.

Kotlin 2.1, when combined with JSpecify annotations in the Gradle API, introduces stricter nullability handling. For more details and potential breakages, see the dedicated upgrading guide section.

#### Support for major and minor version specification in Gradle Wrapper

Gradle supports specifying only a major or minor version when configuring the wrapper.

For example, the following resolves to the latest `9.x.y` release:

    ./gradlew wrapper --gradle-version=9

While the following resolves to the latest `9.1.x` release:

    ./gradlew wrapper --gradle-version=9.1

This feature requires Gradle 9.0.0 or later. Earlier versions don’t follow full semantic versioning and may misinterpret partial versions (e.g., `8.12` might refer to `8.12` (because it's an exact version) and `8.12.1` (semantically the latest version for `8.12`).

Gradle’s version information endpoint has been extended to support this behavior. For instance, https://services.gradle.org/versions/9 lists all versions of Gradle with major version 9.

#### Archive tasks produce reproducible archives by default

Archive tasks such as `Jar`, `Ear`, `War`, `Zip`, `Tar`, and `AbstractArchiveTask` produce reproducible archives by default. This means that generated archives have reproducible file order and preconfigured file timestamps and permissions. As a result archives generated from the same inputs will be identical byte-for-byte.

This change may affect builds that rely on non-deterministic archive characteristics like file order, file system timestamps, or file system permissions, or file executable bit.

For more information, see the upgrading guide section.

#### Detached configurations can resolve dependencies on their own project

Detached configurations are able to resolve dependencies that reference their own project.

To do this, Gradle introduces a new subtype of `ComponentIdentifier` called `RootComponentIdentifier`, which represents the root node of a resolved dependency graph.

When a configuration is resolved, it is first transformed into a synthetic variant. This variant is owned by a synthetic root component, which is identified using `RootComponentIdentifier`. The root component itself exists only to own the root variant.

Dependency graphs resolved from detached configurations and `buildscript` configurations will have a component identified by a `RootComponentIdentifier` at the root of their graph. This lets Gradle differentiate between a detached configuration and the project it lives in.

Resolved project configurations will continue to have their root component live within the project's component and identified by a `ProjectComponentIdentifier`. In future Gradle versions, all configurations, including those declared inside projects (non-detached), will be owned by a synthetic root component identified by a `RootComponentIdentifier`.

#### `JAVA_HOME` environment variable used for toolchain auto-detection

Gradle's toolchain support allows provisioning and selection of specific JDK versions for building projects—compiling code, running tests, and even running Gradle itself.

This release adds support for using the `JAVA_HOME` environment variable as a source for toolchain auto-detection. This change improves consistency between toolchains detected from the command line and those detected by IDEs, which previously did not consider `JAVA_HOME`.

### Documentation improvements

#### Gradle Best Practices

In partnership with JetBrains and Google, we've launched a new Gradle Best Practices guide to help you avoid common pitfalls and write more maintainable, performant builds. These recommendations consolidate community knowledge and Gradle team insights into a single, growing resource. The current version covers best practices in dependency declarations, build structure, task authoring, and more.

For more information, check out the Gradle Best Practices blog post.

## Promoted features

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.

### Promoted features in the Kotlin DSL

The following operator functions in `DependencyHandlerScope` are considered stable:

- `NamedDomainObjectProvider<Configuration>.invoke(dependencyNotation: Any): Dependency?`
- `NamedDomainObjectProvider<Configuration>.invoke(dependencyNotation: String, dependencyConfiguration: ExternalModuleDependency.() -> Unit): ExternalModuleDependency`
- `NamedDomainObjectProvider<Configuration>.invoke(group: String, name: String, version: String?, configuration: String?, classifier: String?, ext: String?): ExternalModuleDependency`
- `NamedDomainObjectProvider<Configuration>.invoke(group: String, name: String, version: String?, configuration: String?, classifier: String?, ext: String?, dependencyConfiguration: ExternalModuleDependency.() -> Unit): ExternalModuleDependency`
- `<T : Any> Configuration.invoke(dependency: Provider<T>, dependencyConfiguration: ExternalModuleDependency.() -> Unit)`
- `<T : Any> Configuration.invoke(dependency: ProviderConvertible<T>, dependencyConfiguration: ExternalModuleDependency.() -> Unit)`
- `<T : Any> NamedDomainObjectProvider<Configuration>.invoke(dependency: Provider<T>)`
- `<T : Any> NamedDomainObjectProvider<Configuration>.invoke(dependency: ProviderConvertible<T>)`
- `<T : Any> NamedDomainObjectProvider<Configuration>.invoke(dependency: Provider<T>, dependencyConfiguration: ExternalModuleDependency.() -> Unit)`
- `<T : Any> NamedDomainObjectProvider<Configuration>.invoke(dependency: ProviderConvertible<T>, dependencyConfiguration: ExternalModuleDependency.() -> Unit)`
- `<T : Any> String.invoke(dependency: Provider<T>, dependencyConfiguration: ExternalModuleDependency.() -> Unit)`
- `<T : Any> String.invoke(dependency: ProviderConvertible<T>, dependencyConfiguration: ExternalModuleDependency.() -> Unit)`
- `<T : ModuleDependency> NamedDomainObjectProvider<Configuration>.invoke(dependency: T, dependencyConfiguration: T.() -> Unit): T`

The following operator functions in `DependencyConstraintHandlerScope` are considered stable:

- `NamedDomainObjectProvider<Configuration>.invoke(dependencyConstraintNotation: Any): DependencyConstraint`
- `NamedDomainObjectProvider<Configuration>.invoke(dependencyConstraintNotation: String, configuration: DependencyConstraint.() -> Unit): DependencyConstraint`

The following top-level functions in `DependencyHandlerExtensions` are considered stable:

- `DependencyHandler.create(dependencyNotation: String, dependencyConfiguration: ExternalModuleDependency.() -> Unit): ExternalModuleDependency`

The following top-level functions in `KotlinDependencyExtensions` are considered stable:

- `PluginDependenciesSpec.embeddedKotlin(module: String): PluginDependencySpec`

The following functions are considered stable:

- `GroovyBuilderScope.hasProperty(name: String): Boolean`

## 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

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 if 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.