Bazel मॉड्यूल, Bazel प्रोजेक्ट होता है. इसके कई वर्शन हो सकते हैं. हर वर्शन, उन अन्य मॉड्यूल के बारे में मेटाडेटा पब्लिश करता है जिन पर वह निर्भर करता है. यह अन्य डिपेंडेंसी मैनेजमेंट सिस्टम में मौजूद जाने-पहचाने कॉन्सेप्ट के जैसा ही है. जैसे, Maven आर्टफ़ैक्ट, npm पैकेज, Go मॉड्यूल या Cargo क्रेट.
किसी मॉड्यूल के लिए, उसके रेपो रूट में 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 फ़ाइलों में उपलब्ध डायरेक्टिव की पूरी सूची देखें.
मॉड्यूल रिज़ॉल्यूशन के लिए, Bazel सबसे पहले रूट मॉड्यूल की MODULE.bazel फ़ाइल को पढ़ता है. इसके बाद, यह किसी भी डिपेंडेंसी की MODULE.bazel फ़ाइल के लिए, Bazel रजिस्ट्री से बार-बार अनुरोध करता है. ऐसा तब तक किया जाता है, जब तक इसे पूरा डिपेंडेंसी ग्राफ़ नहीं मिल जाता.
इसके बाद, Bazel डिफ़ॉल्ट रूप से इस्तेमाल करने के लिए, हर मॉड्यूल का एक वर्शन चुनता है. Bazel, हर मॉड्यूल को एक रेपो के तौर पर दिखाता है. साथ ही, हर रेपो को तय करने का तरीका जानने के लिए, वह फिर से रजिस्ट्री से सलाह लेता है.
वर्शन का फ़ॉर्मैट
Bazel का एक बड़ा ईकोसिस्टम है. साथ ही, प्रोजेक्ट अलग-अलग वर्शनिंग स्कीम का इस्तेमाल करते हैं. सबसे ज़्यादा इस्तेमाल किया जाने वाला वर्शनिंग सिस्टम SemVer है. हालांकि, Abseil जैसे कई बड़े प्रोजेक्ट, अलग-अलग स्कीम का इस्तेमाल करते हैं. इनके वर्शन, तारीख के हिसाब से तय किए जाते हैं. उदाहरण के लिए, 20210324.2).
इस वजह से, Bzlmod, SemVer स्पेसिफ़िकेशन के ज़्यादा आसान वर्शन का इस्तेमाल करता है. इन दोनों में ये अंतर हैं:
- SemVer के मुताबिक, वर्शन के "रिलीज़" वाले हिस्से में तीन सेगमेंट होने चाहिए:
MAJOR.MINOR.PATCH. Bazel में, इस शर्त को आसान बना दिया गया है, ताकि किसी भी संख्या में सेगमेंट बनाए जा सकें. - SemVer में, "release" वाले हिस्से के हर सेगमेंट में सिर्फ़ अंक होने चाहिए. 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 मॉड्यूल सिस्टम में पेश किए गए Minimal Version Selection (MVS) एल्गोरिदम का इस्तेमाल करता है. एमवीएस यह मानकर चलता है कि मॉड्यूल के सभी नए वर्शन, पिछले वर्शन के साथ काम करते हैं. इसलिए, यह किसी भी डिपेंडेंट (हमारे उदाहरण में D 1.1) के ज़रिए तय किए गए सबसे नए वर्शन को चुनता है. इसे "कम से कम" कहा जाता है, क्योंकि D 1.1 ऐसा सबसे पुराना वर्शन है जो हमारी ज़रूरी शर्तों को पूरा कर सकता है. भले ही, D 1.2 या नया वर्शन मौजूद हो, हम उन्हें नहीं चुनते. एमवीएस का इस्तेमाल करने से, वर्शन चुनने की ऐसी प्रोसेस बनती है जो ज़्यादा सटीक और दोहराई जा सकने वाली होती है.
Yank किए गए वर्शन
अगर किसी वर्शन का इस्तेमाल नहीं करना चाहिए, तो रजिस्ट्री उसे yanked के तौर पर मार्क कर सकती है. जैसे, सुरक्षा से जुड़े जोखिम की आशंकाओं के लिए. किसी मॉड्यूल के यैंक किए गए वर्शन को चुनने पर, Bazel एक गड़बड़ी दिखाता है. इस गड़बड़ी को ठीक करने के लिए, या तो नए और बिना हटाए गए वर्शन पर अपग्रेड करें या हटाए गए वर्शन को साफ़ तौर पर अनुमति देने के लिए, --allow_yanked_versions फ़्लैग का इस्तेमाल करें.
कंपैटबिलिटी लेवल
Go में, MVS इस बात को ध्यान में रखता है कि पुराने सिस्टम के साथ काम करने वाले वर्शन को अलग मॉड्यूल के तौर पर माना जाता है. इसलिए, यह काम करता है. SemVer के हिसाब से, इसका मतलब है कि A 1.x और A 2.x को अलग-अलग मॉड्यूल माना जाता है. साथ ही, ये दोनों मॉड्यूल, हल किए गए डिपेंडेंसी ग्राफ़ में एक साथ मौजूद हो सकते हैं. Go में पैकेज पाथ में मुख्य वर्शन को एन्कोड करके, इसे मुमकिन बनाया जाता है. इससे कंपाइल-टाइम या लिंकिंग-टाइम में कोई टकराव नहीं होता.
हालांकि, Bazel इस तरह की गारंटी नहीं दे सकता. इसलिए, इसे "मेजर वर्शन" नंबर की ज़रूरत होती है, ताकि यह पता लगाया जा सके कि कौनसे वर्शन पिछले वर्शन के साथ काम नहीं करते. इस संख्या को कंपैटिबिलिटी लेवल कहा जाता है. इसे हर मॉड्यूल वर्शन में, module() डायरेक्टिव के ज़रिए तय किया जाता है. इस जानकारी की मदद से, Bazel गड़बड़ी का पता लगा सकता है. ऐसा तब होता है, जब उसे पता चलता है कि हल किए गए डिपेंडेंसी ग्राफ़ में, एक ही मॉड्यूल के ऐसे वर्शन मौजूद हैं जो अलग-अलग लेवल पर काम करते हैं.
बदली गई कीमत
Bazel मॉड्यूल रिज़ॉल्यूशन के व्यवहार में बदलाव करने के लिए, MODULE.bazel फ़ाइल में ओवरराइड तय करें. सिर्फ़ रूट मॉड्यूल के ओवरराइड लागू होते हैं. अगर किसी मॉड्यूल का इस्तेमाल डिपेंडेंसी के तौर पर किया जाता है, तो उसके ओवरराइड को अनदेखा कर दिया जाता है.
हर ओवरराइड को किसी मॉड्यूल के नाम के लिए तय किया जाता है. इससे डिपेंडेंसी ग्राफ़ में मौजूद उसके सभी वर्शन पर असर पड़ता है. हालांकि, सिर्फ़ रूट मॉड्यूल के ओवरराइड लागू होते हैं, लेकिन ये ट्रांज़िटिव डिपेंडेंसी के लिए हो सकते हैं. रूट मॉड्यूल सीधे तौर पर इन पर निर्भर नहीं होता.
एक वर्शन को ओवरराइड करना
single_version_override कई कामों के लिए इस्तेमाल किया जाता है:
versionएट्रिब्यूट की मदद से, किसी डिपेंडेंसी को किसी खास वर्शन पर पिन किया जा सकता है. इससे कोई फ़र्क़ नहीं पड़ता कि डिपेंडेंसी ग्राफ़ में डिपेंडेंसी के किन वर्शन का अनुरोध किया गया है.registryएट्रिब्यूट की मदद से, इस डिपेंडेंसी को किसी खास रजिस्ट्री से लाने के लिए मजबूर किया जा सकता है. इसके लिए, आपको सामान्य रजिस्ट्री चुनने की प्रोसेस को फ़ॉलो करने की ज़रूरत नहीं होगी.patch*एट्रिब्यूट की मदद से, डाउनलोड किए गए मॉड्यूल पर लागू करने के लिए पैच का सेट तय किया जा सकता है.
ये सभी एट्रिब्यूट ज़रूरी नहीं हैं. इन्हें एक-दूसरे के साथ मिलाकर इस्तेमाल किया जा सकता है.
एक से ज़्यादा वर्शन को ओवरराइड करना
multiple_version_override को यह तय करने के लिए सेट किया जा सकता है कि एक ही मॉड्यूल के कई वर्शन, हल किए गए डिपेंडेंसी ग्राफ़ में एक साथ मौजूद रह सकें.
मॉड्यूल के लिए, अनुमति वाले वर्शन की सूची साफ़ तौर पर दी जा सकती है. ये सभी वर्शन, रिज़ॉल्यूशन से पहले डिपेंडेंसी ग्राफ़ में मौजूद होने चाहिए. साथ ही, हर अनुमति वाले वर्शन के हिसाब से, कुछ ट्रांज़िटिव डिपेंडेंसी मौजूद होनी चाहिए. समस्या हल होने के बाद, मॉड्यूल के सिर्फ़ वे वर्शन मौजूद रहते हैं जिन्हें अनुमति मिली है. वहीं, Bazel मॉड्यूल के अन्य वर्शन को, उसी कंपैटिबिलिटी लेवल पर अनुमति वाले सबसे नए वर्शन में अपग्रेड कर देता है. अगर एक ही कंपैटिबिलिटी लेवल का कोई नया वर्शन उपलब्ध नहीं है, तो Bazel गड़बड़ी का मैसेज दिखाता है.
उदाहरण के लिए, अगर रिज़ॉल्यूशन से पहले डिपेंडेंसी ग्राफ़ में 1.1, 1.3, 1.5, 1.7, और 2.0 वर्शन मौजूद हैं और मुख्य वर्शन, कंपैटिबिलिटी लेवल है, तो:
- एक से ज़्यादा वर्शन को ओवरराइड करने की सुविधा का इस्तेमाल करके,
1.3,1.7, और2.0को अपग्रेड किया जा सकता है. इससे1.1को1.3में,1.5को1.7में अपग्रेड किया जा सकता है. साथ ही, अन्य वर्शन में कोई बदलाव नहीं होता. - एक से ज़्यादा वर्शन को ओवरराइड करने की सुविधा का इस्तेमाल करके
1.5और2.0को अपग्रेड करने पर गड़बड़ी होती है, क्योंकि1.7के पास उसी कंपैटिबिलिटी लेवल का कोई ऐसा नया वर्शन नहीं है जिस पर अपग्रेड किया जा सके. - एक से ज़्यादा वर्शन को बदलने की सुविधा का इस्तेमाल करके
1.9और2.0को बदलने पर गड़बड़ी होती है. ऐसा इसलिए होता है, क्योंकि समाधान से पहले1.9, डिपेंडेंसी ग्राफ़ में मौजूद नहीं होता है.
इसके अलावा, उपयोगकर्ता registry एट्रिब्यूट का इस्तेमाल करके भी रजिस्ट्री को बदल सकते हैं. यह सिंगल वर्शन बदलने की सुविधा की तरह ही काम करता है.
रजिस्ट्री के बाहर के ओवरराइड
नॉन-रजिस्ट्री ओवरराइड, वर्शन रिज़ॉल्यूशन से किसी मॉड्यूल को पूरी तरह हटा देते हैं. Bazel, इन MODULE.bazel फ़ाइलों का अनुरोध किसी रजिस्ट्री से नहीं करता, बल्कि सीधे तौर पर रेपो से करता है.
Bazel, रजिस्ट्री से बाहर के इन ओवरराइड को सपोर्ट करता है:
उन रिपॉज़िटरी को तय करें जो Bazel मॉड्यूल नहीं हैं
bazel_dep की मदद से, ऐसे रेपो तय किए जा सकते हैं जो अन्य Bazel मॉड्यूल को दिखाते हैं.
कभी-कभी, ऐसी रिपो को तय करने की ज़रूरत होती है जो Bazel मॉड्यूल को नहीं दिखाती है. उदाहरण के लिए, ऐसी रिपो जिसमें डेटा के तौर पर पढ़ी जाने वाली एक सामान्य JSON फ़ाइल होती है.
ऐसे में, use_repo_rule
डायरेक्टिव का इस्तेमाल करके, सीधे तौर पर किसी रेपो को तय किया जा सकता है. इसके लिए, रेपो के नियम को लागू करना होगा. यह रेपो, सिर्फ़ उस मॉड्यूल को दिखेगा जिसमें इसे तय किया गया है.
यह सुविधा, मॉड्यूल एक्सटेंशन के लिए इस्तेमाल किए जाने वाले तरीके का इस्तेमाल करके लागू की जाती है. इससे आपको ज़्यादा आसानी से रेपो तय करने की सुविधा मिलती है.
रिपॉज़िटरी के नाम और स्ट्रिक्ट डिपेंडेंसी
किसी मॉड्यूल को बैक करने वाले रेपो का दिखने वाला नाम, सीधे तौर पर उस पर निर्भर रहने वाले मॉड्यूल के नाम के बराबर होता है. हालांकि, अगर bazel_dep डायरेक्टिव के repo_name एट्रिब्यूट में कोई दूसरा नाम दिया गया है, तो यह नाम अलग हो सकता है. ध्यान दें कि इसका मतलब है कि कोई मॉड्यूल सिर्फ़ अपनी डायरेक्ट डिपेंडेंसी ढूंढ सकता है. इससे ट्रांज़िटिव डिपेंडेंसी में बदलावों की वजह से, अनजाने में होने वाले ब्रेक से बचने में मदद मिलती है.
किसी मॉड्यूल को बैक करने वाले रेपो का कैननिकल नाम, module_name+version (उदाहरण के लिए, bazel_skylib+1.0.3) या module_name+ (उदाहरण के लिए, bazel_features+) होता है. यह इस बात पर निर्भर करता है कि पूरे डिपेंडेंसी ग्राफ़ में मॉड्यूल के कई वर्शन हैं या नहीं. इसके बारे में जानने के लिए, multiple_version_override देखें.
ध्यान दें कि कैननिकल नाम का फ़ॉर्मैट ऐसा एपीआई नहीं है जिस पर आपको भरोसा करना चाहिए. साथ ही, इसमें किसी भी समय बदलाव किया जा सकता है. कैननिकल नाम को हार्ड-कोड करने के बजाय,
इसे सीधे Bazel से पाने के लिए, इस्तेमाल किए जा सकने वाले तरीके का इस्तेमाल करें:
- BUILD और
.bzlफ़ाइलों में,Labelइंस्टेंस परLabel.repo_nameका इस्तेमाल करें.यह इंस्टेंस, repo के नाम से दी गई लेबल स्ट्रिंग से बनाया गया है. उदाहरण के लिए,Label("@bazel_skylib").repo_name. - रनफ़ाइलें ढूंढते समय,
$(rlocationpath ...)का इस्तेमाल करें. इसके अलावा,@bazel_tools//tools/{bash,cpp,java}/runfilesमें मौजूद रनफ़ाइल लाइब्रेरी में से किसी एक का इस्तेमाल करें. अगर आपकोrules_fooके लिए रनफ़ाइलें ढूंढनी हैं, तो@rules_foo//foo/runfilesमें मौजूद रनफ़ाइल लाइब्रेरी में से किसी एक का इस्तेमाल करें. - जब किसी बाहरी टूल, जैसे कि IDE या भाषा सर्वर से Bazel के साथ इंटरैक्ट किया जाता है, तो
bazel mod dump_repo_mappingकमांड का इस्तेमाल करें. इससे आपको रिपॉज़िटरी के किसी सेट के लिए, दिखने वाले नामों से कैननिकल नामों की मैपिंग मिलेगी.
मॉड्यूल एक्सटेंशन की मदद से, मॉड्यूल के स्कोप में अतिरिक्त रिपॉज़िटरी भी जोड़ी जा सकती हैं.