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

Báo cáo vấn đề Xem nguồn Nightly · 7.4 .

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 định nghĩa vùng đệm giao thức. Tuy nhiên, bạn nên có một số kiến thức cơ bản 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 cùng 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 đã tạo sẽ giống như biểu đồ bên dưới. Các mũi tên cho biết mối quan hệ mẹ và con nêu 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 để đảm bảo tính ngắn gọn.

bep-graph

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

Ban đầu, một sự kiện BuildStarted sẽ được phát hành. Sự kiện này thông báo cho chúng tôi rằng bản dựng đã được gọi thông qua lệnh bazel test và sẽ 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 tạo PatternExpanded cung cấp thông tin chi tiết về mục tiêu cụ thể mà mẫu ... được mở rộng thành: //foo:foo_lib//foo:foo_test. Hàm 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. 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ẹ 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 và đường dẫn tệp trong sự kiện. Thay vào đó, các tệp này 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 báo cáo một tập hợp tệp một lần và nhiều mục tiêu tham chiếu đến tập hợp tệp đó. 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 bậc hai 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, 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.

Dưới đây là một thực thể của sự kiện TargetComplete cho mục tiêu //foo:foo_lib trong biểu đồ ở trên, được in ở dạng đại 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 cách sử dụ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. Trọng tải chứa thông tin về việc liệu mục tiêu có được tạo thành công hay không, 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ả về tỷ lệ khung hình trong BEP

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

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

Tiêu thụ NamedSetOfFiles

Xác định 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 mà bạn có thể thực hiện một cách hiệu quả bằng một số biện pháp chuẩn bị. Phần này thảo luận về cấu trúc đệ quy, dùng chung mà sự kiện NamedSetOfFiles cung cấp, khớp với cấu trúc của Depset Starlark.

Người tiêu 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 lần truyền tải với độ phức tạp bậc tư.

namedsetoffiles-bep-graph

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

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à mối quan hệ nghịch đảo của mối quan hệ sự kiện "mẹ-con", trong đó tất cả sự kiện ngoại trừ sự kiện đầu tiên 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ằng một sự kiện Progress không có ngữ nghĩa.

Với những hạn chế về thứ tự và chia sẻ này, người tiêu dùng thông thường phải lưu tất cả các sự kiện NamedSetOfFiles vào vùng đệm cho đến khi luồng BEP hết. Luồng sự kiện JSON và mã Python sau đây minh hoạ cách điền bản đồ từ mục tiêu/phương diện đến cấu phần phần mềm đã tạo trong nhóm đầu ra "mặc định" và cách xử lý đầu ra cho một tập hợp con các mục tiêu/phương diện đã 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)