How do you keep CommandLineArgumentProvider arguments that reference file paths from breaking a relocatable build cache?
answer
- relocatable = same key across checkout dirs
- @PathSensitive(NONE) tracks content only
- @Classpath for tool/agent jars
- absolute path OK in asArguments() output, not as input
- ABSOLUTE sensitivity / @Input on a path kills hits
basics
~20 sTrack the file via @InputFile/@Classpath with an appropriate @PathSensitivity (often NONE or RELATIVE) so the cache key depends on content, not the absolute path. The absolute path appears only in the returned argument string, which isn't fingerprinted.
solid answer
~50 sA relocatable cache entry must produce the same key regardless of where the project checks out on disk. The danger with argument providers is baking an **absolute path** into the cache key. The fix has two halves. First, don't track the path string as an input — track the *file* via `@InputFile`, `@InputFiles`, or `@Classpath`, and choose a `@PathSensitive` mode (`NONE` when only content matters, `RELATIVE` when the path within the project matters). That makes the fingerprint content/relative-path based, not absolute. Second, the absolute path is fine to *emit* from `asArguments()` because the returned strings are not part of the fingerprint — so machine-specific paths in the actual command line don't pollute the key. If you mistakenly mark the file with `@PathSensitive(ABSOLUTE)` or use `@Input` on a `String absolutePath`, you tie the key to the machine's directory layout and cache hits across machines (and even across checkouts) evaporate.
code
kotlin · 9 linesabstract class KeystoreArg : CommandLineArgumentProvider {
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE) // content only -> relocatable
abstract val keystore: RegularFileProperty
override fun asArguments(): Iterable<String> =
// absolute path here is fine: the returned string is NOT a cache input
listOf("-Dkeystore=${keystore.get().asFile.absolutePath}")
}go deeper
Aware that absolute paths can hurt caching and that file inputs should be tracked, even if normalization detail is fuzzy.
Pick @InputFile/@Classpath plus a sensible @PathSensitive and explain content-vs-path tracking.
Reason precisely about relocatability, normalization modes, and why asArguments() output is excluded; debug cross-machine misses.
Own remote-cache hit-rate as a metric, ban absolute-path inputs by policy, and standardize provider normalization across teams to protect shared-cache value.
## The goal: relocatability The build cache is **relocatable** when an entry produced in `/home/ci/agent7/project` matches a build in `/Users/dev/project`. Relocatability is what makes a *shared/remote* cache valuable: developers reuse CI's outputs. Anything that injects an absolute path into a task's input fingerprint destroys it. ## Where providers can go wrong A provider often computes an argument from a file path: ```kotlin override fun asArguments() = listOf("-Dkeystore=${keystore.get().asFile.absolutePath}") ``` The `absolutePath` string is machine-specific. Two rules keep this safe: 1. **Don't let the path be an input.** The return value of `asArguments()` is not fingerprinted, so emitting an absolute path here is fine. 2. **Track the file by content, not absolute path.** Declare the property as a file input with the right normalization: ```kotlin @get:InputFile @get:PathSensitive(PathSensitivity.NONE) abstract val keystore: RegularFileProperty ``` `@PathSensitive(NONE)` means "only the file's content matters; ignore its path entirely." Use `RELATIVE` when the path *relative to the source root* is semantically meaningful (e.g. a classpath where package layout matters Gradle uses `@Classpath`/`@CompileClasspath` which already normalize correctly). Use `NAME_ONLY` when only the filename matters. Avoid `ABSOLUTE` unless the absolute location is genuinely part of the contract — it kills relocatability. ## Classpath-style inputs For agent jars / tool jars passed as arguments, prefer `@Classpath`: ```kotlin @get:Classpath abstract val toolJars: ConfigurableFileCollection ``` `@Classpath` normalizes by content and ignores jar timestamps and (by default) the order-insensitive details that don't affect behavior, giving stable, relocatable keys. ## Diagnosing failures If cache hits work locally but not against the remote cache, suspect absolute paths in the fingerprint. Build scans show the per-property normalization; `--info` and the `org.gradle.caching.debug` flag print individual input hashes so you can see which property changed between machines. ## Summary table (mental model) - Content matters only → `@InputFile` + `@PathSensitive(NONE)` / `@Classpath`. - Relative location matters → `@PathSensitive(RELATIVE)` or `NAME_ONLY`. - Absolute location is the contract → `@PathSensitive(ABSOLUTE)` (rare; accept non-relocatable). - Pure value → `@Input`. - The emitted argument string → never an input; absolute paths there are fine.
- Why is emitting an absolute path inside asArguments() acceptable even for a relocatable cache?Because the strings returned by asArguments() are not part of the task input fingerprint. Only the annotated properties (tracked with content/relative normalization) feed the cache key, so the machine-specific path in the command line never reaches the key.
- When would @PathSensitive(RELATIVE) be more correct than NONE?When the file's path relative to the source root is semantically meaningful — e.g. resources where the relative location affects the output. NONE would wrongly treat two same-content files in different relative locations as identical.
- How would you debug a remote-cache miss you suspect is path-related?Run with `-Dorg.gradle.caching.debug=true` (or a build scan) to print each input property's hash, compare the failing task's fingerprint across the two machines, and look for a property whose hash differs only because of an absolute path.
Fingerprinting a person by their face (content) lets you recognize them in any city; fingerprinting them by their street address (absolute path) means you 'lose' them the moment they move. Path-sensitivity NONE photographs the face.
saying these in an interview costs you the question
- Using @PathSensitive(ABSOLUTE) or @Input on a String absolutePath for a file the process reads.
- Thinking the emitted absolute path in the argument string breaks the cache (it doesn't — it isn't fingerprinted).
- Assuming every file input needs the same normalization regardless of meaning.