Build Event Protocol の完全な仕様については、プロトコル バッファの定義をご覧ください。ただし、仕様を確認する前に、ある程度の直感を得ておくと役立つ場合があります 。
2 つの空のシェル スクリプト
foo.sh と foo_test.sh、および次の BUILD ファイルで構成される単純な Bazel ワークスペースについて考えてみましょう。
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 コマンドで呼び出されたことを示し、子イベントを通知します。
OptionsParsedWorkspaceStatusCommandLineUnstructuredCommandLineBuildMetadataBuildFinishedPatternExpandedProgress
最初の 3 つのイベントは、Bazel の呼び出し方法に関する情報を提供します。
PatternExpanded ビルド イベントは、... パターンが展開された特定のターゲット(//foo:foo_lib と //foo:foo_test)に関する情報を提供します。これを行うには、2 つの
TargetConfiguredイベントを子として宣言します。Configuration イベントは TargetConfigured イベントの前に投稿されていますが、TargetConfigured イベントは Configuration イベントを子イベントとして宣言します。
親子関係に加えて、イベントはビルド イベント識別子を使用して相互に参照することもできます
。たとえば、上のグラフでは、
TargetComplete イベントは NamedSetOfFiles イベントの fileSets
フィールドを参照しています。
ファイルを参照するビルド イベントは通常、ファイル
名とパスをイベントに埋め込みません。代わりに、NamedSetOfFilesイベントのビルド イベント識別子
が含まれます。これには実際のファイル名と
パスが含まれます。NamedSetOfFiles イベントを使用すると、ファイルのセットを一度報告して、
多くのターゲットから参照できます。この構造が必要なのは、そうしないと、
場合によっては Build Event Protocol の出力サイズが
ファイル数の 2 乗で増加するためです。NamedSetOfFiles イベントには、すべてのファイルが埋め込まれていない場合もあります。代わりに、ビルド イベント識別子を使用して他の NamedSetOfFiles イベントを参照します。
プロトコル バッファの JSON 表現で出力された、上のグラフの //foo:foo_lib
ターゲットの TargetComplete イベントのインスタンスを次に示します。
ビルド イベント識別子には、ターゲットが不透明な文字列として含まれており、ビルド イベント識別子を使用して
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 イベントを公開します。たとえば、//: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 次アルゴリズムを使用しないように注意する必要があります。
2 次の複雑さでトラバースするには、数億回のオペレーションが必要です。

図 2.NamedSetOfFiles BEP グラフ。
A NamedSetOfFiles イベントは、それを参照する
TargetComplete イベントまたは NamedSetOfFiles イベントの前に、常に BEP ストリームに表示されます。これは
「親子」イベント関係の逆です。最初のイベントを除くすべてのイベントは、少なくとも 1 つのイベントが通知された後に表示されます。
NamedSetOfFiles イベントは
Progress イベントによって通知されます。
これらの順序付けと共有の制約により、一般的なコンシューマーは BEP ストリームが使い果たされるまで、すべての
NamedSetOfFiles イベントをバッファリングする必要があります。次の 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)