Bzlmod की मदद से एक्सटर्नल डिपेंडेंसी मैनेज करें

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

Bzlmod, Bazel 5.0 में पेश किए गए नए बाहरी डिपेंडेंसी सिस्टम का कोडनेम है. इसे पुराने सिस्टम की उन समस्याओं को हल करने के लिए लॉन्च किया गया था जिन्हें धीरे-धीरे ठीक नहीं किया जा सकता. ज़्यादा जानकारी के लिए, ओरिजनल डिज़ाइन दस्तावेज़ के 'समस्या का ब्यौरा' सेक्शन देखें.

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

अपने प्रोजेक्ट को Bzlmod पर माइग्रेट करने के लिए, Bzlmod माइग्रेशन गाइड देखें. उदाहरण रिपॉज़िटरी में, Bzlmod के इस्तेमाल के उदाहरण भी देखे जा सकते हैं.

Bazel मॉड्यूल

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

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

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

MODULE.bazel

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

module(
    name = "my-module",
    version = "1.0",
)

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

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

MODULE.bazel फ़ाइल, BUILD फ़ाइलों से मिलती-जुलती है, क्योंकि यह कंट्रोल फ़्लो के किसी भी फ़ॉर्मैट के साथ काम नहीं करती. साथ ही, यह load स्टेटमेंट पर भी पाबंदी लगाती है. निर्देशों के साथ काम करने वाली MODULE.bazel फ़ाइलें:

  • module, मौजूदा मॉड्यूल के मेटाडेटा के बारे में बताने के लिए. इसमें मॉड्यूल का नाम, वर्शन वगैरह शामिल है;
  • bazel_dep, अन्य Bazel मॉड्यूल पर डायरेक्ट डिपेंडेंसी बताने के लिए;
  • बदलाव, जिनका इस्तेमाल सिर्फ़ रूट मॉड्यूल (यानी, डिपेंडेंसी के तौर पर इस्तेमाल किए जा रहे मॉड्यूल के बजाय) से किया जा सकता है. इनका इस्तेमाल, किसी डायरेक्ट या ट्रांज़िटिव डिपेंडेंसी के व्यवहार को पसंद के मुताबिक बनाने के लिए किया जाता है:
  • मॉड्यूल एक्सटेंशन से जुड़े निर्देश:

वर्शन का फ़ॉर्मैट

Bazel का एक अलग ईकोसिस्टम है और प्रोजेक्ट, वर्शनिंग की अलग-अलग स्कीम का इस्तेमाल करते हैं. अब तक SemVer सबसे लोकप्रिय है. हालांकि, Abseil जैसी अलग-अलग स्कीम का इस्तेमाल करने वाले लोकप्रिय प्रोजेक्ट भी हैं. इनके वर्शन, तारीख पर आधारित होते हैं, जैसे कि 20210324.2).

इस वजह से, Bzlmod में SemVer स्पेसिफ़िकेशन का ज़्यादा आसान वर्शन अपनाया गया है. इनमें ये अंतर शामिल हैं:

  • SemVer के मुताबिक, वर्शन के "रिलीज़" हिस्से में तीन सेगमेंट होने चाहिए: MAJOR.MINOR.PATCH. Bazel में, इस शर्त को कम किया गया है, ताकि किसी भी संख्या में सेगमेंट बनाए जा सकें.
  • SemVer में, "रिलीज़" वाले हिस्से के हर सेगमेंट में सिर्फ़ अंक होने चाहिए. Bazel में, अक्षरों को भी शामिल करने के लिए इस शर्त को कम किया गया है. साथ ही, तुलना के सेमेटिक्स, "प्री-रिलीज़" वाले हिस्से में मौजूद "आइडेंटिफ़ायर" से मैच करते हैं.
  • इसके अलावा, मेजर, माइनर, और पैच वर्शन में होने वाली बढ़ोतरी के सेमेटिक्स लागू नहीं किए जाते. हालांकि, पुराने सिस्टम के साथ काम करने की सुविधा के बारे में जानकारी पाने के लिए, कंपैटबिलिटी लेवल देखें.

SemVer का कोई भी मान्य वर्शन, Bazel मॉड्यूल का मान्य वर्शन होता है. इसके अलावा, दो SemVer वर्शन a और b की तुलना a < b से तब की जाती है, जब Bazel मॉड्यूल वर्शन के तौर पर उनकी तुलना करने पर भी यही नतीजा मिले.

वर्शन का रिज़ॉल्यूशन

