5.2.3 bazel aquery — Action Graph
When a compiler receives the wrong flag, a linker sees an unexpected library,
or nobody knows which action produces a file, the dependency graph is no longer
specific enough. You need the execution plan Bazel created after analysis.
bazel aquery exposes that plan: actions, their input and output artifacts,
mnemonics, and command lines.1
This makes aquery the answer to “what does Bazel plan to run?”, not “what
already ran, and how long did it take?” The command performs analysis, but it
does not execute the actions it reports.2
5.6.8 Skyscope — Skyframe Visualizer complements this command when the investigation needs a browser view of retained Skyframe relationships rather than action-plan output.
Start with one action
The simplest invocation shows the actions generated for one configured target:
bazel aquery //examples/basic:module_report
If the result contains several actions, narrow it by mnemonic first. A mnemonic
is the short name a rule assigns to an action kind, such as CppCompile,
GoLink, or GlyphReport:
bazel aquery 'mnemonic("GlyphReport", //examples/basic:module_report)'
You can run this command in the mini-ruleset
project. The rule that registers GlyphReport is in
glyph/internal/rules.bzl,
where its mnemonic is chosen as part of registering the action described in
4.2.2 Actions.
This example also separates two roles: the rule author chooses the mnemonic and
declares the execution contract from 4.4.3 Action Execution Contract. The
consultant uses aquery to inspect that contract for a particular target.
In the default text format, an action block contains the fields that drive the investigation:
Mnemonicidentifies the action kind.Targetnames the configured target that generated it.Configurationidentifies the configuration.InputsandOutputsshow artifacts on the action-graph edges.Command Lineshows the program and arguments Bazel plans to use.ActionKeyencodes significant behavior of the action kind, but must be interpreted carefully.
These fields turn a symptom into a testable hypothesis. For a wrong compiler
flag, inspect Command Line. For a missing header or generated file, inspect
Inputs. If all you know is the bad artifact's name, use Outputs to find its
producer.
Choose the right scope
An expression containing only a label selects actions generated by that target.
To inspect the complete plan needed to build it, use deps():
bazel aquery 'deps(//app:server)'
aquery works from the configured target graph after analysis, then exposes the
actions generated from it. It therefore respects the active configuration like
cquery, but moves down one layer—from targets and dependencies to commands and
artifacts.1 If the question is “which select() branch won?”, begin with
5.2.2 bazel cquery — Configured Graph. If it is “what exact command did that choice produce?”, move
to aquery.
Do not add deps() automatically. For a large target it can produce an enormous
result and bury the action you need. Start with the direct target and expand the
scope only when you suspect that the action comes from a dependency.
Filter actions, not targets
aquery adds three regular-expression filters:1
# Action kind
bazel aquery 'mnemonic("CppCompile", deps(//app:server))'
# Actions with an input matching the pattern
bazel aquery 'inputs(".*\\.proto", deps(//app:server))'
# Actions producing an artifact matching the pattern
bazel aquery 'outputs(".*server.*", deps(//app:server))'
Nest filters to combine conditions:
bazel aquery \
'mnemonic("Cpp.*", inputs(".*\\.cc", deps(//app:server)))'
The nesting direction matters. deps() returns configured targets, while
mnemonic(), inputs(), and outputs() turn such a set into actions. Thus
mnemonic("CppCompile", deps(...)) is valid, but deps(mnemonic(...)) is not:
a target function cannot consume actions.1
Build the expression from the inside out: establish the target set, such as
deps(//app:server), then wrap it in action filters.
Read the command line together with its artifacts
The presence or absence of one flag is often insufficient evidence. Inspect these fields together:
- Program and arguments — did Bazel select the expected tool and options?
- Inputs — are the tool, sources, generated files, and parameter files part of the action?
- Outputs — which artifact can later correlate this action with an execution profile or log?
- Configuration and target — are you looking at the intended instance of the label?
Long commands may place arguments in a parameter file. Use
--include_param_files to include its contents. The option also enables command
line output automatically.1
bazel aquery --include_param_files \
'mnemonic("CppCompile", //app:server)'
This is a good first response to “the compiler should receive this flag, but I cannot see it.” Filter by mnemonic, inspect the ordinary command line, then inspect the parameter file. Only then trace the setting back through the rule or toolchain configuration.
Do not confuse the plan with execution
aquery describes a generated action. It does not tell you whether a particular
build got a cache hit, where the action ran, or how long it took. For timing,
use the JSON profile. Bazel's documentation recommends correlating profile data
with aquery through the action's primary output.1 5.7 Cache & Execution
covers execution and cache evidence, while 5.4.2 Critical Path covers timing
and the critical path.
The text output's ActionKey is especially easy to misread. It is the analysis
metadata key returned for the action, encoding significant behavior of that
kind of action. A material rule or command-line change, a relevant environment
or configuration change, or a change to the action-kind implementation can
change it. Bazel typically versions the last case with a UUID.1
That implementation logic is distinct from the executable tool artifact. A
change to the contents of an input file—including a compiler or other tool
listed as an input—does not have to change the ActionKey shown by aquery.
Input-content digests participate later in the execution/cache key. The visible
ActionKey is therefore neither a digest of all input contents nor the key used
by the remote-cache client.1
In practice:
- compare the command, environment, and input list when the action's shape has changed.
- use execution and cache records to explain a hit or miss in a particular invocation.
- never infer “cache hit” or “cache miss” from
aquery'sActionKeyalone.
Choose an output format for the consumer
The default --output=text is suited to manual inspection.
--output=summary provides a compact overview, while --output=commands prints
one command per line. For automation, use structured proto, textproto, or
jsonproto. The enclosing message is analysis.ActionGraphContainer.1
bazel aquery --output=jsonproto \
'mnemonic("GlyphReport", //examples/basic:module_report)' \
> action-graph.json
Do not make a tool depend on record order. The query contract does not guarantee
it.1 Identify actions by fields such as outputs, mnemonic, and target instead
of an array position. For an oversized result, --noinclude_commandline and
--noinclude_artifacts can omit data that is included by default.1
When the question is “what changed between two revisions?”, capture the same
query and structured format on both sides, then choose an explicit matching
policy. Use output paths by default only when both revisions produce comparable
products. Do not treat a mnemonic as an action identity: it names an action kind
and is not unique. bazel_difftools offers mnemonic matching when output paths
changed, but its mnemonic map keeps only one action per mnemonic, so a later
action of the same kind replaces an earlier one. That mode is a lossy heuristic,
not a stable correspondence.3 A small upstream
examples/simple/
fixture demonstrates this before/after workflow and the
project's preview CLI and regression data document the matching behavior.3
For a comparator you need to trust, first dereference the container's dump-local
IDs: they join targets, path fragments, artifacts, and configurations only
within that dump.4 Compare labels, resolved execution paths, and
semantic configuration identity rather than raw IDs. Decide explicitly whether a
configuration checksum belongs to that identity or is itself a change the diff
should expose. Then retain all duplicate candidates and use a composite key
suited to the investigation, such as mnemonic plus target, configuration, and
primary output. Detect and report any remaining ambiguity instead of silently
discarding an action. This is a comparison tool's matching policy, not a
guarantee that aquery assigns stable record identities.
--skyframe_state searches the action graph retained by the current Bazel
server without repeating analysis. It supports only proto and textproto,
and its expression cannot contain target labels. This is useful when you know
an artifact's name but no longer know which previously analyzed target produced
it.1
A short investigation sequence
- State the observation: a wrong flag, missing input, unexpected output, or unknown producer.
- Begin with a specific label, without
deps(). - Narrow the result with
mnemonic(),inputs(), oroutputs(). - Read target, configuration, command, inputs, and outputs together.
- If the question is about what happened, switch from the action plan to a profile, execution log, or cache evidence.
Use bazel aquery to inspect Bazel's post-analysis execution plan. Start from a
narrow target, wrap the target expression in mnemonic(), inputs(), or
outputs() filters, and interpret commands together with artifacts and
configuration. The result describes planned actions. It does not prove their
duration, execution location, or cache result. Its ActionKey is action-analysis
metadata, not the remote-cache lookup key and not a digest of input contents.
Check your understanding · 4 questions
1.A build was unexpectedly slow, and aquery shows a CppCompile action with a stable ActionKey. What can you conclude?
Select one answer
2.A generated file has an unexpected name, and you want to find the action that plans to produce it without flooding the result. Which steps are good starting points?
Select all that apply
3.The compiler command shown by aquery appears to omit a flag that may have been placed in a parameter file. What should you try next?
Select one answer
4.Match each consumer or investigation need to the most suitable evidence or output:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
Footnotes
-
Action Graph Query (aquery) — action-graph semantics, filters, output options, parameter files,
ActionKey, Skyframe mode, and profile correlation ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 -
A guide to Bazel query — practical distinction among
query,cquery, andaqueryby graph layer ↩ -
bazel_difftools — structural action-graph comparison — the upstream README defines output-path and mnemonic matching.
pkg/action/output_map.goimplements each as a one-action-per-key map, so duplicate mnemonics are overwritten by the last action ↩1 ↩2 -
Bazel — core implementation, documentation, and regression corpus —
src/main/protobuf/analysis_v2.protodefines artifact, target, rule-class, aspect, dep-set, and configuration IDs that are valid only for one dump. Action and artifact fields reference those records and path fragments ↩