cquery
是 query
的变体,可正确处理
select()
和构建选项对构建的影响
图表。
为了实现这一目标,它会对 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
在配置的目标图上运行。它没有数据分析
构建操作或对 [test_suite](/versions/6.5.0/reference/be/general#test_suite)
的访问权限等工件
规则,因为它们不是已配置的目标。对于前者,请参阅 [aquery](/versions/6.5.0/docs/aquery)
。
基本语法
一个简单的 cquery
调用如下所示:
bazel cquery "function(//target)"
查询表达式 "function(//target)"
包含以下内容:
function(...)
是要在目标上运行的函数。cquery
支持大多数query
的函数的组成,以及一个 有一些新的想法。//target
是馈送给函数的表达式。在此示例中, 就是一个简单的目标。不过,查询语言还允许嵌套函数。 如需查看示例,请参阅查询方法。
cquery
需要目标才能运行加载和分析
阶段。除非另有说明,否则 cquery
会解析
查询表达式。请参阅 --universe_scope
用于查询顶级构建目标的依赖项。
配置
以下代码行:
//tree:ash (9f87702)
表示 //tree:ash
是在 ID 为 9f87702
的配置中构建的。大多数
目标,这是 build 选项值的不透明哈希值,用于定义
配置。
如需查看配置的完整内容,请运行以下命令:
$ bazel config 9f87702
主机配置使用特殊 ID (HOST)
。非生成的源文件,例如
对于 srcs
中常见的那些字符,请使用特殊 ID (null)
(因为它们
无需进行配置)。
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 a host-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 (HOST)
如果您想精确声明要查询哪个实例,请使用
config
函数。
查看 query
的目标模式
文档,详细了解目标模式。
函数
在一组函数中
受 query
支持,cquery
支持除
allrdeps
,
buildfiles
,
rbuildfiles
,
siblings
、
tests
和
visible
。
cquery
还引入了以下新函数:
config
expr ::= config(expr, word)
config
运算符尝试为
第一个参数表示的标签,以及
第二个参数。
第二个参数的有效值包括 target
、host
、null
或 a
自定义配置哈希。哈希可以从 $
bazel config
或先前 cquery
的输出中检索。
示例:
$ bazel cquery "config(//bar, host)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (HOST) //baz (3732cc8) $ bazel cquery "config(//baz, 3732cc8)"
如果无法在指定的 配置,则仅返回可以找到的 ID。如果没有结果 查询将失败。
选项
构建选项
cquery
在常规 Bazel build 上运行,因此会继承
options 的信息。
使用 cquery 选项
--universe_scope
(逗号分隔列表)
所配置目标的依赖项通常会经过 过渡、 这将导致其配置与从属配置不同。此标志 让您可以查询目标,就像将其构建为依赖项或传递变量一样 另一个目标的依赖项。例如:
# x/BUILD genrule( name = "my_gen", srcs = ["x.in"], outs = ["x.cc"], cmd = "$(locations :tool) $< >$@", tools = [":tool"], ) cc_library( name = "tool", )
Genrule 在 主机配置 因此以下查询将产生以下输出:
查询 | 目标构建 | 输出 |
---|---|---|
bazel cquery "//x:tool" | //x:tool | //x:tool(targetconfig) |
bazel cquery "//x:tool"--universe_scope="//x:my_gen" | //x:my_gen | //x:tool(hostconfig) |
如果设置了此标志,则会构建其内容。如果未设置此字段,
查询表达式中提到的词汇。传递的
构建的目标用作全部查询。无论采用哪种方式
构建的应用必须可在顶层构建(也就是说,要兼容顶级构建)
选项)。cquery
会返回这些查询的传递闭包结果
顶级目标。
即使可以在顶部查询表达式中构建所有目标
建议您不要这样做。例如,明确设置
--universe_scope
可能会导致以下时间过后多次构建目标:
一些无关紧要的配置它还可以帮助您指定
因为目前还无法
以任何其他方式进行完整指定)。您应该设置此标志
如果查询表达式比 deps(//foo)
更复杂,则抛出该异常。
--implicit_deps
(布尔值,default=True)
将此标志设置为 false 会过滤掉 而是通过 Bazel 在其他位置进行设置。这包括已解析的过滤条件 工具链。
--tool_deps
(布尔值,default=True)
将此标志设置为 false 会过滤掉所有已配置的目标,
从查询的目标到它们之间的路径在目标
和
非目标配置。
如果查询的目标采用目标配置,则设置 --notool_deps
将
仅返回同时在目标配置中的目标。如果查询的
目标在非目标配置中,设置 --notool_deps
将仅返回
也属于非目标配置。此设置通常不会影响过滤
已解析的工具链
--include_aspects
(布尔值,default=True)
切面可以
向 build 添加额外的依赖项。默认情况下,cquery
不会遵循切面,因为
它们扩大了可查询图,而这会占用更多内存。但关注这些观众可以收获更多
准确的结果。
如果不担心大型查询对内存的影响,请在以下位置默认启用此标志:
如果您在查询切面时停用了切面,可能会遇到以下问题:在
构建目标 Y,但 cquery somepath(Y, X)
和 cquery deps(Y) | grep 'X'
未返回任何结果,因为依赖关系是通过切面实现的。
输出格式
默认情况下,cquery 输出会生成按依赖关系排序的标签和配置对列表。 您也可以使用其他选项显示结果。
转场效果
--transitions=lite --transitions=full
例如,目标可能会强行让所有服务器都转换到主机配置
tools
属性中的依赖项。这些称为属性
过渡效果。规则也可以对自己的配置强制执行转换,
称为规则类转换此输出格式会输出
例如它们的类型及其对构建的影响
选项。
此输出格式由 --transitions
标志触发,默认情况下,该标志为
已设置为 NONE
。可以设置为 FULL
或 LITE
模式。FULL
模式输出
有关规则类转换和属性转换的信息,包括
迁移前后各选项的详细差异。LITE
种模式
在没有选项 diff 的情况下,输出相同的信息。
协议消息输出
--output=proto
此选项会导致以二进制协议输出生成的目标 缓冲区形式有关协议缓冲区的定义,请访问 src/main/protobuf/analysis.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
的
如需了解详情,请参阅图表输出文档。cquery
也支持 --graph:node_limit
和
--graph:factored
。
文件输出
--output=files
此选项会输出由每个匹配的目标生成的输出文件列表
由类似于 bazel build
末尾输出的列表的查询触发
调用。输出仅包含所请求的
输出组(由
--output_groups
标志。
但包含源文件。
使用 Starlark 定义输出格式
--output=starlark
此输出格式会调用 Starlark
函数,并输出
该调用返回的值。--starlark:file
标志用于指定
Starlark 文件,其中定义了一个名为 format
的函数,其中包含一个参数,
target
。系统会针对每个 Target 调用此函数,
。另外,为方便起见,您也可以只指定
声明为 def format(target): return expr
的函数的正文,
--starlark:expr
标志。
“cquery”Starlark 方言
cquery Starlark 环境与 BUILD 或 .bzl 文件不同。其中包括
Starlark 全核心
内置常量和函数,
以及下面介绍的几个特定于 cquery 的方法,但不是 glob
(举例而言),
native
或 rule
,并且不支持 load 语句。
build_options(target)
build_options(target)
会返回一个映射,其键是构建选项标识符(请参阅
配置)
其值是 Starlark 值。值不是合法 Starlark 的 build 选项
值。
如果目标是输入文件,build_options(target)
会返回 None 作为输入文件
具有 null 配置。
provider(目标)
providers(target)
会返回一个映射,其键为以下名称:
提供方
(例如 "DefaultInfo"
),其值是各自的 Starlark 值。提供商
其中的值不是合法 Starlark 值将从此映射中省略。
示例
输出由 //foo
生成的所有文件的基本名称列表(以空格分隔):
bazel cquery //foo --output=starlark \ --starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
输出 rule 目标在以下位置生成的所有文件的路径列表(以空格分隔)
//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
的解释 查询语言会产生歧义query
会避开的分桶。例如: 如果"//foo"
同时存在于两种配置中,即 应该使用cquery "deps(//foo)"
吗?[config](#config)
函数可以助您一臂之力。- 作为一种较新的工具,
cquery
缺乏对特定用途的支持 案例如需了解详情,请参阅已知问题。
已知问题
cquery
“构建”的所有目标必须具有相同的配置。
在评估查询之前,cquery
会触发构建,至
执行构建操作之前它定位的
"构建"默认从查询中显示的所有标签中选中
(可以将其覆盖
与 --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 图
因此很容易从以前的命令中获取结果
查询。例如,genquery
会对以下实例执行主机转换:
其 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 (host_config) ... $ bazel cquery "//foo:tool" tool(host_config)
临时解决方法:更改任何启动选项,强制重新分析已配置的目标。
例如,将 --test_arg=<whatever>
添加到您的构建命令中。
问题排查
递归目标模式 (/...
)
如果您遇到以下情况:
$ 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
。如需解决此问题,请在 Universe 中明确包含 //foo/...
范围:
$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"
如果这不起作用(例如,//foo/...
中的某些目标无法
使用所选构建标志进行构建),手动将模式解封装到其
包含预处理查询的各软件包:
# 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 "+" -))"