构建事件协议的完整规范可在其协议中找到 缓冲区定义。不过,积累一些直觉可能会有所帮助 然后再查看具体规范
假设有一个简单的 Bazel 工作区,其中包含两个空 Shell 脚本
foo.sh
和 foo_test.sh
以及以下 BUILD
文件:
sh_library(
name = "foo_lib",
srcs = ["foo.sh"],
)
sh_test(
name = "foo_test",
srcs = ["foo_test.sh"],
deps = [":foo_lib"],
)
在此项目上运行 bazel test ...
时,生成的 build 图
构建事件将类似于下图。箭头表示
上述父级与子级关系。请注意,有些构建事件和
为简洁起见,已省略大多数字段。
图 1. BEP 图。
最初,系统会发布 BuildStarted
事件。该事件通知我们
build 是通过 bazel test
命令调用的,并公布子事件:
OptionsParsed
WorkspaceStatus
CommandLine
UnstructuredCommandLine
BuildMetadata
BuildFinished
PatternExpanded
Progress
前三个事件提供有关如何调用 Bazel 的信息。
PatternExpanded
构建事件提供了数据分析
...
模式扩展为以下特定目标:
//foo:foo_lib
和//foo:foo_test
。为此,它会声明两个
TargetConfigured
事件作为子级。请注意,TargetConfigured
事件
将 Configuration
事件声明为子事件,即使 Configuration
已在 TargetConfigured
事件之前发布。
除了父级与子级关系之外,事件还可能相互引用
构建事件标识符例如,在上图中,
TargetComplete
事件在其 fileSets
中引用 NamedSetOfFiles
事件
字段。
引用文件的构建事件通常不会嵌入文件
事件的名称和路径而是包含构建事件标识符
NamedSetOfFiles
事件的名称,其中将包含实际文件名和
路径。NamedSetOfFiles
事件允许将一组文件报告一次,
被许多目标引用的情况此结构是必要的,因为
在某些情况下,Build Event Protocol 输出大小会随着
文件数量。NamedSetOfFiles
事件也可能不包含其所有文件
而是通过其NamedSetOfFiles
build 事件标识符。
以下是 //foo:foo_lib
的 TargetComplete
事件实例
上图中的目标,以 Protocol Buffer 的 JSON 表示法输出。
构建事件标识符包含作为不透明字符串的目标,并指代
Configuration
事件(使用其构建事件标识符)。活动
公布所有子项活动。载荷中包含有关
目标构建成功、输出文件集以及目标类型
。
{
"id": {
"targetCompleted": {
"label": "//foo:foo_lib",
"configuration": {
"id": "544e39a7f0abdb3efdd29d675a48bc6a"
}
}
},
"completed": {
"success": true,
"outputGroup": [{
"name": "default",
"fileSets": [{
"id": "0"
}]
}],
"targetKind": "sh_library rule"
}
}
BEP 的切面结果
普通 build 会评估与 (target, configuration)
关联的操作
对。在启用 aspects 的情况下进行构建时,Bazel
此外,针对受给定已启用方面影响的每个目标,还会评估与 (target, configuration,
aspect)
三元组相关的目标。
BEP 中提供了各个方面的评估结果,尽管缺少
特定于切面的事件类型。对于每个 (target, configuration)
对(带有
则 Bazel 发布了另一个 TargetConfigured
,并且
TargetComplete
事件,其中包含将切面应用于
目标。例如,如果 //:foo_lib
是使用
--aspects=aspects/myaspect.bzl%custom_aspect
,此活动还将显示在
BEP:
{
"id": {
"targetCompleted": {
"label": "//foo:foo_lib",
"configuration": {
"id": "544e39a7f0abdb3efdd29d675a48bc6a"
},
"aspect": "aspects/myaspect.bzl%custom_aspect"
}
},
"completed": {
"success": true,
"outputGroup": [{
"name": "default",
"fileSets": [{
"id": "1"
}]
}]
}
}
消耗 NamedSetOfFiles
确定给定目标(或方面)产生的伪影是很常见的做法
通过一些准备工作即可高效完成的 BEP 用例。此部分
讨论了 NamedSetOfFiles
提供的递归共享结构
事件,该对象与 Starlark Depset 的结构相匹配。
消费者在处理时必须注意避免二次算法
NamedSetOfFiles
事件,因为大型 build 可能包含数万个
需要一次以数亿次的遍历操作,
二次复杂度。
图 2. NamedSetOfFiles
BEP 图表。
NamedSetOfFiles
事件始终会在 BEP 视频流之前显示,
引用它的 TargetComplete
或 NamedSetOfFiles
事件。这是
是“父级-子级”的事件关系,在这种情况下,除第一个事件
会在至少一个事件播报后显示。NamedSetOfFiles
事件是
由没有语义的 Progress
事件公布。
鉴于这些排序和共享限制,典型的使用方必须缓冲所有
NamedSetOfFiles
事件,直到 BEP 流用尽。以下 JSON
事件流和 Python 代码演示了如何使用
针对“default”中的已构建的制品输出组,以及如何
处理构建目标/方面子集的输出:
named_sets = {} # type: dict[str, NamedSetOfFiles]
outputs = {} # type: dict[str, dict[str, set[str]]]
for event in stream:
kind = event.id.WhichOneof("id")
if kind == "named_set":
named_sets[event.id.named_set.id] = event.named_set_of_files
elif kind == "target_completed":
tc = event.id.target_completed
target_id = (tc.label, tc.configuration.id, tc.aspect)
outputs[target_id] = {}
for group in event.completed.output_group:
outputs[target_id][group.name] = {fs.id for fs in group.file_sets}
for result_id in relevant_subset(outputs.keys()):
visit = outputs[result_id].get("default", [])
seen_sets = set(visit)
while visit:
set_name = visit.pop()
s = named_sets[set_name]
for f in s.files:
process_file(result_id, f)
for fs in s.file_sets:
if fs.id not in seen_sets:
visit.add(fs.id)
seen_sets.add(fs.id)