Bazel 查询参考

报告问题 查看来源 Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

本页是 Bazel 查询语言的参考手册,该语言在您使用 bazel query 分析 build 依赖项时使用。它还介绍了 bazel query 支持的输出格式。

如需查看实际用例,请参阅 Bazel 查询方法指南

其他查询参考

除了在加载后阶段目标图上运行的 query 之外,Bazel 还包括操作图查询可配置查询

操作图查询

操作图查询 (aquery) 针对分析后的配置目标图运行,并公开有关操作制品及其关系的信息。如果您对从配置的目标图生成的 Action/Artifact 的属性感兴趣,那么 aquery 会很有用。例如,实际运行的命令及其输入、输出和助记符。

如需了解详情,请参阅 aquery 参考文档

可配置的查询

传统 Bazel 查询在加载后阶段的目标图上运行,因此没有配置及其相关概念。值得注意的是,它无法正确解析 select 语句,而是返回 select 的所有可能解析。不过,可配置的查询环境 cquery 可以正确处理配置,但无法提供此原始查询的所有功能。

如需了解详情,请参阅 cquery 参考文档

示例

用户如何使用 bazel query?以下是一些典型示例:

为什么 //foo 树依赖于 //bar/baz? 显示路径:

somepath(foo/..., //bar/baz:all)

哪些 C++ 库是所有 foo 测试所依赖的,但 foo_bin 目标不依赖?

kind("cc_library", deps(kind(".*test rule", foo/...)) except deps(//foo:foo_bin))

令牌:词法语法

查询语言中的表达式由以下令牌组成:

  • 关键字,例如 let。关键字是语言的保留字,下面将介绍每个关键字。关键字的完整集为:

  • 字词,例如“foo/...”“.*test rule”或“//bar/baz:all”。如果某个字符序列用英文单引号 (') 或英文双引号 (") 括起来,则表示它是一个字词。如果字符序列未加引号,仍可能会被解析为字词。未加引号的字词是由字母字符 A-Za-z、数字 0-9 和特殊字符 */@.-_:$~[](星号、正斜线、@ 符号、英文句点、连字符、下划线、冒号、美元符号、波浪线、左方括号、右方括号)组成的字符序列。不过,未加英文引号的字词不得以连字符 - 或星号 * 开头,即使相对 [目标名称][(/concepts/labels#target-names) 可以以这些字符开头也是如此。

    未加英文引号的字词也不得包含加号 + 或等号 =,即使这些字符在目标名称中是允许使用的。在编写生成查询表达式的代码时,目标名称应加引号。

    在编写从用户提供的值构建 Bazel 查询表达式的脚本时,必须使用引号。

     //foo:bar+wiz    # WRONG: scanned as //foo:bar + wiz.
     //foo:bar=wiz    # WRONG: scanned as //foo:bar = wiz.
     "//foo:bar+wiz"  # OK.
     "//foo:bar=wiz"  # OK.
    

    请注意,此引号是 shell 可能要求的任何引号之外的引号,例如:

    bazel query ' "//foo:bar=wiz" '   # single-quotes for shell, double-quotes for Bazel.

    用英文引号括起的关键字会被视为普通字词。例如,some 是一个关键字,但“some”是一个字词。foo 和“foo”都是字词。

    不过,在目标名称中使用单引号或双引号时要小心。引用一个或多个目标名称时,请仅使用一种类型的引号(全部为单引号或全部为双引号)。

    以下是 Java 查询字符串的示例:

      'a"'a'         # WRONG: Error message: unclosed quotation.
      "a'"a"         # WRONG: Error message: unclosed quotation.
      '"a" + 'a''    # WRONG: Error message: unexpected token 'a' after query expression '"a" + '
      "'a' + "a""    # WRONG: Error message: unexpected token 'a' after query expression ''a' + '
      "a'a"          # OK.
      'a"a'          # OK.
      '"a" + "a"'    # OK
      "'a' + 'a'"    # OK
    

    我们之所以选择这种语法,是为了在大多数情况下无需使用引号。(不寻常的)".*test rule" 示例需要使用引号:它以英文句点开头,并且包含空格。引用 "cc_library" 是不必要的,但不会造成任何危害。

  • 标点符号,例如圆括号 ()、句点 . 和逗号 ,。包含标点符号(上述例外情况除外)的字词必须用引号括起来。

带引号的字词之外的空格字符会被忽略。

Bazel 查询语言概念

Bazel 查询语言是一种表达式语言。每个表达式的计算结果都是一组部分有序的目标,或者等效地说是目标 (DAG)。这是唯一的数据类型。

“集合”和“图”指的是相同的数据类型,但侧重于不同的方面,例如:

  • 设置:目标的偏序不重要。
  • :目标的偏序很重要。

依赖关系图中的环

构建依赖关系图应为无环图。

查询语言使用的算法旨在用于无环图,但可有效应对环。未指定如何处理周期,不应依赖于此。

隐式依赖项

除了在 BUILD 文件中明确定义的 build 依赖项之外,Bazel 还会向规则添加额外的隐式依赖项。例如,每个 Java 规则都隐式依赖于 JavaBuilder。隐式依赖项是使用以 $ 开头的属性建立的,并且无法在 BUILD 文件中替换。

默认情况下,bazel query 在计算查询结果时会考虑隐式依赖项。您可以使用 --[no]implicit_deps 选项更改此行为。请注意,由于查询不考虑配置,因此永远不会考虑潜在的工具链。

健全性

Bazel 查询语言表达式在 build 依赖关系图上运行,该图由所有 BUILD 文件中的所有规则声明隐式定义。请务必了解,此图表在某种程度上是抽象的,并不构成对如何执行构建的所有步骤的完整说明。为了执行 build,还需要配置;如需了解详情,请参阅用户指南的配置部分。

在 Bazel 查询语言中计算表达式的结果对于所有配置均为 true,这意味着它可能是一种保守的过近似,而不是完全精确。如果您使用查询工具来计算构建期间所需的所有源文件的集合,该工具可能会报告比实际需要更多的文件,因为例如,查询工具会包含支持消息翻译所需的所有文件,即使您不打算在构建中使用该功能也是如此。

关于保留图表顺序

操作会保留从其子表达式继承的任何排序限制。您可以将此视为“偏序守恒定律”。举例来说,如果您发出查询来确定特定目标的依赖项的传递闭包,则结果集会根据依赖关系图进行排序。如果您过滤该组,使其仅包含 file 类型的目标,则所得子集中每对目标之间仍存在相同的传递偏序关系,即使这些对在原始图中实际上没有直接关联也是如此。(构建依赖关系图中没有文件-文件边。)

不过,虽然所有运算符都会保留顺序,但某些运算(例如集合运算)本身不会引入任何排序限制。请考虑以下表达式:

deps(x) union y

最终结果集的顺序保证会保留其子表达式的所有排序限制,即 x 的所有传递依赖项都已正确排序。不过,该查询无法保证 y 中目标的顺序,也无法保证 deps(x) 中目标相对于 y 中目标的顺序(y 中也恰好位于 deps(x) 中的目标除外)。

引入排序限制的运算符包括:allpathsdepsrdepssomepath 和目标模式通配符 package:*dir/... 等。

天空查询

Sky Query 是一种在指定宇宙范围内运行的查询模式。

仅在 SkyQuery 中提供的特殊函数

Sky Query 模式具有额外的查询函数 allrdepsrbuildfiles。这些函数在整个宇宙范围内运行(因此它们不适用于常规查询)。

指定全网范围

通过传递以下两个标志(--universe_scope--infer_universe_scope)和 --order_output=no 来激活 Sky Query 模式。 --universe_scope=<target_pattern1>,...,<target_patternN> 指示查询预加载由目标模式指定的传递闭包,目标模式可以是加法模式,也可以是减法模式。然后,系统会在该“范围”内评估所有查询。特别是,allrdepsrbuildfiles 运算符仅返回此范围内的结果。--infer_universe_scope 会告知 Bazel 从查询表达式中推断 --universe_scope 的值。此推断值是查询表达式中唯一目标模式的列表,但可能不是您想要的。例如:

bazel query --infer_universe_scope --order_output=no "allrdeps(//my:target)"

此查询表达式中的唯一目标模式列表为 ["//my:target"],因此 Bazel 会将其视为与以下调用相同:

bazel query --universe_scope=//my:target --order_output=no "allrdeps(//my:target)"

但使用 --universe_scope 的查询结果仅为 //my:target;根据构造,//my:target 的任何反向依赖项都不在全集中!另一方面,请考虑:

bazel query --infer_universe_scope --order_output=no "tests(//a/... + b/...) intersect allrdeps(siblings(rbuildfiles(my/starlark/file.bzl)))"

这是一个有意义的查询调用,旨在计算 tests 扩展中某些目录下的测试目标,这些目录以传递方式依赖于定义使用特定 .bzl 文件的目标。在此,--infer_universe_scope 是一种便利,尤其是在选择 --universe_scope 否则需要您自行解析查询表达式的情况下。

因此,对于使用全范围运算符(例如 allrdepsrbuildfiles)的查询表达式,请务必仅在 --infer_universe_scope 的行为符合您的预期时才使用它。

与默认查询相比,Sky Query 有一些优点和缺点。主要缺点是它无法按图顺序对输出进行排序,因此禁止使用某些输出格式。其优势在于,它提供了默认查询中没有的两个运算符(allrdepsrbuildfiles)。此外,Sky Query 通过内省 Skyframe 图来完成其工作,而不是像默认实现那样创建新图。因此,在某些情况下,它速度更快,使用的内存更少。

表达式:语法的语法和语义

以下是 Bazel 查询语言的语法,以 EBNF 表示法表示:

expr ::= word
       | let name = expr in expr
       | (expr)
       | expr intersect expr
       | expr ^ expr
       | expr union expr
       | expr + expr
       | expr except expr
       | expr - expr
       | set(word *)
       | word '(' int | word | expr ... ')'

以下各部分将按顺序介绍此语法的每个产生式。

目标模式

expr ::= word

从语法上讲,目标模式只是一个字词。它会被解读为一组(无序)目标。最简单的目标模式是标签,用于标识单个目标(文件或规则)。例如,目标模式 //foo:bar 的评估结果是一个包含一个元素(即目标 bar 规则)的集合。

目标模式将标签泛化,以包含软件包和目标的通配符。例如,foo/...:all(或仅 foo/...)是一个目标模式,可评估为包含 foo 目录下每个软件包中所有规则的集合(以递归方式);bar/baz:all 是一个目标模式,可评估为包含 bar/baz 软件包中所有规则的集合,但不包含其子软件包。

同样,foo/...:* 是一个目标模式,它会评估为包含 foo 目录下每个软件包中所有 目标(规则文件)的集合;bar/baz:* 会评估为包含 bar/baz 软件包中所有目标(但不包括其子软件包)的集合。

由于 :* 通配符既可匹配文件也可匹配规则,因此对于查询而言,它通常比 :all 更有用。相反,:all 通配符(在 foo/... 等目标模式中隐式使用)通常对 build 更有用。

bazel query 目标模式的工作方式与 bazel build build 目标相同。如需了解详情,请参阅目标模式,或输入 bazel help target-syntax

目标模式可能会评估为单例集(如果是标签),也可能会评估为包含许多元素的集(例如 foo/...,其中包含数千个元素),如果目标模式与任何目标都不匹配,则可能会评估为空集。

目标模式表达式结果中的所有节点都根据依赖关系正确排序。因此,foo:* 的结果不仅是软件包 foo 中的目标集,也是这些目标的。(不保证结果节点相对于其他节点的相对顺序。)如需了解详情,请参阅图表顺序部分。

变量

expr ::= let name = expr1 in expr2
       | $name

Bazel 查询语言允许定义变量和引用变量。对 let 表达式的求值结果与对 expr2 的求值结果相同,其中变量 name 的所有自由出现都替换为 expr1 的值。

例如,let v = foo/... in allpaths($v, //common) intersect $v 等同于 allpaths(foo/...,//common) intersect foo/...

如果变量引用 name 出现在封闭的 let name = ... 表达式之外,则会发生错误。换句话说,顶级查询表达式不能包含自由变量。

在上述语法产生式中,name 类似于 word,但附加的限制是它必须是 C 编程语言中的合法标识符。对变量的引用必须以“$”字符开头。

每个 let 表达式仅定义一个变量,但您可以嵌套使用它们。

目标模式和变量引用都只包含一个令牌(一个字词),从而造成语法歧义。不过,由于合法的变量名子集与合法的目标模式子集是不相交的,因此不存在语义歧义。

从技术上讲,let 表达式不会提高查询语言的表达能力:任何可以用该语言表达的查询也可以不用这些表达式来表达。不过,它们可以提高许多查询的简洁性,还可能提高查询评估效率。

带括号的表达式

expr ::= (expr)

圆括号用于关联子表达式,以强制执行评估顺序。 带英文括号的表达式的求值结果为其实参的值。

代数集合运算:交集、并集、差集

expr ::= expr intersect expr
       | expr ^ expr
       | expr union expr
       | expr + expr
       | expr except expr
       | expr - expr

这三个运算符用于计算其参数的常规集合运算。每个运算符都有两种形式:一种是标称形式,例如 intersect;另一种是符号形式,例如 ^。这两种形式是等效的;符号形式的输入速度更快。(为清晰起见,本页面的其余部分使用标称形式。)

例如,

foo/... except foo/bar/...

计算结果为与 foo/... 匹配但不与 foo/bar/... 匹配的目标的集合。

您可以将同一查询编写为:

foo/... - foo/bar/...

intersect (^) 和 union (+) 运算是可交换的(对称);except (-) 是非对称的。解析器将这三个运算符都视为左结合且优先级相同,因此您可能需要使用圆括号。例如,以下表达式中的前两个是等效的,但第三个不是:

x intersect y union z
(x intersect y) union z
x intersect (y union z)

从外部来源读取目标:设置

expr ::= set(word *)

set(a b c ...) 运算符用于计算一组(零个或多个)以空格(而非英文逗号)分隔的目标模式的并集。

set() 与 Bourne shell 的 $(...) 功能结合使用,可将一次查询的结果保存到常规文本文件中,使用其他程序(例如标准 UNIX shell 工具)来处理该文本文件,然后将结果重新引入查询工具作为值以供进一步处理。例如:

bazel query deps(//my:target) --output=label | grep ... | sed ... | awk ... > foo
bazel query "kind(cc_binary, set($(<foo)))"

在下一个示例中,kind(cc_library, deps(//some_dir/foo:main, 5)) 是通过使用 awk 程序过滤 maxrank 值来计算的。

bazel query 'deps(//some_dir/foo:main)' --output maxrank | awk '($1 < 5) { print $2;} ' > foo
bazel query "kind(cc_library, set($(<foo)))"

在这些示例中,$(<foo)$(cat foo) 的简写形式,但也可以使用 cat 以外的 shell 命令,例如之前的 awk 命令。

函数

expr ::= word '(' int | word | expr ... ')'

查询语言定义了多个函数。函数名称决定了它所需的实参数量和类型。以下函数可供使用:

依赖项的传递闭包:deps

expr ::= deps(expr)
       | deps(expr, depth)

deps(x) 运算符的求值结果为由其实参集 x 的传递闭包依赖关系形成的图。例如,deps(//foo) 的值是以单个节点 foo 为根的依赖关系图,包括其所有依赖项。deps(foo/...) 的值是依赖关系图,其根是 foo 目录下每个软件包中的所有规则。在此上下文中,“依赖项”仅指规则和文件目标,因此创建这些目标所需的 BUILD 和 Starlark 文件不包含在此处。为此,您应使用 buildfiles 运算符。

生成的图会根据依赖关系进行排序。如需了解详情,请参阅有关图表顺序的部分。

deps 运算符接受一个可选的第二个实参,该实参是一个整数文字,用于指定搜索深度的上限。因此,deps(foo:*, 0) 会返回 foo 软件包中的所有目标,而 deps(foo:*, 1) 还会进一步包含 foo 软件包中任何目标的直接前提条件,deps(foo:*, 2) 还会进一步包含可从 deps(foo:*, 1) 中的节点直接到达的节点,依此类推。(这些数字对应于 minrank 输出格式中显示的排名。) 如果省略 depth 参数,则搜索不受限制:它会计算前提条件的自反传递闭包。

反向依赖项的传递闭包:rdeps

expr ::= rdeps(expr, expr)
       | rdeps(expr, expr, depth)

rdeps(u, x) 运算符用于评估实参集 x 在宇宙集 u 的传递闭包内的反向依赖项。

生成的图会根据依赖关系进行排序。如需了解详情,请参阅有关图顺序的部分。

rdeps 运算符接受可选的第三个实参,该实参是一个整数文字,用于指定搜索深度的上限。生成的图仅包含距离实参集中的任何节点在指定深度内的节点。因此,rdeps(//foo, //common, 1) 的计算结果为 //foo 的传递闭包中直接依赖于 //common 的所有节点。(这些数字与 minrank 输出格式中显示的排名相对应。)如果省略 depth 参数,则搜索范围不受限制。

所有反向依赖项的传递闭包:allrdeps

expr ::= allrdeps(expr)
       | allrdeps(expr, depth)

allrdeps 运算符的行为与 rdeps 运算符完全相同,只不过“全集”是 --universe_scope 标志的评估结果,而不是单独指定。因此,如果传递了 --universe_scope=//foo/...,则 allrdeps(//bar) 等同于 rdeps(//foo/..., //bar)

同一软件包中的直接反向依赖项:same_pkg_direct_rdeps

expr ::= same_pkg_direct_rdeps(expr)

same_pkg_direct_rdeps(x) 运算符的求值结果为与实参集中的目标位于同一软件包中且直接依赖于该实参集中的目标的所有目标。

处理目标的软件包:同级

expr ::= siblings(expr)

siblings(x) 运算符的计算结果为与实参集中的目标位于同一软件包中的完整目标集。

任意选择:部分

expr ::= some(expr)
       | some(expr, count )

some(x, k) 运算符从其实参集 x 中任意选择最多 k 个目标,并求值为仅包含这些目标的集合。参数 k 是可选的;如果缺少该参数,结果将是一个单例集,其中仅包含一个任意选择的目标。如果实参集 x 的大小小于 k,则返回整个实参集 x

例如,表达式 some(//foo:main union //bar:baz) 的计算结果为包含 //foo:main//bar:baz 的单例集,但具体包含哪个值未定义。表达式 some(//foo:main union //bar:baz, 2)some(//foo:main union //bar:baz, 3) 会同时返回 //foo:main//bar:baz

如果实参是单例,则 some 会计算恒等函数:some(//foo:main) 等效于 //foo:main

如果指定的实参集为空(如表达式 some(//foo:main intersect //bar:baz) 中所示),则会出错。

路径运算符:somepath、allpaths

expr ::= somepath(expr, expr)
       | allpaths(expr, expr)

somepath(S, E)allpaths(S, E) 运算符用于计算两组目标之间的路径。这两个查询都接受两个实参:一组起点 S 和一组终点 Esomepath 返回从 S 中的目标到 E 中的目标之间的某个任意路径上的节点图;allpaths 返回从 S 中的任意目标到 E 中的任意目标之间的所有路径上的节点图。

生成的图表会根据依赖关系进行排序。 如需了解详情,请参阅有关图表顺序的部分。

Somepath
somepath(S1 + S2, E),一种可能的结果。
Somepath
somepath(S1 + S2, E),另一种可能的结果。
Allpaths
allpaths(S1 + S2, E)

目标种类过滤:种类

expr ::= kind(word, expr)

kind(pattern, input) 运算符会对一组目标应用过滤条件,并舍弃不属于预期类型的目标。pattern 参数用于指定要匹配的目标类型。

例如,下表显示了 BUILD 文件(针对软件包 p)定义的四个目标的种类:

代码 目标 种类
        genrule(
            name = "a",
            srcs = ["a.in"],
            outs = ["a.out"],
            cmd = "...",
        )
      
//p:a genrule 规则
//p:a.in 源文件
//p:a.out 生成的文件
//p:BUILD 源文件

因此,kind("cc_.* rule", foo/...) 会评估为 foo 下的所有 cc_librarycc_binary 等规则目标的集合,而 kind("source file", deps(//foo)) 会评估为 //foo 目标的依赖项的传递闭包中的所有源文件的集合。

通常需要引用 pattern 实参,因为如果不引用,许多正则表达式(例如 source file.*_test)会被解析器视为非字词。

如果匹配 package group,以 :all 结尾的目标可能不会产生任何结果。请改用 :all-targets

目标名称过滤:过滤

expr ::= filter(word, expr)

filter(pattern, input) 运算符会对一组目标应用过滤条件,并舍弃标签(以绝对形式)与相应模式不匹配的目标;它会评估为输入的一部分。

第一个实参 pattern 是一个包含目标名称的正则表达式的字词。filter 表达式的计算结果为包含所有目标 x 的集合,其中 x 是集合 input 的成员,并且 x 的标签(采用绝对形式,例如 //foo:bar)包含与正则表达式 pattern 的(非锚定)匹配项。由于所有目标名称都以 // 开头,因此它可以替代 ^ 正则表达式锚点。

此运算符通常提供比 intersect 运算符更快、更强大的替代方案。例如,为了查看 //foo:foo 目标的所有 bar 依赖项,可以评估

deps(//foo) intersect //bar/...

不过,此语句需要解析 bar 树中的所有 BUILD 文件,这会很慢,并且容易在不相关的 BUILD 文件中出错。另一种方法是:

filter(//bar, deps(//foo))

它会先计算 //foo 依赖项的集合,然后仅过滤与所提供模式匹配的目标,换句话说,就是名称包含 //bar 作为子字符串的目标。

filter(pattern, expr) 运算符的另一个常见用途是按名称或扩展名过滤特定文件。例如,

filter("\.cc$", deps(//foo))

将提供用于构建 //foo 的所有 .cc 文件的列表。

规则属性过滤:attr

expr ::= attr(word, word, expr)

attr(name, pattern, input) 运算符会对一组目标应用过滤条件,并舍弃不属于规则的目标、未定义属性 name 的规则目标或属性值与提供的正则表达式 pattern 不匹配的规则目标;它会评估为输入的一部分。

第一个实参 name 是规则属性的名称,该属性应与所提供的正则表达式模式相匹配。第二个实参 pattern 是一个针对属性值的正则表达式。attr 表达式的计算结果为包含所有目标 x 的集合,其中 x 是集合 input 的成员,是具有已定义属性 name 的规则,并且属性值包含与正则表达式 pattern 的(非锚定)匹配项。如果 name 是一个可选属性,并且规则未明确指定该属性,则将使用默认属性值进行比较。例如,

attr(linkshared, 0, deps(//foo))

将选择所有允许具有 linkshared 属性(例如 cc_binary 规则)且已明确将其设置为 0 或根本未设置但默认值为 0(例如 cc_binary 规则)的 //foo 依赖项。

列表类型属性(例如 srcsdata 等)会转换为 [value<sub>1</sub>, ..., value<sub>n</sub>] 形式的字符串,以 [ 方括号开头,以 ] 方括号结尾,并使用“,”(英文逗号、空格)分隔多个值。标签会使用标签的绝对形式转换为字符串。例如,属性 deps=[":foo", "//otherpkg:bar", "wiz"] 将转换为字符串 [//thispkg:foo, //otherpkg:bar, //thispkg:wiz]。括号始终存在,因此空列表将使用字符串值 [] 进行匹配。例如,

attr("srcs", "\[\]", deps(//foo))

将选择所有 //foo 依赖项中具有空 srcs 属性的规则,而

attr("data", ".{3,}", deps(//foo))

将选择 //foo 依赖项中在 data 属性中指定了至少一个值的所有规则(由于 //:,每个标签的长度至少为 3 个字符)。

如需选择列表类型属性中具有特定 value 的所有 //foo 依赖项规则,请使用

attr("tags", "[\[ ]value[,\]]", deps(//foo))

之所以会这样,是因为 value 前面的字符是 [ 或空格,而 value 后面的字符是逗号或 ]

规则可见性过滤:可见

expr ::= visible(expr, expr)

visible(predicate, input) 运算符会对一组目标应用过滤条件,并舍弃不具备所需可见性的目标。

第一个实参 predicate 是一组目标,输出中的所有目标都必须对这些目标可见。visible 表达式的计算结果为包含所有目标 x 的集合,其中 x 是集合 input 的成员,并且对于 predicate 中的所有目标 yxy 可见。例如:

visible(//foo, //bar:*)

将选择软件包 //bar 中可以依赖的 //foo 所有目标,而不会违反可见性限制。

标签类型规则属性的评估:标签

expr ::= labels(word, expr)

labels(attr_name, inputs) 运算符会返回集合 inputs 中某个规则的属性 attr_name(类型为“标签”或“标签列表”)中指定的目标集。

例如,labels(srcs, //foo) 会返回 //foo 规则的 srcs 属性中显示的目标的集合。如果 inputs 集中存在多个具有 srcs 属性的规则,则返回这些规则的 srcs 的并集。

展开并过滤 test_suites:测试

expr ::= tests(expr)

tests(x) 运算符会返回集合 x 中的所有测试规则,将所有 test_suite 规则展开为它们所指的单个测试的集合,并按 tagsize 应用过滤。

默认情况下,查询评估会忽略所有 test_suite 规则中的任何非测试目标。您可以使用 --strict_test_suite 选项将此设置更改为错误。

例如,查询 kind(test, foo:*) 会列出 foo 软件包中的所有 *_testtest_suite 规则。所有结果(按定义)都是 foo 软件包的成员。相比之下,查询 tests(foo:*) 将返回 bazel test foo:* 将执行的所有单个测试:这可能包括属于其他软件包的测试,这些测试通过 test_suite 规则直接或间接引用。

软件包定义文件:buildfile

expr ::= buildfiles(expr)

buildfiles(x) 运算符返回一组文件,这些文件定义了集合 x 中每个目标的软件包;换句话说,对于每个软件包,返回其 BUILD 文件,以及通过 load 引用的任何 .bzl 文件。请注意,此方法还会返回包含这些 load 文件的软件包的 BUILD 文件。

此运算符通常用于确定构建指定目标所需的文件或软件包,通常与下文中的 --output package 选项结合使用。例如,

bazel query 'buildfiles(deps(//foo))' --output package

返回 //foo 传递依赖的所有软件包的集合。

软件包定义文件:rbuildfiles

expr ::= rbuildfiles(word, ...)

rbuildfiles 运算符接受以逗号分隔的路径片段列表,并返回以传递方式依赖于这些路径片段的 BUILD 文件集。例如,如果 //foo 是一个软件包,则 rbuildfiles(foo/BUILD) 将返回 //foo:BUILD 目标。如果 foo/BUILD 文件包含 load('//bar:file.bzl'...,则 rbuildfiles(bar/file.bzl) 将返回 //foo:BUILD 目标,以及加载 //bar:file.bzl 的任何其他 BUILD 文件的目标

rbuildfiles 运算符的范围由 --universe_scope 标志指定的 universe 确定。与 BUILD 文件和 .bzl 文件不直接对应的文件不会影响结果。例如,即使在 BUILD 文件中明确提及,系统也会忽略源文件(如 foo.cc)。不过,系统会遵循符号链接,因此如果 foo/BUILD 是指向 bar/BUILD 的符号链接,则 rbuildfiles(bar/BUILD) 会在其结果中包含 //foo:BUILD

rbuildfiles 运算符在道德上几乎是 buildfiles 运算符的逆运算。不过,这种道德反转在一个方向上更明显:rbuildfiles 的输出与 buildfiles 的输入非常相似;前者仅包含软件包中的 BUILD 文件目标,而后者可能包含此类目标。反之,对应关系则较弱。buildfiles 运算符的输出是与所有软件包和 .给定输入所需的 bzl 文件。不过,rbuildfiles 运算符的输入不是这些目标,而是与这些目标对应的路径片段。

软件包定义文件:loadfiles

expr ::= loadfiles(expr)

loadfiles(x) 运算符会返回加载集合 x 中每个目标的软件包所需的 Starlark 文件集。换句话说,对于每个软件包,它都会返回其 BUILD 文件中引用的 .bzl 文件。

输出格式

bazel query 生成图表。 您可以通过 --output 命令行选项指定 bazel query 显示此图表的内容、格式和顺序。

使用 Sky Query 运行时,只允许使用与无序输出兼容的输出格式。具体来说,禁止使用 graphminrankmaxrank 输出格式。

某些输出格式接受其他选项。每个输出选项的名称都以其适用的输出格式为前缀,因此 --graph:factored 仅在使用 --output=graph 时适用;如果使用 graph 以外的输出格式,则无效。同样,--xml:line_numbers 仅在 --output=xml 正在使用时适用。

关于结果的排序

虽然查询表达式始终遵循“图顺序守恒定律”,但呈现结果的方式可以是按依赖关系排序,也可以是无序。这不会影响结果集中的目标,也不会影响查询的计算方式。它只会影响结果输出到 stdout 的方式。此外,在依赖顺序中等效的节点可能会按字母顺序排序,也可能不会按字母顺序排序。您可以使用 --order_output 标志来控制此行为。 (--[no]order_results 标志具有 --order_output 标志的部分功能,但已弃用。)

此标志的默认值为 auto,该值会按字典顺序输出结果。不过,如果使用 somepath(a,b),结果将按 deps 顺序输出。

当此标志为 no--outputbuildlabellabel_kindlocationpackageprotoxml 之一时,输出将以任意顺序打印。这通常是最快的选项。不过,当 --outputgraphminrankmaxrank 时,不支持此功能:对于这些格式,Bazel 始终会按依赖顺序或排名打印结果。

如果此标志为 deps,Bazel 会按某种拓扑顺序(即先输出依赖项)输出结果。不过,如果节点未按依赖顺序排序(因为两者之间没有路径),则可以按任意顺序输出。

如果此标志为 full,Bazel 会以完全确定的(总)顺序打印节点。 首先,所有节点按字母顺序排序。然后,列表中的每个节点都用作后序深度优先搜索的起点,其中,系统会按照后继节点的字母顺序遍历指向未访问节点的出边。最后,节点按访问顺序的相反顺序打印。

按此顺序打印节点可能会比较慢,因此仅在确定性很重要时才应使用此选项。

打印目标的源表单(如在 build 中显示)

--output build

使用此选项时,每个目标的表示形式都好像是用 BUILD 语言手写的。所有变量和函数调用(例如 glob、宏)都会展开,这有助于查看 Starlark 宏的效果。此外,每条有效规则都会报告 generator_name 和/或 generator_function 值,其中包含评估后生成有效规则的宏的名称。

虽然输出使用与 BUILD 文件相同的语法,但不能保证生成有效的 BUILD 文件。

--output label

使用此选项,系统会按拓扑顺序(除非指定了 --noorder_results,请参阅有关结果排序的注释)打印结果图表中每个目标的名称(或标签),每行一个标签。 (拓扑排序是指图节点出现在其所有后继节点之前。)当然,一个图可以有许多可能的拓扑排序(反向后序只是其中一种);具体选择哪种排序方式并未指定。

在输出 somepath 查询的结果时,节点打印顺序就是路径顺序。

注意事项:在某些特殊情况下,可能会有两个具有相同标签的不同目标;例如,sh_binary 规则及其唯一的(隐式)srcs 文件可能都称为 foo.sh。如果查询结果同时包含这两个目标,则输出(采用 label 格式)看起来会包含重复项。使用 label_kind(见下文)格式时,区别会变得很明显:这两个目标的名称相同,但一个的种类为 sh_binary rule,另一个的种类为 source file

--output label_kind

label 类似,此输出格式会按拓扑顺序打印结果图表中每个目标的标签,但此外还会将目标的种类放在标签前面。

--output minrank --output maxrank

label 类似,minrankmaxrank 输出格式会在生成的图表中打印每个目标的标签,但这些标签不是按拓扑顺序显示,而是按排名顺序显示,并且前面会加上排名编号。这些不受结果排序 --[no]order_results 标志的影响(请参阅有关结果排序的注释)。

此格式有两种变体:minrank 按从根节点到每个节点的最短路径的长度对节点进行排名。“根”节点(没有入边的节点)的秩为 0,其后继节点的秩为 1,依此类推。(与往常一样,边从目标指向其前提条件:它所依赖的目标。)

maxrank 根据从根节点到每个节点的最长路径的长度对每个节点进行排名。同样,“根”的排名为 0,所有其他节点的排名都比其所有前任的最大排名高 1。

一个周期中的所有节点都被视为具有相同的排名。(大多数图都是无环图,但出现环只是因为 BUILD 文件包含错误的环。)

这些输出格式有助于发现图的深度。如果用于 deps(x)rdeps(x)allpaths 查询的结果,则排名编号等于从 x 到相应排名中某个节点的最短(使用 minrank)或最长(使用 maxrank)路径的长度。maxrank 可用于确定构建目标所需的最长构建步骤序列。

例如,当分别指定 --output minrank--output maxrank 时,左侧的图会生成右侧的输出。

排名靠后
      minrank

      0 //c:c
      1 //b:b
      1 //a:a
      2 //b:b.cc
      2 //a:a.cc
      
      maxrank

      0 //c:c
      1 //b:b
      2 //a:a
      2 //b:b.cc
      3 //a:a.cc
      
--output location

label_kind 类似,此选项会针对结果中的每个目标打印出目标的种类和标签,但会在前面加上一个字符串,用于描述相应目标的位置(以文件名和行号的形式)。格式类似于 grep 的输出。因此,能够解析后者的工具(例如 Emacs 或 vi)也可以使用查询输出逐步浏览一系列匹配项,从而使 Bazel 查询工具能够用作可识别依赖关系图的“BUILD 文件 grep”。

位置信息因目标类型而异(请参阅 kind 运算符)。对于规则,系统会打印规则声明在 BUILD 文件中的位置。对于源文件,系统会打印实际文件的第 1 行所在的位置。对于生成的文件,系统会输出生成该文件的规则的位置。(查询工具没有足够的信息来查找生成文件的实际位置,而且在任何情况下,如果尚未执行 build,该文件可能不存在。)

--output package

此选项会输出结果集中某个目标所属的所有软件包的名称。名称按字典顺序输出;重复项会被排除。从形式上讲,这是从标签(软件包、目标)集到软件包的投影

外部代码库中的软件包格式为 @repo//foo/bar,而主代码库中的软件包格式为 foo/bar

deps(...) 查询结合使用时,此输出选项可用于查找必须检出的软件包集,以便构建给定的目标集。

显示结果图表

--output graph

此选项会导致查询结果以有向图的形式打印,采用的是常用的 AT&T GraphViz 格式。结果通常会保存到文件(例如 .png.svg)中。(如果工作站上未安装 dot 程序,您可以使用 sudo apt-get install graphviz 命令进行安装。)如需查看调用示例,请参阅下方的示例部分。

此输出格式对于 allpathsdepsrdeps 查询特别有用,因为这些查询的结果包含一组路径,如果以线性形式(例如使用 --output label)呈现,则无法轻松直观地显示。

默认情况下,图表以分解形式呈现。也就是说,拓扑等效的节点会合并为一个具有多个标签的节点。这使得图表更加紧凑且易于阅读,因为典型的结果图表包含高度重复的模式。例如,一个 java_library 规则可能依赖于数百个由同一 genrule 生成的 Java 源文件;在分解后的图中,所有这些文件都由一个节点表示。您可以使用 --nograph:factored 选项停用此行为。

--graph:node_limit n

此选项用于指定输出中图节点的标签字符串的最大长度。较长的标签会被截断;-1 会停用截断功能。由于图通常以分解形式打印,因此节点标签可能非常长。GraphViz 无法处理超过 1024 个字符的标签,而这正是此选项的默认值。除非使用 --output=graph,否则此选项无效。

--[no]graph:factored

默认情况下,图表以分解形式显示,如上文所述。 指定 --nograph:factored 时,系统会打印未进行因式分解的图表。这使得使用 GraphViz 进行可视化变得不切实际,但更简单的格式可能有助于其他工具(例如 grep)进行处理。除非使用 --output=graph,否则此选项无效。

XML

--output xml

此选项会导致以 XML 形式打印生成的目标。输出以 XML 标头开头,例如

  <?xml version="1.0" encoding="UTF-8"?>
  <query version="2">

然后,继续处理结果图中的每个目标的 XML 元素(按拓扑顺序,除非请求了无序结果),最后以终止

</query>

针对 file 类型的目标发出简单条目:

  <source-file name='//foo:foo_main.cc' .../>
  <generated-file name='//foo:libfoo.so' .../>

但对于规则,XML 是结构化的,包含规则的所有属性的定义,包括那些在规则的 BUILD 文件中未明确指定值的属性。

此外,结果还包括 rule-inputrule-output 元素,以便在无需知道 srcs 属性的元素是正向依赖项(前提条件)且 outs 属性的内容是反向依赖项(消费者)的情况下,重建依赖关系图的拓扑。

如果指定了 --noimplicit_deps,则会抑制 隐式依赖项rule-input 元素。

  <rule class='cc_binary rule' name='//foo:foo' ...>
    <list name='srcs'>
      <label value='//foo:foo_main.cc'/>
      <label value='//foo:bar.cc'/>
      ...
    </list>
    <list name='deps'>
      <label value='//common:common'/>
      <label value='//collections:collections'/>
      ...
    </list>
    <list name='data'>
      ...
    </list>
    <int name='linkstatic' value='0'/>
    <int name='linkshared' value='0'/>
    <list name='licenses'/>
    <list name='distribs'>
      <distribution value="INTERNAL" />
    </list>
    <rule-input name="//common:common" />
    <rule-input name="//collections:collections" />
    <rule-input name="//foo:foo_main.cc" />
    <rule-input name="//foo:bar.cc" />
    ...
  </rule>

每个目标的 XML 元素都包含一个 name 属性(其值为目标的标签)和一个 location 属性(其值为 --output location 打印的目标位置)。

--[no]xml:line_numbers

默认情况下,XML 输出中显示的位置包含行号。如果指定了 --noxml:line_numbers,则不会打印行号。

--[no]xml:default_values

默认情况下,XML 输出不包含值是相应属性的默认值的规则属性(例如,如果未在 BUILD 文件中指定该属性,或者明确提供了默认值)。此选项会导致此类属性值包含在 XML 输出中。

正则表达式

查询语言中的正则表达式使用 Java 正则表达式库,因此您可以针对 java.util.regex.Pattern 使用完整语法。

使用外部代码库进行查询

如果 build 依赖于外部代码库中的规则(在 WORKSPACE 文件中定义),则查询结果将包含这些依赖项。例如,如果 //foo:bar 依赖于 //external:some-lib//external:some-lib 绑定到 @other-repo//baz:lib,则 bazel query 'deps(//foo:bar)' 会将 @other-repo//baz:lib//external:some-lib 都列为依赖项。

外部代码库本身不是 build 的依赖项。也就是说,在上面的示例中,//external:other-repo 不是依赖项。不过,可以作为 //external 软件包的成员进行查询,例如:

  # Querying over all members of //external returns the repository.
  bazel query 'kind(http_archive, //external:*)'
  //external:other-repo

  # ...but the repository is not a dependency.
  bazel query 'kind(http_archive, deps(//foo:bar))'
  INFO: Empty results