नियम

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

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

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

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

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 ऑब्जेक्ट के तौर पर दिखाया जाता है. इन ऑब्जेक्ट में, टारगेट लागू करने वाले फ़ंक्शन को लागू करने पर जनरेट किए गए providers होते हैं.

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]

हम इस स्टाइल का इस्तेमाल बिलकुल नहीं करते. साथ ही, नियमों को इससे दूर माइग्रेट किया जाना चाहिए.

Files

फ़ाइलों को File ऑब्जेक्ट से दिखाया जाता है. विश्लेषण के दौरान, Bazel फ़ाइल 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.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, tar या अन्य संग्रह फ़ॉर्मैट) में रैप कर सकती हैं.

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

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

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

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

रनफ़ाइलें

रनफ़ाइलें ऐसी फ़ाइलों का सेट हैं जिन्हें कोई टारगेट, रनटाइम के समय इस्तेमाल करता है, न कि बिल्ड के समय के दौरान. एक्ज़ीक्यूशन चरण के दौरान, Bazel एक डायरेक्ट्री ट्री बनाता है, जिसमें रन फ़ाइल्स पर ले जाने वाले सिमलिंक होते हैं. इससे बाइनरी के लिए एनवायरमेंट की स्थिति तय होती है, ताकि यह रनटाइम के दौरान रनफ़ाइल को ऐक्सेस कर सके.

नियम बनाने के दौरान, रन फ़ाइलें मैन्युअल तरीके से जोड़ी जा सकती हैं. runfiles ऑब्जेक्ट, ctx.runfiles नियम के कॉन्टेक्स्ट पर 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
              ],
          ),
      )
  ]
सेवा देने वाली कंपनियों को पसंद के मुताबिक शुरू करना

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

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

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

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

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

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

लागू किए जा सकने वाले नियम और जांच के नियम के उदाहरण देखें.

सभी नियमों के लिए जोड़े गए एट्रिब्यूट के अलावा, लागू होने वाले नियम और जांच के नियमों में, ऐसे एट्रिब्यूट होते हैं जिनके बारे में साफ़ तौर पर जानकारी दी गई होती है. अनुमान के साथ जोड़े गए एट्रिब्यूट की डिफ़ॉल्ट वैल्यू बदली नहीं जा सकती. हालांकि, निजी नियम को Starlark मैक्रो में रैप करके ऐसा किया जा सकता है. यह डिफ़ॉल्ट को बदल देता है:

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

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

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

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

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

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

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

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

ध्यान दें: पहले, Bazel ने प्रोग्राम चलाने के प्लैटफ़ॉर्म का सिद्धांत नहीं दिया था. इसलिए, सभी बिल्ड ऐक्शन को होस्ट मशीन पर चलाने के लिए तय किया जाता था. इस वजह से, सिर्फ़ एक "होस्ट" कॉन्फ़िगरेशन होता है और एक "होस्ट" ट्रांज़िशन होता है. इसका इस्तेमाल, होस्ट कॉन्फ़िगरेशन में डिपेंडेंसी बनाने के लिए किया जा सकता है. कई नियम अब भी अपने टूल के लिए "होस्ट" ट्रांज़िशन का इस्तेमाल करते हैं. हालांकि, फ़िलहाल यह तरीका इस्तेमाल नहीं किया जा रहा है और जहां संभव है वहां "exec" ट्रांज़िशन का इस्तेमाल करने के लिए, माइग्रेट किया जा रहा है.

"होस्ट" और "exec" कॉन्फ़िगरेशन में कई अंतर हैं:

  • "host" टर्मिनल है, लेकिन "exec" नहीं है: "होस्ट" कॉन्फ़िगरेशन में डिपेंडेंसी होने के बाद, और ट्रांज़िशन की अनुमति नहीं होती. "exec" कॉन्फ़िगरेशन में होने पर, आगे के कॉन्फ़िगरेशन ट्रांज़िशन किए जा सकते हैं.
  • "होस्ट", मोनोलिथिक होता है और "exec" नहीं: सिर्फ़ एक "होस्ट" कॉन्फ़िगरेशन होता है. हालांकि, हर एक्ज़ीक्यूशन प्लैटफ़ॉर्म के लिए अलग-अलग "exec" कॉन्फ़िगरेशन हो सकता है.
  • "होस्ट" यह मानता है कि आप टूल उसी मशीन पर चलाते हैं जिस पर Bazel या बहुत ही मिलती-जुलती मशीन है. अब यह सच नहीं है: आप अपनी लोकल मशीन या रिमोट एक्सक्यूटर पर बिल्ड कार्रवाइयां चला सकते हैं. साथ ही, इस बात की कोई गारंटी नहीं है कि रिमोट एक्ज़ीक्यूटर वही सीपीयू और ओएस है जो आपकी लोकल मशीन पर है.

