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).
इस वजह से, Bazel, SemVer स्पेसिफ़िकेशन के ज़्यादा आसान वर्शन का इस्तेमाल करता है. इन दोनों में ये अंतर हैं:
- SemVer के मुताबिक, वर्शन के "रिलीज़" हिस्से में तीन सेगमेंट होने चाहिए:
MAJOR.MINOR.PATCH. Bazel में, इस शर्त को आसान बना दिया गया है, ताकि कितने भी सेगमेंट इस्तेमाल किए जा सकें. - SemVer में, "रिलीज़" वाले हिस्से के हर सेगमेंट में सिर्फ़ अंक होने चाहिए. Bazel में, अक्षरों को भी अनुमति देने के लिए इसे कम कर दिया गया है. साथ ही, तुलना के सिमैंटिक, "prerelease" वाले हिस्से में मौजूद "पहचानकर्ताओं" से मेल खाते हैं.
- इसके अलावा, मेजर, माइनर, और पैच वर्शन के सिमैंटिक को लागू नहीं किया जाता. हालांकि, पुराने सिस्टम के साथ काम करने की सुविधा के बारे में ज़्यादा जानने के लिए, कंपैटिबिलिटी लेवल देखें.
कोई भी मान्य SemVer वर्शन, Bazel मॉड्यूल का मान्य वर्शन होता है. इसके अलावा, दो SemVer वर्शन a और b की तुलना a < b से तब ही की जाती है, जब Bazel मॉड्यूल वर्शन के तौर पर तुलना करने पर भी ऐसा ही होता है.
आखिर में, मॉड्यूल के वर्शन के बारे में ज़्यादा जानने के लिए, MODULE.bazel
अक्सर पूछे जाने वाले सवाल देखें.
वर्शन चुनना
डायमंड डिपेंडेंसी की समस्या पर विचार करें. यह वर्शन वाली डिपेंडेंसी मैनेजमेंट स्पेस में एक मुख्य समस्या है. मान लें कि आपके पास यह डिपेंडेंसी ग्राफ़ है:
A 1.0
/ \
B 1.0 C 1.1
| |
D 1.0 D 1.1
D के किस वर्शन का इस्तेमाल करना चाहिए? इस समस्या को हल करने के लिए, Bazel, Go मॉड्यूल सिस्टम में पेश किए गए मिनिमल वर्शन सिलेक्शन (एमवीएस) एल्गोरिदम का इस्तेमाल करता है. 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 इस तरह की गारंटी नहीं दे सकता, क्योंकि यह SemVer के आसान वर्शन का इस्तेमाल करता है.
इसलिए, Bazel को SemVer के मेजर वर्शन नंबर के बराबर जानकारी की ज़रूरत होती है, ताकि वह पुराने सिस्टम के साथ काम न करने वाले ("ब्रेकिंग") वर्शन का पता लगा सके. इस संख्या को कंपैटिबिलिटी लेवल कहा जाता है. इसे हर मॉड्यूल वर्शन में, module() डायरेक्टिव के ज़रिए तय किया जाता है. इस जानकारी की मदद से, 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, रजिस्ट्री के बाहर मौजूद इन फ़ाइलों को बदलने की सुविधा देता है:
ध्यान दें कि सोर्स संग्रह MODULE.bazel में वर्शन की वैल्यू सेट करने से, कुछ समस्याएं हो सकती हैं. ऐसा तब होता है, जब मॉड्यूल को नॉन-रजिस्ट्री ओवरराइड से बदला जा रहा हो. इस बारे में ज़्यादा जानने के लिए, MODULE.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का इस्तेमाल करें.यह इंस्टेंस, रिपो के नाम से दिए गए लेबल स्ट्रिंग से बनाया गया है. उदाहरण के लिए,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कमांड का इस्तेमाल करें. इससे, रिपॉज़िटरी के किसी सेट के लिए, नामों को कैननिकल नामों से मैप किया जा सकता है.
मॉड्यूल एक्सटेंशन की मदद से, मॉड्यूल के स्कोप में अतिरिक्त रिपॉज़िटरी भी जोड़ी जा सकती हैं.