डायमंड डिपेंडेंसी की समस्या, वर्शन वाले डिपेंडेंसी मैनेजमेंट स्पेस में आम तौर पर होती है. मान लें कि आपके पास यह डिपेंडेंसी ग्राफ़ है:

       A 1.0
      /     \
   B 1.0    C 1.1
     |        |
   D 1.0    D 1.1

D के किस वर्शन का इस्तेमाल किया जाना चाहिए? इस सवाल को हल करने के लिए, Bzlmod, Go मॉड्यूल सिस्टम में पेश किए गए कम से कम वर्शन चुनने (एमवीएस) एल्गोरिदम का इस्तेमाल करता है. MVS यह मानता है कि किसी मॉड्यूल के सभी नए वर्शन, पुराने वर्शन के साथ काम करते हैं. इसलिए, यह किसी भी डिपेंडेंट (हमारे उदाहरण में D 1.1) के ज़रिए बताए गए सबसे नए वर्शन को चुनता है. इसे "कम से कम" कहा जाता है, क्योंकि D 1.1 यहां कम से कम वर्शन है, जो हमारी ज़रूरतों को पूरा कर सकता है. भले ही, D 1.2 या नया वर्शन मौजूद हो, हम उन्हें नहीं चुनते. इसकी एक और फ़ायदे यह है कि चुना गया वर्शन हाई-फ़िडेलिटी और फिर से बनाया जा सकने वाला होता है.

वर्शन रिज़ॉल्यूशन, रजिस्ट्री के बजाय आपकी मशीन पर स्थानीय तौर पर किया जाता है.

काम करने का लेवल

ध्यान दें कि पुराने सिस्टम के साथ काम करने की सुविधा के बारे में MVS का अनुमान सही है, क्योंकि यह किसी मॉड्यूल के पुराने सिस्टम के साथ काम न करने वाले वर्शन को अलग मॉड्यूल के तौर पर इस्तेमाल करता है. SemVer के हिसाब से, इसका मतलब है कि A 1.x और A 2.x को अलग-अलग मॉड्यूल माना जाता है. साथ ही, ये दोनों मॉड्यूल, रिज़ॉल्व किए गए डिपेंडेंसी ग्राफ़ में एक साथ मौजूद हो सकते हैं. ऐसा इसलिए होता है, क्योंकि Go में मेजर वर्शन को पैकेज पाथ में कोड में बदला जाता है. इसलिए, कॉम्पाइल करने या लिंक करने के दौरान कोई समस्या नहीं होती.

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

डेटा स्टोर करने की जगहों के नाम

Bazel में, हर बाहरी डिपेंडेंसी का एक रिपॉज़िटरी नाम होता है. कभी-कभी, एक ही डिपेंडेंसी का इस्तेमाल अलग-अलग रिपॉज़िटरी के नामों से किया जा सकता है. उदाहरण के लिए, @io_bazel_skylib और @bazel_skylib, दोनों का मतलब Bazel skylib है. इसके अलावा, अलग-अलग प्रोजेक्ट में अलग-अलग डिपेंडेंसी के लिए, एक ही रिपॉज़िटरी के नाम का इस्तेमाल किया जा सकता है.

Bzlmod में, Bazel मॉड्यूल और मॉड्यूल एक्सटेंशन से रिपॉज़िटरी जनरेट की जा सकती हैं. रिपॉज़िटरी के नाम से जुड़ी समस्याओं को हल करने के लिए, हम नए सिस्टम में रिपॉज़िटरी मैपिंग का तरीका अपना रहे हैं. यहां दो अहम बातें बताई गई हैं:

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

    • Bazel मॉड्यूल के रिपॉज़िटरी के लिए: module_name~version
      (उदाहरण. @bazel_skylib~1.0.3)
    • मॉड्यूल एक्सटेंशन के रिपॉज़िटरी के लिए: module_name~version~extension_name~repo_name
      (उदाहरण. @rules_cc~0.0.1~cc_configure~local_config_cc)
  • रिपॉज़िटरी का दिखने वाला नाम: रिपॉज़िटरी का वह नाम जिसका इस्तेमाल किसी रिपॉज़िटरी में BUILD और .bzl फ़ाइलों में किया जाता है. एक ही डिपेंडेंसी के अलग-अलग रिपॉज़िटरी में अलग-अलग नाम हो सकते हैं.
    यह इस तरह तय किया जाता है:

    • Bazel मॉड्यूल रिपॉज़िटरी के लिए: डिफ़ॉल्ट रूप से module_name या bazel_dep में repo_name एट्रिब्यूट से तय किया गया नाम.
    • मॉड्यूल एक्सटेंशन के रिपॉज़िटरी के लिए: use_repo के ज़रिए रिपॉज़िटरी का नाम डालें.

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

