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

Báo cáo sự cố Xem nguồn

Bạn có thể xem thông số kỹ thuật đầy đủ của Giao thức sự kiện bản dựng trong phần định nghĩa vùng đệ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 đơn giản trên Bazel 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 đồ dưới đây. Các mũi tên chỉ ra mối quan hệ mẹ và con nêu trên. Xin lưu ý rằng vì lý do ngắn gọn, một số sự kiện bản dựng và hầu hết các trường đã bị bỏ qua.

biểu đồ bep

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

Ban đầu, sự kiện BuildStarted sẽ được xuất bản. Sự kiện này thông báo cho chúng tôi biết 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

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

Sự kiện tạo bản dựng PatternExpanded cung cấp thông tin chi tiết về việc mẫu ... cụ thể nhắm đến: //foo:foo_lib//foo:foo_test. Thư viện này thực hiện việc này bằng cách khai báo hai sự kiện TargetConfigured là phần tử con. Hãy 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 đế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 xây dựng tham chiếu đến tệp thường không nhúng tên và đường dẫn tệp 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 một sự kiện NamedSetOfFiles, sau đó sẽ chứa tên tệp và đường dẫn 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ì 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 lên theo thứ tự cùng với số lượng tệp. Một sự kiện NamedSetOfFiles cũng có thể không nhúng tất cả tệp mà 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.

Dưới đây là một phiên bản của sự kiện TargetComplete cho mục tiêu //foo:foo_lib từ biểu đồ trên, được in trong cách biểu diễn JSON của vùng đệ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ờ và tham chiếu đến sự kiện Configuration bằng giá trị nhận dạng sự kiện bản dựng. 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 tạo thành công hay chưa, tập hợp tệp đầu ra và loại mục tiêu được tạo.

{
  "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ả khung hình trong BEP

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

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

Tiêu thụ NamedSetOfFiles

Việc 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ố chuẩn bị. Phần này thảo luận về cấu trúc đệ quy, dùng chung do sự kiện NamedSetOfFiles 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ý các sự kiện NamedSetOfFiles 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 quá trình truyền tải với độ phức tạp bậc bốn.

có tênsetoffiles-bep-graph

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

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

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

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)