बिल्ड इवेंट प्रोटोकॉल के उदाहरण

Build Event Protocol के पूरे स्पेसिफ़िकेशन के बारे में, इसके प्रोटोकॉल बफ़र की परिभाषा में जानकारी दी गई है. हालांकि, स्पेसिफ़िकेशन देखने से पहले, कुछ अनुमान लगाना मददगार हो सकता है.

यहां एक सामान्य Bazel वर्कस्पेस का उदाहरण दिया गया है. इसमें दो खाली शेल स्क्रिप्ट 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

पहली इमेज. बीईपी ग्राफ़.

शुरुआत में, BuildStarted इवेंट पब्लिश किया जाता है. इस इवेंट से हमें पता चलता है कि bazel test कमांड का इस्तेमाल करके बिल्ड को शुरू किया गया था. साथ ही, यह चाइल्ड इवेंट के बारे में सूचना देता है:

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

पहले तीन इवेंट से यह पता चलता है कि Bazel को कैसे शुरू किया गया था.

PatternExpanded बिल्ड इवेंट से यह जानकारी मिलती है कि ... पैटर्न किन खास टारगेट तक पहुंचा है: //foo:foo_lib और //foo:foo_test. यह दो TargetConfigured इवेंट को बच्चों के लिए उपलब्ध इवेंट के तौर पर सेट करता है. ध्यान दें कि TargetConfigured इवेंट, Configuration इवेंट को चाइल्ड इवेंट के तौर पर दिखाता है. भले ही, Configuration इवेंट को TargetConfigured इवेंट से पहले पोस्ट किया गया हो.

माता-पिता और बच्चे के बीच के संबंध के अलावा, इवेंट भी एक-दूसरे से जुड़ सकते हैं. इसके लिए, वे अपने बिल्ड इवेंट आइडेंटिफ़ायर का इस्तेमाल करते हैं. उदाहरण के लिए, ऊपर दिए गए ग्राफ़ में TargetComplete इवेंट, अपने fileSets फ़ील्ड में NamedSetOfFiles इवेंट को रेफ़र करता है.

फ़ाइलों का रेफ़रंस देने वाले इवेंट, आम तौर पर इवेंट में फ़ाइल के नाम और पाथ एम्बेड नहीं करते हैं. इसके बजाय, इनमें NamedSetOfFiles इवेंट का बिल्ड इवेंट आइडेंटिफ़ायर होता है. इसमें फ़ाइल के असली नाम और पाथ शामिल होते हैं. NamedSetOfFiles इवेंट की मदद से, फ़ाइलों के सेट की शिकायत एक बार की जा सकती है. साथ ही, कई टारगेट से इसका रेफ़रंस दिया जा सकता है. यह स्ट्रक्चर ज़रूरी है. ऐसा इसलिए, क्योंकि कुछ मामलों में, फ़ाइलों की संख्या बढ़ने पर Build Event Protocol के आउटपुट का साइज़ काफ़ी बढ़ जाता है. ऐसा भी हो सकता है कि किसी NamedSetOfFiles इवेंट में सभी फ़ाइलें एम्बेड न की गई हों. इसके बजाय, वह अपने बिल्ड इवेंट आइडेंटिफ़ायर के ज़रिए अन्य NamedSetOfFiles इवेंट को रेफ़र करता हो.

यहां ऊपर दिए गए ग्राफ़ में मौजूद //foo:foo_lib टारगेट के लिए, TargetComplete इवेंट का एक उदाहरण दिया गया है. इसे प्रोटोकॉल बफ़र के JSON फ़ॉर्मैट में प्रिंट किया गया है. बिल्ड इवेंट आइडेंटिफ़ायर में टारगेट को ओपेक स्ट्रिंग के तौर पर शामिल किया जाता है. साथ ही, यह अपने बिल्ड इवेंट आइडेंटिफ़ायर का इस्तेमाल करके Configuration इवेंट को रेफ़र करता है. इस इवेंट में, बच्चों के लिए होने वाले किसी भी इवेंट की जानकारी नहीं दी गई है. इस पेलोड में यह जानकारी होती है कि टारगेट को सही तरीके से बनाया गया है या नहीं, आउटपुट फ़ाइलों का सेट, और किस तरह का टारगेट बनाया गया है.

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

