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

किसी समस्या की शिकायत करें सोर्स देखें Nightly · 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 का कोई भी मान्य वर्शन, Basel मॉड्यूल का एक मान्य वर्शन है. इसके अलावा, दो SemVer वर्शन a और b एक ही होल्ड की तुलना में a < b की तुलना करते हैं. इन होल्ड की तुलना बज़ल मॉड्यूल वर्शन से की जाती है.

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

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

       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 में, डेटा स्टोर करने की जगहें, Basel मॉड्यूल और मॉड्यूल एक्सटेंशन की मदद से जनरेट की जा सकती हैं. रिपॉज़िटरी के नाम से जुड़ी समस्याओं को हल करने के लिए, हम नए सिस्टम में रिपॉज़िटरी मैपिंग का तरीका अपना रहे हैं. यहां दो अहम बातें बताई गई हैं:

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

    • 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 ने Baज़ेन की रजिस्ट्री से जानकारी मांगकर, डिपेंडेंसी का पता लगाया. 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> के ज़रिए इस्तेमाल किया जाना चाहिए. ऐसा न करने पर, Basel एक गड़बड़ी दिखाएगा.
    • patches/: पैच फ़ाइलों वाली वैकल्पिक डायरेक्ट्री. इसका इस्तेमाल सिर्फ़ तब किया जाता है, जब source.json का टाइप "संग्रह" हो.

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

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

BCR को Bazel कम्यूनिटी मैनेज करती है. योगदान देने वाले लोग, पुश अनुरोध सबमिट कर सकते हैं. Basel 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 वगैरह Basel मॉड्यूल हैं. हर एक को 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 रेपो नियम का इस्तेमाल करते हैं.