建構事件通訊協定範例

回報問題 查看來源 夜間 7.2 7.1 7.0 6.5 6.4

有關 Build Event Protocol 的完整規格,請見其通訊協定 緩衝區定義。然而,建立直覺可能有所幫助 再檢視規格

請考慮一個簡單的 Bazel 工作區,其中包含兩個空白殼層指令碼 foo.shfoo_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 ... 時, 建構事件類似下圖。箭頭表示 上述父項和子項關係請注意,部分建構事件 為求簡潔,我們省略了大部分的欄位。

bep-graph

圖 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_libTargetComplete 事件示例 目標,以通訊協定緩衝區的 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 會發布額外的 TargetConfiguredTargetComplete 事件,結果可能將切面套用至 目標。舉例來說,如果 //: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 事件,因為大型建構作業可能包含數萬個 需要數億個 二次複雜性

namedsetoffiles-bep-graph

圖 2. NamedSetOfFiles BEP 圖表。

NamedSetOfFiles 事件一律顯示在 BEP 串流中,之前 參照該事件的 TargetCompleteNamedSetOfFiles 事件。這是 「父項/子項」的相反詞事件關係,其中所有事件 (第一個事件除外) 才出現。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)