Enable the Gradle configuration cache and configuration on demand - #234
Merged
rubensworks merged 2 commits intoAug 22, 2026
Merged
Conversation
Serialises the configuration phase and replays it, so its cost is paid once instead of on every invocation. Three changes were needed to make the build configuration-cache compatible: - Bump Spotless 6.25.0 -> 7.2.1. Spotless 6.x fails every configuration cache run without a daemon with "Spotless JVM-local cache is stale" (diffplug/spotless#987). - Replace the deprecated indentWithSpaces() with leadingTabsToSpaces(), its Spotless 7 equivalent. Same behaviour (4 spaces). - Look up git tags in getChangelog() via providers.exec instead of Groovy's String.execute(). Starting a process during configuration is unsupported by the configuration cache, and was the only remaining violation in the build. The resulting changelog string is unchanged. Also enables the Gradle daemon, which is the larger win locally: a rebuild after editing a loader-common source drops from ~37s to ~20s. CI is unaffected, as its workflow passes --no-daemon explicitly. Measured on this branch, ./gradlew build with warm caches: up-to-date, --no-daemon 13.8s -> 7.7s (-44%) up-to-date, daemon 3.3s -> 2.1s (-36%) after a loader-common edit 37.1s -> 37.1s (no change) after that edit, daemon 20.1s -> 17.3s (-14%) runGameTestServer 32.5s -> 25.6s (-20%) The configuration cache lives in the project's .gradle directory, which setup-gradle does not save or restore, so CI stores a fresh entry each run rather than reusing one. The benefit is to local iteration. Problems are reported as warnings rather than failing the build, so that a future plugin update reintroducing one degrades gracefully. The build currently reports zero problems in strict mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVogTgqmFMYafVAsnQMs7k
Configuration on demand only configures the projects needed by the
requested tasks. For unqualified tasks like `build` that is still all
four subprojects, so it changes nothing there, but it composes cleanly
with the configuration cache and pays off for qualified invocations:
configuration cache miss, ./gradlew build
on 5.6s / 5.0s (4 projects configured)
off 5.0s / 5.2s (4 projects configured)
configuration cache miss, ./gradlew :loader-neoforge:build
on 1.8s / 1.8s (2 projects configured)
off 4.7s / 4.6s (4 projects configured)
On a configuration cache hit no project is configured at all, so this
only affects runs that miss the cache.
Also drops the configuration-cache.problems=warn setting, so that
problems fail the build rather than being reported and ignored. The
build reports none today, and a plugin update that introduces one
should be visible rather than silently degrading.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NVogTgqmFMYafVAsnQMs7k
|
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Serialises the configuration phase and replays it, so its cost is paid once instead of on every invocation, and only configures the projects a given invocation actually needs.
This started as an investigation into configuration on demand alone. On its own that is the weaker lever — it only skips projects not needed by the requested tasks, and every command in
ci.ymlandAGENTS.mduses unqualified task names (build,test,runGameTestServer,publish, …) that match a task in all four subprojects. Measured onmaster-1.21-lts,./gradlew buildwas 20.4s baseline vs 21.5s with--configure-on-demand— no gain. It only helps qualified invocations like:loader-fabric:build(−28%).JustEnoughItems uses the configuration cache instead, which is what helps
./gradlew builditself. Both are enabled here; they compose cleanly.Changes
Three fixes were needed to make the build configuration-cache compatible:
Spotless JVM-local cache is stale(diffplug/spotless#987).indentWithSpaces()withleadingTabsToSpaces(), its Spotless 7 equivalent. Same behaviour (4 spaces).getChangelog()viaproviders.execinstead of Groovy'sString.execute(). Starting a process during configuration is unsupported by the configuration cache, and was the only remaining violation in the build.Then enables
org.gradle.configuration-cache,org.gradle.configureondemandandorg.gradle.daemon.Numbers
./gradlew build, warm caches, 4 vCPU:--no-daemonloader-commonedit,--no-daemonrunGameTestServerEnabling the daemon on its own accounts for most of it: a rebuild after editing a
loader-commonsource drops from ~37s to ~20s, and an up-to-datebuildfrom 13.8s to 3.3s.Configuration on demand only affects runs that miss the configuration cache — on a hit, no project is configured at all. On a miss:
buildbuild:loader-neoforge:build:loader-neoforge:buildSo it is neutral for
buildand worth −62% for a single-loader build.Scope of the benefit
CI is largely unaffected. The configuration cache lives in the project's
.gradledirectory, whichgradle/actions/setup-gradledoes not save or restore — it only handlescachesandnotificationsunder Gradle User Home. So CI stores a fresh entry each run rather than reusing one. The daemon setting is likewise inert in CI, since the workflow passes--no-daemonexplicitly. The benefit here is to local iteration.Configuration cache problems fail the build rather than being downgraded to warnings, so that a future update to Loom, ModDevGradle or ForgeGradle that reintroduces one is visible instead of silently degrading. The build reports zero problems today.
Verification
All run on this branch with the final settings and no extra command-line flags:
./gradlew buildfrom emptybuild/dirs — succeeds, produces all 12 jars../gradlew build --no-daemon(CI's command) — succeeds../gradlew test runGameTestServer jacocoTestReport(CI's test command) — succeeds,All 2 required tests passed../gradlew :loader-neoforge:build— succeeds, configures 2 projects instead of 4.--no-daemonruns.getChangelog()is byte-identical before and after theproviders.execchange, checked side by side on both code paths.