सख्त डिपेंडेंसी

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

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

  • Bazel मॉड्यूल का कोई भी रिपॉज़िटरी, bazel_dep और use_repo की मदद से, MODULE.bazel फ़ाइल में शामिल सभी रिपॉज़िटरी देख सकता है.
  • मॉड्यूल एक्सटेंशन का रिपॉज़िटरी, एक्सटेंशन देने वाले मॉड्यूल की सभी दिखने वाली डिपेंडेंसी देख सकता है. साथ ही, उसी मॉड्यूल एक्सटेंशन से जनरेट किए गए सभी अन्य रिपॉज़िटरी भी देख सकता है.

रजिस्ट्री

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

इंडेक्स रजिस्ट्री

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

इंडेक्स रजिस्ट्री इस फ़ॉर्मैट में होनी चाहिए:

  • /bazel_registry.json: रजिस्ट्री का मेटाडेटा शामिल करने वाली JSON फ़ाइल, जैसे कि:
    • mirrors, सोर्स संग्रह के लिए इस्तेमाल किए जाने वाले मिरर की सूची बताना.
    • module_base_path, source.json फ़ाइल में local_repository टाइप वाले मॉड्यूल के लिए बेस पाथ तय करना.
  • /modules: इस डायरेक्ट्री में, इस रजिस्ट्री के हर मॉड्यूल के लिए एक सब-डायरेक्ट्री होती है.
  • /modules/$MODULE: यह एक डायरेक्ट्री है, जिसमें इस मॉड्यूल के हर वर्शन के लिए एक सबडायरेक्ट्री होती है. साथ ही, इसमें यह फ़ाइल भी होती है:
    • metadata.json: यह एक JSON फ़ाइल है, जिसमें मॉड्यूल के बारे में जानकारी होती है. इसमें ये फ़ील्ड होते हैं:
      • homepage: प्रोजेक्ट के होम पेज का यूआरएल.
      • maintainers: JSON ऑब्जेक्ट की सूची, जिसमें से हर एक रजिस्ट्री में मॉड्यूल के मैनेजर की जानकारी से जुड़ा होता है. ध्यान दें कि यह ज़रूरी नहीं है कि यह प्रोजेक्ट के लेखकों के नाम से मेल खाए.
      • versions: इस रजिस्ट्री में मौजूद, इस मॉड्यूल के सभी वर्शन की सूची.
      • yanked_versions: इस मॉड्यूल के हटाए गए वर्शन की सूची. फ़िलहाल, ऐसा नहीं किया जा सकता. हालांकि, आने वाले समय में, हटाए गए वर्शन को स्किप कर दिया जाएगा या गड़बड़ी का मैसेज दिखेगा.
  • /modules/$MODULE/$VERSION: ऐसी डायरेक्ट्री जिसमें ये फ़ाइलें शामिल हैं:
    • MODULE.bazel: इस मॉड्यूल वर्शन की MODULE.bazel फ़ाइल.
    • source.json: यह एक JSON फ़ाइल है, जिसमें इस मॉड्यूल वर्शन के सोर्स को फ़ेच करने का तरीका बताया गया है.
      • डिफ़ॉल्ट टाइप "संग्रहित करें" होता है. इसमें ये फ़ील्ड होते हैं:
        • url: सोर्स संग्रह का यूआरएल.
        • integrity: संग्रह का सब-रिसोर्स इंटिग्रिटी चेकसम.
        • strip_prefix: सोर्स संग्रह को निकालते समय, हटाने के लिए डायरेक्ट्री का प्रीफ़िक्स.
        • patches: स्ट्रिंग की सूची, जिसमें हर स्ट्रिंग में पैच फ़ाइल का नाम होता है. इसे, निकाले गए संग्रह पर लागू किया जाता है. पैच फ़ाइलें /modules/$MODULE/$VERSION/patches डायरेक्ट्री में मौजूद होती हैं.
        • patch_strip: यह यूनिक्स पैच के --strip आर्ग्युमेंट जैसा ही है.
      • इन फ़ील्ड के साथ किसी लोकल पाथ का इस्तेमाल करने के लिए, टाइप बदला जा सकता है:
        • type: local_path
        • path: रिपॉज़िटरी का लोकल पाथ, जिसे इस तरह से कैलकुलेट किया जाता है:
          • अगर पाथ एक पूर्ण पाथ है, तो उसका इस्तेमाल उसी तरह किया जाएगा.
          • अगर पाथ एक रिलेटिव पाथ है और module_base_path एक ऐब्सलूट पाथ है, तो पाथ को <module_base_path>/<path> में बदल दिया जाता है
          • अगर पाथ और module_base_path, दोनों रेलेटिव पाथ हैं, तो पाथ को <registry_path>/<module_base_path>/<path> में बदल दिया जाता है. रजिस्ट्री को स्थानीय तौर पर होस्ट किया जाना चाहिए और इसका इस्तेमाल --registry=file://<registry_path> को करना चाहिए. ऐसा न करने पर, Bazel गड़बड़ी का मैसेज दिखाएगा.
    • patches/: पैच फ़ाइलों वाली वैकल्पिक डायरेक्ट्री. इसका इस्तेमाल सिर्फ़ तब किया जाता है, जब source.json का टाइप "संग्रह" हो.

