cquery 是 query 的变体,可正确处理
select() 和 build 选项对 build
图的影响。
它通过对 Bazel 的 分析
阶段的结果运行来实现此目的,
该阶段集成了这些影响。query 相比之下,对
Bazel 加载阶段的结果运行,然后在评估选项之前。
例如:
$ cat > tree/BUILD <<EOF
sh_library(
name = "ash",
deps = select({
":excelsior": [":manna-ash"],
":americana": [":white-ash"],
"//conditions:default": [":common-ash"],
}),
)
sh_library(name = "manna-ash")
sh_library(name = "white-ash")
sh_library(name = "common-ash")
config_setting(
name = "excelsior",
values = {"define": "species=excelsior"},
)
config_setting(
name = "americana",
values = {"define": "species=americana"},
)
EOF
# Traditional query: query doesn't know which select() branch you will choose, # so it conservatively lists all of possible choices, including all used config_settings. $ bazel query "deps(//tree:ash)" --noimplicit_deps //tree:americana //tree:ash //tree:common-ash //tree:excelsior //tree:manna-ash //tree:white-ash # cquery: cquery lets you set build options at the command line and chooses # the exact dependencies that implies (and also the config_setting targets). $ bazel cquery "deps(//tree:ash)" --define species=excelsior --noimplicit_deps //tree:ash (9f87702) //tree:manna-ash (9f87702) //tree:americana (9f87702) //tree:excelsior (9f87702)
每个结果都包含一个唯一标识符 (9f87702),其中
包含目标所构建的配置。
由于 cquery 对配置的目标图运行,因此它无法洞察
build 操作等工件,也无法访问 test_suite
规则,因为它们不是配置的目标。对于前者,请参阅 aquery。
基本语法
简单的 cquery 调用如下所示:
bazel cquery "function(//target)"
查询表达式 "function(//target)" 包含以下内容:
function(...)是要在目标上运行的函数。cquery支持query's 大多数函数,以及 一些新函数。//target是馈送到函数的表达式。在此示例中,该 表达式是一个简单的目标。但查询语言还允许嵌套函数。 如需查看示例,请参阅查询指南。
cquery 需要目标才能完成 加载和分析
阶段。除非另有说明,否则 cquery 会解析
查询表达式中列出的目标。如需查询顶级 build 目标的依赖项,请参阅 --universe_scope
。
配置
以下代码行:
//tree:ash (9f87702)
表示 //tree:ash 是在 ID 为 9f87702 的配置中构建的。对于大多数
目标,这是定义
配置的 build 选项值的不透明哈希。
如需查看配置的完整内容,请运行以下命令:
$ bazel config 9f87702
9f87702 是完整 ID 的前缀。这是因为完整 ID 是
SHA-256 哈希,它们很长且难以跟踪。cquery 可以理解完整 ID 的任何有效
前缀,类似于
Git 短哈希。
如需查看完整 ID,请运行 $ bazel config。
目标模式评估
//foo 对于 cquery 和 query 具有不同的含义。这是因为
cquery 会评估 已配置 的目标,并且 build 图可能具有多个
已配置版本的 //foo。
对于 cquery,查询表达式中的目标模式会评估
为标签与该模式匹配的每个已配置目标。输出是
确定性的,但 cquery 不提供超出
核心查询排序协定的排序保证。
这会为查询表达式生成比 query 更细微的结果。
例如,以下内容可能会生成多个结果:
# Analyzes //foo in the target configuration, but also analyzes # //genrule_with_foo_as_tool which depends on an exec-configured # //foo. So there are two configured target instances of //foo in # the build graph. $ bazel cquery //foo --universe_scope=//foo,//genrule_with_foo_as_tool //foo (9f87702) //foo (exec)
如果您想准确声明要查询的实例,请使用
config 函数。
如需详细了解目标模式,请参阅 query's 目标模式
文档。
函数
在 函数集
支持的 query 中,cquery 支持除
allrdeps、
buildfiles、
rbuildfiles、
siblings、tests 和
visible 之外的所有函数。
cquery 还引入了以下新函数:
config
expr ::= config(expr, word)
config 运算符尝试查找
第一个实参表示的标签的已配置目标,以及
第二个实参指定的配置。
第二个实参的有效值为 null 或 a
自定义配置哈希。可以从 $
bazel config 或之前的 cquery 输出中检索哈希。
示例:
$ bazel cquery "config(//bar, 3732cc8)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (exec) //baz (exec) $ bazel cquery "config(//baz, 3732cc8)"
如果无法在指定 配置中找到第一个实参的所有结果,则仅返回可以找到的结果。如果在指定配置中找不到任何结果,则查询失败。
选项
build 选项
cquery 对常规 Bazel build 运行,因此会继承 build 期间可用的
选项。
使用 cquery 选项
--universe_scope(英文逗号分隔的列表)
通常,已配置目标的依赖项会经历 转换, 这会导致其配置与其依赖项不同。借助此标志 ,您可以查询目标,就好像它是另一个目标的依赖项或传递 依赖项一样。例如:
# x/BUILD
genrule(
name = "my_gen",
srcs = ["x.in"],
outs = ["x.cc"],
cmd = "$(locations :tool) $< >$@",
tools = [":tool"],
)
cc_binary(
name = "tool",
srcs = ["tool.cpp"],
)
Genrule 在 执行配置 中配置其工具,因此以下查询会生成以下输出:
| 查询 | 构建的目标 | 输出 |
|---|---|---|
| bazel cquery "//x:tool" | //x:tool | //x:tool(targetconfig) |
| bazel cquery "//x:tool" --universe_scope="//x:my_gen" | //x:my_gen | //x:tool(execconfig) |
如果设置了此标志,系统会构建其内容。 __如果未设置,系统会改为构建查询表达式中提及的所有目标。已构建目标的传递闭包用作查询的宇宙。无论哪种方式,要构建的目标都必须在顶层可构建(即与顶级选项兼容)。cquery 会在这些
顶级目标的传递闭包中返回结果。
即使可以在顶层构建查询表达式中的所有目标,不这样做也可能是有益的。例如,显式设置
--universe_scope可以防止在您不关心的配置中多次构建目标。它还可以帮助指定您要查找的
目标的哪个配置版本(因为目前无法
以任何其他方式完全指定此版本)。如果您的查询表达式比 deps(//foo) 更复杂,则应设置此标志
。
--implicit_deps(布尔值,默认值为 True)
将此标志设置为 false 会过滤掉所有未在 BUILD 文件中显式设置,而是由 Bazel 在其他位置设置的结果。这包括过滤已解析的 工具链。
--tool_deps(布尔值,默认值为 True)
将此标志设置为 false 会过滤掉所有已配置的目标,对于这些目标,从查询的目标到这些目标的
路径会跨越目标
配置和
非目标配置之间的转换。
如果查询的目标位于目标配置中,则设置 --notool_deps 将
仅返回也位于目标配置中的目标。如果查询的
目标位于非目标配置中,则设置 --notool_deps 将仅返回
也位于非目标配置中的目标。此设置通常不会影响已解析工具链的过滤
。
--include_aspects(布尔值,默认值为 True)
包含由 方面 添加的依赖项。
如果停用了此标志,并且 X 仅通过方面依赖于 Y,则 cquery somepath(X, Y) 和
cquery deps(X) | grep 'Y' 会省略 Y。
输出格式
默认情况下,cquery 会以标签和配置对的依赖项排序列表的形式输出结果。 还有其他选项可用于公开结果。
转场效果
--transitions=lite --transitions=full
配置转换 用于在与顶级目标不同的 配置中构建顶级目标下的目标。
例如,目标可能会对其 tools 属性中的所有
依赖项强制执行到执行配置的转换。这些称为属性
转换。规则还可以对其自己的配置强制执行转换,
称为规则类转换。此输出格式会输出有关
这些转换的信息,例如它们的类型以及它们对 build
选项的影响。
此输出格式由 --transitions 标志触发,该标志默认设置为
NONE。它可以设置为 FULL 或 LITE 模式。FULL 模式会输出
有关规则类转换和属性转换的信息,包括转换前后选项的
详细差异。LITE 模式
会输出相同的信息,但没有选项差异。
协议消息输出
--output=proto
此选项会导致以二进制协议 缓冲区形式输出生成的目标。协议缓冲区的定义位于 src/main/protobuf/analysis_v2.proto。
CqueryResult 是包含 cquery 结果的顶级消息。它
包含 ConfiguredTarget 消息列表和 Configuration
消息列表。每个 ConfiguredTarget 都有一个 configuration_id,其值等于
相应 Configuration 消息的 id 字段的值。
--[no]proto:include_configurations
默认情况下,cquery 结果会返回配置信息作为每个 已配置目标的一部分。如果您想省略此信息并获取格式与查询的 proto 输出完全相同的 proto 输出 ,请将此标志设置为 false。
如需了解更多与 proto 输出相关的选项,请参阅查询的 proto 输出文档 。
图表输出
--output=graph
此选项会生成与 Graphviz 兼容的 .dot 文件作为输出。如需了解详情,请参阅 query's
图表输出文档。cquery
还支持 --graph:node_limit 和
--graph:factored。
文件输出
--output=files
此选项会输出查询匹配的每个目标生成的文件列表,类似于在 bazel build
调用结束时输出的列表。输出仅包含请求的
输出组中公布的文件,具体由
--output_groups标志确定。
它确实包含源文件。
此输出格式输出的所有路径都相对于
execroot,可以通过
bazel info execution_root 获取。如果存在 bazel-out 便利符号链接,
则主代码库中文件的路径也会相对于工作区
目录解析。
config(..., target) 中。使用 Starlark 定义输出格式
--output=starlark
此输出格式会为查询结果中的每个已配置目标调用 Starlark
函数,并输出调用返回的值
。--starlark:file 标志用于指定
Starlark 文件的位置,该文件定义了一个名为 format 的函数,该函数具有一个参数
target。系统会为查询结果中的每个目标
调用此函数。或者,为方便起见,您可以使用
--starlark:expr 标志仅指定声明为 def format(target): return expr 的函数的正文。
“cquery”Starlark 方言
cquery Starlark 环境不同于 BUILD 或 .bzl 文件。它包含
所有核心 Starlark
内置常量和函数,
以及下面介绍的一些 cquery 特有的常量和函数,但不包含(例如)glob、
native 或 rule,并且不支持 load 语句。
build_options(target)
build_options(target) 返回一个映射,其键是 build 选项标识符(请参阅
配置)
,其值是 Starlark 值。此映射中省略了值不是合法的 Starlark
值的 build 选项。
如果目标是输入文件,则 build_options(target) 返回 None,因为输入文件
目标具有 null 配置。
providers(target)
providers(target) 返回一个映射,其键是
提供程序
名称(例如 "DefaultInfo"),其值是 Starlark 值。此映射中省略了值不是合法的 Starlark 值的提供程序
。
示例
输出由 //foo 生成的所有文件的基本名称的空格分隔列表:
bazel cquery //foo --output=starlark \
--starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
输出 规则 目标在
//bar 及其子软件包中生成的所有文件的路径的空格分隔列表:
bazel cquery 'kind(rule, //bar/...)' --output=starlark \
--starlark:expr="' '.join([f.path for f in target.files.to_list()])"
输出由 //foo 注册的所有操作的助记符列表。
bazel cquery //foo --output=starlark \
--starlark:expr="[a.mnemonic for a in target.actions]"
输出由 cc_library //baz 注册的所有编译输出的列表。
bazel cquery //baz --output=starlark \
--starlark:expr="[f.path for f in target.output_groups.compilation_outputs.to_list()]"
输出在构建 //foo 时命令行选项 --javacopt 的值。
bazel cquery //foo --output=starlark \
--starlark:expr="build_options(target)['//command_line_option:javacopt']"
输出每个恰好有一个输出的目标的标签。此示例使用 文件中定义的 Starlark 函数。
$ cat example.cquery
def has_one_output(target):
return len(target.files.to_list()) == 1
def format(target):
if has_one_output(target):
return target.label
else:
return ""
$ bazel cquery //baz --output=starlark --starlark:file=example.cquery
输出每个严格遵循 Python 3 的目标的标签。此示例使用 文件中定义的 Starlark 函数。
$ cat example.cquery
def format(target):
p = providers(target)
py_info = p.get("PyInfo")
if py_info and py_info.has_py3_only_sources:
return target.label
else:
return ""
$ bazel cquery //baz --output=starlark --starlark:file=example.cquery
从用户定义的提供程序中提取值。
$ cat some_package/my_rule.bzl
MyRuleInfo = provider(fields={"color": "the name of a color"})
def _my_rule_impl(ctx):
...
return [MyRuleInfo(color="red")]
my_rule = rule(
implementation = _my_rule_impl,
attrs = {...},
)
$ cat example.cquery
def format(target):
p = providers(target)
my_rule_info = p.get("//some_package:my_rule.bzl%MyRuleInfo'")
if my_rule_info:
return my_rule_info.color
return ""
$ bazel cquery //baz --output=starlark --starlark:file=example.cquery
cquery 与 query
cquery 和 query 相互补充,并在
不同的细分领域中表现出色。请考虑以下因素,以确定哪种方法适合您:
cquery遵循特定的select()分支来 模拟您构建的确切图。query不知道 build 选择哪个 分支,因此通过包含所有分支来过度近似。cquery' 的精确度要求构建比query更多的图。具体而言,cquery会评估已配置的目标,而query仅 评估目标。这需要更多时间并使用更多内存。cquery's 对 查询语言的解释引入了query避免的歧义。例如, 如果"//foo"存在于两个配置中,哪个配置 应使用?cquery "deps(//foo)"config函数可以帮助解决此问题。- 作为一种较新的工具,
cquery缺少对某些用例 的支持。如需了解详情,请参阅已知问题。
已知问题
cquery“构建”的所有目标都必须具有相同的配置。
在评估查询之前,cquery 会触发 build,直到
build 操作执行之前。它
“构建”的目标默认从查询
表达式中出现的所有标签中选择(可以使用
--universe_scope替换此默认行为)。这些
目标必须具有相同的配置。
虽然这些目标通常共享顶级“目标”配置,
但规则可以通过
传入的边缘转换更改自己的配置。
这就是 cquery 的不足之处。
解决方法:如果可能,请将 --universe_scope 设置为更严格的
范围。例如:
# This command attempts to build the transitive closures of both //foo and # //bar. //bar uses an incoming edge transition to change its --cpu flag. $ bazel cquery 'somepath(//foo, //bar)' ERROR: Error doing post analysis query: Top-level targets //foo and //bar have different configurations (top-level targets with different configurations is not supported) # This command only builds the transitive closure of //foo, under which # //bar should exist in the correct configuration. $ bazel cquery 'somepath(//foo, //bar)' --universe_scope=//foo
不支持--output=xml。
输出不确定。
cquery 不会自动清除之前命令中的 build 图,因此容易从过去的查询中获取结果。例如,genrule 对
其 tools 属性施加执行转换,也就是说,它在
执行配置中配置其工具。
您可以在下面看到该转换的持久影响。
$ cat > foo/BUILD <<<EOF
genrule(
name = "my_gen",
srcs = ["x.in"],
outs = ["x.cc"],
cmd = "$(locations :tool) $< >$@",
tools = [":tool"],
)
cc_library(
name = "tool",
)
EOF
$ bazel cquery "//foo:tool"
tool(target_config)
$ bazel cquery "deps(//foo:my_gen)"
my_gen (target_config)
tool (exec_config)
...
$ bazel cquery "//foo:tool"
tool(exec_config)
解决方法:更改任何启动选项以强制重新分析已配置的目标。
例如,将 --test_arg=<whatever> 添加到 build 命令。
问题排查
递归目标模式 (/...)
如果您遇到:
$ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, //foo/...)" ERROR: Error doing post analysis query: Evaluation failed: Unable to load package '[foo]' because package is not in scope. Check that all target patterns in query expression are within the --universe_scope of this query.
这会错误地表明软件包 //foo 不在范围内,即使
--universe_scope=//foo:app 包含该软件包也是如此。这是由于
cquery中的设计限制所致。如需解决此问题,请在宇宙
范围中显式包含 //foo/...:
$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"
如果这不起作用(例如,因为 //foo/... 中的某些目标无法
使用所选 build 标志进行构建),请使用预处理查询手动将模式解包到其
组成软件包中:
# Replace "//foo/..." with a subshell query call (not cquery!) outputting each package, piped into # a sed call converting "<pkg>" to "//<pkg>:*", piped into a "+"-delimited line merge. # Output looks like "//foo:*+//foo/bar:*+//foo/baz". # $ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, $(bazel query //foo/... --output=package | sed -e 's/^/\/\//' -e 's/$/:*/' | paste -sd "+" -))"