如需建構事件通訊協定的完整規格,請參閱其通訊協定緩衝區定義。不過,建議您先建構一些直覺,再查看規格。
假設有一個簡單的 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
事件宣告為子項。請注意,即使 Configuration
是在 TargetConfigured
事件之前發布,TargetConfigured
事件仍會將 Configuration
事件宣告為子事件。
除了父項和子項關係之外,事件也可以使用建構事件 ID 互相參照。例如,在上圖中,TargetComplete
事件參照其 fileSets
欄位中的 NamedSetOfFiles
事件。
參照檔案的建構事件通常不會在事件中嵌入檔案名稱和路徑。而是包含 NamedSetOfFiles
事件的建構事件 ID,且該事件會包含實際的檔案名稱和路徑。NamedSetOfFiles
事件允許系統回報一組檔案一次,並由多個目標參照。之所以需要這個結構,是因為在某些情況下,建構事件通訊協定的輸出大小會隨檔案數量自然成長。NamedSetOfFiles
事件也可能不會嵌入其所有檔案,而是改為透過其建構事件 ID 參照其他 NamedSetOfFiles
事件。
以下是上圖中 //foo:foo_lib
目標的 TargetComplete
事件執行個體,以通訊協定緩衝區的 JSON 表示法顯示。建構事件 ID 包含目標為不透明字串,並使用建構事件 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)
配對相關的動作。如果在啟用目標的情況下進行建構,Bazel 會針對每個受特定已啟用要素影響的目標,評估與 (target, configuration,
aspect)
三組相關的目標。
即使缺少特定事件類型,BEP 還是可以提供面向的評估結果。針對每個 (target, configuration)
配對與適用的面向,Bazel 會發布額外的 TargetConfigured
和 TargetComplete
事件,指出將切面套用至目標後的結果。舉例來說,如果使用 --aspects=aspects/myaspect.bzl%custom_aspect
建構 //:foo_lib
,這個事件也會顯示在 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 程式碼示範如何從 target/aspect 填入對應,並在「預設」輸出群組中建構構件,以及如何處理部分已建構目標/裝置的輸出內容:
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)