How does onlyIf differing from an up-to-date check (outputs.upToDateWhen)? When would you reach for each?
answer
- onlyIf = run at all → SKIPPED
- up-to-date = output still valid → UP-TO-DATE
- onlyIf evaluated first
- upToDateWhen{false} still runs
- different console outcomes
basics
~20 sonlyIf decides whether the task should run at all (false → SKIPPED). The up-to-date check decides whether work is unnecessary because inputs/outputs are unchanged (→ UP-TO-DATE). onlyIf runs first; if it passes, Gradle then does the up-to-date check.
solid answer
~40 sThey answer different questions and produce different outcomes. `onlyIf {}` is a **should-I-run-at-all** gate: a false predicate yields `SKIPPED` and the task's actions never run. The **up-to-date check** (driven by declared inputs/outputs plus `outputs.upToDateWhen {}`) answers **is the existing output still valid** — if so the task is `UP-TO-DATE` and actions are skipped because they'd be redundant. Order: Gradle evaluates `onlyIf` first; only if it returns true does it perform the up-to-date check (and consult the build cache). Use `onlyIf` for runtime conditions unrelated to inputs/outputs (a flag, branch, env var, empty source set). Use up-to-date / proper input-output declarations for incremental-build correctness and caching. `outputs.upToDateWhen { false }` forces a task to always be considered out of date (it still runs); `onlyIf { false }` makes it not run at all.
code
kotlin · 5 linestasks.register("generate") {
onlyIf { !file("src/gen").listFiles().isNullOrEmpty() } // SKIPPED if empty
outputs.dir("build/gen")
outputs.upToDateWhen { gradle.startParameter.isRerunTasks.not() }
}go deeper
Know SKIPPED (onlyIf) vs UP-TO-DATE (up-to-date check) are different outcomes.
Explain that onlyIf runs first and short-circuits the cache; pick the right mechanism per scenario.
Discuss correctness implications: misusing onlyIf for caching breaks incrementality; reserve onlyIf for runtime gating.
Set team conventions: declare inputs/outputs for caching, reserve onlyIf for explicit runtime toggles; audit builds for onlyIf misuse that defeats the cache.
## Two distinct decisions Gradle makes two separate decisions before running a task's actions: 1. **`onlyIf` — should this task run at all?** A `Spec<Task>` predicate. False → outcome `SKIPPED`, actions not run. 2. **Up-to-date check — is the prior output still valid?** Computed from the task's declared `@InputFiles`/`@OutputFiles` (or runtime `inputs`/`outputs`) plus any `outputs.upToDateWhen {}` spec. If everything matches a previous run, outcome `UP-TO-DATE`, actions not run. ## Ordering `onlyIf` is evaluated **before** the up-to-date check. If `onlyIf` returns false, Gradle never reaches the up-to-date logic or the build cache — the task is simply `SKIPPED`. If `onlyIf` returns true, Gradle proceeds to the up-to-date/cache check, which may still skip the *work* (UP-TO-DATE / FROM-CACHE) or run it. ## `outputs.upToDateWhen { ... }` This customizes the up-to-date check, not whether the task runs. Returning `false` forces the task to be treated as out of date so its actions execute every time (e.g. a task whose output depends on the wall-clock). It does **not** produce SKIPPED. ## Picking the right tool - Skip on a **runtime condition** (flag/branch/empty input set, unrelated to caching) → `onlyIf {}`. - Avoid **redundant work** when inputs/outputs are unchanged → declare inputs/outputs properly (and optionally `@CacheableTask`); let the up-to-date check handle it. - Force a task to **always run** → `outputs.upToDateWhen { false }` (still runs) — NOT `onlyIf`. ```kotlin tasks.register("report") { // runs only when explicitly requested onlyIf { project.hasProperty("withReport") } // and even then, re-run only if the timestamp source changed outputs.upToDateWhen { false } } ``` ## Outcomes summary | Mechanism | False/blocked outcome | Question answered | |---|---|---| | onlyIf | SKIPPED | Run at all? | | up-to-date / cache | UP-TO-DATE / FROM-CACHE | Is prior output still valid? |
- If onlyIf returns false, does Gradle still consult the build cache?No. A false onlyIf short-circuits before the up-to-date and cache checks; the task is SKIPPED.
- How do you force a task to always run regardless of inputs?outputs.upToDateWhen { false } — it stays out of date and runs each time. onlyIf would do the opposite (skip).
saying these in an interview costs you the question
- Using onlyIf to express incremental-build correctness instead of declaring inputs/outputs.
- Claiming a false onlyIf yields UP-TO-DATE — it yields SKIPPED.
- Thinking outputs.upToDateWhen{false} skips the task — it forces it to run.