2.1.5 Inspecting the Graph — Query Preview
recommendedThe earlier items in this section used bazel query tactically: inspect a reachable closure, trace a path, or check who depends on what. The missing map is that Bazel exposes three related inspectors, each attached to a different graph layer. <a class="cross-ref" href="/book/1~1~8" title="1.1.8 Basic Query"><span class="cross-ref-id">1.1.8</span> Basic Query</a> introduced the first one. At this level, the useful upgrade is to line all three up with the DAG model: query reads the declared target graph, cquery reads the configured target graph after analysis, and aquery exposes the action graph that analysis produced. They let you inspect Bazel's model without running the compile, link, or test actions themselves.1,2,3,4
The same label appears in three increasingly concrete graph views.
One target, three different questions
The easiest way to remember the query family is by question, not by command name. Relative to the build phases in 2.2.1 Loading, Analysis & Execution:
bazel queryworks on the post-loading target graph. It is the right first tool fordeps(),rdeps(),somepath(), andallpaths()questions about the declared structure.1,5bazel cqueryruns after analysis, so it resolvesselect(), build flags, and configuration-specific dependency choices. Its results include configuration IDs because the same label can appear in more than one configuration.2bazel aqueryshows the actions analysis registered: mnemonics, inputs, outputs, and command lines. It is the lowest-level graph view before execution starts.3
Start with query
For most DAG questions in this level, query is still the default. It is cheaper than the other two because it stops at the declared graph, and it already answers the structural questions from this chapter: reachable closure, reverse dependencies, and dependency paths.1,5
bazel query 'deps(//my:target)'
bazel query 'rdeps(//..., //lib:core)'
When the result gets noisy, --noimplicit_deps trims rule-injected and toolchain edges so you can focus on the dependencies you actually wrote in BUILD files.1,5
Compare: A target's deps uses select() with mutually exclusive Linux and macOS branches. bazel query 'deps(//pkg:tgt)' prints labels from both branches. Did Bazel just tell you this build will compile both?
Reveal
No. Plain query proved that both labels are possible declared edges of the target. It did not prove that one configured build will compile both branches. query runs before configuration is resolved, so it answers "could this target reach that label?" rather than "did this flag set choose that edge?"
That overapproximation is useful when you are hunting broad structural relationships. If the real question is "which branch does Linux CI choose?", switch to cquery for the configured target.
That is the trade-off: query is intentionally conservative. Because it runs before configuration is applied, it can show more possibilities than a specific build will actually choose.1,2
Switch to cquery when configuration matters
That conservative behavior is exactly where cquery starts being useful. If a target uses select(), build flags, or platform-dependent toolchains, plain query answers "what could happen?" while cquery answers "what did this build configuration choose?".2,4
The nice part is that the expression often stays the same:
bazel query 'deps(//my:target)'
bazel cquery 'deps(//my:target)'
What changes is the graph layer. cquery runs over configured targets after analysis, so it can distinguish cases that the loading-phase graph has to overapproximate. That extra precision costs more time and memory than query, which is why query stays the better first look when configuration is not the issue.2,6
Use aquery when you need the action plan
aquery drops below targets and into actions. Instead of telling you only that //my:target eventually depends on a compiler or code generator, it shows the concrete actions Bazel registered for the build: compilation steps, link steps, generated outputs, command lines, and mnemonics.3,4
bazel aquery '//my:target'
This is the preview-level answer to "what will Bazel actually do for this target?" Later, 5.2 Query goes deeper into action-specific filters such as mnemonic(), inputs(), and outputs(), plus the heavier-duty workflows that turn query output into debugging or CI tooling.3,6
Check your understanding · 3 questions
1.Match each query tool to the graph layer it operates on:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
2.When should you prefer bazel cquery over bazel query for a dependency question?
Select one answer
3.True or false: characteristics of the three query tools.
Choose True or False for each sentence
bazel aquery shows planned actions without running any compile or test commands.bazel query is more expensive than bazel cquery because it loads more packages.Footnotes
-
The Bazel Query Reference —
queryas post-loading target-graph inspection, plus implicit-dependency and overapproximation semantics ↩1 ↩2 ↩3 ↩4 ↩5 -
Configurable Query (cquery) — analysis-phase configured graph, configuration IDs, and
select()resolution ↩1 ↩2 ↩3 ↩4 ↩5 -
Action Graph Query (aquery) — actions, artifacts, mnemonics, command lines, and the relation to the configured target graph ↩1 ↩2 ↩3 ↩4
-
A guide to Bazel query — practical three-layer mental model and mapping to loading, analysis, and action inspection ↩1 ↩2 ↩3 ↩4
-
Query guide —
deps(),rdeps(), path queries, and--noimplicit_deps↩1 ↩2 ↩3 -
Deep dive into Bazel queries: from basics to advanced use cases — preview-oriented explanation of
query,cquery, andaquery, including whycqueryis the heavier tool ↩1 ↩2 ↩3