有關 Build Event Protocol 的完整規格,請見其通訊協定 緩衝區定義。然而,建立直覺可能有所幫助 再檢視規格
請考慮一個簡單的 Bazel 工作區,其中包含兩個空白殼層指令碼
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 ...
時,
建構事件類似下圖。箭頭表示
上述父項和子項關係請注意,部分建構事件
為求簡潔,我們省略了大部分的欄位。
圖 1. BEP 圖表。
一開始,系統會發布 BuildStarted
事件。事件通知我們
建構是透過 bazel test
指令叫用,並宣告子事件:
OptionsParsed
WorkspaceStatus
CommandLine
UnstructuredCommandLine
BuildMetadata
BuildFinished
PatternExpanded
Progress
前三個事件提供叫用 Bazel 的方式相關資訊。
PatternExpanded
建構事件提供深入分析
...
模式會擴展到下列特定目標:
//foo:foo_lib
和//foo:foo_test
。做法是
TargetConfigured
事件視為子項。請注意,TargetConfigured
事件
將 Configuration
事件宣告為子項事件,即使 Configuration
已於 TargetConfigured
活動之前張貼。
除了父項和子項關係之外,事件也可能參照彼此
使用建構事件 ID例如,在上方圖表中
TargetComplete
事件參照其 fileSets
中的 NamedSetOfFiles
事件
] 欄位。
參照檔案的建立事件通常不會嵌入檔案
事件中的名稱和路徑而是包含建構事件 ID
NamedSetOfFiles
事件,其中包含實際的檔案名稱和
路徑。NamedSetOfFiles
事件可讓一組檔案回報一次
都是由許多目標所參照結構是必要的,因為
在某些情況下,Build Event Protocol 的輸出大小會透過
檔案數量NamedSetOfFiles
事件也可能沒有所有檔案
而是透過其嵌入的其他 NamedSetOfFiles
事件
建立事件 ID
以下是 //foo:foo_lib
的 TargetComplete
事件示例
目標,以通訊協定緩衝區的 JSON 表示法列印。
建構事件 ID 包含目標為不透明字串,指的是
Configuration
事件。活動沒有
宣告任何子事件。酬載包含
目標、輸出檔案以及目標種類
。
{
"id": {
"targetCompleted": {
"label": "//foo:foo_lib",
"configuration": {
"id": "544e39a7f0abdb3efdd29d675a48bc6a"
}
}
},
"completed": {
"success": true,
"outputGroup": [{
"name": "default",
"fileSets": [{
"id": "0"
}]
}],
"targetKind": "sh_library rule"
}
}
BEP 中的顯示結果
一般版本會評估與 (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
事件,因為大型建構作業可能包含數萬個
需要數億個
二次複雜性
圖 2. NamedSetOfFiles
BEP 圖表。
NamedSetOfFiles
事件一律顯示在 BEP 串流中,之前
參照該事件的 TargetComplete
或 NamedSetOfFiles
事件。這就是
「父項/子項」的相反詞事件關係,其中所有事件 (第一個事件除外)
才出現。NamedSetOfFiles
事件是
(不含語意) 的 Progress
事件所宣告。
考量到這些排序和共用的限制,一般取用者必須緩衝處理所有
NamedSetOfFiles
事件,直到 BEP 串流用盡為止。下列 JSON
事件串流和 Python 程式碼,示範如何從
將目標/切面設定為「預設」中建構的構件以及如何
會處理部分內建目標/切面的輸出內容:
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)