Upgrading Gradle
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Gradle 9.8.1 (released on 7 October 2026) and the upgrade guides of the user manual as of 16 October 2026. Gradle 8.14.6 (released on 8 October 2026) is the last 8.x release and is the starting point of the main upgrade described here. Every example marked as run was executed
with the 9.8.1 wrapper on macOS (Apple silicon), in scratch copies of the tidepool build, once with *.gradle.kts files and once with *.gradle files wherever the syntax differs. The Gradle daemon ran on Oracle JDK 26 (java version "26"), the default JDK of the machine, except where
the text says that JetBrains Runtime 17.0.14 was used. A gradle.properties file in each scratch build set org.gradle.jvmargs=-Xmx512m -Duser.language=en -Duser.country=US, so that the messages are in English, and org.gradle.java.installations.auto-download=false, so that no JDK could be downloaded. The scratch directory is shown as <work>; the at ... stack
frame and (Run with --stacktrace ...) lines that follow each deprecation warning, the lines announcing the start of a daemon, and the long Welcome to Gradle text of the help task are left out of the listings. The examples are compatible with the configuration cache
and ran with --configuration-cache except where the text says otherwise: a configuration cache hit does not repeat the warnings that are printed while the build is configured, so every run that looks for deprecations ran with --no-configuration-cache.
Gradle 8.14.x was not available here, and no Gradle distribution was downloaded. Everything said about 8.14, and every difference between 8.14 and 9.x that was not observable on 9.8.1, comes from the official upgrade guides and release notes and is labelled "not run here". Three more things were not run: --scan
(publishing a Build Scan to a Develocity service requires accepting its terms of use), running Gradle 9 on a Java older than 17 (no such Java is installed) and any wrapper run that would download a distribution from services.gradle.org. The wrapper properties shown on this page are the official ones, with the services.gradle.org URL; the scratch builds
pointed the same wrapper at a local copy of the 9.8.1 archive.
An upgrade of a Gradle build is not one step. A Gradle release changes three things at once: the engine that runs your build (the JVM it needs, the Kotlin and Groovy that compile your scripts, the defaults of the bundled plugins), the API that your scripts and plugins call (deprecated in one release and removed in a later one), and the behavior of tasks that you did not
change. The user manual answers with one upgrade guide per major line and a fixed procedure, and this page walks through the procedure, then through each guide that matters for a build that is on 8.14 today, then through the older majors, because the build files that Gradle users still find in books, blog posts and old repositories
(including the examples of Gradle in Action, which targets Gradle 1.7) stop working on 9.8.1 in specific, recognizable ways. The release history behind all of this is on
Gradle Versions, Compatibility & Release History; this page is where each removal is explained and fixed. The wrapper task itself is described on
Gradle Wrapper, the option --warning-mode on
Gradle Command Line, the configuration cache on
Configuration Cache & Isolated Projects, and the other two tools' equivalents are in the last section: Maven’s are on
Migrating to Maven 4 and
Maven Wrapper, Ant’s on Ant: Deprecations & Migration.
How Gradle versions and deprecations work
Since Gradle 9.0.0 the version number follows semantic versioning: MAJOR.MINOR.PATCH, where a major release may remove public APIs, a minor release adds features and deprecates others, and a patch release fixes bugs and vulnerabilities (9.8.1 is a patch release of 9.8.0 that closes three high-severity
vulnerabilities in the communication between client, daemon and workers, and in the handling of repositories that fail to establish an SSL connection). Before 9.0 the versions were MAJOR.MINOR (8.14) with the patch number appearing only when needed (8.14.6), and a "minor" release such as 7.3 or 8.2 could already contain large features.
The feature lifecycle turns the numbering into a promise. A documented feature is public, and is not removed or changed incompatibly without first being deprecated; a deprecated feature keeps working, prints a warning when it is used, and "may be removed in the next major release"; the manual adds that deprecated features may be supported across major releases but that this is not guaranteed. The deprecation messages of Gradle 9.8.1 state a target release in each message: "This will fail with an error in Gradle 10" for most, "scheduled to be removed in Gradle 11" for a few that were deprecated recently. The incubating state is the exception to the promise: an incubating API may change in any release and the changes are listed in the release notes. The full lifecycle is explained on the versions page.
The consequence for upgrading is simple. A build that is free of deprecation warnings on the last minor release of a line (8.14.x) will, with very few exceptions, compile and run on the first release of the next line (9.0.0), and a build that is free of warnings on a 9.x release will run on every later 9.x release. Almost all upgrade pain comes from skipping that discipline: from moving more than one major release at once, from ignoring the warning summary, or from plugins that use internal APIs.
The upgrade guides of the user manual have a naming scheme that is easy to misread. Each file upgrading_version_N.html covers the minor releases of line N and the move from the last N.x to the first release of the next line; the move to Gradle 9.0.0 has a page of its own. The table lists them with the sections that this page uses.
| Guide | Covers | Used on this page for |
|---|---|---|
|
4.1 to 4.10, and 4.10 to 5.0 |
|
|
5.2 to 5.6, and 5.6 to 6.0 |
FindBugs, JDepend, OSGi, announce and Compare Gradle Builds plugins removed |
|
6.3 to 6.9, and 6.9 to 7.0 |
|
|
7.x, and 7.6 to 8.0 |
warnings that became errors in 8.0 |
|
8.1 to 8.14 |
everything deprecated in 8.x and removed in 9.0 |
|
8.14 to 9.0.0 |
the breaking changes of the major release |
|
9.0.0 to the latest 9.x (9.8.1) |
the minor releases and the deprecations announced for Gradle 10 |
The upgrade procedure
The user manual gives the same four steps at the top of every upgrade guide, from the 4.x guide to the 9.x one, and they are the whole method. In the guide for moving to Gradle 9.0.0 they read:
-
Run
gradle help --scanand look at the Deprecations view of the Build Scan; alternatively rungradle help --warning-mode=allto see the deprecations in the console, "though it may not report as much detailed information". -
Update your plugins, "because a major release can remove public APIs" and the newest version of a plugin is the most likely to be compatible.
-
Run
gradle :wrapper --gradle-version 9.0.0(or the version you are moving to). -
Run the project and debug any errors with the troubleshooting guide.
The guide for moving to 9.0.0 adds a precondition before the first step (for migrating within Gradle 8.x, read the older migration guide first), and the guide for the 9.x releases says the same about the 9.0.0 guide. The 4.x guide adds two more pieces of advice that remain good: move to the latest release of your current line before moving to the next major one, because "Gradle 5.x will generate (potentially less obvious) errors if you try to upgrade directly to it", and do not upgrade Gradle and migrate to the Kotlin DSL at the same time, so that a failure has one cause. The flow, with the commands, is:
Source: Gradle User Manual: Upgrading from 8.x to 9.0.0, Upgrading from 4.x to 5.0, for all users and Troubleshooting Builds
Step 1: find the deprecations
By default Gradle prints the deprecation warnings of a build only as a one-sentence summary at the end of the build. That is useful as a signal, and useless as a list. This is the summary on 9.8.1 for a build that uses a deprecated Groovy DSL idiom (group 'io.tidepool', the "space assignment" syntax) and Project.getProperties(); the build succeeds:
$ ./gradlew :child:help --warning-mode=summary --no-configuration-cache --console=plain
> Configure project :child
hello
> Task :child:help
Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/9.8.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
--warning-mode selects what is printed: summary (the default, shown above), all (every warning, with the build file and line), fail (every warning, and the build fails if there was any) and none. The same choice can be made persistent with the Gradle property org.gradle.warning.mode, in gradle.properties or with -D on the command line,
and the command line wins over the file. With fail the same build prints each warning, and ends with a failure that names the first offending line of the build file:
$ ./gradlew :child:help --warning-mode=fail --no-configuration-cache --console=plain
> Configure project :child
Build file '<work>/child/build.gradle': line 2
Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated. This is scheduled to be removed in Gradle 10. Use assignment ('group = <value>') instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_8.html#groovy_space_assignment_syntax
Build file '<work>/child/build.gradle': line 3
The Project.getProperties method has been deprecated. This will fail with an error in Gradle 10. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecated_get_properties
hello
> Task :child:help
[Incubating] Problems report is available at: file://<work>/build/reports/problems/problems-report.html
FAILURE: Build failed with an exception.
* Where:
Build file '<work>/child/build.gradle' line: 2
* What went wrong:
Deprecated Gradle features were used in this build, making it incompatible with Gradle 10
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights from a Build Scan (powered by Develocity).
> Get more help at https://help.gradle.org.
BUILD FAILED in 1s
1 actionable task: 1 executed
[exit 1]
Each warning has the same anatomy, which is worth knowing because the target of the link is the upgrade guide. First the sentence of what was used; then when it stops working ("This will fail with an error in Gradle 10", or "scheduled to be removed in Gradle 11"); then the replacement; and last
the link to the section of the guide that explains the change, upgrading_version_9.html#deprecated_get_properties in the second warning above, followed by the place in the build ("Build file …: line 3", or the at line that was left out of the listing). In a Kotlin DSL build the Kotlin compiler adds its own w: lines for the deprecated declarations before Gradle’s message, as
the by registering delegates below show. The at line says which script or class made the call, and --stacktrace prints the whole stack of a warning, which is how a deprecation that is triggered by a plugin is traced to the plugin.
The run also wrote a report, and the output announced it (the line starting with [Incubating] Problems report, which the other listings of this page leave out): build/reports/problems/problems-report.html lists the deprecations and other problems of the build as an HTML page that can be opened without a Build Scan. The report is incubating in 9.8.1.
The second way is the Build Scan: gradle help --scan collects the same deprecations in a Deprecations view that groups them by the plugin or script that caused them, with the stack trace and the link to the upgrade guide, which is why the guides recommend it first. Publishing a scan sends build data to a Develocity service and requires accepting its terms of use, so
--scan was not run here; the scan options are described on Performance Profiling & Build Scans.
Three rules make the output reliable. Run with --no-configuration-cache, or after changing a file of the build, because a configuration cache hit skips the configuration phase where most of the warnings are produced. Run the tasks that your CI runs, not only help, because a deprecation that is triggered by a task action (reading Task.project, for
example) is printed only when the task executes. Run the build of every included build and of buildSrc or build-logic too: they are separate builds and their warnings are reported with the others.
Step 2: update the plugins
A plugin is code that runs inside Gradle, so it breaks when the Gradle API it was compiled against changes. The 9.0.0 guide states the practical rule ("using the latest version of a plugin increases the likelihood that it is already compatible with the new major Gradle version") and lists, for plugins that Gradle knows about, floors below which a plugin is not supported by Gradle 9:
| Plugin | Lowest supported on Gradle 8.x | Lowest supported on Gradle 9.x |
|---|---|---|
Kotlin Gradle Plugin |
1.6.10 |
2.0.0 |
Android Gradle Plugin |
7.3.0 |
8.4.0 (and AGP 9.x from Gradle 9.6, see below) |
Gradle Enterprise plugin (now the Develocity plugin, |
3.0 |
3.13.1; versions before 4.0 are deprecated from 9.6 |
Plugins written with the Kotlin DSL and built with Gradle 9 |
(built with 8.x: usable down to Gradle 6.8) |
usable only on Gradle 8.11 or newer (the first release that embeds Kotlin 2) |
Plugins written with the Groovy DSL and built with Gradle 9 |
(built with 7.x or 8.x: usable down to Gradle 5.0) |
usable only on Gradle 7.0 or newer (the first with Groovy 3) |
Two of these rows matter beyond the table. An Android project has to move AGP before it moves Gradle: AGP 8.4.0 is the floor for Gradle 9.0.0, and Gradle 9.6 removed internal classes of the Problems API that AGP 8.x used, so 9.6 and later need AGP 9.x (the failure mentions the removed type; staying on Gradle 9.5 is the other fix). A plugin author who needs to support Gradle older than 8.11 must build the plugin with Gradle 8.x, because
the Kotlin 2.2 compiler of Gradle 9 rejects language versions below 1.8 and always writes Kotlin metadata version 2; the guide shows the languageVersion and apiVersion setting for an 8.x build and says it fails on Gradle 9. How to find the plugins a build uses and their versions: ./gradlew buildEnvironment prints the build script classpath of each build, and the plugin versions of the
version catalog are in Version Catalogs. Third-party plugins that fail with a NoSuchMethodError, NoClassDefFoundError or "Could not find method" during configuration are the typical symptom of a plugin that uses an API Gradle removed; the fix is the plugin’s own newest release, and if there is none, replacing the plugin.
Step 3: update the wrapper
The wrapper is how the version of Gradle becomes part of the repository (see Gradle Wrapper for every key of gradle-wrapper.properties), so upgrading is running the wrapper task with the new version and committing the result. The manual recommends the task over editing distributionUrl,
because the task also refreshes the scripts and the JAR. The guides give the command as gradle :wrapper --gradle-version 9.0.0, and the wrapper chapter gives the pair that is needed to update the JAR as well:
./gradlew wrapper --gradle-version 9.8.1 --gradle-distribution-sha256-sum <sha-256 of the -bin.zip> && ./gradlew wrapper
The first call runs with the old Gradle and rewrites gradle-wrapper.properties; the second runs with the new Gradle, which downloads it and writes the new gradle-wrapper.jar and scripts. When the properties file contains a distributionSha256Sum (a project that verifies its distribution has one) the task refuses to leave a stale checksum behind, so the new sum has to be passed with
--gradle-distribution-sha256-sum; the checksums of all releases are on gradle.org/release-checksums. Moving from 8.14.6 to 9.0.0 is the same command with 9.0.0, and it was not run here, because it would download the distribution of 9.0.0 on the second call.
What could be run is the first half of the sequence with the installed 9.8.1: a scratch project whose properties file was written by hand to look like an older one (a 9.7.1 URL and its checksum) was upgraded with the installed gradle executable, so that nothing was downloaded, and with --no-validate-url, so that the task did not
contact the server:
$ gradle wrapper --gradle-version 9.8.1 --gradle-distribution-sha256-sum dce76f55f8e251a3a1f130eb120f30b3d271de2b76c9b0729d316b5a1b6dc01f --no-validate-url
> Task :wrapper
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.8.1/userguide/configuration_cache_enabling.html
The difference between the properties file before and after (diff, with < for the old file):
3,4c3,4
< distributionSha256Sum=acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a
< distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
---
> distributionSha256Sum=dce76f55f8e251a3a1f130eb120f30b3d271de2b76c9b0729d316b5a1b6dc01f
> distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip
6c6,8
< validateDistributionUrl=true
---
> retries=0
> retryBackOffMs=500
> validateDistributionUrl=false
The URL and the sum changed together, and the file gained retries and retryBackOffMs (the download retry settings, new in Gradle 9.5) and validateDistributionUrl=false from the --no-validate-url option; a normal run would have kept true, which makes the task check with a request that the URL exists. The scripts gradlew and gradlew.bat and the JAR were identical, because the old files in this
scratch project were already those of 9.8.1; moving from a real 8.14 wrapper would change them, which is why the second call is part of the procedure. After the second call ./gradlew --version shows the new release and the four files (gradlew, gradlew.bat, gradle/wrapper/gradle-wrapper.jar, gradle/wrapper/gradle-wrapper.properties) are committed together.
Two details of the release history affect the move. The Windows start script was reworked in 9.5.0 (it no longer leaks environment variable changes into the invoked program, and a custom start script template or a batch file that calls gradlew.bat and relies on the "Terminate batch job" prompt must be reviewed); the 9.5 guide warns that upgrading the wrapper to the new script from a Gradle older than 8.14 can fail once, because cmd.exe re-reads
a script that has changed under it, and recommends going through 8.14 or 9.0.0 first. And since 9.4 the priority of system properties for a wrapper run follows the documented order (command line, then the Gradle user home gradle.properties, then the project’s gradle.properties); before it, a property in the project file could override one given on the command line.
The JVM that runs the wrapper and the daemon is the other half of "which Gradle". Gradle 9.8.1 on JetBrains Runtime 17.0.14, the oldest Java that Gradle 9 supports for the daemon, with the same wrapper and no other change:
$ JAVA_HOME=<jbr-17.0.14>/Contents/Home ./gradlew --version
Build time: 2026-10-07 16:57:46 UTC
Revision: 39daf39feb60a18b1b6ddccde53366522fef516f
Kotlin: 2.4.10
Groovy: 4.0.33
Ant: Apache Ant(TM) version 1.10.17 compiled on April 6 2026
Launcher JVM: 17.0.14 (JetBrains s.r.o. 17.0.14+1-b1367.22)
Daemon JVM: ~/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home (no Daemon JVM specified, using current Java home)
OS: Mac OS X 27.0.1 aarch64
Step 4: run, then make CI keep the build clean
After the wrapper has been updated the build is run and the errors are read with the troubleshooting guide, which has sections for installation, dependency resolution, performance, attaching a debugger, tasks that rerun unexpectedly and IDE integration (see
Best Practices & Troubleshooting). When a plugin turns out to be incompatible, and no newer release exists, staying on the last working Gradle for a while is a legitimate decision; going back is the same wrapper task with the old version.
Once the build is green, keep it green: a build that fails on a new deprecation is a build that is never surprised by a major release. In gradle.properties, for CI only through a CI-specific file or a -D argument, so that a developer who updates a plugin locally is not blocked by a warning that comes from it:
org.gradle.warning.mode=fail
The property worked as the command-line option did: a build with the property set failed on the deprecated group 'io.tidepool' with the same message as above, and -Dorg.gradle.warning.mode=none on the command line overrode it and the build succeeded.
$ ./gradlew :child:help --console=plain
> Configure project :child
Build file '<work>/child/build.gradle': line 2
Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated. This is scheduled to be removed in Gradle 10. Use assignment ('group = <value>') instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_8.html#groovy_space_assignment_syntax
> Task :child:help
FAILURE: Build failed with an exception.
* Where:
Build file '<work>/child/build.gradle' line: 2
* What went wrong:
Deprecated Gradle features were used in this build, making it incompatible with Gradle 10
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights from a Build Scan (powered by Develocity).
> Get more help at https://help.gradle.org.
BUILD FAILED in 412ms
1 actionable task: 1 executed
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.8.1/userguide/configuration_cache_enabling.html
[exit 1]
From Gradle 8.14 to 9.0.0
Gradle 9.0.0 (31 July 2025) is the breaking release of the current line: it is the first to require a newer Java to run, the first to embed Kotlin 2 and Groovy 4 for the build scripts, and the release that removed what had been deprecated in 7.x and 8.x. Everything in this section is in the guide Upgrading from 8.x to 9.0.0. The headline items, then the details with the runs:
| Change | What it means for a build |
|---|---|
Java 17 or newer to run the daemon |
Gradle 8.14 runs on Java 8 to 24; Gradle 9 needs 17 or newer for the daemon. The code can still target older Java through toolchains. |
Kotlin 2.2 and Groovy 4 |
Build scripts and plugins are compiled by a different compiler and language version. |
Plugin floors |
Kotlin Gradle Plugin 2.0.0, Android Gradle Plugin 8.4.0, Develocity plugin 3.13.1. |
Removed APIs |
|
Behavior changes without an error |
Reproducible archives by default, tasks that silently stop running tests, a |
Configuration cache |
The "preferred" mode: still off by default, but a task that is incompatible always discards the cache entry, and unsupported build event listeners fail the build. |
Java 17 to run Gradle
"Gradle 9.0.0 requires a Java Virtual Machine (JVM) version 17 or higher to run the Gradle Daemon." The change was announced in the 8.10 guide, and it is narrow. Gradle can still compile for older Java (the guide says it keeps supporting compilation to Java 6 or later, and the Worker API, running tests on Java 8 and the Tooling API and TestKit clients remain compatible with Java 8); the wrapper and the command-line launcher can start on Java 8, but they
then need a Java 17 or newer for the daemon, set with the Daemon JVM criteria, with org.gradle.java.home, or with JAVA_HOME. --no-daemon and ProjectBuilder in unit tests need Java 17 as well, since they run Gradle in the test JVM. This is why a CI image on Java 11 must change while the project can keep targeting Java 8 or 11:
-
Put the Java that the build targets in the build, with a toolchain (Gradle Toolchains):
java { toolchain { languageVersion = JavaLanguageVersion.of(11) } }. -
Put the Java that runs Gradle in the repository, with the Daemon JVM criteria (
gradle/gradle-daemon-jvm.properties, created by./gradlew updateDaemonJvm, described on The Gradle Daemon); the 9.2 release made that feature stable.
That Gradle 9.8.1 starts on JDK 17 was shown in step 3. Starting it on an older Java was not run here, as no such Java is installed. Java 27 became a supported daemon and toolchain Java in 9.8.0.
Kotlin 2.2 and Groovy 4
Gradle 9.0.0 upgraded the embedded Kotlin from 2.0.21 to 2.2.0 and Groovy from 3.0.24 to 4.0.27; in 9.8.1 they are Kotlin 2.4.10 and Groovy 4.0.33, as ./gradlew --version prints:
Build time: 2026-10-07 16:57:46 UTC
Revision: 39daf39feb60a18b1b6ddccde53366522fef516f
Kotlin: 2.4.10
Groovy: 4.0.33
Ant: Apache Ant(TM) version 1.10.17 compiled on April 6 2026
Launcher JVM: 26 (Oracle Corporation 26+35-2893)
Daemon JVM: /Library/Java/JavaVirtualMachines/jdk-26.jdk/Contents/Home (no Daemon JVM specified, using current Java home)
OS: Mac OS X 27.0.1 aarch64
The embedded versions through the 9.x line (each from the upgrade guide of the release; a patch release can bump Kotlin or Groovy again, so the column is the version at the minor release):
| Gradle | Kotlin | Groovy |
|---|---|---|
8.14 |
2.0.21 (language version 1.8 for scripts and plugins) |
3.0.24 |
9.0.0 |
2.2.0 (language version 2.2 across the toolchain) |
4.0.27 |
9.1.0 |
2.2.0 |
4.0.28 |
9.2.0 |
2.2.20 |
4.0.28 |
9.3.0 |
2.2.21 |
4.0.29 |
9.4.0 |
2.3.0 |
4.0.29 |
9.5.0 |
2.3.20 |
4.0.29 |
9.6.0 |
2.3.21 |
4.0.32 |
9.7.0 |
2.4.0 |
4.0.32 |
9.8.0 and 9.8.1 |
2.4.10 |
4.0.33 |
Kotlin. The Kotlin DSL was compiled with language version 1.8 for compatibility, even though Gradle 8.11 and later embedded Kotlin 2.0. From 9.0 scripts, precompiled script plugins and plugins written in Kotlin use language version 2.2, so the breaking changes of Kotlin 2.0 and 2.1 apply, and the support for language versions 1.4 to 1.7 was removed for plugin authors. Kotlin 2.4.0 (Gradle 9.7)
removed the K1 compiler altogether; the smallest compile target is 2.0, which is itself deprecated. Two source-level changes are the ones that builds hit. The script label this@Build_gradle (and this@Settings_gradle, this@Init_gradle) no longer exists; use project, settings or gradle:
* Where:
Build file '<work>/child/build.gradle.kts' line: 1
* What went wrong:
Script compilation error:
Line 1: println(this@Build_gradle)
^ Unresolved label.
1 error
The second is nullability. The Gradle API is annotated with JSpecify instead of the dormant JSR-305 from 9.0.0, and Kotlin 2.1 and later read those annotations strictly, so code that compiled before can fail: a Provider<T> has a bound T : Any, so a Kotlin extension function must say so, and Property<String?> is no longer valid. This is the real failure, in a buildSrc Kotlin file of a scratch build, followed by the fix:
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
fun <T> Provider<T>.describe(): String = "provider of " + orNull
fun nullableProperty(p: Property<String?>): String? = p.orNull
> Task :buildSrc:compileKotlin FAILED
e: file://<work>/buildSrc/src/main/kotlin/Helpers.kt:4:18 Type argument is not within its bounds: type parameter 'T#2 (of interface Provider<T : Any>)' must be subtype of 'Any', but actual: 'T#1 (of f ...
e: file://<work>/buildSrc/src/main/kotlin/Helpers.kt:6:34 Type argument is not within its bounds: type parameter 'T (of interface Property<T : Any>)' must be subtype of 'Any', but actual: 'String?'.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':buildSrc:compileKotlin' (registered by plugin class 'org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper').
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.btapi.BuildToolsApiCompilationWork
> Compilation error. See log for more details
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
fun <T : Any> Provider<T>.describe(): String = "provider of " + orNull
fun nullableProperty(p: Property<String>): String? = p.orNull
Plugins that still use the javax.annotation annotations continue to work. A related rule for plugin authors: kotlinDslPluginOptions.jvmTarget was removed, and the target of the compiled Kotlin follows the Java toolchain.
Groovy. Groovy 4 renamed packages (to satisfy the Java module system’s split-package rule) and rewrote the parser, so Groovy build scripts that use classes such as groovy.util.* directly may need new imports. Four behavior changes need attention. First, a getter with an is prefix and a Boolean (not boolean) return type is no longer a property for Groovy 4; Gradle still treated such getters as properties
of its own types, announced the change as a deprecation in 8.13, and will drop it in Gradle 10. In a build script of your own:
* Where:
Build file '<work>/child/build.gradle' line: 2
* What went wrong:
A problem occurred evaluating project ':child'.
> No such property: enabled for class: Flag
The fix is a get method (or boolean as the type): the 8.13 guide shows keeping the old isX method with @Deprecated and @ReplacedBy and adding getX. Second, closures with the DELEGATE_FIRST strategy now look up dynamic properties and methods on the delegate before the owner, which can change what a bare name means inside with { } blocks (qualify with owner. or this., or use @CompileStatic).
Third, closures defined in a parent class may no longer reach its private properties or methods from a subclass (a Groovy bug that stays until Groovy 5; the workaround is @CompileStatic). Fourth, plugins written in Groovy 3 that call super methods may fail at run time on Gradle 9, because Groovy 4 resolves super calls differently; rebuild such plugins with Gradle 9.
Removed APIs and features
Gradle 9.0.0 removes what was deprecated during 7.x and 8.x. The table lists the removals that builds meet most often; the guide lists more (see the last row). The "deprecated" column is the release whose upgrade guide first announced it.
| Removed in 9.0.0 | Deprecated in | Use instead |
|---|---|---|
|
7.0 |
|
The conventions: |
8.2 |
The extensions: |
|
7.1 (8.1 added the |
Run Gradle from the directory of the build, or select it with |
|
8.11 |
Inject |
The |
8.7 |
|
Libraries and bundles of a version catalog in the |
8.1 |
Use the plugin aliases of the catalog ( |
The eager accessors of configurations in the Kotlin DSL ( |
8.0 (public API), kept for plugins compiled against older Gradle |
|
|
- |
A Java toolchain. |
The shorthand |
8.8 |
|
|
8.0 |
|
|
8.1, 8.8 |
The cache cleanup settings of the Gradle user home, configured in an init script. |
Unix mode integers for file permissions ( |
8.8 |
|
|
8.x |
|
The Groovy modules |
- |
Declare the Groovy modules as dependencies of your own build logic. |
The failures are not all alike. In the Groovy DSL a removed method or property is a runtime error when the line is evaluated, and the message names the receiver; in the Kotlin DSL it is a compilation error of the whole script, listed all at once. Real outputs from 9.8.1 for the removals above (Kotlin DSL first, then the Groovy DSL):
* Where:
Build file '<work>/child/build.gradle.kts' line: 2
* What went wrong:
Script compilation error:
Line 2: repositories { jcenter() }
^ Unresolved reference 'jcenter'.
1 error
* Where:
Build file '<work>/child/build.gradle' line: 2
* What went wrong:
A problem occurred evaluating project ':child'.
> Could not find method jcenter() for arguments [] on repository container of type org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.
* Where:
Build file '<work>/child/build.gradle.kts' line: 2
* What went wrong:
Script compilation error:
Line 2: sourceCompatibility = JavaVersion.VERSION_17
^ Unresolved reference 'sourceCompatibility'.
1 error
* Where:
Build file '<work>/child/build.gradle.kts' line: 1
* What went wrong:
Script compilation error:
Line 1: println(convention)
^ Unresolved reference 'convention'.
1 error
* Where:
Build file '<work>/child/build.gradle' line: 2
* What went wrong:
A problem occurred evaluating project ':child'.
> Could not get unknown property 'convention' for project ':child' of type org.gradle.api.Project.
* Where:
Build file '<work>/child/build.gradle.kts' line: 1
* What went wrong:
Script compilation errors:
Line 1: exec { commandLine("echo", "x") }
^ Unresolved reference 'exec'.
Line 1: exec { commandLine("echo", "x") }
^ Unresolved reference 'commandLine'.
2 errors
* Where:
Build file '<work>/child/build.gradle' line: 1
* What went wrong:
A problem occurred evaluating project ':child'.
> Could not find method exec() for arguments [build_dsrlfywjlncks6y799wq6vzmc$_run_closure1@1c824407] on project ':child' of type org.gradle.api.Project.
* Where:
Build file '<work>/child/build.gradle.kts' line: 1
* What went wrong:
Script compilation error:
Line 1: tasks { "help"() }
^ No value passed for parameter 'configuration'.
1 error
The error of the last one does not say "removed": the notation "help"() without a lambda is the one that went away, and the Kotlin compiler sees the remaining overload, "name"(configuration), so the message is about the missing parameter. The command-line options show the same problem in a third form, since the unknown option message also prints the usage of the command:
$ ./gradlew -b build.gradle hello --console=plain
Unknown command-line option '-b'.
To see more detail about a task, run gradlew help --task <task>
For example, gradlew help --task build.gradle
The two other things a reader of an old build file looks for are the dependency configurations and the plugins. They were removed long before 9.0, in 7.0, and are in the table of the older majors below; on 9.8.1 they fail as:
* Where:
Build file '<work>/child/build.gradle.kts' line: 2
* What went wrong:
Script compilation error:
Line 2: dependencies { compile("info.picocli:picocli:4.7.7") }
^ Unresolved reference 'compile'.
1 error
Source: Gradle User Manual: Upgrading to Gradle 9, removed APIs and features, removal of jcenter(), removal of conventions, removal of custom build layout options, Upgrading from Gradle 7.x to 8.0, setting custom build layout (7.1), Upgrading within Gradle 8.x, setting custom build layout on StartParameter and GradleBuild (8.1), removal of Project#exec and removal of the "name"() syntax
Behavior changes that do not fail
The dangerous changes of a major release are the ones that keep the build green. The 9.0.0 guide has several; each of the next three was reproduced.
Archives are reproducible by default. The tasks Jar, War, Ear, Zip and every AbstractArchiveTask now sort entries deterministically, give every file a fixed timestamp (1 February 1980 for ZIP-based archives; the exact value depends on the archive type) and permissions 0644, and every directory 0755. Before 9.0 the order depended on the file system and the times and
permissions were those of the files. The change helps builds that hash their outputs or compare them between machines (see Supply-Chain Security and, for Maven’s project.build.outputTimestamp, Maven Security & Reproducible Builds), and surprises builds that read the time or the permissions back, such as a distribution whose scripts must be executable. Restoring the old behavior is
per property, or for permissions with the Gradle property org.gradle.archives.use-file-system-permissions=true. Two source files with different permissions and times (shown with stat), zipped by two tasks, one with the new defaults and one restoring the old behavior:
$ stat -f '%Sp %Sm %N' -t '%Y-%m-%d %H:%M' files/*
-rw-r----- 2022-05-06 07:08 files/alpha.txt
-rw------- 2020-01-02 03:04 files/zeta.txt
val reproducible = tasks.register<Zip>("reproducibleZip") {
from("files")
archiveFileName = "reproducible.zip"
destinationDirectory = layout.buildDirectory.dir("out")
}
val legacy = tasks.register<Zip>("legacyZip") {
from("files")
archiveFileName = "legacy.zip"
destinationDirectory = layout.buildDirectory.dir("out")
isReproducibleFileOrder = false
isPreserveFileTimestamps = true
useFileSystemPermissions()
}
tasks.register('reproducibleZip', Zip) {
from 'files'
archiveFileName = 'reproducible.zip'
destinationDirectory = layout.buildDirectory.dir('out')
}
tasks.register('legacyZip', Zip) {
from 'files'
archiveFileName = 'legacy.zip'
destinationDirectory = layout.buildDirectory.dir('out')
reproducibleFileOrder = false
preserveFileTimestamps = true
useFileSystemPermissions()
}
The two builds were run with ./gradlew reproducibleZip legacyZip --configuration-cache and produced the same archives (the Groovy names of the two properties, reproducibleFileOrder and preserveFileTimestamps, differ from the Kotlin is... names because a Boolean property in the Kotlin DSL is written with its getter name). The entries as stored:
$ zipinfo -l reproducible.zip
Archive: reproducible.zip
Zip file size: 221 bytes, number of entries: 2
-rw-r--r-- 2.0 unx 3 b- 5 defN 80-Feb-01 00:00 alpha.txt
-rw-r--r-- 2.0 unx 6 b- 8 defN 80-Feb-01 00:00 zeta.txt
$ zipinfo -l legacy.zip
Archive: legacy.zip
Zip file size: 221 bytes, number of entries: 2
-rw-r----- 2.0 unx 3 b- 5 defN 22-May-06 07:08 alpha.txt
-rw------- 2.0 unx 6 b- 8 defN 20-Jan-02 03:04 zeta.txt
The first archive carries 80-Feb-01 00:00 and -rw-r--r-- for both files, whatever the files were; the second keeps the permissions and times of the sources (-rw-r----- and -rw-------, 2022 and 2020).
A Test task without configuration no longer runs the tests of the test source set. A Test task that was registered with no configuration used to take its classes and classpath from the test source set by convention (deprecated in 8.1); 9.0 removes the convention, and the build does not fail. The task is skipped because it has no test classes, so a project can stop running a whole test suite without a single warning. The scratch build has one JUnit
Jupiter test and registers otherTest with nothing else:
plugins { java }
repositories { mavenCentral() }
testing.suites.named<JvmTestSuite>("test") { useJUnitJupiter() }
tasks.register<Test>("otherTest")
plugins { id 'java' }
repositories { mavenCentral() }
testing.suites.test { useJUnitJupiter() }
tasks.register('otherTest', Test)
$ ./gradlew test otherTest --console=plain --configuration-cache
Calculating task graph as no cached configuration is available for tasks: test otherTest
> Task :otherTest NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :processResources NO-SOURCE
> Task :compileJava NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileTestJava
> Task :testClasses
> Task :test
BUILD SUCCESSFUL in 2s
2 actionable tasks: 2 executed
Configuration cache entry stored.
:otherTest NO-SOURCE is the whole symptom: the Groovy DSL build printed the same. The fix is to configure the task explicitly: the classes directory and the classpath of the suite, and the test framework (a Test task has no framework by default, so without useJUnitPlatform() the next error appears):
plugins { java }
repositories { mavenCentral() }
testing.suites.named<JvmTestSuite>("test") { useJUnitJupiter() }
val unit = testing.suites.named<JvmTestSuite>("test")
tasks.register<Test>("otherTest") {
useJUnitPlatform()
testClassesDirs = files(unit.map { it.sources.output.classesDirs })
classpath = files(unit.map { it.sources.runtimeClasspath })
}
plugins { id 'java' }
repositories { mavenCentral() }
testing.suites.test { useJUnitJupiter() }
tasks.register('otherTest', Test) {
useJUnitPlatform()
testClassesDirs = testing.suites.test.sources.output.classesDirs
classpath = testing.suites.test.sources.runtimeClasspath
}
$ ./gradlew otherTest --console=plain --configuration-cache --warning-mode=fail
Calculating task graph as no cached configuration is available for tasks: otherTest
> Task :processTestResources NO-SOURCE
> Task :compileJava NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :testClasses UP-TO-DATE
> Task :otherTest
BUILD SUCCESSFUL in 16s
2 actionable tasks: 1 executed, 1 up-to-date
Configuration cache entry stored.
The guide’s own Kotlin example uses the delegate val test by testing.suites.existing(JvmTestSuite::class); the named form above is used instead, because the delegates are deprecated since 9.6 (see below). The better fix is usually not a hand-made Test task at all but a second suite, testing.suites.register<JvmTestSuite>("integrationTest"), covered on
Testing JVM Projects.
A test task that finds no tests fails. When test sources exist, no filter is applied and the task runs but discovers nothing, 9.0 fails the build; this catches the classic migration mistake of JUnit 4 test classes under a JUnit Jupiter engine. A scratch build with a JUnit 4 test class and useJUnitJupiter():
plugins { java }
repositories { mavenCentral() }
dependencies { testImplementation("junit:junit:4.13.2") }
testing.suites.named<JvmTestSuite>("test") { useJUnitJupiter() }
plugins { id 'java' }
repositories { mavenCentral() }
dependencies { testImplementation 'junit:junit:4.13.2' }
testing.suites.test { useJUnitJupiter() }
> Task :testClasses
> Task :test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There are test sources present and no filters are applied, but the test task did not discover any tests to execute. This is likely due to a misconfiguration. Please check your test configuration. If this is not a misconfiguration, this error can be disabled by setting the 'failOnNoDiscoveredTests' property to false.
The Groovy DSL build failed with the same message. The error names the escape hatch, failOnNoDiscoveredTests = false on the task (with tasks.withType<AbstractTestTask>().configureEach { failOnNoDiscoveredTests = false } for all of them), but the right fix is the one the message hints at: either add the Vintage engine
(org.junit.vintage:junit-vintage-engine) or move the tests to Jupiter, or select the framework that the tests use.
The other changes of this kind, in the order of the guide:
-
assemblestops building artifacts of arbitrary configurations. Theassembletask used to build the artifacts of every configuration whosevisibleflag was not false, and thearchivesconfiguration collected them. Both were removed in 9.0; a custom artifact now needstasks.named("assemble") { dependsOn(theArtifactTask) }. Thearchivesconfiguration (deprecated for artifact declaration in 9.1) andConfiguration.visible(deprecated, to be removed in Gradle 11) remain only as deprecated shells. -
Packaging plugins combine. The
earandwarplugins no longer suppress each other and thejar: applyingjava,warandearbuilds a JAR, a WAR and an EAR withassemble. -
Stale class files outside the build directory are no longer deleted, a special case of source set outputs that forced every compile task to be created eagerly.
-
The signing plugin writes OpenPGP signatures that match the version of the key (RFC 9580, version 6 keys); before 9.0 it always wrote version 4 signatures.
-
Gradle Module Metadata that is modified after an eagerly created publication is an error instead of a warning.
-
ValidatePluginsneeds the toolchain plugin (jvm-toolchains, applied by every JVM plugin) and, since 9.2, a Java version that the daemon supports. -
The default versions of the bundled tools moved: Checkstyle 10.24.0, CodeNarc 3.6.0, PMD 7.13.0, JUnit Jupiter 5.12.2, TestNG 7.11.0, Spock 2.3 and Eclipse JGit 7.2.1. A new Checkstyle or PMD version brings new rules, so a quality gate that was green can turn red without a source change; pin the tool version (
toolVersion) while upgrading and move it afterwards as a separate step. -
JavaExecuses the toolchain of thejavaextension whenjava-baseis applied (announced in the 8.14 guide), where it used the Java of the daemon before. A build that sets no toolchain is unaffected; a build that does, and runs aJavaExecor theruntask, now runs on the toolchain’s Java. -
Project directories must exist. An
includeof a project without a directory (or with a read-only one) fails. Deprecated in 8.5, an error from 9.0:
FAILURE: Build failed with an exception.
* What went wrong:
Configuring project with invalid directory
Configuring project ':missing-module' without an existing directory is not allowed. The configured projectDirectory '<work>/missing-module' does not exist, can't be written to or is not a directory.
For more information, please refer to https://docs.gradle.org/9.8.1/userguide/multi_project_builds.html#include_existing_projects_only.
Possible solution: Make sure the project directory exists and is writable.
The configuration cache in Gradle 9
Gradle 9.0.0 does not turn the configuration cache on by default (that is announced for Gradle 10), but it makes it the preferred mode and tightens three things that matter to a migration. An unsupported provider passed to BuildEventsListenerRegistry.onTaskCompletion used to be silently dropped when the cache was on and is now a configuration cache problem that fails the build; the property
org.gradle.configuration-cache.unsafe.ignore.unsupported-build-events-listeners=true restores the old behavior for now and will be removed in Gradle 10. A cache entry is always discarded when the work graph contains a task marked incompatible, even in the warn problems mode, which used to store it. And the deprecations about reaching the build model from a task action
(Task.project, Task.getExtensions(), the task dependencies, an injected Project or Gradle) are warnings without the cache and failures with it, as this single task shows (Kotlin DSL; the Groovy DSL fails the same way):
> Task :child:t
Invocation of Task.project at execution time has been deprecated. This will fail with an error in Gradle 10. This API is incompatible with the configuration cache, which will become the only mode supported by Gradle in a future release. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_7.html#task_project
child
BUILD SUCCESSFUL in 497ms
1 actionable task: 1 executed
> Task :child:t FAILED
1 problem was found storing the configuration cache.
- Task `:child:t` of type `org.gradle.api.DefaultTask`: invocation of 'Task.project' at execution time is unsupported with the configuration cache.
See https://docs.gradle.org/9.8.1/userguide/configuration_cache_requirements.html#config_cache:requirements:use_project_during_execution
See the complete report at file://<work>/build/reports/configuration-cache/8zpfyesyt1kunq46g1mptil9z/avrturodvhz6l3z2qoghbvn6m/configuration-cache-report.html
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':child:t' (registered in build file 'child/build.gradle.kts').
> Invocation of 'Task.project' by task ':child:t' at execution time is unsupported with the configuration cache.
Moving the model reads to configuration time (val projectName = project.name outside the doLast) fixes both. The cache itself is on Configuration Cache & Isolated Projects.
What 8.x deprecated and 9.0 removed or changed (not run here)
Because 8.14.x was not available, the correspondence between the last 8.x line and the removals above is described from the 8.x guide. The 8.x line had these deprecation warnings that were turned into removals or behavior changes, and every one of them was visible with --warning-mode=all on 8.14:
| Deprecated in | Warning in 8.x | Outcome in 9.0 |
|---|---|---|
8.0 |
|
removed |
8.1 |
custom build layout through the |
removed, or behavior change |
8.2 |
plugin conventions (base, java, application, war, ear, project-report) |
conventions removed |
8.3 |
|
still present in 9.8.1 as a deprecated Kotlin property (the compiler warns, Gradle does not) |
8.5 |
including a project without a directory |
error |
8.7 |
|
removed |
8.8 |
Unix permission integers; the build cache retention setting; the |
removed |
8.10 |
running Gradle on older JVMs (announcement) |
Java 17 required |
8.11 |
|
|
8.12 |
|
still deprecated in 9.8.1, error planned for Gradle 10 |
8.13 |
Boolean properties with |
Groovy 4 stops recognizing them; Gradle stops in Gradle 10 |
8.14 |
|
9.0 as announced |
The 8.14 line of the table is the reason the 9.0 guide asks for the latest 8.x release first: 8.14 is the release whose deprecation messages are complete. The property of a deprecation warning that matters in practice is that it exists on the old release; on 9.0 the same code is just an error.
Within Gradle 9: from 9.0.0 to 9.8.1
Gradle 9 has been released every six to eleven weeks, and unlike the move to 9.0.0 the minor releases rarely break a clean build, but each one adds deprecations, and an upgrade guide section with "potential breaking changes". The table is the guide for 9.0.0 to 9.8.1 in one place, release by release; dates are those of the release history. The right-hand column is what to look for in the output of
--warning-mode=all after moving to that release.
| Release | Potential breaking changes | Deprecations introduced |
|---|---|---|
9.1.0 (2025-09-18) |
ASM 9.8 (Java 25), Groovy 4.0.28 |
multi-string dependency notation; |
9.2.0 (2025-10-29) |
Kotlin 2.2.20; the incubating |
|
9.3.0 (2026-01-16) |
|
|
9.4.0 (2026-03-04) |
|
|
9.5.0 (2026-04-28) |
Plugin requests of precompiled settings plugins are validated at compile time; the Windows start script was reworked; |
the |
9.6.0 (2026-06-18) |
AGP 8.x no longer works (it used removed Problems API internals); concurrency primitives (locks, latches) can no longer be serialized by the configuration cache; PMD 7.24.0, CodeNarc 3.7.0, Ant 1.10.17, Groovy 4.0.32, Kotlin 2.3.21 |
implicit lookup of properties in parent projects; Develocity plugin before 4.0; |
9.7.0 (2026-08-06) |
Resilient sync reports failed operations; no checksum files for signature files when publishing; the project cache directory is not watched; Kotlin DSL scripts cannot import |
the software model; the |
9.8.0 (2026-09-24) |
Task logging listeners are stored in the configuration cache; Kotlin |
implicit opt-in to parallel model building; starting external processes at configuration time; enabling the build cache from build logic; implicit task dependencies between builds; |
9.8.1 (2026-10-07) |
a patch release: three high-severity security fixes (deserialization in daemon and worker communication; repositories that fail to establish an SSL connection), toolchain validation on Debian and Ubuntu, a |
none |
Two entries in the table are not what a reader expects, and both could be reproduced. The 9.6 guide, "implicit lookup of properties in parent projects", is the deprecation with the widest effect, because a build that stores a shared value as an extra property (extra["greeting"], ext.greeting) of the root project and reads it by its bare name in a subproject has been doing the same thing for ten years. And "Kotlin DSL delegated
properties" deprecates, in one go, the forms val x by tasks.registering, val x: String by project, val x by extra and the delegates of Property, so a large part of the Kotlin DSL code written between 5.0 and 9.5 produces warnings.
A build with ten deprecations, before and after
The scratch build below has a root project that stores greeting as an extra property and a subproject child that does what the guides call out: reads the property through a delegate (Kotlin) or by bare name (Groovy), declares a dependency with the multi-string notation and with a Project object, registers a task through a delegate, adds an artifact to the archives configuration, runs a process while the build is configured, and
reads the project from a task action. The Groovy version also uses the space-assignment syntax and findAll. The root build.gradle.kts is extra["greeting"] = "hello" and the root build.gradle is ext.greeting = 'hello'; there is a second subproject core with the java-library plugin.
plugins { `java-library` }
val greeting: String? by project
val coreProject = project(":core")
dependencies {
implementation(group = "org.apache.commons", name = "commons-csv", version = "1.14.1")
implementation(coreProject)
}
val specialJar by tasks.registering(Jar::class) {
archiveBaseName = "special"
}
artifacts { add("archives", specialJar) }
val gitRev = ProcessBuilder("echo", "abc123").start().inputStream.bufferedReader().readText().trim()
tasks.register("report") {
doLast { println("greeting=$greeting rev=$gitRev in ${project.name}") }
}
plugins { id 'java-library' }
group 'io.tidepool'
def coreProject = project(':core')
dependencies {
implementation group: 'org.apache.commons', name: 'commons-csv', version: '1.14.1'
implementation coreProject
}
def specialJar = tasks.register('specialJar', Jar) {
archiveBaseName = 'special'
}
artifacts { archives specialJar }
def checks = tasks.findAll { it.name.startsWith('check') }
tasks.register('report') {
doLast { println "greeting=${greeting} checks=${checks*.name} in ${project.name}" }
}
Run with ./gradlew :child:report --warning-mode=all --no-configuration-cache, Kotlin DSL first (the long Kotlin compiler w: lines are shortened at 200 characters where a line ends in ..., and the Gradle messages are complete):
$ ./gradlew :child:report --warning-mode=all --no-configuration-cache --console=plain
> Configure project :child
w: file://<work>/child/build.gradle.kts:3:26: 'fun Project.provideDelegate(any: Any?, property: KProperty<*>): PropertyDelegate' is deprecated. Use 'val property = project.property(name)' or 'val prop ...
w: file://<work>/child/build.gradle.kts:8:5: 'fun DependencyHandler.implementation(group: String?, name: String?, version: String? = ..., configuration: String? = ..., classifier: String? = ..., ext: ...
w: file://<work>/child/build.gradle.kts:12:25: 'fun <U : Task> RegisteringDomainObjectDelegateProviderWithTypeAndAction<out TaskContainer, U>.provideDelegate(receiver: Any?, property: KProperty<*>): E ...
w: file://<work>/child/build.gradle.kts:12:25: 'fun <T : Any, C : PolymorphicDomainObjectContainer<T>, U : T> C.registering(type: KClass<U>, action: U.() -> Unit): RegisteringDomainObjectDelegateProvi ...
w: file://<work>/child/build.gradle.kts:12:25: 'fun <T> ExistingDomainObjectDelegate<out T>.getValue(receiver: Any?, property: KProperty<*>): T' is deprecated. Use the named() API instead. See the Gra ...
The 'val name: Type? by project' property delegate syntax has been deprecated. This is scheduled to be removed in Gradle 10. Use 'val property = project.findProperty(name)' instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#kotlin_dsl_delegated_properties
Declaring dependencies using multi-string notation has been deprecated. This will fail with an error in Gradle 10. Please use single-string notation ("org.apache.commons:commons-csv:1.14.1") or DependencyFactory instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#dependency_multi_string_notation
Using a Project object as a dependency notation has been deprecated. This will fail with an error in Gradle 10. Please use the project(String) method on DependencyHandler or the createProjectDependency(String) method on DependencyFactory instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#dependency_project_notation
The 'val name by registering(Type::class) { }' property delegate syntax has been deprecated. This is scheduled to be removed in Gradle 10. Use 'val element = register<Type>(name) { }' instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#kotlin_dsl_delegated_properties
The archives configuration has been deprecated for artifact declaration. This will fail with an error in Gradle 10. Add artifacts as direct task dependencies of the 'assemble' task instead of declaring them in the archives configuration. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#sec:archives-configuration
Starting an external process at configuration time has been deprecated. This will fail with an error in Gradle 11. Use ProviderFactory.exec/javaexec or a ValueSource to obtain the process output, or move the process execution into a task. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#config_time_external_processes
> Task :child:report
Implicit lookup of properties in parent projects has been deprecated. This will fail with an error in Gradle 10. Property 'greeting' was not declared in project ':child' and was resolved from root project 'tidepool'. This lookup was initiated by 'val ... by project'. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecated_implicit_lookup_in_parent_projects
Invocation of Task.project at execution time has been deprecated. This will fail with an error in Gradle 10. This API is incompatible with the configuration cache, which will become the only mode supported by Gradle in a future release. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_7.html#task_project
greeting=hello rev=abc123 in child
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
And the Groovy DSL:
$ ./gradlew :child:report --warning-mode=all --no-configuration-cache --console=plain
> Configure project :child
Build file '<work>/child/build.gradle': line 3
Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated. This is scheduled to be removed in Gradle 10. Use assignment ('group = <value>') instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_8.html#groovy_space_assignment_syntax
Build file '<work>/child/build.gradle': line 8
Declaring dependencies using multi-string notation has been deprecated. This will fail with an error in Gradle 10. Please use single-string notation ("org.apache.commons:commons-csv:1.14.1") or DependencyFactory instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#dependency_multi_string_notation
Build file '<work>/child/build.gradle': line 9
Using a Project object as a dependency notation has been deprecated. This will fail with an error in Gradle 10. Please use the project(String) method on DependencyHandler or the createProjectDependency(String) method on DependencyFactory instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#dependency_project_notation
Build file '<work>/child/build.gradle': line 16
The archives configuration has been deprecated for artifact declaration. This will fail with an error in Gradle 10. Add artifacts as direct task dependencies of the 'assemble' task instead of declaring them in the archives configuration. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#sec:archives-configuration
Build file '<work>/child/build.gradle': line 18
The DomainObjectCollection.findAll(Closure) method has been deprecated. This is scheduled to be removed in Gradle 10. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#findAll_removal
> Task :child:report
Build file '<work>/child/build.gradle': line 21
Implicit lookup of properties in parent projects has been deprecated. This will fail with an error in Gradle 10. Property 'greeting' was not declared in project ':child' and was resolved from root project 'tidepool'. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecated_implicit_lookup_in_parent_projects
Build file '<work>/child/build.gradle': line 21
Invocation of Task.project at execution time has been deprecated. This will fail with an error in Gradle 10. This API is incompatible with the configuration cache, which will become the only mode supported by Gradle in a future release. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_7.html#task_project
greeting=hello checks=[check] in child
BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed
Every message maps to a section of the 9.x guide through the link at its end, and to a replacement in the message. In the order of the Kotlin output:
| Warning | Fix | Since |
|---|---|---|
|
|
9.6 |
|
|
9.1 |
|
|
9.6 |
|
|
9.6 |
|
|
9.1 |
|
|
9.8 (error in Gradle 11) |
|
read it at configuration time into a local |
8.12, and an error with the configuration cache |
The Groovy rows are the same mapping plus two of its own: the space assignment group 'io.tidepool' becomes group = 'io.tidepool' (deprecated in 8.12), and tasks.findAll { ... } becomes tasks.matching { ... } (9.4). The fixed builds follow; they print no deprecation under --warning-mode=fail, and run with the configuration cache, which is what makes the second run of each (Reusing configuration cache) meaningful. The value now comes from gradle.properties (greeting=hello)
and the root build file is gone.
plugins { `java-library` }
val greeting = providers.gradleProperty("greeting")
dependencies {
implementation("org.apache.commons:commons-csv:1.14.1")
implementation(project(":core"))
}
val specialJar = tasks.register<Jar>("specialJar") {
archiveBaseName = "special"
}
tasks.named("assemble") { dependsOn(specialJar) }
val gitRev = providers.exec { commandLine("echo", "abc123") }.standardOutput.asText.map { it.trim() }
tasks.register("report") {
val greeting = greeting
val gitRev = gitRev
val projectName = project.name
doLast { println("greeting=${greeting.get()} rev=${gitRev.get()} in $projectName") }
}
plugins { id 'java-library' }
group = 'io.tidepool'
def greeting = providers.gradleProperty('greeting')
dependencies {
implementation 'org.apache.commons:commons-csv:1.14.1'
implementation project(':core')
}
def specialJar = tasks.register('specialJar', Jar) {
archiveBaseName = 'special'
}
tasks.named('assemble') { dependsOn(specialJar) }
def checks = tasks.matching { it.name.startsWith('check') }
tasks.register('report') {
def projectName = project.name
def greetingValue = greeting
def checkNames = checks.collect { it.name }
doLast { println "greeting=${greetingValue.get()} checks=${checkNames} in ${projectName}" }
}
The first run of each, with ./gradlew :child:report --warning-mode=fail --configuration-cache, Kotlin DSL then Groovy DSL:
$ ./gradlew :child:report --warning-mode=fail --configuration-cache --console=plain
Calculating task graph as no cached configuration is available for tasks: :child:report
> Task :child:report
greeting=hello rev=abc123 in child
BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed
Configuration cache entry stored.
$ ./gradlew :child:report --warning-mode=fail --configuration-cache --console=plain
Calculating task graph as configuration cache cannot be reused because file 'child/build.gradle' has changed.
> Task :child:report
greeting=hello checks=[check] in child
BUILD SUCCESSFUL in 643ms
1 actionable task: 1 executed
Configuration cache entry stored.
Both are compatible with the configuration cache and free of deprecations; the second run of the Kotlin build reused the entry:
$ ./gradlew :child:report --warning-mode=fail --configuration-cache --console=plain
Reusing configuration cache.
> Task :child:report
greeting=hello rev=abc123 in child
BUILD SUCCESSFUL in 604ms
1 actionable task: 1 executed
Configuration cache entry reused.
Deprecations that need a single line to reproduce
The remaining 9.x deprecations are easier to see one at a time. Each of the following was produced by a one-line build file in a subproject child (Kotlin DSL unless it says Groovy), with --warning-mode=all --no-configuration-cache; only the new line of output is shown.
Groovy "space assignment" (8.12, Gradle 10). In regular Groovy propName value is a method call; Gradle generates the method, and the generated form is going away. Write propName = value; where a real method exists, write propName(value).
Build file '<work>/child/build.gradle': line 1
Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated. This is scheduled to be removed in Gradle 10. Use assignment ('group = <value>') instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_8.html#groovy_space_assignment_syntax
Build file '<work>/child/build.gradle': line 2
Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated. This is scheduled to be removed in Gradle 10. Use assignment ('version = <value>') instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_8.html#groovy_space_assignment_syntax
Project.getProperties() (9.6, Gradle 10). The map merges properties from many places and walks the parent chain; providers.gradleProperty("name") returns a typed provider of a Gradle property, project.property("name") or findProperty one value.
The Project.getProperties method has been deprecated. This will fail with an error in Gradle 10. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecated_get_properties
Implicit lookup in parent projects (9.6, Gradle 10). A subproject read a property that only the root project defines. The messages for the Kotlin delegate and for a Groovy bare name:
The 'val name: Type? by project' property delegate syntax has been deprecated. This is scheduled to be removed in Gradle 10. Use 'val property = project.findProperty(name)' instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#kotlin_dsl_delegated_properties
Implicit lookup of properties in parent projects has been deprecated. This will fail with an error in Gradle 10. Property 'greeting' was not declared in project ':child' and was resolved from root project 'lab'. This lookup was initiated by 'val ... by project'. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecated_implicit_lookup_in_parent_projects
Build file '<work>/child/build.gradle': line 1
Implicit lookup of properties in parent projects has been deprecated. This will fail with an error in Gradle 10. Property 'greeting' was not declared in project ':child' and was resolved from root project 'lab'. This lookup was initiated by a dynamic invocation in the build script. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecated_implicit_lookup_in_parent_projects
The feature preview NO_IMPLICIT_LOOKUP_IN_PARENT_PROJECTS switches a build to the Gradle 10 behavior before the deprecations are all fixed, which prevents new accidental lookups from creeping in (enableFeaturePreview("NO_IMPLICIT_LOOKUP_IN_PARENT_PROJECTS") in the settings file). With it, the same Groovy line is an error, and the Kotlin delegate (whose own deprecation remains) gets null instead of the root’s value:
* Where:
Build file '<work>/child/build.gradle' line: 1
* What went wrong:
A problem occurred evaluating project ':child'.
> Could not get unknown property 'greeting' for project ':child' of type org.gradle.api.Project.
The 'val name: Type? by project' property delegate syntax has been deprecated. This is scheduled to be removed in Gradle 10. Use 'val property = project.findProperty(name)' instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#kotlin_dsl_delegated_properties
null
buildNeeded (9.6, Gradle 10). Running the task prints the deprecation after the build tasks ran (it is the task itself that is deprecated):
> Task :child:buildNeeded
The buildNeeded task has been deprecated. This is scheduled to be removed in Gradle 10. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecate_build_needed_build_dependents_tasks
Enabling the build cache from build logic (9.8, Gradle 10). gradle.startParameter.isBuildCacheEnabled = true in settings.gradle.kts; the supported way is the Gradle property org.gradle.caching=true or --build-cache.
The StartParameter.setBuildCacheEnabled(boolean) method has been deprecated. This is scheduled to be removed in Gradle 10. Use the 'org.gradle.caching' Gradle property to enable or disable the build cache instead. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#deprecation_enabling_build_cache_from_build_logic
Toolchain properties as project properties (9.1, Gradle 10). -Porg.gradle.java.installations.auto-detect=false becomes -Dorg.gradle.java.installations.auto-detect=false; the build still ran:
> Configure project :child
Specifying 'org.gradle.java.installations.auto-detect' as a project property on the command line has been deprecated. This is scheduled to be removed in Gradle 10. Instead, specify it as a Gradle property: '-Dorg.gradle.java.installations.auto-detect=false'. Consult the upgrading guide for further information: https://docs.gradle.org/9.8.1/userguide/upgrading_version_9.html#toolchain-project-properties
Feature preview ENHANCED_GRAPH_ORDERING (9.7). A preview that opts in to the dependency resolution ordering that becomes the default in Gradle 10 (breadth-first for the DEFAULT sort order, a topological CONSUMER_FIRST option, constraint edges ignored for all sort options). enableFeaturePreview("ENHANCED_GRAPH_ORDERING") was accepted by 9.8.1 without output; a misspelled name is an error:
* Where:
Settings file '<work>/settings.gradle.kts' line: 3
* What went wrong:
There is no feature named NO_SUCH_PREVIEW
Source: Gradle User Manual: Upgrading from 9.7.0 and earlier, implicit lookup of properties and methods in parent projects, opting into the Gradle 10 behavior, Kotlin DSL delegated properties, getProperties(), multi-string notation, Project objects as dependency notation, the archives configuration, external processes at configuration time, findAll, enabling the build cache from build logic, toolchain project properties, buildNeeded and buildDependents, improved dependency resolution ordering and Groovy space assignment
Announced for Gradle 10
Gradle 10 has not been released; what follows is the list of things that the guides of Gradle 9.8.1 say will change, stated as announced and not as released. The sentence the guides repeat is that the configuration cache "will be enabled by default in Gradle 10", and that is why many deprecations are now reported even when the cache is off: they are the behaviors that the cache cannot support.
| Announced change | Announced in | What to do now |
|---|---|---|
The configuration cache is on by default |
9.0 ("preferred mode"), restated in 9.6-9.8 |
Run the build with |
Implicit lookup of properties and methods in parent projects is removed |
9.6 |
|
|
9.6, 9.6, 8.13, 8.12 |
Replace by the explicit forms in the sections above. |
Multi-string dependency notation, |
9.1, 9.6, 9.3, 9.3 |
Single-string notation, |
Reading |
8.12 and 9.6 |
Capture values at configuration time or declare task inputs. |
|
9.1 |
|
|
9.6 |
Run |
The IDE file generation tasks ( |
9.6 |
Import the project from the IDE; the |
The software model ( |
9.7 |
|
|
9.7 |
Rename the properties. |
Implicit parallel model building from |
9.8 |
Set |
External processes at configuration time with |
9.8 |
A task, |
Enabling the build cache from build logic; |
9.8 |
|
Tasks that consume the outputs of a task in another build of a composite build without declaring a dependency |
9.8 |
Declare the dependency (a warning in 9.8, an error in Gradle 10). |
Attribute values that are not |
9.8 |
Make an enum implement |
macOS on AMD64 as a supported platform |
9.8 |
Move to Apple silicon; JDK 27 and later are not available for macOS x64. |
Publishing a project that depends on an unpublished project |
9.3 |
Publish the dependency or remove it. |
The older majors: 4 to 8
An old build rarely sits on 8.14. Builds copied from books and blog posts sit on 1.x to 4.x, and the practical question is what a 4.x build needs. The upgrade guides were written one hop at a time, and the supported way to move is to follow them one hop at a time: first to the last release of the old line, then to the next major, using the deprecation warnings of the old line to find the changes. The table lists, for each hop, the changes that a build file written in the style of the Gradle 1.x and 2.x era notices first (the guides list many more).
| Hop | Changes that break typical build files |
|---|---|
4.10 to 5.0 (2018-11-26) |
Gradle 5 needs Java 8 to run (4.x needs Java 7). |
5.6 to 6.0 (2019-11-08) |
The FindBugs, JDepend, OSGi, announce and build-announcements, and Compare Gradle Builds plugins are removed. Dependencies should no longer be declared using the |
6.9 to 7.0 (2021-04-09) |
The |
7.6 to 8.0 (2023-02-13) |
Warnings that became errors: referencing tasks of an included build with |
8.14 to 9.0.0 |
The previous section. |
The first column’s dates are the release dates from the release history. For 4.10 to 5.0, the guide for the 4.x line has the sentence that sums up why a direct jump is risky and what to do instead: upgrade to the latest 4.10.x release first "to get the most useful warnings and deprecations information before moving to 5.0".
Gradle in Action on Gradle 9.8.1
Gradle in Action (Muschko, Manning 2014) targets Gradle 1.7 and the Groovy DSL, and it is still a good book about the model: projects, tasks, the lifecycle, configurations and the Tooling API did not change in kind. What changed is the syntax and the plugin set around them, and the changes can be traced to specific releases. This section takes the build of the book’s style, shaped like the tidepool project, and runs it on 9.8.1 one error at a time. The features the book teaches have their own pages in this section; here the question is only what a reader who types in the book’s example finds out.
The book’s build file on Gradle 9.8.1
The scratch project is a single-project build of the tidepool sources, with the idioms of Gradle 1.7: apply plugin, the maven plugin, archivesBaseName, sourceCompatibility, jcenter(), compile and testCompile, a task defined with <<, and uploadArchives with a Maven deployer.
apply plugin: 'java'
apply plugin: 'maven'
group = 'io.tidepool'
version = '1.0.0'
archivesBaseName = 'tidepool'
sourceCompatibility = 1.7
repositories {
jcenter()
}
dependencies {
compile 'info.picocli:picocli:4.7.7'
testCompile 'junit:junit:4.12'
}
task hello << {
println 'Hello from Gradle 1.7'
}
uploadArchives {
repositories {
mavenDeployer {
repository(url: "file://localhost/tmp/repo")
}
}
}
Every error below is the first one that ./gradlew hello printed on 9.8.1 after the previous fix, with the lines before * Try: kept. There is nothing to compare on 8.14 here (not run here), but in every case the cause is a removal from the table of this page, and the order of the errors is the order in which Groovy evaluates the file.
1. apply plugin: 'maven'. The legacy maven plugin was deprecated in 6.0 and removed in 7.0. Delete the line; the replacement, maven-publish, comes with the last step.
* Where:
Build file '<work>/build.gradle' line: 2
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Plugin with id 'maven' not found.
* Try:
2. archivesBaseName. One of the conventions, removed in 9.0 (deprecated in 8.2). The base extension has the setting under another name.
* Where:
Build file '<work>/build.gradle' line: 5
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Could not set unknown property 'archivesBaseName' for root project 'tidepool' of type org.gradle.api.Project.
* Try:
3. sourceCompatibility. Another convention; the extension is java, and a toolchain is the better statement (see Gradle Toolchains). The replacement uses 17, because a JDK 26 javac no longer compiles for Java 7.
* Where:
Build file '<work>/build.gradle' line: 6
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Could not set unknown property 'sourceCompatibility' for root project 'tidepool' of type org.gradle.api.Project.
* Try:
4. jcenter(). Deprecated in 7.0 and removed in 9.0; the guide notes that the JCenter repository was redirected to Maven Central in August 2024.
* Where:
Build file '<work>/build.gradle' line: 9
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Could not find method jcenter() for arguments [] on repository container of type org.gradle.api.internal.artifacts.dsl.DefaultRepositoryHandler.
* Try:
5. compile. Removed in 7.0 (deprecated since 6.0). The rule of thumb of the 7.0 guide: compile becomes implementation unless the dependency is part of the public API of a library, in which case it is api and the java-library plugin is needed.
* Where:
Build file '<work>/build.gradle' line: 13
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Could not find method compile() for arguments [info.picocli:picocli:4.7.7] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
* Try:
6. testCompile. Removed in 7.0, replaced by testImplementation. Likewise runtime by runtimeOnly and testRuntime by testRuntimeOnly.
* Where:
Build file '<work>/build.gradle' line: 14
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Could not find method testCompile() for arguments [junit:junit:4.12] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
* Try:
7. task hello << { }. The left-shift operator on a task was deprecated in 4.x and removed in 5.0. Groovy now tries to call a method leftShift on the task; the message does not mention the operator, which is why this error confuses people. doLast is the replacement, and tasks.register is the lazy form (4.9).
* Where:
Build file '<work>/build.gradle' line: 17
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Could not find method leftShift() for arguments [build_9k16nfr5ao6rve5hpmmkb35h3$_run_closure5@1e90fd6b] on task ':hello' of type org.gradle.api.DefaultTask.
* Try:
8. uploadArchives. The uploadArchives task came with the maven plugin and was removed in 7.0 with it. Publishing is the maven-publish plugin, described on Publishing.
* Where:
Build file '<work>/build.gradle' line: 23
* What went wrong:
A problem occurred evaluating root project 'tidepool'.
> Could not find method uploadArchives() for arguments [build_9k16nfr5ao6rve5hpmmkb35h3$_run_closure6@620fd1e3] on root project 'tidepool' of type org.gradle.api.Project.
* Try:
After the eight fixes the build file is the one below, written in the Groovy DSL and then in the Kotlin DSL. It uses the plugins { } block (Gradle 2.1 and the Plugin Portal), the application plugin so that the result can be run, a toolchain, mavenCentral(), the commons-csv dependency that the tidepool sources need (the build without it failed with package org.apache.commons.csv does not exist), and
maven-publish to a directory of the build.
plugins {
id 'application'
id 'maven-publish'
}
group = 'io.tidepool'
version = '1.0.0'
base { archivesName = 'tidepool' }
java {
toolchain { languageVersion = JavaLanguageVersion.of(17) }
}
application { mainClass = 'io.tidepool.cli.Main' }
repositories {
mavenCentral()
}
dependencies {
implementation 'org.apache.commons:commons-csv:1.14.1'
implementation 'info.picocli:picocli:4.7.7'
}
tasks.register('hello') {
doLast {
println 'Hello from Gradle 9'
}
}
publishing {
publications {
maven(MavenPublication) {
from components.java
}
}
repositories {
maven { url = layout.buildDirectory.dir('repo') }
}
}
plugins {
application
`maven-publish`
}
group = "io.tidepool"
version = "1.0.0"
base { archivesName = "tidepool" }
java {
toolchain { languageVersion = JavaLanguageVersion.of(17) }
}
application { mainClass = "io.tidepool.cli.Main" }
repositories {
mavenCentral()
}
dependencies {
implementation("org.apache.commons:commons-csv:1.14.1")
implementation("info.picocli:picocli:4.7.7")
}
tasks.register("hello") {
doLast {
println("Hello from Gradle 9")
}
}
publishing {
publications {
register<MavenPublication>("maven") {
from(components["java"])
}
}
repositories {
maven { url = layout.buildDirectory.dir("repo").get().asFile.toURI() }
}
}
Both files ran with ./gradlew hello run --args="data/stations.csv SF" publish --warning-mode=fail --configuration-cache. The Java 17 toolchain was the JetBrains Runtime 17.0.14 that is installed on the machine and detected automatically, while the daemon ran on JDK 26. The Kotlin DSL output follows; the Groovy DSL printed the same tasks, with generatePomFileForMavenPublication and compileJava reported as UP-TO-DATE because the
Groovy project had been compiled by an earlier run.
$ ./gradlew hello run --args="data/stations.csv SF" publish --warning-mode=fail --configuration-cache --console=plain
Calculating task graph as no cached configuration is available for tasks: hello run --args=data/stations.csv SF publish
> Task :hello
Hello from Gradle 9
> Task :processResources NO-SOURCE
> Task :generatePomFileForMavenPublication
> Task :compileJava
> Task :classes
> Task :jar
> Task :generateMetadataFileForMavenPublication
> Task :publishMavenPublicationToMavenRepository
> Task :publish
> Task :run
San Francisco: -0.869 m
BUILD SUCCESSFUL in 4s
7 actionable tasks: 7 executed
Configuration cache entry stored.
The plugins that the book uses and Gradle 9.8.1 no longer finds are the next question, and which ones are still bundled. Each id was applied alone, with apply plugin: 'id' in a build file of a scratch project, and the help task was run; the line that Gradle printed for each:
maven -> Plugin with id 'maven' not found.
findbugs -> Plugin with id 'findbugs' not found.
jdepend -> Plugin with id 'jdepend' not found.
jetty -> Plugin with id 'jetty' not found.
sonar -> Plugin with id 'sonar' not found.
sonar-runner -> Plugin with id 'sonar-runner' not found.
osgi -> Plugin with id 'osgi' not found.
announce -> Plugin with id 'announce' not found.
build-announcements -> Plugin with id 'build-announcements' not found.
compare-gradle-builds -> Plugin with id 'compare-gradle-builds' not found.
jshint -> Plugin with id 'jshint' not found.
rhino -> Plugin with id 'rhino' not found.
play -> Plugin with id 'play' not found.
All thirteen are the same failure, and the release that removed each is, from the guides and release notes:
| Plugin id | Removed in |
|---|---|
|
7.0 (legacy publication system; use |
|
6.0 (the 5.x guide names the third-party SpotBugs plugin as the alternative to FindBugs) |
|
6.0 |
|
6.0 |
|
6.0 (a Build Scan is the replacement) |
|
4.0 (deprecated in 3.0; the release notes recommend Gretty, third party) |
|
7.0 (the experimental JavaScript plugins; the 5.x guide announced the removal as "the next major release") |
|
6.0 or 7.0: the guide for 5.x lists the Play plugins as removed in 6.0, and the guide for 6.x lists "Removal of Play Framework plugins" in 7.0 |
|
3.0 (the legacy Sonar plugin; SonarQube’s own |
|
not verified: the release that removed it is not stated in the guides that were read |
The plugins that did apply, each in its own run, were java, checkstyle, pmd, jacoco, codenarc, eclipse, idea, war, ear, application, signing, ivy-publish, maven-publish, distribution, scala, groovy, project-report, build-dashboard and cpp-library, which
is the answer to "where did the checks of the book go": Checkstyle, PMD and JaCoCo are still bundled, Cobertura never was, and FindBugs and JDepend are not.
The idioms of the book, one by one
Every row of the table is a statement of the book (or a practice it recommends) that is wrong or outdated on Gradle 9.8.1, with the release that changed it and the current form. The rows that were reproduced above say so; the others come from the cited pages.
| Book statement or practice | Current status | Source |
|---|---|---|
|
Removed in 5.0 (Gradle 5.0 changes: |
upgrading_version_4 |
|
Removed in 7.0; |
upgrading_version_6 |
|
The |
plugins |
Groovy DSL only |
The Kotlin DSL became 1.0 in 5.0 and the default of |
release notes 8.2 |
|
The |
build_init_plugin |
The |
Deprecated in 3.0, removed in 4.0; the third-party Gretty plugin is the usual replacement. Reproduced above. |
release notes 4.0 |
The |
Removed in 7.0; |
upgrading_version_6 |
|
Stable and the standard form of publishing. |
publishing_maven |
FindBugs, JDepend, announce and build-announcements plugins; the build comparison plugin |
Removed in 6.0 (deprecated 5.x); SpotBugs and others are third-party plugins. Reproduced above. |
upgrading_version_5 |
JavaScript plugins (rhino, envjs, coffeescript, jshint) |
Removed in 7.0 (the outline of this section had 6.0; the 6.x guide says 7.0). Reproduced above. |
upgrading_version_6 |
The |
The legacy Sonar plugin was removed in 3.0; |
release notes 3.0 |
The Cobertura plugin |
Was always third party; the core JaCoCo plugin is the standard. |
jacoco_plugin |
The daemon as an opt-in ( |
On by default since 3.0. |
release notes 3.0 |
JDK 1.5+ to run Gradle; Groovy 1.8.6 |
Java 17 or newer for the daemon since 9.0; Groovy 4 and Kotlin 2 are embedded; older Java only through toolchains. Reproduced above. |
compatibility |
|
Project-level |
toolchains |
JCenter and Bintray |
|
upgrading_major_version_9 |
|
Convention plugins in |
sharing_build_logic_between_subprojects |
Versions in |
Version catalogs (stable in 7.4; Version Catalogs). Reading an |
version_catalogs |
No configuration cache; |
The configuration cache is stable since 8.1 and the preferred mode since 9.0, and reading |
configuration_cache_requirements |
No build cache |
Production-ready since 4.0 (Build Cache). |
build_cache |
Integration tests through hand-made source sets |
The JVM Test Suite plugin. |
jvm_test_suite_plugin |
Plugin tests with |
TestKit |
test_kit |
Gradle 1.x version numbers |
Semantic versioning since 9.0. |
release notes 9.0.0 |
The |
The IDEs import projects through the Tooling API; the file generation tasks of the plugins are deprecated since 9.6, while the |
third_party_integration |
CloudBees RUN@cloud, Sublime Text, Grunt, Vagrant with Puppet, the 2013 Jenkins pipeline plugins |
Discontinued or replaced (not verified individually). |
- |
Source: Gradle User Manual: Upgrading from 4.x, Upgrading from 6.x, Upgrading from 5.x, Using Plugins, Gradle 8.2 release notes, Build Init Plugin, Maven Publish Plugin, JaCoCo Plugin, Compatibility Matrix, Toolchains, convention plugins versus cross-configuration, Version Catalogs, Configuration Cache Requirements, Build Cache, JVM Test Suite Plugin, TestKit and Third-party IDE integration
Gradle, Maven and Ant
The three tools differ in what "version of the tool" means, and so in what an upgrade is.
| Ant | Maven | Gradle | |
|---|---|---|---|
Pin the tool version in the repository |
No wrapper; the Ant on the path is used. Ant 1.10 needs Java 8 or later, and Ant 1.9, which ran on Java 5, has been end of life since 19 June 2024 (Ant: Deprecations & Migration). |
The Maven Wrapper: |
The |
What the new major needs to run |
1.10: Java 8. |
Maven 4: Java 17 (Migrating to Maven 4). |
Gradle 9: Java 17 for the daemon. |
How the old line tells you what breaks |
The release notes (WHATSNEW) and the list of deprecated tasks on the page linked above. |
The latest 3.x prints warnings about the model; Maven 3.9.16 warns where 3.10.0 fails, and Maven 4 can be made to fail on warnings (see the page on migrating to Maven 4). |
|
The tool that migrates the files |
- |
|
None for Gradle itself: the |
The Gradle practice that stands out is the deprecation discipline: because the warnings carry the target release and a link, an upgrade can be planned as a task list. Moving a build between the tools is a different problem, covered on Migrating Between Ant, Maven & Gradle, Migrating from Maven and Ant Integration.
Checklist for an upgrade
-
Commit a green build. Note the Gradle version (
./gradlew --version), the Java of the daemon and the plugin versions (./gradlew buildEnvironment). -
Move to the last release of the current line (for 8.x, 8.14.6) and read its section in the upgrade guide.
-
Run
./gradlew <the tasks CI runs> --warning-mode=all --no-configuration-cache, andhelp --scanwhen the scan is allowed. Fix every warning, starting with the ones that your own scripts cause; update the plugins that cause the others. -
Run again with
--warning-mode=failuntil it passes. Run with--configuration-cacheand fix the problems it reports. -
Make sure the daemon will have a Java the target Gradle supports (17 or newer for 9.x), and that toolchains say which Java compiles, tests and runs the code.
-
Move the wrapper:
./gradlew wrapper --gradle-version <target> --gradle-distribution-sha256-sum <sum>, then./gradlew wrapperagain; check thegradle-wrapper.propertiesdiff and commit the four wrapper files. -
Run the build and the tests. Compare the produced archives if you depend on their bytes, and check that every test task still runs tests.
-
Pin the versions of the quality tools (Checkstyle, PMD, JaCoCo) until the upgrade has settled.
-
Set
org.gradle.warning.mode=fail(or-Dorg.gradle.warning.mode=fail) on CI so that the next deprecation is a failing build before it becomes an error. -
Do not migrate to the Kotlin DSL, a version catalog or a new plugin in the same change.
Where to go next
-
Gradle Wrapper: every key of the properties file, checksums and download settings.
-
Gradle Versions, Compatibility & Release History: the release table, compatibility and the support policy.
-
Configuration Cache & Isolated Projects: the mode that Gradle 10 is expected to make the default.
-
Best Practices & Troubleshooting: the troubleshooting guide for the errors that an upgrade produces.
-
Kotlin DSL and Lazy Configuration & Providers: the replacements of the deprecated Kotlin delegates and eager APIs.