Bazel सेंट्रल रजिस्ट्री

Bazel Central Registry (BCR), एक इंडेक्स रजिस्ट्री है. यह bcr.bazel.build पर मौजूद है. इसके कॉन्टेंट को GitHub रिपॉज़िटरी bazelbuild/bazel-central-registry से बैक अप लिया जाता है.

BCR को Bazel कम्यूनिटी मैनेज करती है. योगदान देने वाले लोग, पुश अनुरोध सबमिट कर सकते हैं. Bazel Central Registry की नीतियां और प्रक्रियाएं देखें.

सामान्य इंडेक्स रजिस्ट्री के फ़ॉर्मैट का पालन करने के अलावा, बीसीआर के लिए हर मॉड्यूल वर्शन (/modules/$MODULE/$VERSION/presubmit.yml) के लिए एक presubmit.yml फ़ाइल ज़रूरी है. इस फ़ाइल में, कुछ ज़रूरी बिल्ड और टेस्ट टारगेट बताए गए हैं. इनका इस्तेमाल, इस मॉड्यूल वर्शन की पुष्टि करने के लिए किया जा सकता है. साथ ही, बीसीआर की सीआई पाइपलाइन इसका इस्तेमाल, बीसीआर में मॉड्यूल के बीच इंटरऑपरेबिलिटी (एक-दूसरे के साथ काम करने की सुविधा) को पक्का करने के लिए करती है.

रजिस्ट्री चुनना

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

मॉड्यूल एक्सटेंशन

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

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

          [ A 1.1                ]
          [   * maven.dep(X 2.1) ]
          [   * maven.pom(...)   ]
              /              \
   bazel_dep /                \ bazel_dep
            /                  \
[ B 1.2                ]     [ C 1.0                ]
[   * maven.dep(X 1.2) ]     [   * maven.dep(X 2.1) ]
[   * maven.dep(Y 1.3) ]     [   * cargo.dep(P 1.1) ]
            \                  /
   bazel_dep \                / bazel_dep
              \              /
          [ D 1.4                ]
          [   * maven.dep(Z 1.4) ]
          [   * cargo.dep(Q 1.1) ]

ऊपर दिए गए डिपेंडेंसी ग्राफ़ के उदाहरण में, A 1.1 और B 1.2 वगैरह Bazel मॉड्यूल हैं. इनमें से हर एक को MODULE.bazel फ़ाइल माना जा सकता है. हर मॉड्यूल, मॉड्यूल एक्सटेंशन के लिए कुछ टैग तय कर सकता है. यहां कुछ टैग, एक्सटेंशन "maven" के लिए तय किए गए हैं और कुछ "cargo" के लिए. जब यह डिपेंडेंसी ग्राफ़ तैयार हो जाता है (उदाहरण के लिए, हो सकता है कि B 1.2 में असल में D 1.3 पर bazel_dep हो, लेकिन C की वजह से इसे D 1.4 पर अपग्रेड कर दिया गया हो), तो "maven" एक्सटेंशन चलाया जाता है. इससे सभी maven.* टैग पढ़े जा सकते हैं. साथ ही, उसमें मौजूद जानकारी का इस्तेमाल करके यह तय किया जा सकता है कि कौनसे रिपॉज़िटरी बनाने हैं. इसी तरह, "cargo" एक्सटेंशन के लिए भी.

एक्सटेंशन का इस्तेमाल

