skip to content

Walk through what Gradle does with declared inputs/outputs to decide whether an ad-hoc task is UP-TO-DATE.

level: middleimportance: must knowfreq 60%

answer

  1. fingerprint inputs + snapshot outputs
  2. stored in task history (.gradle)
  3. recompute + compare next run
  4. content hash not timestamp
  5. deleted/modified output => rerun
  6. first run has no history

basics

~10 s

Gradle fingerprints the declared inputs and snapshots outputs, stores them in task history, and on the next run recomputes and compares. If both match the prior run, it skips the task as UP-TO-DATE.

solid answer

~50 s

At execution time Gradle takes the task's declared `inputs` (file contents/paths and property values) and `outputs` (the set of output files/dirs and their state) and computes a fingerprint. It persists this in the project's task-history store. On a later invocation it recomputes the current fingerprint and compares against the stored one. The task is **UP-TO-DATE** (actions skipped) only if: the set of inputs is identical and unchanged, the declared output locations still exist and haven't been modified outside Gradle, and the task code/up-to-date predicates haven't changed. Any difference — a changed input byte, a new/removed input file, a deleted output, a changed `inputs.property` value — makes it out of date and it re-runs. Note that adding/removing an output, or an output being tampered with externally, also triggers a rerun. This is content-based, not purely timestamp-based.

code

bash · 4 lines
bash
./gradlew generateReport --info
# first run: executes (no history)
./gradlew generateReport --info
# second run: '> Task :generateReport UP-TO-DATE'

go deeper

for a junior

Know Gradle compares this run's inputs/outputs to the last run and skips if unchanged.

for a middle

Describe fingerprinting, task history, content-vs-timestamp, and what invalidates (changed input, missing output, changed property).

for a senior

Discuss --info diagnosis, output tampering detection, and the link to caching when outputs are declared.

for a principal

Ensure builds are deterministic so up-to-date checks are trustworthy across machines and CI.

## The up-to-date check, step by step Gradle's incremental engine runs this for each task that has declared inputs/outputs: 1. **Collect declarations.** Read everything registered on `inputs` (`file`/`files`/`dir`/`property`) and `outputs` (`file`/`files`/`dir`). 2. **Fingerprint inputs.** For file inputs, hash content and relative paths (with the task's path-sensitivity normalization). For `inputs.property`, serialize and hash the value. The combined hash is the input fingerprint. 3. **Snapshot outputs.** Record which output files exist and their content/state. 4. **Compare with history.** Gradle keeps a per-task **task history** in the build's `.gradle` state. It compares the freshly computed input fingerprint and output snapshot to the stored ones from the previous run. 5. **Decide.** - All inputs unchanged **and** outputs present/unchanged **and** task implementation unchanged ⇒ report `UP-TO-DATE`, skip actions. - Otherwise ⇒ execute the task, then store the new fingerprints. ## What invalidates a task - An input **file's content** changed. - An input file was **added or removed** from a declared collection/dir. - An `inputs.property` **value** changed. - A declared **output was deleted** or modified outside Gradle. - The **task's code/up-to-date predicate** changed (Gradle tracks the task class/action implementation). ## Content, not just timestamps Unlike a naïve `make`, Gradle hashes file **content**. Touching a file without changing bytes does **not** make the task out of date. This avoids needless reruns when only modification times shift (e.g., after a checkout). ## First run and `--info` The very first run has no stored history, so the task always executes. Run with `--info` to see the reason a task ran (e.g., 'No history is available' or 'Input file X has changed'). ```bash ./gradlew generateReport --info # > Task :generateReport # Input property 'title' has changed for task ':generateReport' ``` ## Where outputs fit Declaring outputs is what lets Gradle detect external tampering and is also required for the build cache. A task with inputs but no outputs can be marked up to date based on inputs, but it can't be cached and Gradle can't detect that its results were deleted.

  • If you 'touch' an input file but don't change its contents, does the task re-run?
    No. Gradle fingerprints file content, so an unchanged byte stream stays up to date despite a newer modification timestamp.
  • How can you see WHY a task was not up to date?
    Run with --info; Gradle prints the reason, e.g. 'Input file ... has changed', 'Input property ... has changed', or 'No history is available' on a first/clean run.
  • Does a task with inputs but no declared outputs get cached?
    No. The build cache requires declared outputs to store and restore. Such a task can still be UP-TO-DATE based on inputs, but it cannot be cached, and Gradle cannot detect if its results were deleted.

saying these in an interview costs you the question

  • Saying Gradle compares only timestamps — it hashes content.
  • Forgetting that deleting/modifying an output also forces a rerun.
  • Claiming a task is up to date on its very first run.

context