本页是您在使用 bazel query
分析 build 依赖项时所使用的 Bazel 查询语言的参考手册。还介绍了 bazel query
支持的输出格式。
如需了解实际用例,请参阅 Bazel 查询操作方法。
其他查询参考
除了在加载后阶段目标图上运行的 query
之外,Bazel 还包括操作图查询和可配置查询。
操作图表查询
操作图查询 (aquery
) 对分析后配置目标图执行操作,并公开有关操作和工件及其关系的信息。当您对通过配置的目标图生成的操作/工件的属性感兴趣时,aquery
非常有用。例如,实际命令运行及其输入、输出和助记符。
如需了解详情,请参阅查询参考文档。
可配置查询
传统的 Bazel 查询是在加载后阶段目标图上运行的,因此不涉及配置及其相关概念。值得注意的是,它无法正确解析 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、数字 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,也必须进行配置;如需了解详情,请参阅《用户指南》的配置部分。
对于所有配置,以 Bazel 查询语言评估表达式的结果为 true,这意味着它可能属于保守的过度近似,并不精确。如果您使用查询工具计算构建期间需要的所有源文件集合,则它报告的内容可能会超出实际需要的文件集,例如,即使您不打算在 build 中使用该功能,查询工具也会包含支持消息转换所需的所有文件。
关于图顺序的保留
运算会保留从其子表达式继承的所有排序约束。您可以将这种方法视为“部分顺序守护定律”。请看以下示例:如果您发出查询来确定特定目标依赖项的传递闭包,则生成的集将根据依赖关系图进行排序。如果您对设置为仅包含 file
种类的目标进行过滤,则生成的子集中的每对目标之间仍存在相同的传递部分排序关系,即使在原始图中实际上这些对都没有直接连接也是如此。(构建依赖关系图中没有文件-文件边缘)。
不过,虽然所有运算符都会保留顺序,但某些运算(如集合运算)本身不会引入任何排序约束。请思考以下表达式:
deps(x) union y
最终结果集的顺序会保证保留其子表达式的所有排序约束,也就是说,x
的所有传递依赖项会按照彼此正确排序。不过,该查询不保证 y
中目标的顺序,也不能保证 deps(x)
中目标相对于 y
中的目标的顺序(y
中的目标也恰好位于 deps(x)
中除外)。
引入排序约束的运算符包括:allpaths
、deps
、rdeps
、somepath
和目标模式通配符 package:*
、dir/...
等。
星空查询
星空查询是一种在指定宇宙范围内运行的查询模式。
仅在 SkyQuery 中提供的特殊函数
Sky Query 模式具有其他查询函数 allrdeps
和 rbuildfiles
。这些函数在整个宇宙范围内运行(这就是它们不适用于普通查询的原因)。
指定 Universe 范围
通过传递以下两个标志(--universe_scope
或 --infer_universe_scope
)和 --order_output=no
可激活 Sky Query 模式。--universe_scope=<target_pattern1>,...,<target_patternN>
指示查询预加载由目标模式指定的目标模式的传递闭包(可以是加法和减法)。然后,系统会在此“范围”中评估所有查询。特别是,allrdeps
和 rbuildfiles
运算符仅返回此范围内的结果。--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)))"
这是一个有意义的查询调用,它会尝试在某些目录下,以传递方式依赖于定义使用特定 .bzl
文件的目标下的目标的 tests
扩展中计算测试目标。此处,--infer_universe_scope
是一种便利,尤其是在选择 --universe_scope
会要求您自行解析查询表达式的情况下。
因此,对于使用全局范围的运算符(如 allrdeps
和 rbuildfiles
)的查询表达式,请仅在其行为符合您的预期时才使用 --infer_universe_scope
。
与默认查询相比,Sky Query 有一些优缺点。主要缺点是,它无法按图表顺序对输出进行排序,因此不允许使用某些输出格式。其优点在于,它提供了默认查询中未提供的两种运算符(allrdeps
和 rbuildfiles
)。同样,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/...
等目标模式中)通常对构建更有用。
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
与字词类似,但附加了额外的限制,即它是 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 ...)
运算符计算零个或零个以上目标模式的并集,以空格(无英文逗号)分隔。
与 Bourne shell 的 $(...)
功能结合使用时,set()
提供了一种方法,可将一个查询的结果保存在常规文本文件中,使用其他程序(例如标准 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 ... ')'
查询语言定义了多个函数。函数的名称决定了它所需的参数的数量和类型。您可以使用以下函数:
allpaths
attr
buildfiles
rbuildfiles
deps
filter
kind
labels
loadfiles
rdeps
allrdeps
same_pkg_direct_rdeps
siblings
some
somepath
tests
visible
依赖项的传递闭包: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)
运算符的求值结果为宇宙集 u 的传递闭包内的参数集 x 的反向依赖关系。
生成的图根据依赖关系进行排序。如需了解详情,请参阅有关图顺序的部分。
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 和一组终点 E。somepath
会返回从 S 中的目标到 E 中目标的某任意路径上的节点图;allpaths
会返回从 S 中的任意目标到 E 中任意目标的所有路径上的节点图。
生成的图根据依赖关系进行排序。 如需了解详情,请参阅有关图顺序的部分。
somepath(S1 + S2, E) ,一种可能的结果。 |
somepath(S1 + S2, E) ,这是另一个可能的结果。 |
allpaths(S1 + S2, E) |
目标种类过滤:Kind
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_library
、cc_binary
等规则目标的集合,而 kind("source file", deps(//foo))
求值为 //foo
目标依赖项的传递闭包中所有源文件的集合。
通常需要用引号 pattern 参数,因为如果没有引号,解析器就不会将许多正则表达式(例如 source
file
和 .*_test
)视为字词。
与 package group
匹配时,以 :all
结尾的目标可能不会产生任何结果。请改用 :all-targets
。
目标名称过滤:filter
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
文件。
规则属性过滤:属性
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))
会选择所有获准具有链接共享属性(例如 cc_binary
规则)的 //foo
依赖项,并将其明确设置为 0 或者根本不设置,但默认值为 0(例如,对于 cc_binary
规则)。
列表类型属性(例如 srcs
、data
等)会转换为 [value<sub>1</sub>, ..., value<sub>n</sub>]
形式的字符串,以 [
括号开头,以 ]
括号结尾,并使用“,
”(英文逗号、空格)来分隔多个值。使用标签的绝对形式将标签转换为字符串。例如,属性 deps=[":foo",
"//otherpkg:bar", "wiz"]
将被转换为字符串 [//thispkg:foo, //otherpkg:bar, //thispkg:wiz]
。
方括号始终存在,因此空列表将使用字符串值 []
进行匹配。例如,
attr("srcs", "\[\]", deps(//foo))
将在srcs
属性为空的//foo
依赖项中选择所有规则
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 中的所有目标,x 对 y 可见。y例如:
visible(//foo, //bar:*)
系统将选择软件包 //bar
中 //foo
可以依赖的所有目标,而不违反可见性限制。
评估标签类型的规则属性:labels
expr ::= labels(word, expr)
labels(attr_name, inputs)
运算符会返回在 inputs 集内某个规则中,在类型为“label”或“label of label”的属性 attr_name 中指定的一组目标。
例如,labels(srcs, //foo)
会返回 //foo
规则的 srcs
属性中显示的一组目标。如果 inputs 集中有多个规则具有 srcs
属性,则返回它们 srcs
的并集。
展开和过滤 test_suites: testing
expr ::= tests(expr)
tests(x)
运算符会返回 x 集中所有测试规则的集合,将任何 test_suite
规则扩展为它们引用的单独测试集合,并按 tag
和 size
应用过滤。
默认情况下,查询评估会忽略所有 test_suite
规则中的任何非测试目标。可使用 --strict_test_suite
选项将其更改为错误。
例如,查询 kind(test, foo:*)
会列出 foo
软件包中的所有 *_test
和 test_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
文件的目标
--universe_scope
标志指定的宇宙。不直接对应于 BUILD
文件和 .bzl
文件的文件不会影响结果。例如,源文件(如 foo.cc
)将被忽略,即使 BUILD
文件中明确提及它们也是如此。不过,系统会考虑符号链接,因此,如果 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 运行时,系统仅允许使用与无序输出兼容的输出格式。具体而言,禁止使用 graph
、minrank
和 maxrank
输出格式。
某些输出格式接受附加选项。每个输出选项的名称都以其适用的输出格式作为前缀,因此 --graph:factored
仅在使用 --output=graph
时才适用;如果使用 graph
以外的输出格式,则无效。同样,--xml:line_numbers
仅在使用 --output=xml
时才适用。
关于结果的排序
虽然查询表达式始终遵循“图顺序守则”,但结果的呈现方式既可按依赖项有序,也可无序。这不会影响结果集中的目标或查询的计算方式。它只会影响将结果输出到 stdout 的方式。此外,依赖项顺序等效的节点不一定按字母顺序排序。--order_output
标志可用于控制此行为。
(--[no]order_results
标记具有 --order_output
标记的一部分功能,现已弃用。)
此标志的默认值为 auto
,会按字典顺序输出结果。不过,如果使用 somepath(a,b)
,系统会改为按 deps
顺序输出结果。
当此标志为 no
并且 --output
为 build
、label
、label_kind
、location
、package
、proto
或 xml
之一时,输出将按任意顺序输出。这通常是最快的选项。不过,当 --output
为 graph
、minrank
或 maxrank
之一时,就不支持它了:使用这些格式时,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 proto
将查询输出作为 QueryResult
协议缓冲区输出。
以带长度的协议缓冲区格式输出目标
--output streamed_proto
输出 Target
协议缓冲区的以长度分隔流。当目标太多,无法容纳在单个 QueryResult
中或 (ii) 开始处理而 Bazel 仍在输出时,这 (i) 可以解决协议缓冲区的大小限制。
以文本 proto 格式输出目标
--output textproto
与 --output proto
类似,输出 QueryResult
协议缓冲区,但以文本格式输出。
以 ndjson 格式输出目标
--output streamed_jsonproto
与 --output streamed_proto
类似,会以 ndjson 格式输出 Target
协议缓冲区流。
按排名顺序输出每个目标的标签
--output minrank --output maxrank
与 label
一样,minrank
和 maxrank
输出格式会输出结果图中每个目标的标签,但它们不是按拓扑顺序显示,而是按排名顺序显示,后跟其秩编号。而不受结果排序 --[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 查询工具可用作依赖项图感知型“grep for BUILD 文件”。
位置信息因目标种类而异(请参阅 Kind 运算符)。对于规则,系统会显示规则的声明在 BUILD
文件中的位置。对于源文件,将输出实际文件的第 1 行位置。对于生成的文件,系统会输出生成该文件的规则的位置。(查询工具没有足够的信息来查找生成文件的实际位置;在任何情况下,如果尚未执行构建,则该文件可能不存在。)
输出这组软件包
--output package
此选项会输出结果集中某个目标所属的所有软件包的名称。这些名称按字典顺序输出;不包括重复项。正式地说,这是从标签集(软件包、目标)到软件包的投影。
外部代码库中的软件包格式为 @repo//foo/bar
,而主代码库中的软件包的格式为 foo/bar
。
与 deps(...)
查询结合使用时,此输出选项可用于查找为构建一组给定目标而必须检出的一组软件包。
显示结果图表
--output graph
此选项会使查询结果以常用的 AT&T GraphViz 格式输出为有向图。通常,结果会保存到文件中,例如 .png
或 .svg
。(如果您的工作站上未安装 dot
程序,您可以使用 sudo apt-get install graphviz
命令进行安装。)如需查看示例调用,请参阅以下示例部分。
此输出格式对 allpaths
、deps
或 rdeps
查询特别有用,其中结果包含一组路径,这些路径在以线性形式(例如使用 --output label
)呈现时无法轻松直观呈现。
默认情况下,该图以因式分解形式呈现。也就是说,将拓扑等效的节点合并为具有多个标签的单个节点。这会使图表更加紧凑和易读,因为典型的结果图包含高度重复的模式。例如,java_library
规则可能依赖于数百个 Java 源文件,这些文件均由同一 genrule
生成;在因式分解图中,所有这些文件均由一个节点表示。可使用 --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-input
和 rule-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
的完整语法。
使用外部代码库进行查询
如果构建依赖于外部代码库(在 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