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 ...
चलाने पर, जनरेट किए गए बिल्ड इवेंट का बिल्ड ग्राफ़, नीचे दिए गए ग्राफ़ जैसा दिखेगा. तीर, ऊपर बताए गए पैरंट और चाइल्ड के संबंध को दिखाते हैं. ध्यान दें कि कुछ बिल्ड इवेंट और ज़्यादातर फ़ील्ड को छोटा करने के लिए हटाया गया है.
पहली इमेज. बीईपी ग्राफ़.
शुरुआत में, 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
बीईपी ग्राफ़.
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)