Direct answers
1. Timestamp monitoring: Gradle does not provide a configuration to watch file modification timestamps for cache invalidation. Cache keys are computed solely from declared task inputs and outputs; any file not listed as an input is invisible to the cache.
2. Declaring external files as inputs: Yes. Add every external dependency to the task's inputs using inputs.file(), inputs.files(), or a custom input property that stores a content hash (e.g., SHA‑256). This makes the file part of the cache key.
3. Retention/eviction policy: No built-in TTL, LRU, or size-based eviction exists for the local build cache. You must manually run gradle cleanBuildCache or delete entries from ~/.gradle/caches/build-cache-*/.
Why stale entries happen
Gradle's build cache assumes a pure functional model: the same inputs always produce the same outputs. When an external tool or a manual edit changes a file that a task reads but does not declare, the input hash stays the same, so Gradle reuses the cached output. This is by design, not a bug.
Steps to make external changes visible
- Declare the file as an input. In the task definition, add:
tasks.named('yourTask') {
inputs.file(layout.projectDirectory.file('path/to/external-file'))
// or for multiple files
inputs.files(fileTree('external-dir').matching { include '*.txt' })
}
- For large or rarely changing files, use a checksum file. Generate a
.sha256 alongside the external file and declare that checksum file as the input instead. This keeps key computation fast.
// Example: generate checksum in a preceding task
tasks.register('checksumExternal') {
doLast {
def file = layout.projectDirectory.file('external-data.bin')
def hash = file.bytes.encodeHex() // replace with real SHA-256
layout.buildDirectory.file('external-data.bin.sha256').write(hash)
}
}
tasks.named('yourTask') {
inputs.file(layout.buildDirectory.file('external-data.bin.sha256'))
}
- Verify the input participates in the cache key. Run with debug logging:
./gradlew yourTask --info
# or
./gradlew yourTask -Dorg.gradle.caching.debug=true
Look for "Cache miss" after modifying the external file. You can also print the input hash in a doLast block:
tasks.named('yourTask') {
doLast {
println "Input hash: " + inputs.files.hashCode()
}
}
- Clean the cache after correcting input declarations. Run once:
./gradlew cleanBuildCache
Then rebuild to populate fresh entries.
Assumptions and uncertainty
- Behavior described matches Gradle 7.x and 8.x. Older versions may differ in debug flag names.
- Remote cache (e.g., Gradle Enterprise, S3) behaves the same way: keys include only declared inputs. Machines missing the external file will see cache hits incorrectly if the file is not declared.
- Incremental build (not the build cache) also ignores undeclared files. The same input declarations fix both.
One diagnostic detail needed
What produces the external files — a separate Gradle task, an external CLI tool, or manual edits? The answer determines whether you should wire the producer task as a dependency (preferred) or rely on checksum files.