Ví dụ về giao thức sự kiện cho bản dựng

Bạn có thể tìm thấy thông số kỹ thuật đầy đủ của Giao thức sự kiện bản dựng trong định nghĩa bộ đệm giao thức. Tuy nhiên, bạn nên xây dựng một số trực giác trước khi xem thông số kỹ thuật.

Hãy xem xét một không gian làm việc Bazel đơn giản bao gồm hai tập lệnh shell trống foo.shfoo_test.sh và tệp BUILD sau:

sh_library(
    name = "foo_lib",
    srcs = ["foo.sh"],
)

sh_test(
    name = "foo_test",
    srcs = ["foo_test.sh"],
    deps = [":foo_lib"],
)

Khi chạy bazel test ... trên dự án này, biểu đồ bản dựng của các sự kiện bản dựng được tạo sẽ giống với biểu đồ bên dưới. Các mũi tên cho biết mối quan hệ mẹ và con đã đề cập ở trên. Xin lưu ý rằng một số sự kiện bản dựng và hầu hết các trường đã bị bỏ qua để ngắn gọn.

bep-graph

Hình 1. Biểu đồ BEP.

Ban đầu, một sự kiện BuildStarted được xuất bản. Sự kiện này thông báo cho chúng ta rằng bản dựng được gọi thông qua lệnh bazel test và thông báo các sự kiện con:

  • OptionsParsed
  • WorkspaceStatus
  • CommandLine
  • UnstructuredCommandLine
  • BuildMetadata
  • BuildFinished
  • PatternExpanded
  • Progress

Ba sự kiện đầu tiên cung cấp thông tin về cách Bazel được gọi.

Sự kiện bản dựng PatternExpanded cung cấp thông tin chi tiết về những mục tiêu cụ thể mà mẫu ... đã mở rộng thành: //foo:foo_lib//foo:foo_test. Sự kiện này thực hiện bằng cách khai báo hai TargetConfigured sự kiện là sự kiện con. Xin lưu ý rằng sự kiện TargetConfigured khai báo sự kiện Configuration là sự kiện con, mặc dù Configuration đã được đăng trước sự kiện TargetConfigured.

Ngoài mối quan hệ mẹ và con, các sự kiện cũng có thể tham chiếu lẫn nhau bằng cách sử dụng giá trị nhận dạng sự kiện bản dựng. Ví dụ: trong biểu đồ ở trên, sự kiện TargetComplete tham chiếu đến sự kiện NamedSetOfFiles trong trường fileSets.

Các sự kiện bản dựng tham chiếu đến tệp thường không nhúng tên tệp và đường dẫn vào sự kiện. Thay vào đó, chúng chứa giá trị nhận dạng sự kiện bản dựng của sự kiện NamedSetOfFiles, sau đó sẽ chứa tên và đường dẫn tệp thực tế. Sự kiện NamedSetOfFiles cho phép một tập hợp tệp được báo cáo một lần và được nhiều mục tiêu tham chiếu. Cấu trúc này là cần thiết vì nếu không, trong một số trường hợp, kích thước đầu ra của Giao thức sự kiện bản dựng sẽ tăng theo cấp số nhân với số lượng tệp. Sự kiện NamedSetOfFiles cũng có thể không nhúng tất cả các tệp của sự kiện đó, mà thay vào đó tham chiếu đến các sự kiện NamedSetOfFiles khác thông qua giá trị nhận dạng sự kiện bản dựng của chúng.

Dưới đây là một thực thể của sự kiện TargetComplete cho //foo:foo_lib mục tiêu từ biểu đồ ở trên, được in ở dạng biểu diễn JSON của bộ đệm giao thức. Giá trị nhận dạng sự kiện bản dựng chứa mục tiêu dưới dạng một chuỗi mờ đục và tham chiếu đến sự kiện Configuration bằng cách sử dụng giá trị nhận dạng sự kiện bản dựng của sự kiện đó. Sự kiện này không thông báo bất kỳ sự kiện con nào. Tải trọng chứa thông tin về việc mục tiêu có được xây dựng thành công hay không, tập hợp các tệp đầu ra và loại mục tiêu được xây dựng.

{
  "id": {
    "targetCompleted": {
      "label": "//foo:foo_lib",
      "configuration": {
        "id": "544e39a7f0abdb3efdd29d675a48bc6a"
      }
    }
  },
  "completed": {
    "success": true,
    "outputGroup": [{
      "name": "default",
      "fileSets": [{
        "id": "0"
      }]
    }],
    "targetKind": "sh_library rule"
  }
}

Kết quả khía cạnh trong BEP

Các bản dựng thông thường đánh giá các hành động được liên kết với (target, configuration) cặp. Khi xây dựng với các khía cạnh được bật, Bazel sẽ đánh giá thêm các mục tiêu được liên kết với các bộ ba (target, configuration, aspect) cho từng mục tiêu bị ảnh hưởng bởi một khía cạnh được bật nhất định.

Kết quả đánh giá cho các khía cạnh có sẵn trong BEP mặc dù không có các loại sự kiện dành riêng cho khía cạnh. Đối với mỗi cặp (target, configuration) có một khía cạnh áp dụng, Bazel sẽ xuất bản thêm một sự kiện TargetConfiguredTargetComplete mang kết quả từ việc áp dụng khía cạnh cho mục tiêu. Ví dụ: nếu //:foo_lib được xây dựng bằng --aspects=aspects/myaspect.bzl%custom_aspect, thì sự kiện này cũng sẽ xuất hiện trong 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"
      }]
    }]
  }
}

Sử dụng NamedSetOfFiles

Xác định các cấu phần phần mềm do một mục tiêu (hoặc khía cạnh) nhất định tạo ra là một trường hợp sử dụng BEP phổ biến có thể được thực hiện hiệu quả với một số bước chuẩn bị. Phần này thảo luận về cấu trúc đệ quy, được chia sẻ do NamedSetOfFiles sự kiện cung cấp, khớp với cấu trúc của Depset Starlark.

Người dùng phải cẩn thận để tránh các thuật toán bậc hai khi xử lý NamedSetOfFiles sự kiện vì các bản dựng lớn có thể chứa hàng chục nghìn sự kiện như vậy, đòi hỏi hàng trăm triệu thao tác trong một lần duyệt có độ phức tạp bậc hai.

namedsetoffiles-bep-graph

Hình 2. Biểu đồ BEP NamedSetOfFiles.

Sự kiện NamedSetOfFiles luôn xuất hiện trong luồng BEP trước sự kiện TargetComplete hoặc NamedSetOfFiles tham chiếu đến sự kiện đó. Đây là mối quan hệ sự kiện "mẹ-con" ngược lại, trong đó tất cả các sự kiện, trừ sự kiện đầu tiên, đều xuất hiện sau ít nhất một sự kiện thông báo về sự kiện đó. Sự kiện NamedSetOfFiles được thông báo bởi sự kiện Progress không có ngữ nghĩa.

Với các ràng buộc về thứ tự và chia sẻ này, một người dùng thông thường phải đệm tất cả các NamedSetOfFiles sự kiện cho đến khi luồng BEP cạn kiệt. Luồng sự kiện JSON và mã Python sau đây minh hoạ cách điền một bản đồ từ mục tiêu/khía cạnh đến các cấu phần phần mềm đã xây dựng trong nhóm đầu ra "mặc định" và cách xử lý các đầu ra cho một tập hợp con các mục tiêu/khía cạnh đã xây dựng:

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)