可配置查询 (cquery)

报告问题 查看来源 每晚 · 7.2。 · 7.1敬上 · 7.0 · 6.5 条 · 6.4

cqueryquery 的变体,可正确处理 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

目标模式评估

//foocqueryquery 中的含义不同。这是因为 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, testsvisible

cquery 还引入了以下新函数:

config

expr ::= config(expr, word)

config 运算符尝试为 第一个参数表示的标签,以及 第二个参数。

第二个参数的有效值包括 targethostnull 或 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=&quot;//x:my_gen&quot; //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。可以设置为 FULLLITE 模式。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(举例而言), nativerule,并且不支持 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

cqueryquery相辅相成,在 多个不同的小众领域。请考虑以下事项,以确定哪个选项适合您:

  • 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=&lt;whatever&gt; 添加到您的构建命令中。

问题排查

递归目标模式 (/...)

如果您遇到以下情况:

$ 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 "+" -))"