बीईपी में आसपेक्ट के नतीजे

सामान्य बिल्ड, (target, configuration) पेयर से जुड़ी कार्रवाइयों का आकलन करते हैं. आस्पेक्ट चालू करके बिल्ड करने पर, Bazel (target, configuration, aspect) ट्रिपलेट से जुड़े टारगेट का भी आकलन करता है. ऐसा, चालू किए गए किसी आस्पेक्ट से प्रभावित हर टारगेट के लिए किया जाता है.

किसी पहलू के लिए इवेंट टाइप मौजूद न होने पर भी, बीईपी में पहलुओं के लिए आकलन के नतीजे उपलब्ध होते हैं. हर (target, configuration) के लिए, Bazel एक अतिरिक्त TargetConfigured और TargetComplete इवेंट पब्लिश करता है. इसमें टारगेट पर पहलू लागू करने का नतीजा होता है. उदाहरण के लिए, अगर //:foo_lib को --aspects=aspects/myaspect.bzl%custom_aspect की मदद से बनाया गया है, तो यह इवेंट बीईपी में भी दिखेगा:

{
  "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 का इस्तेमाल करना

किसी टारगेट (या पहलू) से बनाए गए आर्टफ़ैक्ट का पता लगाना, बीईपी का एक सामान्य इस्तेमाल है. इसे कुछ तैयारी के साथ आसानी से किया जा सकता है. इस सेक्शन में, NamedSetOfFiles इवेंट की ओर से दिए गए रिकर्सिव और शेयर किए गए स्ट्रक्चर के बारे में बताया गया है. यह स्ट्रक्चर, Starlark Depset के स्ट्रक्चर से मेल खाता है.

उपयोगकर्ताओं को NamedSetOfFiles इवेंट प्रोसेस करते समय, क्वाडैटिक एल्गोरिदम से बचना चाहिए. ऐसा इसलिए, क्योंकि बड़े बिल्ड में इस तरह के हज़ारों इवेंट हो सकते हैं. इसके लिए, क्वाडैटिक कॉम्प्लेक्सिटी वाले ट्रैवर्सल में करोड़ों ऑपरेशन की ज़रूरत होती है.

namedsetoffiles-bep-graph

दूसरी इमेज. NamedSetOfFiles बीईपी ग्राफ़.

NamedSetOfFiles इवेंट, बीईपी स्ट्रीम में हमेशा पहले दिखता है. इसके बाद, इससे जुड़ा TargetComplete या NamedSetOfFiles इवेंट दिखता है. यह "पैरंट-चाइल्ड" इवेंट के संबंध का उल्टा है. इसमें पहले इवेंट को छोड़कर बाकी सभी इवेंट, कम से कम एक इवेंट के बाद दिखते हैं. NamedSetOfFiles इवेंट की सूचना, Progress इवेंट से दी जाती है. इसमें कोई सिमैंटिक नहीं होता.

ऑर्डर करने और शेयर करने से जुड़ी इन पाबंदियों को देखते हुए, किसी सामान्य उपभोक्ता को सभी NamedSetOfFiles इवेंट तब तक बफ़र करने चाहिए, जब तक बीईपी स्ट्रीम खत्म नहीं हो जाती. नीचे दिए गए JSON इवेंट स्ट्रीम और Python कोड से पता चलता है कि "default" आउटपुट ग्रुप में, टारगेट/पहलू से लेकर बनाए गए आर्टफ़ैक्ट तक मैप को कैसे भरा जाता है. साथ ही, इससे यह भी पता चलता है कि बनाए गए टारगेट/पहलू के सबसेट के लिए आउटपुट को कैसे प्रोसेस किया जाता है:

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)