跪拜 Guibai
← Back to the summary

Gradle 9.6.0 Stops Invalidating Configuration Cache for CI-Only Parameters

Foreword

CI often passes temporary parameters to Gradle, such as version numbers, channel IDs, and build switches.

If these parameters are only used during the task execution phase, they should not affect configuration caching. However, in older versions, project properties passed via -Dorg.gradle.project.* or ORG_GRADLE_PROJECT_* could cause the Configuration Cache to recalculate whenever their values changed.

The most noteworthy fix in Gradle 9.6.0 is this configuration cache correction. Android projects with many CI parameters will feel this more than single-module local builds.

Image

Parameter changes no longer invalidate the cache unnecessarily

Gradle's Configuration Cache caches the results of the configuration phase. A previous annoyance: when project properties were passed via system properties or environment variables, any change in their values could invalidate the configuration cache, even if the property was never read during the configuration phase.

Consider this minimal example:

tasks.register("printValue") {
    val value = providers.gradleProperty("value").orElse("N/A")

    doLast {
        println("value: ${value.get()}")
    }
}

Here, value is read during the task execution phase, not the configuration phase.

In older versions, if you passed a value via -Dorg.gradle.project.value=1 or ORG_GRADLE_PROJECT_value=1, and then changed it next time, Gradle might consider the cache non-reusable.

Gradle 9.6.0 recognizes that this property does not participate in the configuration phase, so the following commands can continue to reuse the configuration cache:

./gradlew --configuration-cache printValue -Dorg.gradle.project.value=2

ORG_GRADLE_PROJECT_value=3 ./gradlew --configuration-cache printValue

For Android projects, typical scenarios include CI build numbers, channel package parameters, whether to upload mapping files, or whether to enable a certain publishing action. These values often differ between builds but are frequently used only within task actions.

If the value is only read during the execution phase, Gradle 9.6.0 will not recompute the task graph just because the value changed.

This optimization will not automatically fix all configuration cache issues in a project. If a custom task reads environment variables during the configuration phase, or if a plugin performs I/O during configuration, the cache will still be invalidated. What 9.6.0 fixes is the over-tracking of project properties that are never read during the configuration phase.

Image

CI commands: no more waiting for input

Another CI-related change is --non-interactive. This flag disables interactive console prompts, making it suitable for pipelines and scripts without user input.

In CI, you can write:

./gradlew --non-interactive --configuration-cache :app:assembleRelease

It doesn't address build speed, but rather the problem of builds hanging in automated environments. If a build command enters a state waiting for input, CI will typically just time out, and the logs may not immediately reveal the cause.

NO_COLOR=1 ./gradlew --non-interactive test

NO_COLOR is also suitable for CI. When the environment variable NO_COLOR is non-empty, Gradle disables color output. This prevents control characters from cluttering logs collected by the platform.

The test report also received a small enhancement. In the HTML report generated by the Test task, table column headers are now clickable for sorting. Numeric columns like test count, failure count, skipped count, and duration default to descending order; the success rate column defaults to ascending order. This saves a bit of time when checking slow tests and failing classes in multi-module projects.

Image

Root variables: stop sneaky reads

This release includes a deprecation that will affect older Gradle scripts: implicit lookup of parent project properties and methods from subprojects now triggers a warning, and this behavior will be removed in Gradle 10.

In Groovy DSL, it's easy to write scripts like this:

// root build.gradle
ext.foo = "hello"

// child/build.gradle
println(foo)

child/build.gradle does not define foo, so Gradle looks it up in the parent project and resolves it from ext.foo in the root project. This used to work, but as projects grow, it leaves hidden dependencies. A typo could accidentally match a property with the same name in the parent project.

Starting with Gradle 9.6.0, implicit references and APIs like findProperty(), property(), and hasProperty() will emit deprecation warnings if they resolve values from a parent project.

Shared configuration should be moved to explicit locations. Simple values go into gradle.properties:

compileSdk=36
minSdk=26

In Kotlin DSL, read them via Provider:

val compileSdkVersion = providers.gradleProperty("compileSdk")
    .map(String::toInt)

android {
    compileSdk = compileSdkVersion.get()
}

More complex convention configuration should be placed in convention plugins. For example, unify compileSdk, minSdk, and Kotlin compilation parameters across Android library modules, rather than having each submodule implicitly fetch values from the root project.

After addressing these warnings, you can enable Gradle 10 behavior early in settings.gradle.kts:

enableFeaturePreview("NO_IMPLICIT_LOOKUP_IN_PARENT_PROJECTS")

This is particularly useful for older Android projects. Many projects migrated from single-module to multi-module setups years ago, piling up variables in the root project's ext, subprojects, and allprojects blocks. Gradle 9.6.0 won't break the build immediately, but it has opened the migration window.

Image

How to upgrade

Upgrade by modifying the Wrapper:

./gradlew :wrapper --gradle-version=9.6.0
./gradlew :wrapper

After execution, gradle/wrapper/gradle-wrapper.properties will point to the new Gradle distribution:

distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip

For compatibility, Gradle 9.6.0 itself requires JVM 17 to 26. The bundled Kotlin is 2.3.21, and the Kotlin DSL language version remains 2.2. On the Android side, Gradle 9.6.0 has been tested with AGP versions 9.0 through 9.3.0-alpha06.

Image

Finally

What version of Gradle have you all upgraded to?

#Android #Gradle #AGP #CI #Build Optimization