"exec" और "host" कॉन्फ़िगरेशन, दोनों में एक जैसे विकल्प से जुड़े बदलाव लागू होते हैं. उदाहरण के लिए, --host_compilation_mode से --compilation_mode सेट करें, --host_cpu से --cpu सेट करें वगैरह. इसमें अंतर यह है कि "होस्ट" कॉन्फ़िगरेशन, दूसरे सभी फ़्लैग की डिफ़ॉल्ट वैल्यू से शुरू होता है. वहीं, "exec" कॉन्फ़िगरेशन, टारगेट कॉन्फ़िगरेशन के आधार पर फ़्लैग के मौजूदा वैल्यू से शुरू होता है.

कॉन्फ़िगरेशन फ़्रैगमेंट

नियम, 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
    ...
)

ctx.fragments सिर्फ़ टारगेट कॉन्फ़िगरेशन के लिए कॉन्फ़िगरेशन फ़्रैगमेंट उपलब्ध कराता है. अगर आपको होस्ट कॉन्फ़िगरेशन के लिए फ़्रैगमेंट ऐक्सेस करने हैं, तो इसके बजाय ctx.host_fragments का इस्तेमाल करें.

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

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

लॉजिक को हमेशा कवरेज मोड में चालू रखना चाहिए (चाहे टारगेट के सोर्स के लिए खास तौर पर कोई ज़रूरी टूल या सिस्टम काम करता हो या नहीं), उसे ctx.configuration.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

नियमों में यह जानकारी भी होनी चाहिए कि coverage_common.instrumented_files_info का इस्तेमाल करके बनाए गए, InstrumentedFilesInfo की सेवा देने वाली कंपनी के साथ कवरेज के लिए कौनसे एट्रिब्यूट काम के हैं. 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 नहीं दिखाया जाता है, तो हर गैर-टूल डिपेंडेंसी एट्रिब्यूट के साथ एक डिफ़ॉल्ट वैल्यू बन जाती है, जो dependency_attributes में cfg को cfg एट्रिब्यूट स्कीमा में "host" या "exec" पर सेट नहीं करती. (यह सबसे अच्छा व्यवहार नहीं है, क्योंकि इसमें srcs जैसे एट्रिब्यूट को source_attributes के बजाय dependency_attributes में डाला जाता है. हालांकि, इससे डिपेंडेंसी चेन में आने वाले सभी नियमों के लिए, साफ़ तौर पर कवरेज कॉन्फ़िगरेशन की ज़रूरत नहीं पड़ती.)

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

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

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

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

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

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

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

  2. अगर बिल्ड में दूसरी कार्रवाइयां, कंपाइल करने की कार्रवाई के बजाय चल सकती हैं, तो पुष्टि करने वाली कार्रवाइयों के खाली आउटपुट को उन कार्रवाइयों में भी जोड़ना होगा (उदाहरण के लिए, java_library का सोर्स जार आउटपुट). यह तब भी समस्या होती है, जब कंपाइल करने के बजाय की जाने वाली नई कार्रवाइयां बाद में जोड़ी जाती हैं और पुष्टि करने वाला खाली आउटपुट गलती से छूट जाता है.

पुष्टि करने वाले आउटपुट ग्रुप का इस्तेमाल करके, इन समस्याओं को हल किया जा सकता है.

पुष्टि करने के आउटपुट का ग्रुप

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

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

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

टारगेट की पुष्टि करने वाली कार्रवाइयां तीन मामलों में नहीं चलाई जाती हैं:

  • जब टारगेट, एक टूल के तौर पर निर्भर होता है
  • जब टारगेट, इंप्लिसिट डिपेंडेंसी के रूप में निर्भर करता है (उदाहरण के लिए, "_" से शुरू होने वाला कोई एट्रिब्यूट)
  • जब टारगेट, होस्ट या exec कॉन्फ़िगरेशन में बनाया जाता है.

यह माना जाता है कि इन टारगेट के अपने अलग-अलग बिल्ड और टेस्ट होते हैं, जिनसे पुष्टि करने में हुई किसी भी गड़बड़ी का पता चल जाता है.

पुष्टि करने वाले आउटपुट ग्रुप का इस्तेमाल करना

पुष्टि करने वाले आउटपुट ग्रुप को _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) उपलब्ध कराते हैं.

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

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

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

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