तारीख सेव करें: BazelCon 2023, 24 से 25 अक्टूबर तक Google म्यूनिख में होगा! ज़्यादा जानें

नियम

समस्या की शिकायत करें सोर्स देखें

नियम, कार्रवाई की ऐसी सीरीज़ के बारे में बताता है जो आउटपुट पर मिलने वाले सेट को बनाने के लिए इनपुट पर लागू करती है. इन सेवाओं कोनियम लागू करने की सुविधा के ज़रिए वापस किया जाता है. उदाहरण के लिए, C++ बाइनरी नियम:

  1. .cpp सोर्स फ़ाइलों का इनपुट पाएं (इनपुट).
  2. सोर्स फ़ाइलों पर g++ चलाएं (कार्रवाई).
  3. रनटाइम के लिए उपलब्ध कराने के लिए, DefaultInfo सेवा देने वाली कंपनी को एक्ज़ीक्यूटेबल आउटपुट और दूसरी फ़ाइलें दें.
  4. टारगेट करने वाली सेवा और उसकी डिपेंडेंसी से इकट्ठा की गई C++-खास जानकारी के साथ CcInfo सेवा देने वाली कंपनी दिखाएं.

बेज़ल के नज़रिए से, 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 फ़ंक्शन जिन्हें कॉल करने के नियम कहते हैं, उन्हें 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",
        ),
    },
)

इस उदाहरण में, कंपाइलर //tools:example_compiler पर हर तरह का example_library टारगेट हर समय निर्भर करता है. इससे 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(...))]

उन्हें इंडेक्स नोटेशन ([]) का इस्तेमाल करके ऐक्सेस किया जा सकता है. इसमें कुंजी के तौर पर, सेवा देने वाली कंपनी का टाइप भी शामिल होता है. Starstark में तय की गई कस्टम प्रोवाइडर हो सकती हैं या स्टारलार्क ग्लोबल वैरिएबल के रूप में उपलब्ध सेवा देने वाली कंपनियां हो सकती हैं.

उदाहरण के लिए, अगर कोई नियम 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, टार या अन्य संग्रह फ़ॉर्मैट) में रैप कर सकती हैं.

कार्रवाइयों में उनके सभी इनपुट होने चाहिए. लिस्टिंग में इस्तेमाल नहीं किए जाने वाले इनपुट की अनुमति नहीं है, लेकिन वे इनपुट असरदार नहीं हैं.

कार्रवाइयों से उनके सभी आउटपुट बनने चाहिए. वे अन्य फ़ाइलें बना सकते हैं, लेकिन आउटपुट में कोई भी चीज़ उपभोक्ताओं के लिए उपलब्ध नहीं होगी. सभी बताए गए आउटपुट किसी कार्रवाई से लिखे जाने चाहिए.

कार्रवाइयों की तुलना शुद्ध फ़ंक्शन से की जा सकती है; उन्हें सिर्फ़ दिए गए इनपुट पर निर्भर करना चाहिए; और कंप्यूटर की जानकारी, उपयोगकर्ता नाम, घड़ी, नेटवर्क या I/O डिवाइसों को ऐक्सेस करने से बचना चाहिए (इनपुट डालने और आउटपुट आउटपुट को छोड़कर). यह अहम है, क्योंकि आउटपुट को कैश मेमोरी में सेव किया जाता है और उसका फिर से इस्तेमाल किया जाता है.

डिपेंडेंसी का समाधान Bazel से किया जाता है, जो यह तय करता है कि कौनसी कार्रवाइयां करनी हैं. अगर डिपेंडेंसी ग्राफ़ में कोई साइकल मौजूद है, तो यह एक गड़बड़ी है. किसी काम को करने से यह गारंटी नहीं मिलती कि उसे चलाया जाएगा या नहीं. यह इस बात पर निर्भर करता है कि बिल्ड के लिए आउटपुट की ज़रूरत है या नहीं.

सेवा देने वाले संगठन

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

नियम लागू करने का फ़ंक्शन, इंस्टैंट टारगेट की डिपेंडेंसी से ही सेवा देने वाली कंपनियों को पढ़ सकता है. इसलिए, नियमों की मदद से, टारगेट की डिपेंडेंसी से कोई भी ऐसी जानकारी फ़ॉरवर्ड करनी होती है जो टारगेट के उपभोक्ताओं को पता होनी चाहिए. आम तौर पर, यह जानकारी 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 वाले filegroup टारगेट हो सकते हैं और deps से कोड दिया जा सकता है.

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
              ],
          ),
      )
  ]
