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 में, अक्षरों को भी अनुमति देने के लिए इसे कम कर दिया गया है. साथ ही, तुलना के सिमैंटिक, "prerelease" वाले हिस्से में मौजूद "पहचानकर्ताओं" से मेल खाते हैं.
- इसके अलावा, मेजर, माइनर, और पैच वर्शन में बढ़ोतरी के सिमैंटिक लागू नहीं किए जाते. (हालांकि, पुराने सिस्टम के साथ काम करने की सुविधा के बारे में ज़्यादा जानने के लिए, कंपैटबिलिटी लेवल देखें.)
कोई भी मान्य 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)
- Bazel मॉड्यूल के रेपो के लिए:
रिपॉज़िटरी का नाम: रिपॉज़िटरी का वह नाम जिसे रिपॉज़िटरी में मौजूद
BUILDऔर.bzlफ़ाइलों में इस्तेमाल किया जाना है. एक ही डिपेंडेंसी के अलग-अलग नाम हो सकते हैं.
इसे इस तरह तय किया जाता है:
हर रिपॉज़िटरी में, सीधे तौर पर निर्भरता रखने वाली रिपॉज़िटरी की मैपिंग डिक्शनरी होती है. यह डिक्शनरी, रिपॉज़िटरी के नाम को कैननिकल रिपॉज़िटरी के नाम पर मैप करती है.
हम लेबल बनाते समय, रिपॉज़िटरी के नाम का पता लगाने के लिए रिपॉज़िटरी मैपिंग का इस्तेमाल करते हैं. ध्यान दें कि कैननिकल रिपॉज़िटरी के नामों में कोई टकराव नहीं होता है. साथ ही, MODULE.bazel फ़ाइल को पार्स करके, रिपॉज़िटरी के नामों के इस्तेमाल का पता लगाया जा सकता है. इसलिए, टकरावों का आसानी से पता लगाया जा सकता है और अन्य डिपेंडेंसी पर असर डाले बिना उन्हें हल किया जा सकता है.
स्ट्रिक्ट डिपेंडेंसी
डिपेंडेंसी स्पेसिफ़िकेशन के नए फ़ॉर्मैट की मदद से, हम ज़्यादा बेहतर तरीके से जांच कर सकते हैं. खास तौर पर, अब हम यह पक्का करते हैं कि कोई मॉड्यूल सिर्फ़ उन रीपो का इस्तेमाल कर सकता है जिन्हें उसकी सीधी डिपेंडेंसी से बनाया गया है. इससे ट्रांज़िटिव डिपेंडेंसी ग्राफ़ में किसी तरह का बदलाव होने पर, अनजाने में होने वाली गड़बड़ियों को ठीक करने में मदद मिलती है.
स्ट्रिक्ट डिप्लॉयमेंट डिपेंडेंसी, रिपॉज़िटरी मैपिंग के आधार पर लागू की जाती है. हर रिपॉज़िटरी के लिए रिपॉज़िटरी मैपिंग में, उसकी सभी डायरेक्ट डिपेंडेंसी शामिल होती हैं. इसमें कोई दूसरी रिपॉज़िटरी नहीं दिखती. हर रिपॉज़िटरी के लिए, दिखने वाली डिपेंडेंसी इस तरह तय की जाती हैं:
- Bazel मॉड्यूल रेपो,
MODULE.bazelफ़ाइल में शामिल की गई सभी रेपो कोbazel_depऔरuse_repoकी मदद से देख सकता है. - मॉड्यूल एक्सटेंशन की रेपो, एक्सटेंशन देने वाले मॉड्यूल की सभी डिपेंडेंसी देख सकती है. साथ ही, उसी मॉड्यूल एक्सटेंशन से जनरेट की गई अन्य सभी रेपो भी देख सकती है.
रजिस्ट्री
Bzlmod, Bazel रजिस्ट्रियों से उनकी जानकारी का अनुरोध करके, डिपेंडेंसी का पता लगाता है. Bazel रजिस्ट्री, Bazel मॉड्यूल का डेटाबेस होती है. सिर्फ़ इंडेक्स रजिस्ट्री का इस्तेमाल किया जा सकता है. यह एक लोकल डायरेक्ट्री या स्टैटिक एचटीटीपी सर्वर होता है, जो किसी खास फ़ॉर्मैट का पालन करता है. हम आने वाले समय में, सिंगल-मॉड्यूल रजिस्ट्री के लिए सहायता जोड़ने का प्लान बना रहे हैं. ये सिर्फ़ गिट रिपॉज़िटरी होती हैं, जिनमें किसी प्रोजेक्ट का सोर्स और इतिहास होता है.
इंडेक्स रजिस्ट्री
इंडेक्स रजिस्ट्री, एक लोकल डायरेक्ट्री या स्टैटिक एचटीटीपी सर्वर होता है. इसमें मॉड्यूल की सूची के बारे में जानकारी होती है. जैसे, उनका होम पेज, रखरखाव करने वाले लोग, हर वर्शन की 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 फ़ाइल है. इसमें इस मॉड्यूल वर्शन के सोर्स को फ़ेच करने के तरीके के बारे में जानकारी होती है.- डिफ़ॉल्ट टाइप "archive" होता है. इसमें ये फ़ील्ड होते हैं:
url: सोर्स संग्रह का यूआरएल.integrity: यह संग्रह का सबरीसोर्स इंटिग्रिटी चेकसम है.strip_prefix: सोर्स संग्रह को निकालते समय हटाने के लिए डायरेक्ट्री प्रीफ़िक्स.patches: स्ट्रिंग की सूची. इनमें से हर स्ट्रिंग, निकाली गई संग्रह फ़ाइल पर लागू करने के लिए पैच फ़ाइल का नाम बताती है. पैच फ़ाइलें,/modules/$MODULE/$VERSION/patchesडायरेक्ट्री में मौजूद होती हैं.patch_strip: यह Unix patch के--stripतर्क के जैसा ही है.
- इन फ़ील्ड के साथ लोकल पाथ का इस्तेमाल करने के लिए, टाइप को बदला जा सकता है:
type:local_pathpath: यह रिपो का लोकल पाथ होता है. इसे इस तरह से कैलकुलेट किया जाता है:- अगर पाथ एक ऐब्सलूट पाथ है, तो इसका इस्तेमाल वैसे ही किया जाएगा.
- अगर पाथ एक रिलेटिव पाथ है और
module_base_pathएक ऐब्सलूट पाथ है, तो पाथ को<module_base_path>/<path>पर ले जाया जाता है - अगर पाथ और
module_base_path, दोनों रेलेटिव पाथ हैं, तो पाथ को<registry_path>/<module_base_path>/<path>के तौर पर हल किया जाता है. रजिस्ट्री को स्थानीय तौर पर होस्ट किया जाना चाहिए और--registry=file://<registry_path>को इसका इस्तेमाल करना चाहिए. ऐसा न होने पर, Bazel गड़बड़ी का मैसेज दिखाएगा.
- डिफ़ॉल्ट टाइप "archive" होता है. इसमें ये फ़ील्ड होते हैं:
patches/: यह एक वैकल्पिक डायरेक्ट्री है. इसमें पैच फ़ाइलें होती हैं. इसका इस्तेमाल सिर्फ़ तब किया जाता है, जबsource.jsonका टाइप "archive" होता है.
Bazel Central Registry
Bazel Central Registry (BCR) एक इंडेक्स रजिस्ट्री है. यह bcr.bazel.build पर मौजूद है. इसका कॉन्टेंट, GitHub रिपो bazelbuild/bazel-central-registry से लिया गया है.
बीसीआर को Bazel कम्यूनिटी मैनेज करती है. योगदान देने वाले लोग, पुल अनुरोध सबमिट कर सकते हैं. Bazel Central Registry की नीतियां और प्रक्रियाएं देखें.
बीसीआर को सामान्य इंडेक्स रजिस्ट्री के फ़ॉर्मैट के साथ-साथ, हर मॉड्यूल वर्शन (/modules/$MODULE/$VERSION/presubmit.yml) के लिए presubmit.yml फ़ाइल की भी ज़रूरत होती है. इस फ़ाइल में, कुछ ज़रूरी बिल्ड और टेस्ट टारगेट के बारे में बताया गया होता है. इनका इस्तेमाल, इस मॉड्यूल वर्शन की वैधता की जांच करने के लिए किया जा सकता है. साथ ही, इसका इस्तेमाल बीसीआर की सीआई पाइपलाइन करती हैं. इससे यह पक्का किया जा सकता है कि बीसीआर में मौजूद मॉड्यूल एक-दूसरे के साथ काम कर सकें.
रजिस्ट्रियां चुनना
Bazel के --registry फ़्लैग का इस्तेमाल, उन रजिस्ट्री की सूची तय करने के लिए किया जा सकता है जिनसे मॉड्यूल का अनुरोध करना है. इसलिए, अपने प्रोजेक्ट को इस तरह से सेट अप किया जा सकता है कि वह तीसरे पक्ष या इंटरनल रजिस्ट्री से डिपेंडेंसी फ़ेच करे. पहले किए गए रजिस्ट्रेशन को प्राथमिकता दी जाती है. आसानी के लिए, --registry फ़्लैग की सूची को अपने प्रोजेक्ट की .bazelrc फ़ाइल में रखा जा सकता है.
मॉड्यूल एक्सटेंशन
मॉड्यूल एक्सटेंशन की मदद से, मॉड्यूल सिस्टम को बढ़ाया जा सकता है. इसके लिए, वे डिपेंडेंसी ग्राफ़ में मौजूद मॉड्यूल से इनपुट डेटा पढ़ते हैं. साथ ही, डिपेंडेंसी को हल करने के लिए ज़रूरी लॉजिक लागू करते हैं. इसके बाद, वे repo नियमों को कॉल करके repo बनाते हैं. ये आज के 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.* टैग पढ़ने की अनुमति मिल जाती है. साथ ही, इसमें मौजूद जानकारी का इस्तेमाल करके यह तय किया जाता है कि कौनसी रिपॉज़िटरी बनानी हैं.
इसी तरह, "कार्गो" एक्सटेंशन के लिए भी ऐसा किया जा सकता है.
एक्सटेंशन के इस्तेमाल से जुड़ी जानकारी
एक्सटेंशन, 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 डायरेक्टिव का इस्तेमाल करें. ऐसा deps की ज़रूरी शर्त को पूरा करने और लोकल रेपो के नाम में होने वाले टकराव से बचने के लिए किया जाता है.
use_repo(
maven,
"org_junit_junit",
guava="com_google_guava_guava",
)
किसी एक्सटेंशन से जनरेट किए गए रेपो, उसके एपीआई का हिस्सा होते हैं. इसलिए, आपके दिए गए टैग से आपको पता होना चाहिए कि "maven" एक्सटेंशन, "org_junit_junit" और "com_google_guava_guava" नाम के रेपो जनरेट करेगा. use_repo की मदद से, अपने मॉड्यूल के स्कोप में उनका नाम बदला जा सकता है. जैसे, यहां "guava" नाम दिया गया है.
एक्सटेंशन की परिभाषा
मॉड्यूल एक्सटेंशन को, रेपो के नियमों की तरह ही तय किया जाता है. इसके लिए, module_extension फ़ंक्शन का इस्तेमाल किया जाता है.
दोनों में एक फ़ंक्शन होता है. हालांकि, रेपो के नियमों में कई एट्रिब्यूट होते हैं, जबकि मॉड्यूल एक्सटेंशन में कई tag_classes होते हैं. इनमें से हर एक में कई एट्रिब्यूट होते हैं. टैग क्लास, इस एक्सटेंशन के इस्तेमाल किए गए टैग के लिए स्कीमा तय करती हैं. ऊपर दिए गए काल्पनिक "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 से संपर्क करने और समस्या हल करने के लिए, CLI यूटिलिटी Coursier का इस्तेमाल करते हैं. आखिर में, हम रिज़ॉल्यूशन के नतीजे का इस्तेमाल करके कई रिपो बनाते हैं. इसके लिए, हम काल्पनिक maven_single_jar
रिपो के नियम का इस्तेमाल करते हैं.
बाहरी लिंक
- Bazel की बाहरी डिपेंडेंसी को बेहतर बनाने से जुड़ा दस्तावेज़ (Bzlmod के डिज़ाइन से जुड़ा मूल दस्तावेज़)
- Bazel Central Registry की नीतियां और प्रक्रियाएं
- Bazel Central Registry GitHub repo
- Bzlmod पर BazelCon 2021 की बातचीत