BXL cquery vs. Buck2 CLI cquery - Divergence in Configuration Handling
Overview
The command buck2 cquery
and function cquery
in bxl share the same goal but
sometimes yield different results. This document explains why these commands
might produce divergent results for identical-looking queries and how their
underlying mechanisms diverge. By contrasting their approaches to configuration
universes, we clarify how to align BXL scripts with CLI behavior for consistent
results.
A Concrete Example
Imagine we have:
- A target
X
with a default configurationcfg_a
. - Targets
A
,B
, andC
inroot//path/...
with a default configurationcfg_b
. - Both
A
andC
directly depend onX
, but each dependency is configured ascfg_b
.
Assume that our goal is to determine the direct reverse dependencies of X
within root//path/...
. Let's look at two approaches:
buck2 cquery
We can do a query like this:
buck2 cquery 'rdeps(root//path/..., X, 1)`
It will return A (cfg_b)
, C (cfg_b)
and X(cfg_b)
cquery
in bxl
def _main(ctx):
res = ctx.cquery().rdeps("root//path/...", "X", depth = 1)
ctx.output.print(res)
main = bxl_main(
cli_args = {},
impl = _main
)
It will return empty.
Core Difference
CLI buck2 cquery
When no target universe is
provided via the --target-universe
CLI argument, it constructs the
target universe though the
following process:
Phase 1: Target Resolution
-
Pattern Resolution: Resolve target patterns (
root//path/...
andX
) to obtain unconfigured targets:A
,B
,C
, andX
. -
Target Configuration: With no additional configuration arguments, the default target platform is applied. This produces configured targets:
A (cfg_b)
B (cfg_b)
C (cfg_b)
X (cfg_a)
-
Universe Construction: Build the target universe using these configured targets. Since
A (cfg_b)
andC (cfg_b)
depend onX (cfg_b)
, the universe includes:A (cfg_b)
B (cfg_b)
C (cfg_b)
X (cfg_a)
X (cfg_b) -
Resolve the literal in the universe: Lookup the original patterns within the constructed universe:
root//path/...
resolves toA (cfg_b)
,B (cfg_b)
,C (cfg_b)
X
resolves to both configurations:X (cfg_a)
andX (cfg_b)
Phase 2: run rdeps function
It will go though rdeps
logic:
Within the universe of targets under root//path/...
(i.e., A (cfg_b)
,
B (cfg_b)
, and C (cfg_b)
), identify the targets whose direct dependencies
include X (cfg_a)
or X (cfg_b)
. In this case, those targets are A (cfg_b)
and C (cfg_b)
.
Furthermore, if X (cfg_a)
or X (cfg_b)
itself exists within the universe of
A (cfg_b)
, B (cfg_b)
, and C (cfg_b)
, it should also be included in the
results. In this case, X (cfg_b)
is part of the universe, so it is included.
Thus, the final result is A (cfg_b)
, C (cfg_b)
, and X (cfg_b)
.
cquery
in bxl
The bxl logic differs from the buck2 cquery
approach.
Phase 1: resolve arguments to configured target nodes if needed
If the arguments are not configured target(s), apply the default target platform (if not given) to do the configurations. So in this case:
- The first argument resolves to
A (cfg_b)
,B (cfg_b)
andC (cfg_b)
- The second argument resolves to
X (cfg_a)
.
Phase 2: run rdeps function
This phase follows the same logic of "Phase 2" in CLI buck2 cquery
But in this case, the second argument is different, it only has X (cfg_a)
.
Since A (cfg_b)
, B (cfg_b)
and C (cfg_b)
do not directly depend on
X (cfg_a)
, the result will be empty here.
Aligning BXL with CLI Behavior
To replicate the same results in BXL, you should provide a universe explicitly:
- Create a universe of all arguments
universe = ctx.target_universe(["root//path/...", "X"])
- Lookup args in the universe
arg0 = universe.looup("root//path/...")
arg1 = universe.looup("X")
- Run cquery
res = ctx.cquery().rdeps(arg0, arg1, depth=1)
Alternatively, you can also achieve the same result with a single command:
res = ctx.cquery().eval('rdeps(root//path/..., X, 1)')
ctx.cquery().eval()
will do same logic when we do in CLI
Reflection: Philosophy of Configuration Management in CLI and cquery
The divergence between CLI and BXL cquery reflects a broader design trade-off:
- CLI: Optimized for user-friendliness, abstracting configuration logic.
- BXL: Prioritizes flexibility for advanced use cases, requiring explicit control (such as ability to provide target universes).