सेवा देने वाली कंपनियों को अपने हिसाब से शुरू करना

हो सकता है कि कस्टम प्रीप्रोसेस और पुष्टि करने वाले लॉजिक के साथ, सेवा देने वाली कंपनी का नाम सुरक्षित रखना पड़े. इसका इस्तेमाल यह पक्का करने के लिए किया जा सकता है कि प्रोवाइडर के सभी इंस्टेंस, कुछ खास वैरिएंट का पालन करते हैं या किसी इंस्टेंस को समझने के लिए, एपीआई को बेहतर बनाते हैं.

ऐसा करने के लिए, init कॉलबैक को provider फ़ंक्शन पर पास करना होगा. अगर यह कॉलबैक दिया जाता है, तो 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 निर्देश से जोड़ा जा सकता है. एक्ज़ीक्यूट किए जा सकने वाले और टेस्ट के नियम, rule कॉल में True पर सही executable या test आर्ग्युमेंट सेट करके बनाए जाते हैं:

example_binary = rule(
   implementation = _example_binary_impl,
   executable = True,
   ...
)

example_test = rule(
   implementation = _example_binary_impl,
   test = True,
   ...
)

जांच के नियमों के नाम ऐसे होने चाहिए जो _test से खत्म होते हों. (टारगेट नामों की जांच भी अक्सर कन्वेंशन में _test से होती है, लेकिन ऐसा करना ज़रूरी नहीं है.) बिना टेस्ट वाले नियमों के लिए यह सफ़िक्स नहीं होना चाहिए.

दोनों तरह के नियमों को एक्ज़ीक्यूटेबल आउटपुट फ़ाइल बनानी चाहिए (जिसे पहले से बताया जा सकता है या नहीं) जो run या test निर्देशों से शुरू किया जाएगा. {0}Bazel को यह बताने के लिए कि इस नियम का इस्तेमाल किस एक्ज़ीक्यूट के रूप में करना है, उसे दिए गए executable आर्ग्युमेंट DefaultInfo सेवा देने वाले के रूप में पास करें. यह 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 के साथ एक्ज़ीक्यूटेबल टारगेट चलाया जाता है, तो run files डायरेक्ट्री का रूट, एक्ज़ीक्यूटेबल के बगल में होता है. पाथ इससे जुड़े होते हैं:

# 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 निर्देश चलाया जाता है, तो निर्देश में दिए गए टारगेट के कुछ आउटपुट को अनुरोध किया जाता है. Bazel, सिर्फ़ इन फ़ाइलों को बनाता है या उन फ़ाइलों को बनाता है जो सीधे तौर पर या किसी दूसरे तरीके से निर्भर करती हैं. (ऐक्शन ग्राफ़ के हिसाब से, Bazel सिर्फ़ उन कार्रवाइयों को लागू करता है जिन पर अनुरोध की गई फ़ाइलों की ट्रांज़िटल डिपेंडेंसी के तौर पर संपर्क किया जा सकता है.)

