借助 aquery
命令,您可以在 build 图中查询操作。它在分析后的已配置目标图上运行,并提供有关操作、工件及其关系的信息。
如果您对通过已配置的目标图表生成的操作/工件的属性感兴趣,aquery
非常有用。例如,实际运行的命令及其输入/输出/助记符。
该工具接受多个命令行选项。 值得注意的是,aquery 命令在常规 Bazel 构建之上运行,并继承了构建期间可用的一组选项。
它支持的函数集与传统 query
提供的函数集相同,但支持 siblings
、buildfiles
和 tests
。
aquery
输出示例(不含具体详细信息):
$ bazel aquery 'deps(//some:label)' action 'Writing file some_file_name' Mnemonic: ... Target: ... Configuration: ... ActionKey: ... Inputs: [...] Outputs: [...]
基本语法
以下是 aquery
语法的简单示例:
bazel aquery "aquery_function(function(//target))"
查询表达式(用引号引起来)由以下内容组成:
aquery_function(...)
:特定于aquery
的函数。如需了解更多详情,请参阅下文。function(...)
:与传统query
相同的标准函数。//target
是相关目标的标签。
# aquery examples: # Get the action graph generated while building //src/target_a $ bazel aquery '//src/target_a' # Get the action graph generated while building all dependencies of //src/target_a $ bazel aquery 'deps(//src/target_a)' # Get the action graph generated while building all dependencies of //src/target_a # whose inputs filenames match the regex ".*cpp". $ bazel aquery 'inputs(".*cpp", deps(//src/target_a))'
使用查询函数
aquery
函数有三个:
inputs
:按输入过滤操作。outputs
:按输出过滤操作mnemonic
:按助记符过滤操作
expr ::= inputs(word, expr)
inputs
运算符返回构建 expr
时生成的操作,其输入文件名与 word
提供的正则表达式匹配。
$ bazel aquery 'inputs(".*cpp", deps(//src/target_a))'
outputs
和 mnemonic
函数具有类似的语法。
您还可以通过组合函数来实现 AND 运算。例如:
$ bazel aquery 'mnemonic("Cpp.*", (inputs(".*cpp", inputs("foo.*", //src/target_a))))'
以上命令可查找构建 //src/target_a
涉及的所有操作,其助记符与 "Cpp.*"
匹配,输入与 ".*cpp"
和 "foo.*"
模式匹配。
生成的语法错误示例如下:
$ bazel aquery 'deps(inputs(".*cpp", //src/target_a))' ERROR: aquery filter functions (inputs, outputs, mnemonic) produce actions, and therefore can't be the input of other function types: deps deps(inputs(".*cpp", //src/target_a))
选项
构建选项
aquery
在常规 Bazel 构建之上运行,因此会继承构建期间可用的一组选项。
Aquery 选项
--output=(text|summary|proto|jsonproto|textproto), default=text
默认输出格式 (text
) 是人类可读的,请使用 proto
、textproto
或 jsonproto
作为机器可读的格式。proto 消息为 analysis.ActionGraphContainer
。
--include_commandline, default=true
在输出中包含操作命令行的内容(可能较大)。
--include_artifacts, default=true
在输出中包含操作输入和输出的名称(可能较大)。
--include_aspects, default=true
是否在输出中包含 Aspect 生成的操作。
--include_param_files, default=false
包含命令中使用的参数文件的内容(可能很大)。
--include_file_write_contents, default=false
包含 actions.write()
操作的文件内容和 SourceSymlinkManifest
操作的清单文件内容。文件内容通过 --output=
xxxproto
返回 file_contents
字段中。使用 --output=text
时,输出将包含 FileWriteContents: [<base64-encoded file contents>]
行
--skyframe_state, default=false
从 Skyframe 中转储操作图表,无需执行额外的分析。
其他工具和功能
查询 Skyframe 的状态
Skyframe 是 Bazel 的评估和增量模型。在 Bazel 服务器的每个实例上,Skyframe 会存储根据先前运行分析阶段而构建的依赖关系图。
在某些情况下,查询 Skyframe 上的操作图会很有用。以下是一个示例用例:
- 运行
bazel build //target_a
- 运行
bazel build //target_b
- 已生成文件“
foo.out
”。
作为 Bazel 用户,我想确定 foo.out
是通过构建 //target_a
还是 //target_b
生成的。
可以运行 bazel aquery 'outputs("foo.out", //target_a)'
和 bazel aquery 'outputs("foo.out", //target_b)'
来确定负责创建 foo.out
的操作,进而找出创建目标的操作。但是,之前构建的不同目标的数量可能大于 2,这使得运行多个 aquery
命令变得很麻烦。
作为替代方案,您可以使用 --skyframe_state
标志:
# List all actions on Skyframe's action graph $ bazel aquery --output=proto --skyframe_state # or # List all actions on Skyframe's action graph, whose output matches "foo.out" $ bazel aquery --output=proto --skyframe_state 'outputs("foo.out")'
在 --skyframe_state
模式下,aquery
会获取 Skyframe 在 Bazel 实例上保留的操作图的内容(可选)对其执行过滤并输出内容,而无需重新运行分析阶段。
特别注意事项
输出格式
--skyframe_state
目前仅适用于 --output=proto
和 --output=textproto
在查询表达式中不包含目标标签
目前,无论目标如何,--skyframe_state
都会查询 Skyframe 上存在的整个操作图。将查询中指定的目标标签与 --skyframe_state
一起视为语法错误:
# WRONG: Target Included $ bazel aquery --output=proto --skyframe_state **//target_a** ERROR: Error while parsing '//target_a)': Specifying build target(s) [//target_a] with --skyframe_state is currently not supported. # WRONG: Target Included $ bazel aquery --output=proto --skyframe_state 'inputs(".*.java", **//target_a**)' ERROR: Error while parsing '//target_a)': Specifying build target(s) [//target_a] with --skyframe_state is currently not supported. # CORRECT: Without Target $ bazel aquery --output=proto --skyframe_state $ bazel aquery --output=proto --skyframe_state 'inputs(".*.java")'
比较 aquery 输出
您可以使用 aquery_differ
工具比较两个不同的查询调用的输出。例如,当您对规则定义进行一些更改,并且想要验证正在运行的命令行是否未发生变化时。aquery_differ
是实现此目的的工具。
bazelbuild/bazel 代码库中提供了该工具。如需使用它,请将代码库克隆到本地机器。用法示例:
$ bazel run //tools/aquery_differ -- \ --before=/path/to/before.proto \ --after=/path/to/after.proto \ --input_type=proto \ --attrs=cmdline \ --attrs=inputs
上述命令会返回 before
和 after
查询输出之间的区别:哪些操作出现在其中一个查询中,而另一个未出现、哪些操作在每个查询输出中有不同的命令行/输入...。运行上述命令的结果为:
Aquery output 'after' change contains an action that generates the following outputs that aquery output 'before' change doesn't: ... /list of output files/ ... [cmdline] Difference in the action that generates the following output(s): /path/to/abc.out --- /path/to/before.proto +++ /path/to/after.proto @@ -1,3 +1,3 @@ ... /cmdline diff, in unified diff format/ ...
命令选项
--before, --after
:要比较的查询输出文件
--input_type=(proto|text_proto), default=proto
:输入文件的格式。支持 proto
和 textproto
查询输出。
--attrs=(cmdline|inputs), default=cmdline
:要比较的操作的属性。
宽高比
切面可以相互叠加。然后,这些切面生成的操作的查询输出将包含切面路径,这是应用于生成该操作的目标的切面序列。
切面示例:
t0 ^ | <- a1 t1 ^ | <- a2 t2
以 ti 作为规则 ri 的目标,这会将切面 ai 应用于其依赖项。
假设 a2 在应用于目标 t0 时生成了操作 X。对于操作 X,bazel aquery --include_aspects 'deps(//t2)'
的文本输出将如下所示:
action ... Mnemonic: ... Target: //my_pkg:t0 Configuration: ... AspectDescriptors: [//my_pkg:rule.bzl%**a2**(foo=...) -> //my_pkg:rule.bzl%**a1**(bar=...)] ...
这意味着操作 X
是由应用于 a1(t0)
的切面 a2
生成的,其中 a1(t0)
是应用于目标 t0
的切面 a1
的结果。
每个 AspectDescriptor
都采用以下格式:
AspectClass([param=value,...])
AspectClass
可以是 Aspect 类(针对原生切面)或 bzl_file%aspect_name
(针对 Starlark Aspects)的名称。AspectDescriptor
按依赖关系图的拓扑顺序排序。
与 JSON 配置文件关联
虽然查询会提供在 build 中运行的操作的相关信息(操作的原因、其输入/输出),而 JSON 配置文件会说明这些操作的执行时间和时长。可以通过一个共性(操作的主要输出)将这两组信息结合起来。
如需在 JSON 配置文件中添加操作的输出,请使用 --experimental_include_primary_output --noslim_profile
生成配置文件。精简配置文件与包含主要输出不兼容。默认情况下,查询会包含操作的主要输出。
我们目前尚未提供用于组合这两种数据源的规范工具,但您应该能够使用上述信息构建自己的脚本。
已知问题
处理共享操作
有时,操作会在配置的目标之间共享。
在执行阶段,这些共享操作只被视为一次且仅执行一次。不过,查询操作在执行前、分析后操作图上运行,因此会将这些操作视为输出工件具有完全相同的 execPath
的单独操作。因此,会显示重复的工件。
可在 GitHub 上找到查询问题/计划的功能列表。
常见问题解答
即使输入文件的内容发生更改,ActionKey 也会保持不变。
在查询的上下文中,ActionKey
是指从 ActionAnalysisMetadata#getKey 获取的 String
:
Returns a string encoding all of the significant behaviour of this Action that might affect the output. The general contract of `getKey` is this: if the work to be performed by the execution of this action changes, the key must change. ... Examples of changes that should affect the key are: - Changes to the BUILD file that materially affect the rule which gave rise to this Action. - Changes to the command-line options, environment, or other global configuration resources which affect the behaviour of this kind of Action (other than changes to the names of the input/output files, which are handled externally). - An upgrade to the build tools which changes the program logic of this kind of Action (typically this is achieved by incorporating a UUID into the key, which is changed each time the program logic of this action changes). Note the following exception: for actions that discover inputs, the key must change if any input names change or else action validation may falsely validate.
这不包括对输入文件内容的更改,并且不要与 RemoteCacheClient#ActionKey 混淆。
更新
如有任何问题/功能请求,请在此处提交问题。