एक्सटेंशन, Bazel मॉड्यूल में ही होस्ट किए जाते हैं. इसलिए, अपने मॉड्यूल में किसी एक्सटेंशन का इस्तेमाल करने के लिए, आपको पहले उस मॉड्यूल में bazel_dep जोड़ना होगा. इसके बाद, उसे स्कोप में लाने के लिए, पहले से मौजूद use_extension फ़ंक्शन को कॉल करें. यहां दिए गए उदाहरण को देखें. यह MODULE.bazel फ़ाइल का एक स्निपेट है. इसमें rules_jvm_external मॉड्यूल में बताए गए काल्पनिक "maven" एक्सटेंशन का इस्तेमाल किया गया है:

bazel_dep(name = "rules_jvm_external", version = "1.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")

एक्सटेंशन को स्कोप में शामिल करने के बाद, उसके लिए टैग तय करने के लिए बिंदु-सिंटैक्स का इस्तेमाल किया जा सकता है. ध्यान दें कि टैग, उनसे जुड़ी टैग क्लास के मुताबिक तय किए गए स्कीमा के मुताबिक होने चाहिए. एक्सटेंशन की परिभाषा यहां देखें. यहां कुछ maven.dep और maven.pom टैग के बारे में बताने वाला उदाहरण दिया गया है.

maven.dep(coord="org.junit:junit:3.0")
maven.dep(coord="com.google.guava:guava:1.2")
maven.pom(pom_xml="//:pom.xml")

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

use_repo(
    maven,
    "org_junit_junit",
    guava="com_google_guava_guava",
)

किसी एक्सटेंशन से जनरेट किए गए रिपॉज़िटरी, उसके एपीआई का हिस्सा होते हैं. इसलिए, आपके तय किए गए टैग से आपको पता चलना चाहिए कि "maven" एक्सटेंशन, "org_junit_junit" और "com_google_guava_guava" नाम का एक रिपॉज़िटरी जनरेट करेगा. use_repo की मदद से, अपने मॉड्यूल के दायरे में इनका नाम बदला जा सकता है. जैसे, यहां "आंवला".

एक्सटेंशन की परिभाषा

मॉड्यूल एक्सटेंशन, रिपॉज़िटरी के नियमों की तरह ही तय किए जाते हैं. इसके लिए, module_extension फ़ंक्शन का इस्तेमाल किया जाता है. दोनों में लागू करने का फ़ंक्शन होता है. हालांकि, रिपॉज़िटरी के नियमों में कई एट्रिब्यूट होते हैं, जबकि मॉड्यूल एक्सटेंशन में कई tag_class होते हैं. इनमें से हर एक में कई एट्रिब्यूट होते हैं. टैग क्लास, इस एक्सटेंशन के इस्तेमाल किए गए टैग के लिए स्कीमा तय करती हैं. ऊपर दिए गए काल्पनिक "maven" एक्सटेंशन के उदाहरण को जारी रखते हुए:

# @rules_jvm_external//:extensions.bzl
maven_dep = tag_class(attrs = {"coord": attr.string()})
maven_pom = tag_class(attrs = {"pom_xml": attr.label()})
maven = module_extension(
    implementation=_maven_impl,
    tag_classes={"dep": maven_dep, "pom": maven_pom},
)

इन एलान से यह साफ़ तौर पर पता चलता है कि ऊपर बताए गए एट्रिब्यूट स्कीमा का इस्तेमाल करके, maven.dep और maven.pom टैग तय किए जा सकते हैं.

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

# @rules_jvm_external//:extensions.bzl
load("//:repo_rules.bzl", "maven_single_jar")
def _maven_impl(ctx):
  coords = []
  for mod in ctx.modules:
    coords += [dep.coord for dep in mod.tags.dep]
  output = ctx.execute(["coursier", "resolve", coords])  # hypothetical call
  repo_attrs = process_coursier(output)
  [maven_single_jar(**attrs) for attrs in repo_attrs]

ऊपर दिए गए उदाहरण में, हम डिपेंडेंसी ग्राफ़ (ctx.modules) के सभी मॉड्यूल देखते हैं. इनमें से हर मॉड्यूल एक bazel_module ऑब्जेक्ट होता है. इसका tags फ़ील्ड, मॉड्यूल पर मौजूद सभी maven.* टैग दिखाता है. इसके बाद, हम Maven से संपर्क करने और समस्या हल करने के लिए, सीएलआई टूल 'Coursier' का इस्तेमाल करते हैं. आखिर में, हम रिज़ॉल्यूशन के नतीजे का इस्तेमाल करके कई रिपॉज़िटरी बनाते हैं. इसके लिए, हम maven_single_jar रिपॉज़िटरी के नियम का इस्तेमाल करते हैं.