नियम

किसी समस्या की शिकायत करें सोर्स देखें रात · 7.4 को अपनाएं. 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

Bazel के हिसाब से, g++ और स्टैंडर्ड C++ लाइब्रेरी भी इस नियम के इनपुट हैं. नियम लिखने वाले के तौर पर, आपको न सिर्फ़ उपयोगकर्ता के लिए उपलब्ध कराए गए इनपुट के साथ-साथ उन सभी टूल और लाइब्रेरी को भी शामिल कर सकते हैं जो इन्हें लागू करने के लिए ज़रूरी हैं कार्रवाइयां.

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

कुछ नियम, 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 आउटपुट (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",
        ),
    },
)

इस उदाहरण में, example_library टाइप के हर टारगेट की, कंपाइलर //tools:example_compiler पर आश्रित होना ज़रूरी है. इससे आपको को शुरू करने वाली कार्रवाइयों को जनरेट करने के लिए example_library का लागू करने का फ़ंक्शन कंपाइलर, भले ही उपयोगकर्ता ने अपने लेबल को इनपुट के तौर पर पास नहीं किया हो. _compiler एक निजी एट्रिब्यूट है. इसलिए, इस तरह के नियम के सभी टारगेट में ctx.attr._compiler हमेशा //tools:example_compiler पर ले जाएगा. इसके अलावा, एट्रिब्यूट compiler को नाम दिए बिना भी ऐसा किया जा सकता है अंडरस्कोर डालें और डिफ़ॉल्ट मान बनाए रखें. इससे लोग, अगर ज़रूरी हो, तो अलग-अलग कंपाइलर, लेकिन उसे कंपाइलर के बारे में जानकारी की ज़रूरत नहीं होती है लेबल.

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

आउटपुट एट्रिब्यूट

आउटपुट एट्रिब्यूट, जैसे कि attr.output और attr.output_list, किसी आउटपुट फ़ाइल में यह एलान करेगा कि टारगेट से जनरेट होता है. ये, डिपेंडेंसी एट्रिब्यूट से दो तरीकों से अलग होते हैं:

  • ये किसी और जगह पर तय किए गए टारगेट का रेफ़रंस देने के बजाय, आउटपुट फ़ाइल के टारगेट तय करते हैं.
  • आउटपुट फ़ाइल के टारगेट, इंस्टैंशिएट किए गए नियम के टारगेट पर निर्भर करते हैं अलग-अलग तरह से काम करता है.

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

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

लागू करने का फ़ंक्शन

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

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

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

टारगेट

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

ctx.attr में हर फ़ील्ड के नाम से मिलते-जुलते फ़ील्ड हैं डिपेंडेंसी एट्रिब्यूट, जिसमें Target ऑब्जेक्ट हैं. ये ऑब्जेक्ट हर डायरेक्ट के बारे में बताते हैं पर निर्भर है. label_list एट्रिब्यूट के लिए, यह यहां दी गई सूची में मौजूद है: Targets. label एट्रिब्यूट के लिए, यह एक Target या None है.

टारगेट लागू करने वाले फ़ंक्शन का इस्तेमाल करके, प्रोवाइडर के ऑब्जेक्ट की सूची बनाई जाती है:

return [ExampleInfo(headers = depset(...))]

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

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

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

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

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

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

सेवा देने वाली कंपनियां

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

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

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

रनफ़ाइलें

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

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

कुछ नियम, एट्रिब्यूट के बारे में बताते हैं. इन्हें आम तौर पर data, जिसके आउटपुट इसमें जोड़े जाते हैं लक्ष्य का runfiles. रनफ़ाइल को 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 निर्देश से शुरू किया गया. लागू किए जा सकने वाले और जांच के नियमों को इन्होंने बनाया है executable को सेट करें या rule को किए गए कॉल में True के लिए test तर्क:

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

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

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

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

Runfiles की जगह

जब किसी एक्ज़ीक्यूटेबल टारगेट को 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)

runfiles डायरेक्ट्री में मौजूद File का पाथ, File.short_path से मेल खाता है.

bazel से सीधे तौर पर चलाया गया बाइनरी, runfiles डायरेक्ट्री के रूट के बगल में होता है. हालांकि, रनफ़ाइलों से from नाम वाले बाइनरी, ऐसा नहीं कर सकते. इस समस्या को कम करने के लिए, हर बाइनरी को एक तरीका देना चाहिए, ताकि वह किसी एनवायरमेंट या कमांड-लाइन आर्ग्युमेंट/फ़्लैग का इस्तेमाल करके, अपने runfiles रूट को पैरामीटर के तौर पर स्वीकार कर सके. इससे बाइनरी को सही कैननिकल रनफ़ाइल रूट पास करने की अनुमति मिलती है उसे बाइनरी पर ले जाते हैं. अगर यह सेट नहीं है, तो बाइनरी यह अनुमान लगा सकती है कि इसे सबसे पहले कॉल किया गया था और वह आस-पास मौजूद runfiles डायरेक्ट्री खोज सकती है.

उन्नत विषय

आउटपुट फ़ाइलों का अनुरोध किया जा रहा है

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

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

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

कॉन्फ़िगरेशन

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  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 टाइप में सुविधाओं का एक जटिल सेट होता है. इनमें से कई सुविधाएं लेगसी वजहों से रखी जाती हैं. यहां दिए गए सुझावों से, समस्या को हल करने में आसानी होती है:

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

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

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

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

अब तक, 'बेज़ल' प्रोवाइडर, 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. लेगसी प्रोवाइडर का इस्तेमाल करने वाले नियमों में बदलाव करके, आधुनिक प्रोवाइडर का इस्तेमाल करें. अगर किसी एट्रिब्यूट की घोषणा के लिए, लेगसी प्रोवाइडर की ज़रूरत होती है, साथ ही, उन्हें अपडेट करने के बजाय, मॉडर्न प्रोवाइडर की ज़रूरत के हिसाब से अपडेट करें. इसके अलावा, आपके पास पहले चरण में यह काम करना छोड़ दें. इसके लिए, उपभोक्ताओं को provider: इसका इस्तेमाल करके, लेगसी प्रोवाइडर की मौजूदगी का पता लगाने के लिए जांच करना hasattr(target, 'foo'), या FooInfo in target का इस्तेमाल करने वाली नई कंपनी.

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