ตัวอย่างโปรโตคอลเหตุการณ์การสร้าง

คุณดูข้อกำหนดทั้งหมดของ Build Event Protocol ได้ในคำจำกัดความของโปรโตคอลบัฟเฟอร์ อย่างไรก็ตาม การสร้างความเข้าใจเบื้องต้นก่อนดูข้อกำหนดอาจเป็นประโยชน์

ลองพิจารณาพื้นที่ทำงาน Bazel อย่างง่ายที่ประกอบด้วยสคริปต์เชลล์ว่าง 2 รายการ ได้แก่ foo.sh และ foo_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

เหตุการณ์ 3 รายการแรกจะให้ข้อมูลเกี่ยวกับวิธีเรียกใช้ Bazel

เหตุการณ์บิลด์ PatternExpanded จะให้ข้อมูลเชิงลึกเกี่ยวกับเป้าหมายที่เฉพาะเจาะจงซึ่งรูปแบบ ... ขยายเป็น //foo:foo_lib และ //foo:foo_test โดยประกาศเหตุการณ์ TargetConfigured 2 รายการเป็นเหตุการณ์ย่อย โปรดทราบว่าเหตุการณ์ TargetConfigured จะประกาศเหตุการณ์ Configuration เป็นเหตุการณ์ย่อย แม้ว่าระบบจะโพสต์ Configuration ก่อนเหตุการณ์ TargetConfigured

นอกเหนือจากความสัมพันธ์หลักและย่อยแล้ว เหตุการณ์ยังอาจอ้างอิงถึงกันโดยใช้ตัวระบุเหตุการณ์บิลด์ ตัวอย่างเช่น ในกราฟด้านบน เหตุการณ์ TargetComplete จะอ้างอิงถึงเหตุการณ์ NamedSetOfFiles ในฟิลด์ fileSets

เหตุการณ์การสร้างที่อ้างอิงถึงไฟล์มักจะไม่ฝังชื่อและเส้นทางของไฟล์ไว้ในเหตุการณ์ แต่จะมีตัวระบุเหตุการณ์การสร้างของเหตุการณ์ NamedSetOfFiles ซึ่งจะมีชื่อและเส้นทางของไฟล์จริง เหตุการณ์ NamedSetOfFiles อนุญาตให้รายงานชุดไฟล์ได้ครั้งเดียวและเป้าหมายหลายรายการอ้างอิงถึงได้ โครงสร้างนี้จำเป็นเนื่องจากในบางกรณี ขนาดเอาต์พุตของ Build Event Protocol จะเพิ่มขึ้นแบบยกกำลังสองตามจำนวนไฟล์ นอกจากนี้ เหตุการณ์ NamedSetOfFiles อาจไม่ได้ฝังไฟล์ทั้งหมดไว้ แต่จะอ้างอิงถึงเหตุการณ์ NamedSetOfFiles อื่นๆ ผ่านตัวระบุเหตุการณ์การสร้างแทน

ด้านล่างนี้คืออินสแตนซ์ของเหตุการณ์ TargetComplete สำหรับเป้าหมาย //foo:foo_lib จากกราฟด้านบน ซึ่งพิมพ์ในรูปแบบ JSON ของโปรโตคอลบัฟเฟอร์ ตัวระบุเหตุการณ์การสร้างจะมีเป้าหมายเป็นสตริงทึบแสงและอ้างอิงถึงเหตุการณ์ Configuration โดยใช้ตัวระบุเหตุการณ์การสร้าง เหตุการณ์นี้ไม่ได้ประกาศเหตุการณ์ย่อย เพย์โหลดจะมีข้อมูลเกี่ยวกับความสำเร็จในการสร้างเป้าหมาย ชุดไฟล์เอาต์พุต และประเภทของเป้าหมายที่สร้าง

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

ผลลัพธ์ของ Aspect ใน BEP

การสร้างทั่วไปจะประเมินการดำเนินการที่เชื่อมโยงกับคู่ (target, configuration) เมื่อสร้างโดยเปิดใช้ Aspect ไว้ Bazel จะประเมินเป้าหมายที่เชื่อมโยงกับ (target, configuration, aspect) เพิ่มเติมสำหรับแต่ละเป้าหมายที่ได้รับผลกระทบจาก Aspect ที่เปิดใช้

ผลลัพธ์การประเมินสำหรับ Aspect จะพร้อมใช้งานใน BEP แม้ว่าจะไม่มีประเภทเหตุการณ์ที่เฉพาะเจาะจงกับ Aspect สำหรับคู่ (target, configuration) แต่ละคู่ที่มี Aspect ที่เกี่ยวข้อง Bazel จะเผยแพร่เหตุการณ์ TargetConfigured และ TargetComplete เพิ่มเติมซึ่งมีผลลัพธ์จากการใช้ Aspect กับเป้าหมาย ตัวอย่างเช่น หากสร้าง //: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

การระบุอาร์ติแฟกต์ที่สร้างโดยเป้าหมาย (หรือ Aspect) ที่ระบุเป็นกรณีการใช้งาน BEP ทั่วไปที่ทำได้อย่างมีประสิทธิภาพด้วยการเตรียมการบางอย่าง ส่วนนี้ จะกล่าวถึงโครงสร้างแบบเรียกซ้ำและแชร์ที่NamedSetOfFiles เหตุการณ์มีให้ ซึ่งตรงกับโครงสร้างของ Depsetของ Starlark

ผู้ใช้ต้องระมัดระวังเพื่อหลีกเลี่ยงอัลกอริทึมแบบยกกำลังสองเมื่อประมวลผลเหตุการณ์ NamedSetOfFiles เนื่องจากงานสร้างขนาดใหญ่อาจมีเหตุการณ์ดังกล่าวหลายหมื่นรายการ ซึ่งต้องใช้การดำเนินการหลายร้อยล้านรายการในการข้ามที่มีความซับซ้อนแบบยกกำลังสอง

namedsetoffiles-bep-graph

รูปที่ 2 กราฟ BEP ของ NamedSetOfFiles

เหตุการณ์ NamedSetOfFiles จะปรากฏในสตรีม BEP ก่อน เหตุการณ์ TargetComplete หรือ NamedSetOfFiles ที่อ้างอิงถึงเหตุการณ์ดังกล่าวเสมอ ซึ่งเป็นความสัมพันธ์ของเหตุการณ์แบบ "หลัก-ย่อย" ที่กลับกัน โดยเหตุการณ์ทั้งหมด ยกเว้นเหตุการณ์แรก จะปรากฏขึ้นหลังจากมีเหตุการณ์อย่างน้อย 1 เหตุการณ์ประกาศเหตุการณ์ดังกล่าว เหตุการณ์ NamedSetOfFiles จะประกาศโดยเหตุการณ์ Progress ที่ไม่มีความหมาย

เมื่อพิจารณาข้อจำกัดด้านการจัดลำดับและการแชร์เหล่านี้ ผู้ใช้ทั่วไปจะต้องบัฟเฟอร์เหตุการณ์ NamedSetOfFiles ทั้งหมดไว้จนกว่าสตรีม BEP จะหมด สตรีมเหตุการณ์ JSON และโค้ด Python ต่อไปนี้แสดงวิธีป้อนข้อมูลแผนที่จากเป้าหมาย/Aspect ไปยังอาร์ติแฟกต์ที่สร้างในกลุ่มเอาต์พุต "default" และวิธีประมวลผลเอาต์พุตสำหรับเป้าหมาย/Aspect ที่สร้างขึ้นบางส่วน

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)