As projects grow larger, Gradle build times can get longer and longer. Mastering incremental builds and the caching mechanisms can significantly speed up day-to-day development.
How Incremental Builds Work
Gradle decides whether a Task needs to run again by comparing its inputs and outputs:
- Inputs: source files, dependencies, configuration
- Outputs: the generated classes, jars, reports and so on
- If neither the inputs nor the outputs have changed → skip (UP-TO-DATE)
Declaring Inputs and Outputs for a Custom Task
task generateDocs {
inputs.dir 'src/docs' // input directory
outputs.dir 'build/docs' // output directory
doLast {
// the logic that generates the docs
copy {
from inputs.files
into outputs.files.singleFile
}
}
}
The second run shows UP-TO-DATE and skips it entirely.
Build Cache
# gradle.properties
org.gradle.caching=true
Once enabled, Gradle caches Task outputs. Even after switching branches or rebuilding after a clean, as long as the inputs have not changed the outputs can be restored from the cache.
Remote Build Cache (Shared Across CI)
org.gradle.caching=true
# requires Gradle Enterprise or a self-hosted node
Cache entries from CI builds can be pulled locally, greatly reducing the time of the first build.
Configuration Cache
An experimental feature in Gradle 7.0+:
org.gradle.configuration-cache=true
It caches the result of the configuration phase and skips the whole configuration phase. The effect is striking on large multi-module projects.
Parallel Builds
org.gradle.parallel=true
Independent modules are compiled in parallel. There is a clear improvement for multi-module projects.
Configuration on Demand
org.gradle.configureondemand=true
Only the modules that are actually needed are configured, instead of all of them. It fits a limited set of scenarios, so enable it with care.
Daemon Tuning
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
Gradle enables the Daemon by default. Give it enough JVM memory to avoid frequent GC.
Profiling Build Time
# generate a build scan report
./gradlew build --scan
# see how long each task takes
./gradlew build --profile
--profile generates an HTML report under build/reports/profile/.
Skipping Unnecessary Work
# skip the tests
./gradlew build -x test
# build only the modules you need
./gradlew :module-api:build
Parallel Tests
test {
maxParallelForks = Runtime.runtime.availableProcessors()
}
Run tests in parallel on a multi-core CPU; the effect is obvious when you have a lot of tests.
A small step forward every day is a new height!