How do you react to the moment the task graph is fully built, before any task executes? When is that useful?
answer
- gradle.taskGraph.whenReady { graph -> }
- graphPopulated(graph) listener
- fires once between configuration and execution
- graph.hasTask(...) / allTasks
- fail fast before any task runs
basics
~10 sUse gradle.taskGraph.whenReady { graph -> ... } (or addTaskExecutionGraphListener with graphPopulated). It fires after configuration, once the full task graph is known but before execution starts, so you can inspect which tasks will run.
solid answer
~40 sThe `TaskExecutionGraph` (`gradle.taskGraph`) becomes fully populated at the end of the configuration phase, right before execution. You hook that moment with `gradle.taskGraph.whenReady { graph -> ... }` or by registering a `TaskExecutionGraphListener` whose `graphPopulated(graph)` callback fires once. At that point you can call `graph.hasTask(":publish")` or iterate `graph.allTasks` to decide things based on the *actual* set of tasks that will run — for example, fail fast if a release task is requested without a credential, enable extra checks only when `check` is in the graph, or print a build banner. It's strictly read-once-known: you know the final plan but haven't started running it. This is graph-readiness observation, distinct from per-task before/afterExecute hooks.
code
kotlin · 5 linesgradle.taskGraph.whenReady { graph ->
if (graph.hasTask(":publish") && System.getenv("DEPLOY_TOKEN") == null) {
throw GradleException("DEPLOY_TOKEN required when :publish is in the graph")
}
}go deeper
Know that whenReady fires once after the graph is built and lets you check which tasks will run.
Explain hasTask/allTasks usage and fail-fast scenarios; distinguish whenReady from per-task hooks.
Discuss configuration-cache implications of mutating state in whenReady and prefer read-only fail-fast checks.
Standardize cross-cutting graph-readiness policies (release guards, conditional verification) in a shared convention plugin rather than scattered per-build scripts.
## The task graph and when it's ready After Gradle finishes the **configuration** phase, it has resolved the complete **task execution graph** — every task that will run for the requested goals, plus all their dependencies, in a deterministic order. The `TaskExecutionGraph` object (reachable as `gradle.taskGraph`) is only *fully populated* at this boundary, between configuration and execution. ## Hooking graph readiness Two equivalent ways: - Closure: `gradle.taskGraph.whenReady { graph -> ... }` - Listener: `gradle.taskGraph.addTaskExecutionGraphListener(listener)` where the listener implements `graphPopulated(TaskExecutionGraph graph)`. Both fire exactly once, after the graph is known and before the first task runs. ## What you can do with the graph The `TaskExecutionGraph` exposes: - `hasTask(String path)` / `hasTask(Task)` — is this task in the plan? - `getAllTasks()` — the ordered list of tasks that will execute. - `whenReady { }`, `beforeTask { }`, `afterTask { }` — the same execution hooks. Typical uses: ```kotlin gradle.taskGraph.whenReady { val releasing = it.hasTask(":publishPlugins") || it.hasTask(":publish") if (releasing && System.getenv("SIGNING_KEY") == null) { throw GradleException("Refusing to publish without SIGNING_KEY") } if (it.hasTask(":check")) { // toggle stricter settings only when verification will run } } ``` ## Why react to readiness instead of inside a task Deciding based on the graph lets you **fail fast** before any work happens, and lets unrelated tasks adjust their behavior based on what *else* is in the build. Doing the same check inside one task action would run too late (after other tasks may have executed) and wouldn't see the whole plan. ## Relationship to other hooks `whenReady` answers "what will run?" once. `beforeTask`/`afterTask` (and `TaskExecutionListener.beforeExecute`/`afterExecute`) answer "this specific task is starting/finishing" repeatedly during execution. They're complementary. ## Configuration-cache note Logic that mutates project/global state from `whenReady` is generally cache-incompatible. Reading the graph to fail fast is usually fine, but heavy mutation in these callbacks is discouraged in cache-enabled builds.
- How many times does whenReady fire in a single build?Exactly once, when the full graph has been populated, just before execution begins.
- Why is hasTask in whenReady better than checking the requested task names from the command line?Command-line names are only the requested goals; the graph includes transitively-pulled-in tasks and finalizers, so hasTask reflects what will actually run.
saying these in an interview costs you the question
- Saying whenReady fires per task — it fires once for the whole graph.
- Claiming the graph is fully populated during configuration of a single build script — it's only complete after all configuration ends.