नियम से उन कार्रवाई की सीरीज़ के बारे में पता चलता है जो आउटपुट के सेट को बनाने के लिए बेज़ल इनपुट पर लागू होती हैं. ये सेवा देने वाली कंपनियां, नियम के लागू करने की सुविधा के ज़रिए दिखाती हैं. उदाहरण के लिए, C++ बाइनरी नियम:
.cpp
सोर्स फ़ाइलों (इनपुट) का सेट लें.- सोर्स फ़ाइलों पर
g++
चलाएं (कार्रवाई). - रनटाइम के लिए उपलब्ध कराने के लिए,
DefaultInfo
प्रोवाइडर को एक्ज़ीक्यूटेबल आउटपुट और दूसरी फ़ाइलों के साथ दिखाएं. CcInfo
सेवा देने वाली कंपनी को लक्ष्य और उसकी डिपेंडेंसी से इकट्ठा की गई C++-खास जानकारी के साथ दिखाएं.
बेज़ल के नज़रिए से, g++
और मानक C++ लाइब्रेरी भी इस नियम के इनपुट हैं. नियम लिखने वाले के तौर पर, आपको किसी नियम के लिए न सिर्फ़ उपयोगकर्ता से मिलने वाले इनपुट को ध्यान में रखना चाहिए, बल्कि कार्रवाइयों को लागू करने के लिए ज़रूरी सभी टूल और लाइब्रेरी को भी शामिल करना चाहिए.
किसी भी नियम को बनाने या उसमें बदलाव करने से पहले, पक्का करें कि आप Bazel के फ़ेज़ बनाने के चरणों से परिचित हों. बिल्ड के तीन चरणों को समझना ज़रूरी है (लोड करना, विश्लेषण करना, और उन्हें लागू करना). नियमों और मैक्रो के बीच के अंतर को समझने के लिए, मैक्रो के बारे में जानना भी उपयोगी होता है. शुरू करने के लिए, सबसे पहले नियम ट्यूटोरियल देखें. इसके बाद, इस पेज को रेफ़रंस के तौर पर इस्तेमाल करें.
कुछ नियम Bazel में पहले से मौजूद होते हैं. cc_library
और java_binary
जैसे नेटिव नियम, कुछ खास भाषाओं के लिए उपलब्ध हैं.
अपने नियम तय करके, आप उन भाषाओं और टूल के लिए भी ऐसी ही सहायता दे सकते हैं
जो Bazel में काम नहीं करती.
Bazel, Starlark भाषा का इस्तेमाल करके, नियमों को लिखने के लिए एक एक्सटेंसिबिलिटी मॉडल उपलब्ध कराता है. ये नियम .bzl
फ़ाइलों में लिखे गए हैं, जिन्हें सीधे BUILD
फ़ाइलों से लोड किया जा सकता है.
अपना नियम तय करते समय, आपको यह तय करने की सुविधा मिलती है कि वह किन एट्रिब्यूट के लिए काम करता है और वह इससे किस तरह आउटपुट हासिल करेगा.
नियम का implementation
फ़ंक्शन, विश्लेषण के चरण के दौरान अपना व्यवहार तय करता है. यह फ़ंक्शन कोई बाहरी निर्देश
नहीं चलाता है. इसके बजाय, यह उन कार्रवाई को रजिस्टर करता है जिन्हें बाद में इस्तेमाल किया जाएगा. इसका इस्तेमाल नियम के आउटपुट बनाने के लिए किया जाएगा, बशर्ते उनकी ज़रूरत हो.
नियम बनाना
.bzl
फ़ाइल में, नया नियम तय करने के लिए नियम फ़ंक्शन का इस्तेमाल करें
और नतीजे को ग्लोबल वैरिएबल में सेव करें. rule
पर किए जाने वाले कॉल में
एट्रिब्यूट और
लागू करने की सुविधा के बारे में बताया जाता है:
example_library = rule(
implementation = _example_library_impl,
attrs = {
"deps": attr.label_list(),
...
},
)
यह example_library
नाम के नियम का प्रकार बताता है.
rule
को किए जाने वाले कॉल में यह भी बताया जाना चाहिए कि नियम से, लागू हो सकने वाला आउटपुट (executable=True
के साथ) या खास तौर पर, एक्ज़ीक्यूटेबल टेस्ट (test=True
के साथ) बनता है या नहीं. अगर बाद वाले नियम में, टेस्ट नियम लागू होता है और नियम का नाम _test
से खत्म होना चाहिए.
टारगेट इंस्टैंशिएशन
नियम लोड किए जा सकते हैं और BUILD
फ़ाइलों में कॉल किए जा सकते हैं:
load('//some/pkg:rules.bzl', 'example_library')
example_library(
name = "example_target",
deps = [":another_target"],
...
)
बिल्ड के नियम पर होने वाले हर कॉल से कोई फ़ायदा नहीं मिलता, लेकिन इसका टारगेट तय करने का खराब असर होता है. इसे नियम तुरंत बनाना कहा जाता है. इससे नए टारगेट का नाम और टारगेट के एट्रिब्यूट की वैल्यू के बारे में पता चलता है.
नियमों को Starlark फ़ंक्शन से भी कॉल किया जा सकता है और .bzl
फ़ाइलों में लोड किया जा सकता है.
Starlark के फ़ंक्शन, कॉल के नियमों को स्टारलार्क मैक्रो कहते हैं.
स्टारलार्क मैक्रो को आखिरकार BUILD
फ़ाइलों से कॉल किया जाना चाहिए और उन्हें केवल लोडिंग चरण के दौरान ही कॉल किया जा सकता है, जब BUILD
फ़ाइलों का मूल्यांकन लक्ष्यों को इंस्टैंशिएट करने के लिए किया जाता है.
विशेषताएं
एट्रिब्यूट, नियम का तर्क होता है. विशेषताएं किसी टारगेट के लागू करने के खास मान दे सकती हैं या वे दूसरे टारगेट को रेफ़र कर सकती हैं, जिससे डिपेंडेंसी का ग्राफ़ बनता है.
नियम के खास एट्रिब्यूट, जैसे कि srcs
या deps
को मैप के एट्रिब्यूट के नाम से स्कीमा (attr
मॉड्यूल का इस्तेमाल करके बनाए गए) को rule
के attrs
पैरामीटर में पास करके तय किया जाता है.
name
और visibility
जैसे सामान्य एट्रिब्यूट, सभी नियमों में पूरी तरह जोड़े जाते हैं. खास तौर पर,
खास तौर से लागू होने वाले और टेस्ट के लिए बने नियमों में एट्रिब्यूट जोड़े जाते हैं. किसी नियम में साफ़ तौर पर जोड़े गए एट्रिब्यूट, attrs
को दिए गए शब्दकोश में शामिल नहीं किए जा सकते.
डिपेंडेंसी एट्रिब्यूट
सोर्स कोड प्रोसेस करने वाले नियम, आम तौर पर अलग-अलग तरह की डिपेंडेंसी को मैनेज करने के लिए, ये एट्रिब्यूट तय करते हैं:
srcs
टारगेट फ़ाइलों को प्रोसेस करने वाली सोर्स फ़ाइलें तय करता है. अक्सर, एट्रिब्यूट स्कीमा से पता चलता है कि किस तरह की फ़ाइल एक्सटेंशन, सोर्स फ़ाइल के टाइप के लिए प्रोसेस किए जा सकते हैं. हेडर फ़ाइलों वाली भाषाओं के नियमों में आम तौर पर, टारगेट और उसके उपभोक्ताओं की ओर से प्रोसेस किए गए हेडर के लिए एक अलगhdrs
एट्रिब्यूट तय किया जाता है.deps
किसी टारगेट के लिए कोड डिपेंडेंसी तय करता है. एट्रिब्यूट स्कीमा से पता चलना चाहिए कि उन डिपेंडेंसी को किन कंपनियों को देना चाहिए. (उदाहरण के लिए,cc_library
सेCcInfo
मिलता है.)data
फ़ाइलों को रनटाइम के दौरान किसी भी एक्ज़ीक्यूटेबल पर उपलब्ध कराने के लिए कहा जाता है, जो टारगेट पर निर्भर करता है. इससे आर्बिट्ररी फ़ाइलों को तय करने की अनुमति मिल जाएगी.
example_library = rule(
implementation = _example_library_impl,
attrs = {
"srcs": attr.label_list(allow_files = [".example"]),
"hdrs": attr.label_list(allow_files = [".header"]),
"deps": attr.label_list(providers = [ExampleInfo]),
"data": attr.label_list(allow_files = True),
...
},
)
ये डिपेंडेंसी एट्रिब्यूट के उदाहरण हैं. इनपुट लेबल के बारे में बताने वाला कोई भी एट्रिब्यूट (जो attr.label_list
,
attr.label
या attr.label_keyed_string_dict
से तय होता है) टारगेट और टारगेट के बीच, एक खास तरह की डिपेंडेंसी के बारे में बताता है. ये लेबल, टारगेट (या उनसे जुड़े Label
ऑब्जेक्ट) के लिए, टारगेट तय होने पर उस एट्रिब्यूट में शामिल होते हैं. रिपॉज़िटरी और शायद इन लेबल का पाथ, तय किए गए टारगेट के हिसाब से
समाधान किया जाता है.
example_library(
name = "my_target",
deps = [":other_target"],
)
example_library(
name = "other_target",
...
)
इस उदाहरण में, other_target
, my_target
का डिपेंडेंसी है. इसलिए,
other_target
का विश्लेषण पहले किया जाएगा. अगर टारगेट के डिपेंडेंसी ग्राफ़ में कोई साइकल होती है, तो यह एक गड़बड़ी होती है.
निजी एट्रिब्यूट और डिपेंडेंसी
डिफ़ॉल्ट वैल्यू वाला कोई डिपेंडेंसी एट्रिब्यूट, इंडिपेंडेंट डिपेंडेंसी बनाता है. यह साफ़ तौर पर नहीं दिखता है, क्योंकि यह टारगेट ग्राफ़ का हिस्सा है. इसे उपयोगकर्ता BUILD
फ़ाइल में नहीं डालते. इंप्लिसिट डिपेंडेंसी, नियम और टूल (बिल्ड-टाइम निर्भरता, जैसे कि कंपाइलर) के बीच के संबंध को हार्ड कोड करने के लिए काम की होती हैं. इसकी वजह यह है कि ज़्यादातर मामलों में उपयोगकर्ता, टूल का इस्तेमाल करने में दिलचस्पी नहीं दिखाता. नियम को लागू करने वाले फ़ंक्शन में, इसे दूसरी डिपेंडेंसी के तौर पर माना जाता है.
अगर आपको इंप्लिसिट डिपेंडेंसी देनी है, ताकि उपयोगकर्ता को वह वैल्यू बदलने की अनुमति न मिले, तो एट्रिब्यूट को निजी बनाया जा सकता है. इसके लिए, इसे अंडरस्कोर (_
) से शुरू होने वाला नाम दें. प्राइवेट एट्रिब्यूट में डिफ़ॉल्ट वैल्यू होनी चाहिए. आम तौर पर, इस बात पर निर्भर करने के लिए, निजी एट्रिब्यूट का इस्तेमाल करना ही सही रहता है.
example_library = rule(
implementation = _example_library_impl,
attrs = {
...
"_compiler": attr.label(
default = Label("//tools:example_compiler"),
allow_single_file = True,
executable = True,
cfg = "exec",
),
},
)
इस उदाहरण में, example_library
प्रकार के हर टारगेट का कंपाइलर //tools:example_compiler
पर निर्भर होना ज़रूरी है. इससे, example_library
को लागू करने वाला फ़ंक्शन, ऐसी कार्रवाइयां जनरेट करने में मदद करता है जो कंपाइलर को शुरू करती हैं. भले ही, उपयोगकर्ता ने अपना लेबल इनपुट के तौर पर पास न किया हो. _compiler
एक निजी एट्रिब्यूट है. इसलिए, इस तरह के टारगेट के सभी टारगेट में, ctx.attr._compiler
हमेशा //tools:example_compiler
को दिखाएगा. इसके अलावा, compiler
एट्रिब्यूट को अंडरस्कोर के बिना नाम दिया जा सकता है और डिफ़ॉल्ट वैल्यू रखी जा सकती है. इसकी मदद से, उपयोगकर्ता ज़रूरत पड़ने पर दूसरे कंपाइलर को बदल सकते हैं. हालांकि, इसके लिए कंपाइलर के लेबल के बारे में जानकारी होने की ज़रूरत नहीं होती है.
इंप्लिसिट डिपेंडेंसी का इस्तेमाल, आम तौर पर उन टूल के लिए किया जाता है जो नियम लागू करने की जगह में सेव होते हैं. अगर टूल, कार्रवाई करने के प्लैटफ़ॉर्म या किसी दूसरे डेटा स्टोर करने की जगह से लिया गया है, तो नियम को उस टूल को टूलचेन से लेना चाहिए.
आउटपुट विशेषताएं
आउटपुट एट्रिब्यूट, जैसे कि attr.output
और
attr.output_list
, उस आउटपुट फ़ाइल के बारे में बताते हैं जिसे
टारगेट जनरेट करता है. ये डिपेंडेंसी एट्रिब्यूट से दो तरह से अलग हैं:
- वे आउटपुट फ़ाइल टारगेट को परिभाषित करते हैं, न कि कहीं और तय किए गए टारगेट पर.
- आउटपुट फ़ाइल टारगेट इंस्टैंट नियमों के टारगेट पर निर्भर होती है, इसके बजाय दूसरा तरीका.
आम तौर पर, आउटपुट एट्रिब्यूट का इस्तेमाल सिर्फ़ तब होता है, जब किसी नियम को उपयोगकर्ता के तय किए हुए
उन नामों के साथ आउटपुट बनाने की ज़रूरत होती है जो टारगेट के नाम पर आधारित नहीं होते. अगर किसी नियम में
एक आउटपुट एट्रिब्यूट है, तो उसे आम तौर पर out
या outs
नाम दिया जाता है.
आउटपुट एट्रिब्यूट, पहले से तय किए गए आउटपुट बनाने के पसंदीदा तरीके हैं. इन तरीकों पर खास तौर पर निर्भर किया जा सकता है या कमांड लाइन में अनुरोध किया जा सकता है.
लागू करने की सुविधा
हर नियम के लिए implementation
फ़ंक्शन होना ज़रूरी है. ये फ़ंक्शन पूरी तरह
विश्लेषण के चरण में लागू किए जाते हैं. साथ ही, ये
लोड होने के चरण में जनरेट हुए टारगेट के ग्राफ़ को
कार्रवाई के ग्राफ़ में बदल देते हैं. इन्हें
इस्तेमाल के हिसाब से एक्ज़ीक्यूशन चरण के दौरान किया जाता है. इसकी वजह से, लागू करने का फ़ंक्शन, असल में फ़ाइलों को पढ़ या लिख नहीं सकता.
नियम लागू करने के फ़ंक्शन आम तौर पर निजी होते हैं (इन्हें एक अंडरस्कोर का नाम दिया जाता है). आम तौर पर, इनका नाम उनके नियम जैसा ही होता है. हालांकि, इनके नाम का मतलब _impl
होता है.
लागू करने के फ़ंक्शन में एक पैरामीटर शामिल होता है: नियम का संदर्भ, जिसे आम तौर पर ctx
कहा जाता है. वे मोबाइल और इंटरनेट सेवा देने वाली कंपनियों
की सूची दिखाते हैं.
टारगेट
डिपेंडेंसी, विश्लेषण के समय Target
ऑब्जेक्ट के तौर पर दिखाई जाती हैं. इन ऑब्जेक्ट में, टारगेट करने वाले एपीआई को लागू करने के दौरान जनरेट हुई सेवा देने वाली कंपनियां शामिल होती हैं.
ctx.attr
में हर डिपेंडेंसी एट्रिब्यूट के नाम से जुड़े फ़ील्ड होते हैं. इसमें, उस एट्रिब्यूट के ज़रिए सीधे तौर पर डिपेंडेंसी दिखाने वाले Target
ऑब्जेक्ट होते हैं. label_list
एट्रिब्यूट के लिए, यह
Targets
की सूची है. label
एट्रिब्यूट के लिए, यह सिर्फ़ Target
या None
होता है.
प्रोवाइडर ऑब्जेक्ट की सूची, टारगेट लागू करने के फ़ंक्शन के ज़रिए दी जाती है:
return [ExampleInfo(headers = depset(...))]
उन्हें इंडेक्स नोटेशन ([]
) का इस्तेमाल करके, कुंजी के तौर पर सेवा देने वाली कंपनी के प्रकार
के साथ ऐक्सेस किया जा सकता है. इनमें Starlark में दी गई पसंद के मुताबिक सेवा देने वाली कंपनियां या
Starlark के ग्लोबल वैरिएबल के तौर पर उपलब्ध सेवा देने वाली स्थानीय कंपनियां शामिल हैं.
उदाहरण के लिए, अगर कोई नियम hdrs
एट्रिब्यूट के ज़रिए हेडर फ़ाइलें लेता है और उन्हें टारगेट और उसके उपभोक्ताओं की कंपाइलेशन कार्रवाइयों के बारे में बताता है, तो यह उन्हें इस तरह इकट्ठा कर सकता है:
def _example_library_impl(ctx):
...
transitive_headers = [hdr[ExampleInfo].headers for hdr in ctx.attr.hdrs]
उस लेगसी स्टाइल के लिए जिसमें टारगेट किए गए फ़ंक्शन से struct
को मिलता है, न कि प्रोवाइडर ऑब्जेक्ट की सूची से:
return struct(example_info = struct(headers = depset(...)))
Target
ऑब्जेक्ट से जुड़े फ़ील्ड से सेवा देने वाली कंपनियों को वापस लाया जा सकता है:
transitive_headers = [hdr.example_info.headers for hdr in ctx.attr.hdrs]
इस स्टाइल का इस्तेमाल करने की सलाह बिल्कुल नहीं दी जाती. इसलिए, नियमों को यहां से हटा देना चाहिए.
फ़ाइलें
फ़ाइलों को File
ऑब्जेक्ट से दिखाया जाता है. बेज़ल, विश्लेषण के दौरान
फ़ाइल I/O परफ़ॉर्म नहीं करता है. इसलिए, इन ऑब्जेक्ट का इस्तेमाल
सीधे फ़ाइल का कॉन्टेंट पढ़ने या लिखने के लिए नहीं किया जा सकता. इसके बजाय, उन्हें कार्रवाई ग्राफ़ बनाने के फ़ंक्शन (ctx.actions
देखें) में पास किया जाता है, ताकि ऐक्शन ग्राफ़ के हिस्से बनाए जा सकें.
File
एक सोर्स फ़ाइल या कोई जनरेट की गई फ़ाइल हो सकती है. हर एक जनरेट की गई फ़ाइल ठीक एक कार्रवाई का आउटपुट होनी चाहिए. सोर्स फ़ाइलें किसी भी कार्रवाई का आउटपुट
नहीं हो सकतीं.
हर डिपेंडेंसी एट्रिब्यूट के लिए, ctx.files
के संबंधित फ़ील्ड में उस एट्रिब्यूट के ज़रिए सभी डिपेंडेंसी के डिफ़ॉल्ट आउटपुट की सूची मौजूद होती है:
def _example_library_impl(ctx):
...
headers = depset(ctx.files.hdrs, transitive=transitive_headers)
srcs = ctx.files.srcs
...
ctx.file
में, डिपेंडेंसी के लिए एक File
या None
एट्रिब्यूट होता है. इस एट्रिब्यूट की विशेषताएं allow_single_file=True
पर सेट होती हैं.
ctx.executable
का काम ctx.file
की तरह ही होता है, लेकिन इसमें
डिपेंडेंसी एट्रिब्यूट के लिए सिर्फ़ ऐसे फ़ील्ड शामिल होते हैं जिनकी विशेषताएं executable=True
सेट होती हैं.
आउटपुट की जानकारी देना
विश्लेषण के दौरान, किसी नियम को लागू करने का फ़ंक्शन आउटपुट जनरेट कर सकता है.
लोड होने के दौरान, सभी लेबल के बारे में जानकारी होनी ज़रूरी है. इसलिए, इन दूसरे आउटपुट में कोई लेबल नहीं होता. आउटपुट के लिए File
ऑब्जेक्ट,
ctx.actions.declare_file
और
ctx.actions.declare_directory
का इस्तेमाल करके बनाए जा सकते हैं. अक्सर,
आउटपुट के नाम, टारगेट के नाम पर आधारित होते हैं,
ctx.label.name
:
def _example_library_impl(ctx):
...
output_file = ctx.actions.declare_file(ctx.label.name + ".output")
...
आउटपुट एट्रिब्यूट के लिए बनाए गए आउटपुट जैसा, पहले से बताए गए आउटपुट के लिए, File
ऑब्जेक्ट, ctx.outputs
से जुड़े फ़ील्ड से वापस पाए जा सकते हैं.
कार्रवाइयां
किसी कार्रवाई के ज़रिए इनपुट के सेट से आउटपुट का सेट जनरेट करने का तरीका बताया गया है, जैसे कि "hello.c पर gcc चलाएं और hello.o पाएं". कोई कार्रवाई बनाने पर, Bazel तुरंत कोई निर्देश नहीं देता. यह इसे डिपेंडेंसी के ग्राफ़ में रजिस्टर करता है, क्योंकि कोई कार्रवाई किसी दूसरी कार्रवाई के आउटपुट पर निर्भर हो सकती है. उदाहरण के लिए, C में लिंकर को कंपाइलर के बाद कॉल किया जाना चाहिए.
ctx.actions
में, इस्तेमाल करने के अलग-अलग कामों के बारे में बताया गया है:
- एक्ज़ीक्यूटेबल चलाने के लिए,
ctx.actions.run
. - शेल कमांड चलाने के लिए
ctx.actions.run_shell
. ctx.actions.write
पर क्लिक करें.- टेंप्लेट से फ़ाइल जनरेट करने के लिए,
ctx.actions.expand_template
.
ctx.actions.args
का इस्तेमाल कार्रवाइयों के लिए तर्कों को बेहतर तरीके से
इकट्ठा करने के लिए किया जा सकता है. यह स्क्रिप्ट एक्ज़ीक्यूशन के समय तक फ़्लैट होने से बचती है:
def _example_library_impl(ctx):
...
transitive_headers = [dep[ExampleInfo].headers for dep in ctx.attr.deps]
headers = depset(ctx.files.hdrs, transitive=transitive_headers)
srcs = ctx.files.srcs
inputs = depset(srcs, transitive=[headers])
output_file = ctx.actions.declare_file(ctx.label.name + ".output")
args = ctx.actions.args()
args.add_joined("-h", headers, join_with=",")
args.add_joined("-s", srcs, join_with=",")
args.add("-o", output_file)
ctx.actions.run(
mnemonic = "ExampleCompile",
executable = ctx.executable._compiler,
arguments = [args],
inputs = inputs,
outputs = [output_file],
)
...
इन कार्रवाइयों में, इनपुट फ़ाइलों की सूची या डिपसेट इस्तेमाल किया जाता है. साथ ही, आउटपुट फ़ाइलों की ऐसी सूची जनरेट की जाती है जो खाली न हो. इनपुट और आउटपुट फ़ाइलों का सेट विश्लेषण फ़ेज़ के दौरान पता होना चाहिए. यह डिपेंडेंसी के एट्रिब्यूट के साथ-साथ, दूसरी विशेषताओं के वैल्यू पर निर्भर कर सकता है. हालांकि, यह एक्ज़ीक्यूशन के नतीजे पर निर्भर नहीं कर सकता. उदाहरण के लिए, अगर आपकी कार्रवाई में zip कमांड नहीं है, तो आपको यह बताना होगा कि किन फ़ाइलों के खुलने की संभावना है. (अनज़िप करने से पहले). जो कार्रवाइयां आंतरिक रूप से वैरिएबल संख्या में बनाई जाती हैं, उन्हें एक ही फ़ाइल (जैसे कि zip, tar या अन्य संग्रह फ़ॉर्मैट) में रैप किया जा सकता है.
कार्रवाइयों में उनके सभी इनपुट की सूची होनी चाहिए. इस्तेमाल नहीं की जाने वाली लिस्टिंग इनपुट की अनुमति है, लेकिन यह असरदार नहीं है.
कार्रवाइयों को अपने सभी आउटपुट बनाने होंगे. वे दूसरी फ़ाइलें भी लिख सकते हैं. हालांकि, इनमें से कोई भी चीज़ उपभोक्ताओं के लिए उपलब्ध नहीं होगी. एलान किए गए सभी आउटपुट किसी कार्रवाई से लिखे जाने चाहिए.
कार्रवाइयों की तुलना शुद्ध फ़ंक्शन के साथ की जा सकती है: उन्हें सिर्फ़ दिए गए इनपुट पर निर्भर करना चाहिए. साथ ही, उन्हें कंप्यूटर की जानकारी, उपयोगकर्ता नाम, घड़ी, नेटवर्क या I/O डिवाइसों को ऐक्सेस करने से बचना चाहिए (सिर्फ़ इनपुट और लिखने के आउटपुट को छोड़कर). यह इसलिए ज़रूरी है, क्योंकि आउटपुट को कैश मेमोरी में सेव किया जाएगा और उसे दोबारा इस्तेमाल किया जाएगा.
डिपेंडेंसी का समाधान, बेज़ल से होता है. इससे तय होता है कि कौनसी कार्रवाई करनी है. अगर डिपेंडेंसी ग्राफ़ में कोई साइकल है, तो यह गड़बड़ी दिखेगी. किसी भी कार्रवाई का निर्माण इस बात की गारंटी नहीं देता है कि उसे निष्पादित किया जाएगा, जो इस बात पर निर्भर करता है कि उसका आउटपुट बिल्ड के लिए आवश्यक है या नहीं.
सेवा देने वाली कंपनियां
सेवा देने वाली कंपनियां वे जानकारी होती हैं जो किसी नियम पर निर्भर करती हैं और जो उस पर निर्भर करती हैं. इस डेटा में आउटपुट फ़ाइलें, लाइब्रेरी, टूल की कमांड लाइन से पास होने वाले पैरामीटर या टारगेट ऑडियंस के बारे में ऐसी कोई भी जानकारी शामिल हो सकती है जिसके बारे में उपभोक्ताओं को पता होना चाहिए.
नियम लागू करने का फ़ंक्शन, टारगेट किए गए टारगेट की डिपेंडेंसी से ही, सेवा देने वाली कंपनियों को पढ़ सकता है. इसलिए, नियमों को टारगेट की डिपेंडेंसी से जुड़ी ऐसी कोई भी जानकारी फ़ॉरवर्ड करना ज़रूरी है जो टारगेट के उपभोक्ताओं को पता होना चाहिए. आम तौर पर, यह जानकारी इकट्ठा करके depset
बनाई जाती है.
टारगेट फ़ंक्शन देने वालों की सूची, ऑब्जेक्ट को लागू करने के फ़ंक्शन से मिली Provider
ऑब्जेक्ट की सूची से होती है.
लागू करने के पुराने फ़ंक्शन भी, लेगसी शैली में लिखे जा सकते हैं. इसमें फ़ंक्शन लागू करने के लिए, फ़ंक्शन की सूची के बजाय, struct
दिया जाता है. इस स्टाइल का इस्तेमाल करने की सलाह बिल्कुल नहीं दी जाती. इसलिए, नियमों को यहां से हटा देना चाहिए.
डिफ़ॉल्ट आउटपुट
टारगेट के डिफ़ॉल्ट आउटपुट वे आउटपुट हैं जिनका अनुरोध डिफ़ॉल्ट रूप से, कमांड लाइन में बिल्ड के लिए अनुरोध करने पर किया जाता है. उदाहरण के लिए, java_library
टारगेट //pkg:foo
में डिफ़ॉल्ट आउटपुट के तौर पर foo.jar
डेटा होता है, ताकि
bazel build //pkg:foo
कमांड से बनाया जा सके.
डिफ़ॉल्ट आउटपुट, DefaultInfo
के files
पैरामीटर से तय किए जाते हैं:
def _example_library_impl(ctx):
...
return [
DefaultInfo(files = depset([output_file]), ...),
...
]
अगर किसी नियम को लागू करने पर DefaultInfo
नहीं दिखता है या files
पैरामीटर नहीं दिया जाता है, तो DefaultInfo.files
डिफ़ॉल्ट रूप से सभी पहले से तय किए गए आउटपुट पर सेट होता है. ये आम तौर पर आउटपुट एट्रिब्यूट से बनाए जाते हैं.
कार्रवाइयां करने वाले नियमों से डिफ़ॉल्ट आउटपुट मिलने चाहिए, भले ही वे आउटपुट सीधे इस्तेमाल न किए जाते हों. उन कार्रवाइयों को काट दिया जाता है जिनके अनुरोध किए गए आउटपुट के ग्राफ़ में शामिल नहीं होते हैं. अगर किसी आउटपुट का इस्तेमाल सिर्फ़ टारगेट के ग्राहक ही करते हैं, तो टारगेट को आइसोलेट करके बनाने पर, ये कार्रवाइयां नहीं की जाएंगी. इससे डीबग करना ज़्यादा मुश्किल हो जाता है, क्योंकि सिर्फ़ काम न कर पाने वाले टारगेट को फिर से बनाने से, फ़ेलिंग दोबारा नहीं होगी.
रनफ़ाइल
रनफ़ाइल, रनटाइम के समय इस्तेमाल की जाने वाली ऐसी फ़ाइलों का सेट होती हैं जिन्हें टारगेट करने के लिए इस्तेमाल किया जाता है. प्लान के चलने की अवधि के दौरान, बेज़ल एक डायरेक्ट्री ट्री बनाता है, जिसमें रनफ़ाइल की ओर सिमलिंक होते हैं. यह बाइनरी के लिए एनवायरमेंट को स्टेज करता है, ताकि यह रनटाइम के दौरान रनफ़ाइल ऐक्सेस कर सके.
रनफ़ाइल को नियम बनाते समय मैन्युअल रूप से जोड़ा जा सकता है.
runfiles
ऑब्जेक्ट, नियम के संदर्भ में runfiles
तरीके से बनाए जा सकते हैं, ctx.runfiles
. इसके बाद, उन्हें DefaultInfo
पर runfiles
पैरामीटर में पास किया जा सकता है. लागू किए जा सकने वाले नियमों के एक्ज़ीक्यूटेबल आउटपुट को रनप्ले की फ़ाइल में जोड़ा जाता है.
कुछ नियमों में उन एट्रिब्यूट के बारे में बताया गया है जिन्हें आम तौर पर
data
कहा जाता है. इनके आउटपुट टारगेट की रनफ़ाइल
में जोड़े जाते हैं. रनफ़ाइल को data
के साथ-साथ उन सभी एट्रिब्यूट से भी मर्ज किया जाना चाहिए जो शायद एक्ज़ीक्यूट करने के लिए कोड दे सकते हैं. आम तौर पर, इसमें srcs
शामिल होता है, जिसमें data
के साथ जुड़े data
टारगेट शामिल हो सकते हैं.
def _example_library_impl(ctx):
...
runfiles = ctx.runfiles(files = ctx.files.data)
transitive_runfiles = []
for runfiles_attr in (
ctx.attr.srcs,
ctx.attr.hdrs,
ctx.attr.deps,
ctx.attr.data,
):
for target in runfiles_attr:
transitive_runfiles.append(target[DefaultInfo].default_runfiles)
runfiles = runfiles.merge_all(transitive_runfiles)
return [
DefaultInfo(..., runfiles = runfiles),
...
]
सेवा देने वाली पसंदीदा कंपनियां
सेवा देने वाली कंपनियों के बारे में जानकारी देने के लिए provider
फ़ंक्शन का इस्तेमाल किया जा सकता है. इससे नियम के बारे में खास जानकारी मिलती है:
ExampleInfo = provider(
"Info needed to compile/link Example code.",
fields={
"headers": "depset of header Files from transitive dependencies.",
"files_to_link": "depset of Files from compilation.",
})
नियम लागू करने की प्रोसेस के बाद, कंपनी के इंस्टेंस बनाए और लौटाए जा सकते हैं:
def _example_library_impl(ctx):
...
return [
...
ExampleInfo(
headers = headers,
files_to_link = depset(
[output_file],
transitive = [
dep[ExampleInfo].files_to_link for dep in ctx.attr.deps
],
),
)
]
सेवा देने वाली कंपनियों को अपने हिसाब से सेट करें
हो सकता है कि अपनी पसंद के मुताबिक प्रीप्रोसेसिंग और पुष्टि करने वाले लॉजिक से, सेवा देने वाली किसी कंपनी का इंस्टैंट इस्तेमाल सुरक्षित रहे. इसका इस्तेमाल यह पक्का करने के लिए किया जा सकता है कि सेवा देने वाली सभी कंपनियां कुछ खास वैरिएंट का पालन करती हैं या उपयोगकर्ताओं को किसी इंस्टेंस को समझने के लिए ज़्यादा बेहतर एपीआई देने के लिए.
ऐसा करने के लिए, provider
फ़ंक्शन में init
कॉलबैक पास करें. अगर यह कॉलबैक दिया जाता है, तो
provider()
की रिटर्न वैल्यू, दो वैल्यू के ट्यूल के तौर पर बदल जाती है: प्रोवाइडर
का निशान, जो init
के इस्तेमाल न होने पर सामान्य रिटर्न वैल्यू होती है और "रॉ
कंस्ट्रक्टर".
इस मामले में, जब सेवा देने वाली कंपनी के चिह्न को कॉल किया जाता है, तो एक नए इंस्टेंस के बजाय सीधे init
कॉलबैक पर फ़ॉरवर्ड किए गए आर्ग्युमेंट को भेज दिया जाता है. कॉलबैक
की रिटर्न वैल्यू, वैल्यू के लिए एक मैप मैपिंग फ़ील्ड नाम (स्ट्रिंग) होनी चाहिए;
इसका इस्तेमाल नए इंस्टेंस के फ़ील्ड को शुरू करने के लिए किया जाता है. ध्यान दें कि
कॉलबैक में कोई भी हस्ताक्षर हो सकता है और अगर तर्क हस्ताक्षर से मेल नहीं खाते हैं, तो गड़बड़ी की
जानकारी दी जाती है जैसे कि कॉलबैक को सीधे तौर पर शुरू किया गया था.
वहीं, रॉ कंस्ट्रक्टर, init
कॉलबैक को बायपास करेगा.
यह उदाहरण, अपने आर्ग्युमेंट को पहले से प्रोसेस करने और उनकी पुष्टि करने के लिए, init
का इस्तेमाल करता है:
# //pkg:exampleinfo.bzl
_core_headers = [...] # private constant representing standard library files
# It's possible to define an init accepting positional arguments, but
# keyword-only arguments are preferred.
def _exampleinfo_init(*, files_to_link, headers = None, allow_empty_files_to_link = False):
if not files_to_link and not allow_empty_files_to_link:
fail("files_to_link may not be empty")
all_headers = depset(_core_headers, transitive = headers)
return {'files_to_link': files_to_link, 'headers': all_headers}
ExampleInfo, _new_exampleinfo = provider(
...
init = _exampleinfo_init)
export ExampleInfo
नियम लागू करने से, सेवा देने वाली कंपनी का पता इस तरह से लगाया जा सकता है:
ExampleInfo(
files_to_link=my_files_to_link, # may not be empty
headers = my_headers, # will automatically include the core headers
)
रॉ कंस्ट्रक्टर का इस्तेमाल ऐसे वैकल्पिक सार्वजनिक फ़ैक्ट्री फ़ंक्शन को तय करने के लिए किया जा सकता है जो init
लॉजिक से नहीं गुज़रता है. उदाहरण के लिए, exampleinfo.bzl में हम
यह तय कर सकते हैं:
def make_barebones_exampleinfo(headers):
"""Returns an ExampleInfo with no files_to_link and only the specified headers."""
return _new_exampleinfo(files_to_link = depset(), headers = all_headers)
आम तौर पर, रॉ कंस्ट्रक्टर ऐसे वैरिएबल को सीमित करता है जिसका नाम अंडरस्कोर (_new_exampleinfo
के ऊपर) से शुरू होता है, ताकि उपयोगकर्ता कोड उसे लोड न कर सके. साथ ही, वह आर्बिट्रेरी प्रोवाइडर इंस्टेंस जनरेट न कर सके.
init
का इस्तेमाल दूसरे तरीकों से भी किया जा सकता है. इससे, उपयोगकर्ता को सेवा देने वाली कंपनी के सिंबल
को पूरी तरह से कॉल करने से रोका जा सकता है. साथ ही, उसे फ़ैक्ट्री फ़ंक्शन का इस्तेमाल करने के लिए मजबूर किया जा सकता है:
def _exampleinfo_init_banned(*args, **kwargs):
fail("Do not call ExampleInfo(). Use make_exampleinfo() instead.")
ExampleInfo, _new_exampleinfo = provider(
...
init = _exampleinfo_init_banned)
def make_exampleinfo(...):
...
return _new_exampleinfo(...)
एक्ज़ीक्यूटेबल नियम और टेस्ट नियम
एक्ज़ीक्यूटेबल नियमों से उन टारगेट के बारे में पता चलता है जिन्हें bazel run
कमांड से शुरू किया जा सकता है.
जांच के नियम एक खास तरह के एक्ज़ीक्यूटेबल नियम होते हैं, जिनके टारगेट को bazel test
निर्देश से भी शुरू किया जा सकता है. एक्ज़ीक्यूटेबल और टेस्ट के नियम,
executable
या
test
आर्ग्युमेंट को rule
कॉल में True
पर सेट करके बनाए जाते हैं:
example_binary = rule(
implementation = _example_binary_impl,
executable = True,
...
)
example_test = rule(
implementation = _example_binary_impl,
test = True,
...
)
जांच के नियमों में _test
से खत्म होने वाले नाम होने चाहिए. (टेस्ट के लिए टारगेट के नाम, अक्सर कन्वेंशन के हिसाब से _test
पर भी खत्म होते हैं. हालांकि, ऐसा करना ज़रूरी नहीं है.) बिना टेस्ट वाले नियमों में यह सफ़िक्स
नहीं होना चाहिए.
दोनों तरह के नियमों के लिए, एक्ज़ीक्यूटेबल आउटपुट फ़ाइल का इस्तेमाल करना ज़रूरी है. यह फ़ाइल, run
या test
निर्देशों के मुताबिक ही शुरू की जा सकती है और नहीं भी. executable
Bazel में यह बताने के लिए कि नियम के किन आउटपुट को इस एक्ज़ीक्यूटेबल के तौर पर इस्तेमाल करना है, इसे
रिटर्न किए गए DefaultInfo
प्रोवाइडर के executable
आर्ग्युमेंट के तौर पर पास करें. इस executable
को नियम के डिफ़ॉल्ट आउटपुट में जोड़ा जाता है (ताकि आपको executable
और files
, दोनों को पास करने की ज़रूरत न पड़े). इसे रन फ़ाइल में भी जोड़ा जाता है:
def _example_binary_impl(ctx):
executable = ctx.actions.declare_file(ctx.label.name)
...
return [
DefaultInfo(executable = executable, ...),
...
]
इस फ़ाइल को जनरेट करने वाली कार्रवाई, फ़ाइल पर एक्ज़ीक्यूटेबल बिट को सेट करना चाहिए. ctx.actions.run
या ctx.actions.run_shell
कार्रवाई के लिए, कार्रवाई शुरू करने वाले बुनियादी टूल का इस्तेमाल करना चाहिए. ctx.actions.write
की
कार्रवाई के लिए, is_executable=True
पास करें.
लेगसी व्यवहार के तौर पर, एक्ज़ीक्यूटेबल नियमों में खास ctx.outputs.executable
पहले से बताया गया आउटपुट होता है. अगर आप DefaultInfo
का इस्तेमाल करके कोई फ़ाइल नहीं डालते हैं, तो यह फ़ाइल, डिफ़ॉल्ट एक्ज़ीक्यूटेबल के तौर पर काम करेगी; इसका इस्तेमाल नहीं किया जाना चाहिए. आउटपुट के इस तरीके को बंद कर दिया गया है, क्योंकि यह विश्लेषण के समय एक्ज़ीक्यूटेबल फ़ाइल के नाम को पसंद के मुताबिक नहीं बनाता है.
लागू होने वाले नियम और जांच के नियम के उदाहरण देखें.
लागू किए जा सकने वाले नियमों और जांच के नियमों में, सभी नियमों के लिए जोड़े गए एट्रिब्यूट के साथ-साथ, खास तौर पर अन्य एट्रिब्यूट भी शामिल होते हैं. डिफ़ॉल्ट रूप से जोड़े गए एट्रिब्यूट की डिफ़ॉल्ट सेटिंग बदली नहीं जा सकतीं. हालांकि, इसे स्टारलार्क मैक्रो में निजी नियम रैप करके किया जा सकता है, जो डिफ़ॉल्ट को बदल देता है:
def example_test(size="small", **kwargs):
_example_test(size=size, **kwargs)
_example_test = rule(
...
)
रनफ़ाइल स्थान
अगर bazel run
या test
के साथ एक्ज़ीक्यूटेबल टारगेट चलाया जाता है, तो रूट फ़ाइल की डायरेक्ट्री का रूट, एक्ज़ीक्यूटेबल के बगल में हो जाता है. पाथ इस तरह से जोड़े जाते हैं:
# Given launcher_path and runfile_file:
runfiles_root = launcher_path.path + ".runfiles"
workspace_name = ctx.workspace_name
runfile_path = runfile_file.short_path
execution_root_relative_path = "%s/%s/%s" % (
runfiles_root, workspace_name, runfile_path)
रनफ़ाइल डायरेक्ट्री में File
का पाथ
File.short_path
से जुड़ा होता है.
सीधे bazel
से लागू की गई बाइनरी, runfiles
डायरेक्ट्री के रूट के पास है. हालांकि, रनफ़ाइल से बाइनरी के लिए एक ही अनुमान नहीं लगाया जा सकता. इसे कम करने के लिए, हर बाइनरी को एनवायरमेंट या कमांड लाइन आर्ग्युमेंट/फ़्लैग का इस्तेमाल करके, पैरामीटर के तौर पर अपनी रनफ़ाइल रूट को स्वीकार करने की सुविधा देनी चाहिए. ऐसा करने से, बाइनरी फ़ाइल, बाइनरी फ़ाइल के रूट के लिए, सही कैननिकल फ़ाइल रूट को पास कर पाती है. अगर इस वैल्यू को सेट नहीं किया जाता है, तो बाइनरी अनुमान लगा सकती है कि यह पहला बाइनरी है. इसके बाद, यह पास की रनफ़ाइल डायरेक्ट्री ढूंढती है.
उन्नत विषय
आउटपुट फ़ाइलों का अनुरोध करना
एक टारगेट में कई आउटपुट फ़ाइलें हो सकती हैं. bazel build
निर्देश चलाए जाने पर, उस निर्देश के लिए दिए गए टारगेट के कुछ आउटपुट अनुरोध किए गए माने जाते हैं. बेज़ेल सिर्फ़ इन फ़ाइलों को बनाता है. साथ ही, वे फ़ाइलें भी जनरेट करता है जो सीधे तौर पर या किसी दूसरे तरीके से, इस बात पर निर्भर करती हैं. (ऐक्शन ग्राफ़ की बात करें, तो बेज़ल सिर्फ़ उन कार्रवाइयों को एक्ज़ीक्यूट करता है जिन तक
अनुरोध की गई फ़ाइलों की, निर्भरता
के आधार पर पहुंचा जा सकता है.)
डिफ़ॉल्ट आउटपुट के अलावा, कमांड लाइन पर किसी भी पहले से तय आउटपुट के लिए साफ़ तौर पर अनुरोध किया जा सकता है. नियम, आउटपुट एट्रिब्यूट के ज़रिए पहले से तय किए गए आउटपुट के बारे में बता सकते हैं. ऐसे में उपयोगकर्ता, नियम को
इंस्टैंशिएट करते समय, आउटपुट के लिए साफ़ तौर पर लेबल चुनता है. आउटपुट एट्रिब्यूट के लिए
File
ऑब्जेक्ट पाने के लिए, ctx.outputs
की मिलती-जुलती विशेषता का इस्तेमाल करें. नियमों में, टारगेट के नाम के हिसाब से पहले से बताए गए आउटपुट के बारे में भी बताया जा सकता है. हालांकि, यह सुविधा बंद कर दी गई है.
डिफ़ॉल्ट आउटपुट के अलावा, आउटपुट समूह भी होते हैं, जो ऐसी आउटपुट फ़ाइलों के संग्रह होते हैं जिनका एक साथ अनुरोध किया जा सकता है. इनके लिए अनुरोध किया जा सकता है
--output_groups
. उदाहरण के लिए, अगर //pkg:mytarget
टारगेट किसी ऐसे नियम टाइप का है जिसमें debug_files
आउटपुट ग्रुप है, तो इन फ़ाइलों को bazel build //pkg:mytarget
--output_groups=debug_files
चलाकर बनाया जा सकता है. जिन नामों के बारे में पहले से जानकारी नहीं दी गई है उन पर लेबल नहीं लगाए जाते,
इसलिए उनका अनुरोध सिर्फ़ डिफ़ॉल्ट आउटपुट या आउटपुट समूह
में किया जा सकता है.
आउटपुट ग्रुप, OutputGroupInfo
की सेवा देने वाली कंपनी के साथ तय किए जा सकते हैं. ध्यान दें कि पहले से मौजूद कई कंपनियों के उलट, OutputGroupInfo
आर्बिट्ररी नाम वाले पैरामीटर ले सकता है, ताकि नाम वाले आउटपुट ग्रुप तय किए जा सकें:
def _example_library_impl(ctx):
...
debug_file = ctx.actions.declare_file(name + ".pdb")
...
return [
DefaultInfo(files = depset([output_file]), ...),
OutputGroupInfo(
debug_files = depset([debug_file]),
all_files = depset([output_file, debug_file]),
),
...
]
इसके अलावा, कई कंपनियों के उलट, OutputGroupInfo
असर और उस नियम को चुना जा सकता है जिस पर वह पहलू लागू होता है. हालांकि, ऐसा तब तक होता है, जब तक वे एक ही आउटपुट ग्रुप के बारे में नहीं बताते. इस मामले में, सेवा देने वाली
कंपनी को मर्ज कर दिया जाता है.
ध्यान रखें कि आम तौर पर OutputGroupInfo
का इस्तेमाल, टारगेट से लेकर उपभोक्ताओं की कार्रवाइयों की खास तरह की फ़ाइलों को बताने के लिए नहीं किया जाना चाहिए. इसके बजाय, नियम लागू करने वाली कंपनियों के बारे में बताएं.
कॉन्फ़िगरेशन
मान लें कि आपको किसी दूसरे आर्किटेक्चर के लिए, C++ बाइनरी बनाना है. बिल्ड मुश्किल हो सकता है. इसमें कई चरण शामिल हो सकते हैं. कुछ इंटरमीडिएटर बाइनरी, जैसे कि कंपाइलर और कोड जनरेटर, एक्ज़ीक्यूशन प्लैटफ़ॉर्म (जो आपके होस्ट या किसी रिमोट एक्ज़ीक्यूटर हो सकते हैं) पर चलने पड़ते हैं. लक्ष्य आउटपुट के लिए अंतिम आउटपुट जैसी कुछ बाइनरी बनाई गई होनी चाहिए.
इसी वजह से, Bazel में "कॉन्फ़िगरेशन" और ट्रांज़िशन का एक कॉन्सेप्ट है. सबसे ज़्यादा टारगेट (कमांड लाइन पर अनुरोध किए गए टारगेट) को "टारगेट" कॉन्फ़िगरेशन में बनाया जाता है, जबकि एक्ज़ीक्यूशन प्लैटफ़ॉर्म पर चलने वाले टूल को "exec" कॉन्फ़िगरेशन में बनाया जाता है. कॉन्फ़िगरेशन के आधार पर, नियम अलग-अलग तरह की कार्रवाइयां जनरेट कर सकते हैं. उदाहरण के लिए, कंपाइलर को पास किए गए सीपीयू आर्किटेक्चर को बदलने के लिए. कुछ मामलों में, अलग-अलग कॉन्फ़िगरेशन के लिए एक ही लाइब्रेरी की ज़रूरत हो सकती है. अगर ऐसा होता है, तो उसका विश्लेषण किया जाएगा और उसे कई बार बनाया जा सकता है.
डिफ़ॉल्ट रूप से, Bazel उसी टारगेट पर निर्भरता बनाता है, जिसकी सेटिंग टारगेट के रूप में होती है. दूसरे शब्दों में, वह ट्रांज़िशन के बिना होता है. जब कोई डिपेंडेंसी कोई ऐसा टूल होता है जिसकी मदद से टारगेट बनाया जाता है, तो उससे जुड़े एट्रिब्यूट से किसी खास कॉन्फ़िगरेशन में ट्रांज़िशन की जानकारी देनी चाहिए. इससे, एक्ज़ीक्यूशन प्लैटफ़ॉर्म के लिए टूल और सभी डिपेंडेंसी बन जाती हैं.
हर डिपेंडेंसी एट्रिब्यूट के लिए, cfg
का इस्तेमाल करके यह तय किया जा सकता है कि डिपेंडेंसी एक ही कॉन्फ़िगरेशन में बनाई जानी चाहिए या किसी खास कॉन्फ़िगरेशन में ट्रांज़िशन होनी चाहिए.
अगर डिपेंडेंसी एट्रिब्यूट में executable=True
का फ़्लैग है, तो cfg
को साफ़ तौर पर सेट किया जाना चाहिए. ऐसा इसलिए किया जाता है, ताकि गलती से, गलत कॉन्फ़िगरेशन के लिए टूल न बनाया जा सके.
उदाहरण देखें
आम तौर पर, रनटाइम के दौरान जिन सोर्स, डिपेंडेंट लाइब्रेरी, और एक्ज़ीक्यूटेबल की ज़रूरत होती है वे एक ही कॉन्फ़िगरेशन का इस्तेमाल कर सकते हैं.
बिल्ड के हिस्से के तौर पर चलाए जाने वाले टूल (जैसे कि कंपाइलर या कोड जनरेटर)
इसे एक खास कॉन्फ़िगरेशन के लिए बनाया जाना चाहिए. इस मामले में, एट्रिब्यूट में
cfg="exec"
बताएं.
इसके अलावा, रनटाइम के दौरान इस्तेमाल किए जा सकने वाले एक्ज़ीक्यूटेबल को (जैसे कि टेस्ट के हिस्से के रूप में) टारगेट कॉन्फ़िगरेशन के लिए बनाया जाना चाहिए. इस मामले में, एट्रिब्यूट में
cfg="target"
बताएं.
cfg="target"
असल में कुछ नहीं करता: यह पूरी तरह से एक सुविधा की तरह है, ताकि नियम डिज़ाइनर का इरादा उनके इरादों के बारे में साफ़ तौर पर पता चल सके. जब executable=False
,
इसका मतलब है कि cfg
ज़रूरी नहीं है, तो इसे सिर्फ़ तब सेट करें, जब इसे पढ़ना आसान हो.
cfg=my_transition
उपयोगकर्ता के हिसाब से ट्रांज़िशन का इस्तेमाल करने के लिए भी cfg=my_transition
का इस्तेमाल किया जा सकता है. इसकी मदद से, नियम बनाने वाले लोग, कॉन्फ़िगरेशन में बदलाव करने में काफ़ी मदद करते हैं. साथ ही, बनाने वाले ग्राफ़ को बड़ा करके, आसानी से समझ में आने लायक होने की सुविधा मिलती है.
ध्यान दें: पुराने तौर पर, Bazel में एक्ज़ीक्यूशन प्लैटफ़ॉर्म का कॉन्सेप्ट नहीं था. इसके बजाय, इसे बनाने की सभी कार्रवाइयों को होस्ट मशीन पर दिखाया जाता था. Bazel के 6.0 वर्शन से पहले के वर्शन में, इसे दिखाने के लिए एक अलग "होस्ट" कॉन्फ़िगरेशन बनाया गया था. अगर आपको कोड या पुराने दस्तावेज़ में "होस्ट" के रेफ़रंस दिखते हैं, तो इसका मतलब ही होता है. हमारा सुझाव है कि कॉन्सेप्ट के ऊपर लगने वाले अतिरिक्त कॉन्सेप्ट से बचने के लिए, आप Bazel 6.0 या इसके बाद के वर्शन का इस्तेमाल करें.
कॉन्फ़िगरेशन फ़्रैगमेंट
नियम
कॉन्फ़िगरेशन फ़्रैगमेंट को ऐक्सेस कर सकते हैं, जैसे कि
cpp
, java
, और jvm
. हालांकि, ऐक्सेस से जुड़ी गड़बड़ियों से बचने के लिए, सभी ज़रूरी फ़्रैगमेंट को शामिल किया जाना चाहिए:
def _impl(ctx):
# Using ctx.fragments.cpp leads to an error since it was not declared.
x = ctx.fragments.java
...
my_rule = rule(
implementation = _impl,
fragments = ["java"], # Required fragments of the target configuration
host_fragments = ["java"], # Required fragments of the host configuration
...
)
रनफ़ाइल सिमलिंक
आम तौर पर, रनफ़ाइल ट्री में फ़ाइल का रिलेटिव पाथ, सोर्स ट्री या जनरेट किए गए आउटपुट ट्री में उस फ़ाइल के रिलेटिव पाथ जैसा ही होता है. अगर किसी वजह से इन दोनों में अंतर है, तो root_symlinks
या symlinks
के बारे में जानकारी दी जा सकती है. root_symlinks
, फ़ाइलों के लिए एक डिक्शनरी मैपिंग पाथ है,
जहां पाथ, रनफ़ाइल डायरेक्ट्री के रूट से मिलते-जुलते होते हैं. symlinks
डिक्शनरी वही है, लेकिन पाथ के नाम में सफ़िक्स का इस्तेमाल किया जाता है.
...
runfiles = ctx.runfiles(
root_symlinks = {"some/path/here.foo": ctx.file.some_data_file2}
symlinks = {"some/path/here.bar": ctx.file.some_data_file3}
)
# Creates something like:
# sometarget.runfiles/
# some/
# path/
# here.foo -> some_data_file2
# <workspace_name>/
# some/
# path/
# here.bar -> some_data_file3
अगर symlinks
या root_symlinks
का इस्तेमाल किया जाता है, तो रनफ़ाइल ट्री में एक ही पाथ पर दो अलग-अलग फ़ाइलों को मैप न करें. इससे बिल्ड में गड़बड़ी
होने के साथ गड़बड़ी हो सकती है. गड़बड़ी को ठीक करने के लिए, आपको अपने
ctx.runfiles
आर्ग्युमेंट में बदलाव करना होगा. आपके नियम का इस्तेमाल करने वाले सभी टारगेट की जांच की जाएगी. साथ ही, उन टारगेट पर निर्भर सभी टारगेट के लिए भी यह जांच की जाएगी. अगर आपके टूल का इस्तेमाल, किसी दूसरे टूल से कुछ समय के लिए किया जाता है, तो यह तरीका जोखिम भरा हो सकता है. यह ज़रूरी है कि किसी टूल की रनफ़ाइल और उसकी सभी डिपेंडेंसी में, सिमलिंक के नाम अलग-अलग हों.
कोड कवरेज
जब coverage
निर्देश चलाया जाता है, तब बिल्ड को कुछ टारगेट के लिए कवरेज इंस्ट्रूमेंटेशन जोड़ना पड़ सकता है. बिल्ड, इंस्ट्रुमेंट की गई सोर्स फ़ाइलों की सूची भी इकट्ठा करता है. टारगेट का सबसेट, फ़्लैग से कंट्रोल किया जाता है
--instrumentation_filter
.
टेस्ट टारगेट शामिल नहीं किए जाते, बशर्ते
--instrument_test_targets
के बारे में न बताया गया हो.
अगर किसी नियम को लागू करने पर बिल्ड टाइम के दौरान कवरेज इंस्ट्रूमेंटेशन जुड़ जाता है, तो उसके लागू करने के फ़ंक्शन में इसे ध्यान में रखना ज़रूरी है. ctx.coverage_scienceed, अगर टारगेट के स्रोतों को इंस्ट्रूमेंट किया जाना चाहिए, तो कवरेज मोड में सही दिखाता है.
# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
# Do something to turn on coverage for this compile action
वह तर्क जिसे हमेशा कवरेज मोड में चालू रखना होता है (चाहे टारगेट के स्रोत खास तौर पर इंस्ट्रुमेंट किए गए हों या न किए गए हों) ctx.config.coverage_enable पर निर्भर हो सकता है.
अगर इस नियम में सीधे तौर पर, डिपेंडेंसी से जुड़े सोर्स शामिल हैं (जैसे कि हेडर फ़ाइलें), तो हो सकता है कि कंपाइल के समय वाले इंस्ट्रूमेंटेशन को भी चालू करना पड़े.
# Are this rule's sources or any of the sources for its direct dependencies
# in deps instrumented?
if (ctx.configuration.coverage_enabled and
(ctx.coverage_instrumented() or
any([ctx.coverage_instrumented(dep) for dep in ctx.attr.deps]))):
# Do something to turn on coverage for this compile action
नियमों में यह भी बताना चाहिए कि InstrumentedFilesInfo
की सेवा देने वाली कंपनी के कवरेज के लिए कौनसे एट्रिब्यूट काम के हैं. इन्हें बनाने के लिए, coverage_common.instrumented_files_info
का इस्तेमाल किया जाता है.
instrumented_files_info
के dependency_attributes
पैरामीटर में, रनटाइम डिपेंडेंसी के सभी एट्रिब्यूट की सूची शामिल होनी चाहिए. इन एट्रिब्यूट में, deps
जैसी कोड डिपेंडेंसी और data
जैसी डेटा डिपेंडेंसी शामिल हैं. अगर कवरेज इंस्ट्रूमेंटेशन जोड़ा जा सकता है, तो source_attributes
पैरामीटर में
नियम की सोर्स फ़ाइल एट्रिब्यूट की सूची शामिल होनी चाहिए:
def _example_library_impl(ctx):
...
return [
...
coverage_common.instrumented_files_info(
ctx,
dependency_attributes = ["deps", "data"],
# Omitted if coverage is not supported for this rule:
source_attributes = ["srcs", "hdrs"],
)
...
]
अगर InstrumentedFilesInfo
नहीं दिखता है, तो हर गैर-टूल डिपेंडेंसी एट्रिब्यूट के साथ एक डिफ़ॉल्ट कॉन्फ़िगरेशन बन जाता है. यह एट्रिब्यूट एट्रिब्यूट में, "host"
या "exec"
के लिए cfg
पर सेट नहीं होता है. (यह सबसे सही तरीका नहीं है, क्योंकि यह source_attributes
के बजाय dependency_attributes
में srcs
जैसे एट्रिब्यूट डालता है. हालांकि, इससे डिपेंडेंसी चेन में सभी नियमों के लिए, विस्फोटक कवरेज वाले कॉन्फ़िगरेशन की ज़रूरत नहीं होती.)
पुष्टि करने के लिए कार्रवाइयां
कभी-कभी आपको बिल्ड से जुड़ी किसी चीज़ की पुष्टि करनी पड़ सकती है. इसके लिए, पुष्टि सिर्फ़ आर्टफ़ैक्ट (सोर्स फ़ाइलें या जनरेट की गई फ़ाइलें) में ही मौजूद होती है. यह जानकारी आर्टफ़ैक्ट में है, इसलिए विश्लेषण के समय नियम इस पुष्टि को नहीं कर सकते, क्योंकि नियमों में फ़ाइलों को पढ़ा नहीं जा सकता. इसके बजाय, कार्रवाइयों के चलने के दौरान यह पुष्टि ज़रूर करें. अगर पुष्टि नहीं हो पाती है, तो कार्रवाई नहीं हो पाएगी और बिल्ड भी हो जाएगा.
पुष्टि करने के लिए जो उदाहरण चलाए जा सकते हैं, उनके उदाहरण हैं स्टैटिक ऐनलिसिस, लिंटिंग, डिपेंडेंसी, और कॉन्सेप्ट की जांच. साथ ही, स्टाइल की जांच भी की जा सकती है.
पुष्टि करने की कार्रवाइयों से, बिल्ड की परफ़ॉर्मेंस को बेहतर बनाने में मदद मिलती है. इसमें वे कार्रवाइयां शामिल हैं जो आर्टफ़ैक्ट को अलग-अलग कार्रवाइयों के लिए बनाने के लिए ज़रूरी नहीं हैं. उदाहरण के लिए, अगर कंपाइल करने और लिंटिंग करने वाली किसी एक कार्रवाई को कई चीज़ों के साथ मिलाकर दिखाया जाता है
ये "पुष्टि करने की कार्रवाइयां" अक्सर कोई ऐसी चीज़ नहीं बनाती हैं जिसका इस्तेमाल बिल्ड में कहीं और किया जाता है, क्योंकि उन्हें सिर्फ़ अपने इनपुट से जुड़ी बातों की पुष्टि करनी होती है. हालांकि, इससे समस्या पैदा होती है: अगर पुष्टि करने की कार्रवाई से कोई ऐसा कॉन्टेंट जनरेट नहीं होता है जिसका इस्तेमाल बिल्ड में किसी दूसरी जगह किया गया हो, तो किसी नियम को कार्रवाई के लिए कैसे भेजा जाता है? पहले हमें, पुष्टि करने के लिए कार्रवाई का आउटपुट चाहिए, जिसमें फ़ाइल खाली थी. उस बिल्ड को कुछ अहम कार्रवाई के इनपुट में आर्टिफ़िशियल जोड़ना था:
यह तरीका काम करता है, क्योंकि कंपाइल कार्रवाई के चलने पर बैज़ल हमेशा पुष्टि करने की कार्रवाई करता है, लेकिन इसकी कुछ कमियां हैं:
पुष्टि करने की कार्रवाई, बिल्ड के अहम पाथ में होती है. Bazel को लगता है कि कंपाइल कार्रवाई करने के लिए, खाली आउटपुट की ज़रूरत होगी. हालांकि, कंपाइल कार्रवाई से इनपुट को अनदेखा कर दिया जाएगा. हालांकि, कंपाइल कार्रवाई को चलाने के लिए पहले खाली वैल्यू डालना ज़रूरी होगा. इससे पैरललिज़्म कम होता है और बिल्ड धीमा होता है.
अगर बिल्ड में अन्य कार्रवाइयां कंपाइल कार्रवाई के बजाय चल सकती हैं, तो पुष्टि करने की कार्रवाई के खाली आउटपुट को भी उन कार्रवाइयों में जोड़ना होगा (उदाहरण के लिए,
java_library
का सोर्स जार आउटपुट). यह तब भी होता है, जब कंपाइल कार्रवाई के बजाय बाद में चलने वाली नई कार्रवाइयां जोड़ी जाती हैं और पुष्टि करने का खाली आउटपुट गलती से बंद हो जाता है.
इन समस्याओं को हल करने के लिए, पुष्टि करने वाले आउटपुट ग्रुप का इस्तेमाल करें.
पुष्टि करने वाले आउटपुट का ग्रुप
पुष्टि करने वाले आउटपुट का समूह, एक तरह का आउटपुट ग्रुप होता है. इसे पुष्टि करने वाली कार्रवाइयों के उन आउटपुट को होल्ड करने के लिए डिज़ाइन किया जाता है जो इस्तेमाल नहीं किए गए हैं. इस तरह, उन्हें दूसरे कामों के इनपुट में जोड़े जाने की ज़रूरत नहीं पड़ती है.
यह ग्रुप खास है, क्योंकि --output_groups
फ़्लैग की वैल्यू पर ध्यान दिए बिना इसके टारगेट का अनुरोध हमेशा किया जाता है. इस बात से कोई फ़र्क़ नहीं पड़ता कि टारगेट कैसे निर्भर है (उदाहरण के लिए, कमांड लाइन पर, डिपेंडेंसी के तौर पर या टारगेट के इंप्लिसिट आउटपुट से). ध्यान रखें कि कैश मेमोरी और इंक्रीमेंटल (बढ़ने वाली सामान्य प्रक्रिया)
अब भी लागू होगी: अगर पुष्टि करने वाली कार्रवाई के इनपुट में कोई बदलाव नहीं हुआ है और पुष्टि करने की कार्रवाई पहले सफल हो गई है, तो पुष्टि की कार्रवाई नहीं की जाएगी.
इस आउटपुट ग्रुप का इस्तेमाल करने के लिए अब भी पुष्टि करने वाली कार्रवाइयों से कुछ फ़ाइल का आउटपुट मिलता है, यहां तक कि खाली फ़ील्ड भी. इसके लिए, आपको कुछ ऐसे टूल रैप करने पड़ सकते हैं जो आम तौर पर, आउटपुट नहीं बनाते, ताकि कोई फ़ाइल बनाई जा सके.
टारगेट की पुष्टि करने की कार्रवाइयां, तीन मामलों में नहीं चलती हैं:
- जब टारगेट, टूल के तौर पर निर्भर होता है
- जब टारगेट, इंडिपेंडेंट डिपेंडेंसी के तौर पर निर्भर करता है (उदाहरण के लिए, "_" से शुरू होने वाला एट्रिब्यूट)
- जब टारगेट को होस्ट या एक्ज़ीक्यूट कॉन्फ़िगरेशन में बनाया जाता है.
यह माना जाता है कि इन टारगेट के अपने अलग बिल्ड और टेस्ट होते हैं. इनकी मदद से, पुष्टि में होने वाली किसी भी गड़बड़ी का पता चलता है.
पुष्टि करने वाले आउटपुट ग्रुप का इस्तेमाल करना
पुष्टि करने वाले आउटपुट ग्रुप का नाम _validation
है. इसे किसी और आउटपुट ग्रुप की तरह इस्तेमाल किया जाता है:
def _rule_with_validation_impl(ctx):
ctx.actions.write(ctx.outputs.main, "main output\n")
ctx.actions.write(ctx.outputs.implicit, "implicit output\n")
validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
ctx.actions.run(
outputs = [validation_output],
executable = ctx.executable._validation_tool,
arguments = [validation_output.path])
return [
DefaultInfo(files = depset([ctx.outputs.main])),
OutputGroupInfo(_validation = depset([validation_output])),
]
rule_with_validation = rule(
implementation = _rule_with_validation_impl,
outputs = {
"main": "%{name}.main",
"implicit": "%{name}.implicit",
},
attrs = {
"_validation_tool": attr.label(
default = Label("//validation_actions:validation_tool"),
executable = True,
cfg = "exec"),
}
)
ध्यान दें कि पुष्टि करने वाले आउटपुट की फ़ाइल DefaultInfo
या इनपुट को किसी दूसरी कार्रवाई में नहीं जोड़ा जाता है. इस नियम के प्रकार के टारगेट की पुष्टि की कार्रवाई तब भी चलेगी, जब टारगेट लेबल के हिसाब से हो या टारगेट के किसी भी इंप्लिसिट आउटपुट पर निर्भर हो.
आम तौर पर, यह ज़रूरी है कि पुष्टि की कार्रवाइयों से मिलने वाले नतीजे, सिर्फ़ पुष्टि करने वाले आउटपुट ग्रुप में जाएं. साथ ही, उन्हें दूसरी कार्रवाइयों के इनपुट में नहीं जोड़ा जाता, क्योंकि इससे समानांतरता का नुकसान हो सकता है. हालांकि, ध्यान दें कि फ़िलहाल Bazel में इसे लागू करने की कोई खास जांच नहीं है. इसलिए, आपको यह जांच करनी चाहिए कि Starlark नियमों की जांच में किसी भी कार्रवाई के इनपुट में, पुष्टि करने से जुड़ी कार्रवाई के आउटपुट नहीं जोड़े गए हैं. उदाहरण के लिए :
load("@bazel_skylib//lib:unittest.bzl", "analysistest")
def _validation_outputs_test_impl(ctx):
env = analysistest.begin(ctx)
actions = analysistest.target_actions(env)
target = analysistest.target_under_test(env)
validation_outputs = target.output_groups._validation.to_list()
for action in actions:
for validation_output in validation_outputs:
if validation_output in action.inputs.to_list():
analysistest.fail(env,
"%s is a validation action output, but is an input to action %s" % (
validation_output, action))
return analysistest.end(env)
validation_outputs_test = analysistest.make(_validation_outputs_test_impl)
पुष्टि करने वाले ऐक्शन फ़्लैग
पुष्टि करने की कार्रवाइयों को --run_validations
कमांड लाइन फ़्लैग से कंट्रोल किया जाता है. यह डिफ़ॉल्ट रूप से, 'सही है' पर सेट होता है.
बंद की गई सुविधाएं
रोके गए पहले से बताए गए आउटपुट
पहले से तय किए गए आउटपुट का इस्तेमाल करने के, दो तरह के अब इस्तेमाल में नहीं हैं:
rule
काoutputs
पैरामीटर, पहले से तय किए गए आउटपुट लेबल जनरेट करने के लिए, आउटपुट एट्रिब्यूट के नामों और स्ट्रिंग टेंप्लेट के बीच की मैपिंग तय करता है. जो एलान नहीं किए गए आउटपुट का इस्तेमाल करते हैं औरDefaultInfo.files
में साफ़ तौर पर आउटपुट जोड़ते हैं. नियम टारगेट लेबल का इस्तेमाल उन नियमों के इनपुट के रूप में करें जो पहले से तय आउटपुट के लेबल के बजाय आउटपुट का इस्तेमाल करते हैं.एक्ज़ीक्यूटेबल नियमों के लिए,
ctx.outputs.executable
नियम टारगेट के समान नाम वाले पहले से तय किए गए एक्ज़ीक्यूटेबल आउटपुट के बारे में बताता है. आउटपुट के बारे में साफ़ तौर पर एलान करें, उदाहरण के लिए,ctx.actions.declare_file(ctx.label.name)
के साथ. साथ ही, पक्का करें कि एक्ज़ीक्यूटेबल जनरेट करने वाला निर्देश, कार्रवाई के लिए अनुमतियां सेट करता है. एक्ज़ीक्यूटेबल आउटपुट कोDefaultInfo
केexecutable
पैरामीटर के लिए साफ़ तौर पर पास करें.
रनफ़ाइल से बचने के लिए सुविधाएं
ctx.runfiles
और runfiles
टाइप में सुविधाओं का एक कॉम्प्लेक्स सेट है. इनमें से कई लेगसी वजहों से सेव किए गए हैं.
नीचे दिए गए सुझावों से जटिलता को कम करने में मदद मिलती है:
ctx.runfiles
केcollect_data
औरcollect_default
मोड का इस्तेमाल करने से बचें. ये मोड, हार्डकोड की गई डिपेंडेंसी के कुछ किनारों पर, रन फ़ाइल को छिपाते हैं. इससे, भ्रम की स्थिति पैदा होती है. इसके बजाय,ctx.runfiles
केfiles
याtransitive_files
पैरामीटर का इस्तेमाल करके याrunfiles = runfiles.merge(dep[DefaultInfo].default_runfiles)
के साथ डिपेंडेंसी से रनफ़ाइल में मर्ज करके फ़ाइलें जोड़ें.DefaultInfo
कंस्ट्रक्टर केdata_runfiles
औरdefault_runfiles
के इस्तेमाल से बचें. इसके बजाय,DefaultInfo(runfiles = ...)
बताएं. "डिफ़ॉल्ट" और "डेटा" रनफ़ाइल के बीच का अंतर, लेगसी वजहों से बनाया जाता है. उदाहरण के लिए, कुछ नियमdata_runfiles
में डिफ़ॉल्ट आउटपुट देते हैं, लेकिनdefault_runfiles
में नहीं.data_runfiles
का इस्तेमाल करने के बजाय, नियमों में दोनों शामिल होने चाहिए. ये डिफ़ॉल्ट आउटपुट होने चाहिए. साथ ही, रनफ़ाइल देने वाले एट्रिब्यूट सेdefault_runfiles
मर्ज होने चाहिए. अक्सरdata
runfiles
DefaultInfo
से (आम तौर पर, सिर्फ़ मौजूदा नियम और इसकी डिपेंडेंसी के बीच की फ़ाइल को मर्ज करने के लिए),DefaultInfo.default_runfiles
,नDefaultInfo.data_runfiles
का इस्तेमाल करें.
पुरानी साइटों से माइग्रेट करना
अब तक, Bazel की सेवा देने वाली कंपनियां, Target
ऑब्जेक्ट पर सादे फ़ील्ड थीं. इन्हें
डॉट ऑपरेटर का इस्तेमाल करके ऐक्सेस किया गया था. साथ ही, फ़ील्ड को नियम लागू करने के फ़ंक्शन के ज़रिए दिखाए गए स्ट्रक्चर में डालकर बनाया गया था.
इस स्टाइल को रोक दिया गया है और इसका इस्तेमाल नए कोड में नहीं किया जाना चाहिए; माइग्रेट करने से जुड़ी जानकारी के लिए नीचे देखें. सेवा देने वाली नई कंपनी के नाम में गड़बड़ी होने से बचा जाता है. इसमें डेटा को छिपाने की सुविधा भी होती है. इसके लिए, सेवा देने वाली कंपनी के चिह्न का इस्तेमाल करके, उसे वापस पाने के लिए, सेवा देने वाली कंपनी के इंस्टेंस को ऐक्सेस करने वाले कोड की ज़रूरत होती है.
फ़िलहाल, लेगसी सेवा देने वाली कंपनियों की मदद की जा सकती है. कोई नियम इस तरह से, पुरानी और आधुनिक, दोनों तरह की सेवा देने वाली कंपनियों को दिखा सकता है:
def _old_rule_impl(ctx):
...
legacy_data = struct(x="foo", ...)
modern_data = MyInfo(y="bar", ...)
# When any legacy providers are returned, the top-level returned value is a
# struct.
return struct(
# One key = value entry for each legacy provider.
legacy_info = legacy_data,
...
# Additional modern providers:
providers = [modern_data, ...])
अगर इस नियम के उदाहरण के लिए, dep
एक Target
ऑब्जेक्ट है, तो सेवा देने वाली कंपनियों और उनके कॉन्टेंट को dep.legacy_info.x
और dep[MyInfo].y
के तौर पर वापस पाया जा सकता है.
providers
के अलावा, दिखाए गए स्ट्रक्चर में ऐसे दूसरे फ़ील्ड भी हो सकते हैं जिनका खास मतलब होता है (और इस तरह, इसके लिए कोई पुरानी कंपनी नहीं बनाई जाती):
files
,runfiles
,data_runfiles
,default_runfiles
, औरexecutable
फ़ील्ड,DefaultInfo
के नाम वाले फ़ील्ड से मेल खाते हैं.DefaultInfo
सेवा देने वाली कंपनी को दिखाते समय, इनमें से किसी भी फ़ील्ड को दिखाने की अनुमति नहीं है.output_groups
फ़ील्ड, स्ट्रक्चर की वैल्यू लेता है औरOutputGroupInfo
से मेल खाता है.
नियमों के provides
एलानों में और
providers
डिपेंडेंसी एट्रिब्यूट के एलान में, लेगसी प्रोवाइडर को स्ट्रिंग के तौर पर पास किया जाता है. साथ ही, आधुनिक कंपनियों को उनके *Info
सिंबल से पास किया जाता है. माइग्रेट करते समय, स्ट्रिंग से सिंबल में बदलना न भूलें. मुश्किल या बड़े नियम सेट के लिए, जहां सभी नियमों को आसानी से अपडेट नहीं किया जा सकता है, उनके लिए नीचे दिए गए कदमों का पालन करने से आपको ज़्यादा समय मिल सकता है:
ऊपर बताए गए सिंटैक्स का इस्तेमाल करके, पुरानी और आधुनिक, दोनों सेवाएं देने वाली कंपनी के नियमों को बदलें. लेगसी प्रोवाइडर को लौटाने का एलान करने वाले नियमों के लिए, उस एलान को अपडेट करें, ताकि इसमें कंपनी की पुरानी और आधुनिक, दोनों सेवाओं को शामिल किया जा सके.
पुराने प्रोवाइडर का इस्तेमाल करने के लिए, लेगसी प्रोवाइडर का इस्तेमाल करने के लिए नियमों में बदलाव करें. अगर किसी एट्रिब्यूट के बारे में एलान, लेगसी प्रोवाइडर के लिए ज़रूरी है, तो उसे भी अपडेट करें. वैकल्पिक रूप से, आप पहले चरण की मदद से इस काम को बीच में छोड़ सकते हैं, ताकि उपभोक्ता या तो सेवा देने वाली किसी कंपनी को स्वीकार करें/उसे ज़रूरी बना लें:
hasattr(target, 'foo')
का इस्तेमाल करके लेगसी सेवा देने वाली कंपनी की मौजूदगी की जांच करें याFooInfo in target
का इस्तेमाल करके सेवा देने वाली नई कंपनी की जांच करें.सभी नियमों से लेगसी प्रोवाइडर को पूरी तरह हटाएं.