Skip rebuilding buildSrc and included builds in Gradle
Gradle can restore the compiled result of buildSrc and of an included build from the cache, so in our runs a change in one part rebuilt only that part, except that a new public method in the included build also rebuilt the app. At FosterStack we prove everything, so here is how we proved it to ourselves: six real runs showing what was restored and what was rebuilt.
The short answer
buildSrcand the included build each stored and restored their owncompileJavaresult.- An edit in one of them rebuilt that part, and in our runs not the others, unless a public method changed.
- In our run the cache was set up once, in the root
settings.gradle.ktsandgradle.properties. Nothing extra was needed inbuildSrcor in the included build's build file.
What we ran
Three parts: a buildSrc folder with one Java class, an included build called lib with one Java class, and an app project that uses lib. Every run used a fresh copy of the project, a new empty Gradle home folder, and the local build cache switched off, so the only build state the runs shared was the cache server (they also used the same Gradle install and the same Java). Runs 3 to 6 each made one edit on a fresh copy of the original code; the edits were not stacked. All runs used gradle :app:build --console=plain --no-daemon on the same Mac, with Java 27 (Homebrew). All six builds ended with BUILD SUCCESSFUL.
| Run | buildSrc | lib (included build) | app |
|---|---|---|---|
| 1. First build, as CI would do it | ran | ran | ran |
| 2. Fresh copy, same code | FROM-CACHE | FROM-CACHE | FROM-CACHE |
3. Edit buildSrc | ran | FROM-CACHE | FROM-CACHE |
4. Edit inside a method in lib | FROM-CACHE | ran | FROM-CACHE |
5. Add a public method to lib | FROM-CACHE | ran | ran |
6. Edit only app | FROM-CACHE | FROM-CACHE | ran |
The columns show the compileJava task of each part. These are three lines from run 2, with the lines between them left out:
> Task :buildSrc:compileJava FROM-CACHE
...
> Task :lib:compileJava FROM-CACHE
...
> Task :app:compileJava FROM-CACHE
What this means for your build
- buildSrc is cached like any other project. A change to it recompiled it (run 3). It did not recompile
liborappin our run. - An included build is cached too. Its tasks show up as
:lib:compileJavaand came from the cache in run 2. - A change inside a method did not spread (run 4); a new public method did (run 5). That matches what we saw with modules in Gradle build cache for a monorepo.
We tried one small class in each part. We did not test a buildSrc that holds plugins, convention plugins, or other languages, and we did not test an included build that is itself a large multi-module build.
The files we used
Root settings.gradle.kts. The address is for a test on one machine over plain HTTP; use your server's address and HTTPS across a network.
rootProject.name = "composite"
includeBuild("lib")
include(":app")
buildCache {
local { isEnabled = false }
remote<HttpBuildCache> {
url = uri("http://127.0.0.1:18220/")
isPush = true
isAllowInsecureProtocol = true
}
}
The root gradle.properties has one line, org.gradle.caching=true. The root build.gradle.kts has one line that prints a value from the buildSrc class. lib/settings.gradle.kts is rootProject.name = "lib". buildSrc/build.gradle.kts is plugins { java }. lib/build.gradle.kts is plugins { `java-library` } with group = "demo", and app/build.gradle.kts adds implementation("demo:lib").
If something does not come from the cache
Work through why the hit rate is low. To see what Gradle stores, see what Gradle stores in the build cache.
Run on Gradle 9.8.0 with FosterStack Cache 0.2.1, macOS (arm64), Java 27 (Homebrew).