Bazel 查询参考

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

如需了解实际用例,请参阅 Bazel 查询操作方法

其他查询参考文档

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

操作图表查询

操作图查询 (aquery) 针对分析后配置目标图运行,并公开有关操作工件及其关系的信息。如果您对由配置的目标图表生成的操作/工件的属性感兴趣,aquery 非常有用。例如,运行实际命令及其输入、输出和助记符。

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

可配置的查询

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

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

示例

人们如何使用 bazel query?典型示例如下:

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

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

所有 foo 测试都依赖于哪些 C++ 库且 foo_bin 目标不依赖哪些 C++ 库?

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 以及特殊字符 */@.-_:$~[](星号、正斜杠、at、句点、连字符、下划线、英文冒号、美元符号、波形符、左方括号、右方括号)。但是,不带英文引号的字词不得以连字符 - 或星号 * 开头,即使相对 [target name][(/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 文件中明确定义的构建依赖项之外,Bazel 还会向规则添加其他隐式依赖项。例如,每条 Java 规则都隐式依赖于 JavaBuilder。隐式依赖项是使用以 $ 开头的属性建立的,不能在 BUILD 文件中被替换。

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

声音

Bazel 查询语言表达式基于构建依赖项图表运行,该图表由所有 BUILD 文件中的所有规则声明隐式定义。请务必了解,此图有点抽象,并不构成如何执行构建的所有步骤的完整说明。要执行构建,您还需要有配置;如需了解详情,请参阅《用户指南》的配置部分。

对于所有配置,使用 Bazel 查询语言评估表达式的结果均为 true,这意味着这可能是保守的过度近似,而非精确。如果您使用查询工具计算构建期间需要的所有源文件集,则报告的数据可能会超过实际需要的数量,因为,举例来说,查询工具将包含支持消息转换所需的所有文件,即使您不打算在构建中使用该功能也是如此。

保存图表顺序

操作会保留从其子表达式继承的任何排序限制。您可以将这一点看作“仅保留部分顺序的定律”。举例说明:如果您发出查询以确定特定目标的依赖项的传递关闭,则结果集将根据依赖项图进行排序。如果您过滤该集合,使其仅包含 file 种类的目标,那么在生成的子集中,每一对目标都具有相同的传递部分排序关系,即使这些实体对中实际上没有直接关联也是如此。(构建依赖项图表中没有文件-文件边缘)。

不过,虽然所有运算符都保留顺序,但有些操作(如设置操作引入)它们自己的任何排序限制。请思考以下表达式:

deps(x) union y

最终结果集保证保留其子表达式的所有排序约束,也就是说,x 的所有传递依赖项都将正确排序。但是,此查询无法保证 y 中目标的顺序以及 y 中目标的顺序(除了 y 中的目标恰好位于 deps(x) 中的顺序之外)。

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

Sky 查询

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

特殊功能仅适用于 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 查询有一些优缺点。主要缺点是它无法按图表顺序对其输出进行排序,因此某些输出格式被禁止。其优点是,它提供两个默认查询中未提供的运算符(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/... 等目标模式中隐式)通常对于构建更有用。

bazel query 目标模式与 bazel 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 = ... 表达式中,则会出现错误。换句话说,顶级查询表达式不能包含自由变量。

在上述语法生产中,nameword 类似,但需要额外限制其是 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)

从外部来源读取目标:set

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 ... ')'

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

依赖项的传递关闭:依赖项

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

deps(x) 运算符求值为因其参数集 x 的依赖项的传递性闭合所形成的图表。例如,deps(//foo) 的值是以根节点 foo 为根的依赖关系图,包括其所有依赖项。deps(foo/...) 的值是依赖关系图,其根是 foo 目录下每个软件包中的所有规则。在此上下文中,& dependencies 仅表示规则和文件目标,因此创建这些目标所需的 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(x) 运算符会从其参数集 x 中任意选择一个目标,并计算仅包含该目标的单例集。例如,表达式 some(//foo:main union //bar:baz) 求值为包含 //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(S1 + S2, E),一个可能的结果。
路径
somepath(S1 + S2, E),还有另一个可能的结果。
所有路径
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))

将选择允许具有链接共享特性(例如 cc_binary 规则)的所有 //foo 依赖项,并将其明确设置为 0 或根本不设置它,但默认值为 0(例如对于 cc_binary 规则)。

列表类型属性(如 srcsdata 等)将转换为 [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 个字符)。

规则可见性过滤:可见

expr ::= visible(expr, expr)

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

第一个参数 predicate 是一组值,输出中的所有目标都必须对用户可见。visible 表达式会针对包含所有目标 x 的集合求值,以便 x 是集合 input 的成员,并且对于 predicate x 中的所有 yyy 可见。例如:

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

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

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

expr ::= labels(word, expr)

labels(attr_name, inputs) 运算符会返回 inputs 类型的规则 attr_name 的属性“&label”或“标签列表”中指定的一组目标。

例如,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.bzlBUILD 文件的目标

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

rbuildfiles 运算符在道德上几乎与 buildfiles 运算符相反。不过,这个道理倒置更倾向于单向:rbuildfiles 的输出就像 buildfiles 的输入一样;前者仅包含软件包中的 BUILD 文件目标,而后者可能包含此类目标。另一方面,对应度较弱。buildfiles 运算符的输出是与所有软件包和 相对应的目标。给定输入所需的 bzl 文件。但是,rbuildfiles 运算符的输入不是这些目标,而是对应于这些目标的路径 Fragment。

软件包定义文件: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 会以完全确定(总计)的顺序输出节点。首先,所有节点均按字母顺序排序。然后,列表中的每个节点都会用作后序深度优先搜索的起点,在该位置中,未访问节点的传出边缘会按照字母顺序从后续节点开始遍历。最后,节点会按照访问顺序进行输出。

按此顺序输出节点的速度可能较慢,因此应仅在确定性重要时使用。

打印目标的源代码形式,就像版本一样

--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 按从根节点到节点的最长路径的长度对每个节点进行排名。同样,“roots”的排名为 0,所有其他节点的排名高于其前身的最大排名。

一个周期中的所有节点均视为具有相同排名。(大多数图表是无循环的,但出现循环只是因为 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 用于构建文件”。

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

--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 属性(其值为 target 的标签)和一个 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