डिफ़ॉल्ट आउटपुट के साथ ही, किसी भी पहले से बताए गए आउटपुट का कमांड लाइन में साफ़ तौर पर किया जा सकता है. नियम आउटपुट विशेषताओं के ज़रिए पहले से बताए गए आउटपुट तय कर सकते हैं. इस स्थिति में, जब उपयोगकर्ता नियम को इंस्टैंशिएट करता है, तो आउटपुट के लिए लेबल साफ़ तौर पर चुन लेता है. आउटपुट एट्रिब्यूट के लिए, 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 का एक "कॉन्फ़िगरेशन" और ट्रांज़िशन है. (सबसे ऊपर दिए गए टारगेट (कमांड लाइन पर अनुरोध किए गए) को "टारगेट" कॉन्फ़िगरेशन में बनाया गया है, जबकि एक्ज़ीक्यूशन प्लैटफ़ॉर्म पर चलने वाले टूल "एक्ज़ीक्यूट" कॉन्फ़िगरेशन में बनाए जाते हैं. नियम, कॉन्फ़िगरेशन के आधार पर अलग-अलग कार्रवाइयां जनरेट कर सकते हैं. उदाहरण के लिए, कंपाइलर को पास किए गए सीपीयू का आर्किटेक्चर बदलने के लिए. कुछ मामलों में, अलग-अलग कॉन्फ़िगरेशन के लिए एक ही लाइब्रेरी की ज़रूरत पड़ सकती है. अगर ऐसा होता है, तो उसका विश्लेषण किया जाएगा. साथ ही, उसे कई बार बनाया जा सकता है.

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

हर डिपेंडेंसी एट्रिब्यूट के लिए, cfg का इस्तेमाल करके यह तय किया जा सकता है कि डिपेंडेंसी उसी कॉन्फ़िगरेशन में होनी चाहिए या एक्ज़ीक्यूट कॉन्फ़िगरेशन में होनी चाहिए. अगर executable=True के लिए डिपेंडेंसी एट्रिब्यूट में फ़्लैग मौजूद है, तो cfg को साफ़ तौर पर सेट करना ज़रूरी है. यह गलती से गलत कॉन्फ़िगरेशन के लिए टूल बनाने से बचाता है. उदाहरण देखें

आम तौर पर, रनटाइम के दौरान जिन सोर्स, डिपेंडेंसी लाइब्रेरी, और एक्ज़ीक्यूटेबल की ज़रूरत होती है वे एक ही कॉन्फ़िगरेशन का इस्तेमाल कर सकते हैं.

बिल्ड के हिस्से के रूप में इस्तेमाल किए जाने वाले टूल (जैसे कंपाइलर या कोड जनरेटर) एक्ज़ीक्यू कॉन्फ़िगरेशन के लिए बनाए जाने चाहिए. इस मामले में, विशेषता में cfg="exec"बताएं.

अगर ऐसा नहीं है, तो रनटाइम के दौरान इस्तेमाल किए जाने वाले एक्ज़ीक्यूटेबल (जैसे कि टेस्ट के हिस्से) को टारगेट कॉन्फ़िगरेशन के लिए बनाया जाना चाहिए. इस मामले में, विशेषता में cfg="target"बताएं.

cfg="target" असल में कुछ नहीं करता. यह सिर्फ़ एक सुविधा है, जिसकी मदद से नियम डिज़ाइनरों को अपने इरादे के बारे में साफ़ तौर पर बताना होता है. जब executable=False, जिसका मतलब है कि cfg ज़रूरी नहीं है, सिर्फ़ तब ही सेट करें, जब उसे पढ़ने में आसानी हो.

cfg=my_transitionउपयोगकर्ता के तय किए गए ट्रांज़िशन का इस्तेमाल करने के लिए भी cfg=my_transition का इस्तेमाल किया जा सकता है. इसकी मदद से, नियम बनाने वाले लेखकों को कॉन्फ़िगरेशन में आसानी से बदलाव करने की सुविधा मिलती है. साथ ही, बिल्ड ग्राफ़ को बड़ा और समझने में आसान बनाया जा सकता है.

ध्यान दें: अब तक, Bazel को एक्ज़ीक्यूशन प्लैटफ़ॉर्म के बारे में नहीं बताया गया था. इसके बजाय, बिल्ड की सभी कार्रवाइयों को होस्ट मशीन पर चलाया जाता था. 6.0 से पहले के Bazels वर्शन ने इसे दिखाने के लिए एक अलग "होस्ट" कॉन्फ़िगरेशन बनाया. अगर आपको कोड या पुराने दस्तावेज़ में "होस्ट" का रेफ़रंस दिखता है, तो यहां इसका मतलब बताया गया है. हमारा सुझाव है कि आप 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 बताया न गया हो.

अगर किसी नियम को लागू करने से बिल्ड टाइम में कवरेज इंस्ट्रूमेंटेशन जुड़ जाता है, तो इसे लागू करने वाले फ़ंक्शन में इसे ध्यान में रखना ज़रूरी है. अगर किसी टारगेट के स्रोत को लागू किया जाना है, तो कवरेज कवर_इंस्ट्रक्टेड, कवरेज मोड में 'सही' दिखाता है:

# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
  # Do something to turn on coverage for this compile action

तर्क को हमेशा कवरेज मोड में चालू रखने की ज़रूरत होती है (चाहे टारगेट के स्रोत खास तौर से इंस्ट्रूमेंट किए गए हों या न हों) ctx.config.coverage_Enabled पर जोड़े जा सकते हैं.

अगर इस नियम में कंपाइल करने से पहले, सीधे तौर पर अपनी डिपेंडेंसी के स्रोत शामिल किए गए हैं, जैसे कि हेडर फ़ाइलें, तो कंपाइल-टाइम इंस्ट्रुमेंट की सुविधा भी चालू करनी पड़ सकती है. ऐसा तब होगा, जब डिपेंडेंसी के स्रोत लागू होने चाहिए:

# 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 पर सेट नहीं होता है dependency_attributes. (यह सामान्य व्यवहार नहीं है, क्योंकि source_attributes के बजाय dependency_attributes में srcs जैसे एट्रिब्यूट डाले जाते हैं. इससे, डिपेंडेंसी चेन में सभी नियमों के लिए, साफ़ तौर पर कवरेज की ज़रूरत नहीं होती.)

पुष्टि करने के लिए कार्रवाइयां

कभी-कभी आपको बिल्ड के बारे में पुष्टि करनी होती है और पुष्टि करने के लिए ज़रूरी जानकारी सिर्फ़ आर्टफ़ैक्ट (सोर्स फ़ाइलें या जनरेट की गई फ़ाइलें) में उपलब्ध होती है. यह जानकारी आर्टफ़ैक्ट में मौजूद है, इसलिए विश्लेषण के समय इस नियम का इस्तेमाल नहीं किया जा सकता. ऐसा इसलिए है, क्योंकि नियम फ़ाइलों को पढ़ नहीं सकते. इसके बजाय, कार्रवाइयों के दौरान यह पुष्टि की जानी चाहिए. अगर यूआरएल की पुष्टि नहीं हो पाती है, तो कार्रवाई नहीं हो पाएगी.

स्टैटिक विश्लेषण, लिंट, और डिपेंडेंसी, और स्टाइल की जांच, पुष्टि करने के उदाहरण हो सकते हैं.

पुष्टि करने की कार्रवाइयों से, आपको परफ़ॉर्मेंस को बेहतर बनाने में मदद मिल सकती है. इसकी मदद से, उन कार्रवाइयों के कुछ हिस्सों को अलग-अलग कामों के लिए इस्तेमाल किया जा सकता है जिनकी ज़रूरत नहीं है. उदाहरण के लिए, अगर कंपाइलेशन और लिंट करने वाली कोई एक कार्रवाई, कंपाइलेशन की कार्रवाई और लिंटिंग की कार्रवाई के तौर पर अलग की जा सकती है, तो लिंट से जुड़ी कार्रवाई को पुष्टि करने की कार्रवाई के तौर पर चलाया जा सकता है. साथ ही, अन्य कार्रवाइयों के साथ-साथ चलाया जा सकता है.

"पुष्टि करने की कार्रवाइयां" अक्सर कोई ऐसी चीज़ नहीं बनाती हैं जिसका इस्तेमाल बिल्ड में कहीं और किया जाता है, क्योंकि उन्हें सिर्फ़ अपने इनपुट के बारे में बातें करने की ज़रूरत होती है. हालांकि यह एक समस्या के बारे में बताता है: अगर पुष्टि करने से जुड़ी कार्रवाई से कोई ऐसा डेटा जनरेट नहीं होता है जिसका इस्तेमाल बिल्ड में कहीं और किया गया है, तो नियम बनाने की कार्रवाई कैसे होगी? अब तक, पुष्टि करने का ऐक्शन आउटपुट एक खाली फ़ाइल थी. और उस आउटपुट को बिल्ड में किसी दूसरी अहम कार्रवाई के इनपुट में, आर्टिफ़िशियल तरीके से जोड़ना है:

यह इसलिए काम करता है, क्योंकि कंपाइल कार्रवाई के चलने पर बेज़ल हमेशा पुष्टि करने की कार्रवाई करेंगे. हालांकि, इसके कुछ नुकसान भी हैं:

  1. पुष्टि करने की कार्रवाई बिल्ड के अहम पाथ में है. बेज़ल का मानना है कि कंपाइल कार्रवाई चलाने के लिए खाली आउटपुट की ज़रूरत होती है. हालांकि, कंपाइल करने की कार्रवाई पहले, इनपुट की कार्रवाई करेगी, लेकिन इनपुट कंपाइल को अनदेखा कर देगा. इससे पैरललिज़्म कम होता है और बिल्ड कम होता है.

  2. हो सकता है कि बिल्ड में दूसरी कार्रवाइयां, कंपाइल करने की कार्रवाई के बजाय चलें. इसके बाद, पुष्टि करने की कार्रवाइयों के खाली आउटपुट को उन कार्रवाइयों में भी जोड़ना होगा (उदाहरण के लिए, 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 या किसी दूसरी कार्रवाई में इनपुट के तौर पर नहीं जोड़ा जाता है. इस तरह के टारगेट के लिए पुष्टि की कार्रवाई तब भी चलती रहेगी, जब टारगेट को लेबल पर आधारित हो या टारगेट के किसी इंप्लिसिट आउटपुट पर सीधे तौर पर या किसी दूसरे तरीके से निर्भर किया गया हो.

आम तौर पर, यह ज़रूरी होता है कि पुष्टि करने के ऐक्शन के आउटपुट, सिर्फ़ पुष्टि करने वाले आउटपुट ग्रुप में जाएं. इन्हें अन्य कार्रवाइयों के इनपुट में नहीं जोड़ा जाता. ऐसा इसलिए, क्योंकि इससे पैरललिज़्म की संख्या कम हो सकती है. हालांकि, ध्यान दें कि फ़िलहाल बेज़ल के पास इसे लागू करने से जुड़ी कोई खास जांच नहीं है. इसलिए, आपको इसकी जांच करनी चाहिए कि 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 पैरामीटर के साथ साफ़ तौर पर भेजें.

बचने के लिए Files फ़ाइलें

ctx.runfiles और runfiles टाइप की सुविधाओं का एक कॉम्प्लेक्स सेट है. इनमें से कई सुविधाओं को लेगसी वजहों से शामिल किया गया है. ये सुझाव, जटिलता को कम करने में मदद करते हैं:

  • ctx.runfiles के collect_data और collect_default मोड का इस्तेमाल करने से बचें. ये मोड भ्रमित करने के तरीकों से कुछ हार्डकोडेड डिपेंडेंसी के किनारों पर रनफ़ाइल हैं. इसके बजाय, ctx.runfiles के files या transitive_files पैरामीटर का इस्तेमाल करके या files को डिपेंडेंसी से runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles) के साथ मर्ज करके, फ़ाइलें जोड़ें.

  • DefaultInfo कंस्ट्रक्टर के data_runfiles और default_runfiles का इस्तेमाल करने से बचें. इसके बजाय, DefaultInfo(runfiles = ...) बताएं. "डिफ़ॉल्ट" और "डेटा" रनफ़ाइल के बीच का अंतर, लेगसी वजहों से तय होता है. उदाहरण के लिए, कुछ नियम data_runfiles में डिफ़ॉल्ट आउटपुट डालते हैं, लेकिन default_runfiles में नहीं. data_runfiles का इस्तेमाल करने के बजाय, नियमों में दोनों को डिफ़ॉल्ट वैल्यू के तौर पर डिफ़ॉल्ट रूप से शामिल किया जाना चाहिए. साथ ही, इन नियमों को उन एट्रिब्यूट में से default_runfiles को मर्ज कर देना चाहिए जो रनफ़ाइल (अक्सर data) देते हैं.

  • DefaultInfo से runfiles लेते समय (आम तौर पर, सिर्फ़ मौजूदा नियम और उसकी डिपेंडेंसी के बीच फ़ाइल मर्ज करने के लिए), DefaultInfo.default_runfiles का इस्तेमाल करें, न कि DefaultInfo.data_runfiles का.

लेगसी कंपनियों से माइग्रेट करना

अब तक, Baze ऐप्लिकेशन देने वाले लोग 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 चिह्न से पास किया जाता है. माइग्रेट करते समय स्ट्रिंग से सिंबल में बदलना न भूलें. मुश्किल या बड़े नियम वाले सेट के लिए, जहां सभी नियमों को एक के बाद एक अपडेट करना मुश्किल होता है, अगर आप ये तरीके अपनाएं:

  1. ऊपर दिए गए सिंटैक्स का इस्तेमाल करके, उन नियमों में बदलाव करें जो लेगसी सेवा देने वाली कंपनी को देते हैं. ऐसे नियमों के लिए जो एलान करते हैं कि वे लेगसी सेवा देने वाली कंपनी को दिखाएंगे. पुराने और आधुनिक, दोनों तरह के प्रोवाइडर को शामिल करने के लिए, उस एलान को अपडेट करें.

  2. उन नियमों में बदलाव करें जो मॉडर्न सेवा देने वाली कंपनी की जगह इस्तेमाल करते हैं. अगर किसी एट्रिब्यूट के एलान के लिए, लेगसी सेवा देने वाली कंपनी की ज़रूरत है, तो उसे भी अपडेट करें. वैकल्पिक रूप से, आप इस चरण को चरण 1 से बीच में छोड़ सकते हैं, ताकि उपभोक्ताओं को दोनों में से किसी एक कंपनी को स्वीकार या ज़रूरी करने के लिए: hasattr(target, 'foo') का इस्तेमाल करके लेगसी सेवा देने वाली कंपनी की मौजूदगी की जांच करें या FooInfo in target का इस्तेमाल करके सेवा देने वाली नई कंपनी से संपर्क करें.

  3. सभी नियमों से लेगसी प्रोवाइडर को पूरी तरह से हटा दें.