aquery 命令可让您查询 build 图中的操作。
它对分析后的配置目标图进行操作,并公开
有关 操作、工件及其关系的信息。
aquery 如果您对从配置目标图生成的操作/工件
的属性感兴趣,会很有用。例如,实际运行的命令
及其输入/输出/助记符。
该工具接受多个命令行选项。 值得注意的是,aquery 命令在常规 Bazel build 的基础上运行,并继承 了 build 期间可用的一组选项。
它支持与传统
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 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 函数
有三个 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))
选项
build 选项
aquery 在常规 Bazel build 的基础上运行,因此继承了 build 期间可用的一组
选项
。
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 操作的清单文件的内容。文件内容在
file_contents 字段中与 --output=xxxproto 一起返回。
使用 --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 调用输出。
例如:当您对规则定义进行一些更改并想要验证正在运行的
命令行是否未更改时。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 输出之间的差异:
哪些操作存在于一个输出中,但不存在于另一个输出中;哪些操作在每个 aquery 输出中具有不同的
命令行/输入,等等。运行上述命令的结果如下:
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:要比较的 aquery 输出文件
--input_type=(proto|text_proto), default=proto:输入文件
的格式。支持 proto 和 textproto aquery 输出。
--attrs=(cmdline|inputs), default=cmdline:要比较的操作的属性
。
Aspect-on-Aspect
Aspect 可以相互应用。然后,这些 Aspect 生成的操作的 aquery 输出将包含 Aspect 路径,即应用于生成操作的目标的 Aspect 序列。
Aspect-on-Aspect 示例:
t0 ^ | <- a1 t1 ^ | <- a2 t2
假设 ti 是规则 ri 的目标,该规则对其依赖项应用 Aspect 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) 的 Aspect a2 生成的,其中 a1(t0) 是应用于目标 t0 的 Aspect a1 的结果。
每个 AspectDescriptor 的格式如下:
AspectClass([param=value,...])
AspectClass 可以是 Aspect 类的名称(对于原生 Aspect),也可以是
bzl_file%aspect_name(对于 Starlark Aspect)。AspectDescriptor 按
依赖关系图
的拓扑顺序排序。
与 JSON 配置文件关联
虽然 aquery 提供了有关 build 中正在运行的操作的信息(运行原因、 输入/输出),但 JSON 配置文件 会告诉我们这些操作的执行时间和持续时间。 可以通过一个共同点(操作的主要输出)将这两组信息结合起来。
如需在 JSON 配置文件中包含操作的输出,请使用
--experimental_include_primary_output --noexperimental_slim_json_profile生成配置文件。
精简配置文件与包含主要输出不兼容。默认情况下,aquery 会包含操作的主要输出
。
我们目前没有提供将这两个数据源结合起来的规范工具,但您应该能够 使用上述信息构建自己的脚本。
已知问题
处理共享操作
有时,操作会在配置目标之间 共享 。
在执行阶段,这些共享操作会
被简单地视为一个操作,并且仅执行一次。
但是,aquery 对执行前、分析后的操作图进行操作,因此会将这些操作视为单独的操作,其输出工件具有完全相同的 execPath。因此,
等效的工件会重复显示。
您可以在 GitHub上找到 aquery 问题/计划功能列表。
常见问题解答
即使输入文件的内容发生了更改,ActionKey 仍保持不变。
在 aquery 的上下文中,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混淆。
更新
如有任何问题/功能请求,请点击此处提交问题。