Bazel मॉड्यूल, एक ऐसा Bazel प्रोजेक्ट है जिसमें कई वर्शन हो सकते हैं. इनमें से हर वर्शन, उन अन्य मॉड्यूल के बारे में मेटाडेटा पब्लिश करता है जिन पर वह निर्भर करता है. यह, डिपेंडेंसी मैनेजमेंट के अन्य सिस्टम में मौजूद कॉन्सेप्ट से मिलता-जुलता है. जैसे, Maven आर्टफ़ैक्ट, npm पैकेज, Go मॉड्यूल या Cargo क्रेट.
किसी मॉड्यूल के repo रूट में (WORKSPACE
फ़ाइल के बगल में) 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
फ़ाइल को पढ़ता है. इसके बाद, वह Bazel रजिस्ट्री से किसी भी डिपेंडेंसी की MODULE.bazel
फ़ाइल का बार-बार अनुरोध करता है. ऐसा तब तक किया जाता है, जब तक वह पूरा डिपेंडेंसी ग्राफ़ नहीं ढूंढ लेता.
इसके बाद, Bazel डिफ़ॉल्ट रूप से इस्तेमाल करने के लिए, हर मॉड्यूल का एक वर्शन चुनें. Baज़र, हर मॉड्यूल के लिए रेपो की जानकारी देता है और रजिस्ट्री के बारे में दोबारा सलाह लेता है. इससे यह पता चलता है कि हर मॉड्यूल के लिए, रिपो की जानकारी कैसे तय की जाती है.
वर्शन का फ़ॉर्मैट
Bazel का एक अलग ईकोसिस्टम है और प्रोजेक्ट, वर्शनिंग की अलग-अलग स्कीम का इस्तेमाल करते हैं. अब तक, SemVer सबसे लोकप्रिय है. हालांकि, कुछ ऐसे प्रोजेक्ट भी हैं जिनमें अलग-अलग स्कीम का इस्तेमाल किया जाता है. जैसे, Abseil. इनके वर्शन तारीख पर आधारित होते हैं, जैसे कि 20210324.2
).
इस वजह से, Bzlmod SemVer खास निर्देश का ज़्यादा आसान वर्शन अपनाता है. इनमें ये अंतर शामिल हैं:
- SemVer का मानना है कि वर्शन के "रिलीज़" वाले हिस्से में तीन सेगमेंट होने चाहिए:
MAJOR.MINOR.PATCH
. Basel में, इस शर्त को ढीला कर दिया गया है, ताकि कितने भी सेगमेंट की अनुमति दी जा सके. - SemVer में, "रिलीज़" वाले हिस्से के हर सेगमेंट में सिर्फ़ अंक होने चाहिए. बेज़ेल में, अक्षरों के इस्तेमाल की अनुमति देने के लिए इसे ढीला कर दिया गया है. साथ ही, तुलना का सिमेंटिक्स "प्रीरिलीज़" वाले हिस्से में मौजूद "आइडेंटिफ़ायर" से मेल खाता है.
- इसके अलावा, मेजर, माइनर, और पैच वर्शन में होने वाली बढ़ोतरी के सेमेटिक्स लागू नहीं किए जाते. हालांकि, हम पुराने सिस्टम के साथ काम करने की सुविधा के बारे में जानकारी देने के लिए, कंपैटिबिलिटी लेवल का इस्तेमाल करते हैं.
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
या नया वर्शन मौजूद हो, हम उन्हें नहीं चुनते. एमवीएस का इस्तेमाल करने से, वर्शन चुनने की ऐसी प्रोसेस बनती है जो हाई-फ़िडेलिटी और फिर से बनाई जा सकने वाली होती है.
यांक किए गए वर्शन
रजिस्ट्री, कुछ वर्शन को हटाया गया के तौर पर दिखा सकती है. ऐसा तब किया जाता है, जब उन वर्शन का इस्तेमाल नहीं किया जाना चाहिए (जैसे, सुरक्षा से जुड़ी समस्याओं की वजह से). किसी मॉड्यूल के यैंक किए गए वर्शन को चुनते समय Baज़र एक गड़बड़ी दिखाता है. इस गड़बड़ी को ठीक करने के लिए, किसी ऐसे नए वर्शन पर अपग्रेड करें जिसे हटाया नहीं गया है या हटाए गए वर्शन को अनुमति देने के लिए, --allow_yanked_versions
फ़्लैग का इस्तेमाल करें.
कंपैटिबिलिटी लेवल
Go में, पुराने सिस्टम के साथ काम करने की सुविधा के बारे में MVS का अनुमान सही होता है. ऐसा इसलिए होता है, क्योंकि यह किसी मॉड्यूल के पुराने सिस्टम के साथ काम न करने वाले वर्शन को अलग मॉड्यूल के तौर पर इस्तेमाल करता है. SemVer के हिसाब से, इसका मतलब है कि A 1.x
और A 2.x
को अलग-अलग मॉड्यूल माना जाता है. साथ ही, ये रिज़ॉल्व किए गए डिपेंडेंसी ग्राफ़ में एक साथ मौजूद हो सकते हैं. ऐसा करने पर, मेजर वर्शन को Go में पैकेज पाथ में एन्कोड करना मुमकिन हो जाता है. इससे, कंपाइल-टाइम या लिंक-टाइम से जुड़ी कोई समस्या नहीं होती.
हालांकि, Bazel ऐसी कोई गारंटी नहीं दे सकता. इसलिए, पुराने वर्शन के साथ काम न करने वाले वर्शन का पता लगाने के लिए, उसे "मुख्य वर्शन" के नंबर की ज़रूरत होती है. इस संख्या को कंपैटबिलिटी लेवल कहा जाता है. हर मॉड्यूल वर्शन से, इस संख्या को module()
डायरेक्टिव में तय किया जाता है. इस जानकारी की मदद से, Basel को गड़बड़ी का पता चल सकता है. ऐसा तब होता है, जब उसे पता चलता है कि एक ही मॉड्यूल के अलग-अलग वर्शन के साथ काम करने के अलग-अलग लेवल, रिज़ॉल्व किया गया डिपेंडेंसी ग्राफ़ में मौजूद हैं.
ओवरराइड
Bazel मॉड्यूल रिज़ॉल्यूशन के व्यवहार में बदलाव करने के लिए, MODULE.bazel
फ़ाइल में बदलाव करें. सिर्फ़ रूट मॉड्यूल के बदलाव लागू होते हैं — अगर किसी मॉड्यूल का इस्तेमाल डिपेंडेंसी के तौर पर किया जाता है, तो उसके बदलावों को अनदेखा कर दिया जाता है.
हर बदलाव, एक खास मॉड्यूल नाम के लिए तय किया जाता है. इससे डिपेंडेंसी ग्राफ़ में उसके सभी वर्शन पर असर पड़ता है. हालांकि, सिर्फ़ रूट मॉड्यूल के ओवरराइड का असर पड़ता है, लेकिन वे ट्रांज़िटिव डिपेंडेंसी के लिए हो सकते हैं जिन पर रूट मॉड्यूल सीधे तौर पर निर्भर नहीं होता.
एक वर्शन में बदलाव
single_version_override
का इस्तेमाल कई कामों के लिए किया जाता है:
version
एट्रिब्यूट की मदद से, किसी डिपेंडेंसी को किसी खास वर्शन पर पिन किया जा सकता है. भले ही, डिपेंडेंसी ग्राफ़ में डिपेंडेंसी के किन वर्शन का अनुरोध किया गया हो.registry
एट्रिब्यूट की मदद से, इस डिपेंडेंसी को किसी खास रजिस्ट्री से लाने के लिए मजबूर किया जा सकता है. इसके लिए, रजिस्ट्री चुनने की सामान्य प्रोसेस का पालन करने की ज़रूरत नहीं होती.patch*
एट्रिब्यूट की मदद से, डाउनलोड किए गए मॉड्यूल पर लागू करने के लिए पैच का एक सेट तय किया जा सकता है.
ये सभी एट्रिब्यूट ज़रूरी नहीं हैं. साथ ही, इन्हें एक-दूसरे के साथ मिलाया और जोड़ा जा सकता है.
एक से ज़्यादा वर्शन ओवरराइड करने वाला वर्शन
multiple_version_override
का इस्तेमाल करके, एक ही मॉड्यूल के कई वर्शन को एक साथ इस्तेमाल किया जा सकता है.
मॉड्यूल के लिए, अनुमति वाले वर्शन की एक खास सूची दी जा सकती है, जो रिज़ॉल्यूशन से पहले डिपेंडेंसी ग्राफ़ में मौजूद होनी चाहिए. अनुमति वाले हर वर्शन के हिसाब से कुछ ट्रांज़िटिव डिपेंडेंसी मौजूद होनी चाहिए. रिज़ॉल्यूशन के बाद, मॉड्यूल के सिर्फ़ वे वर्शन बचे रहते हैं जिन्हें अनुमति मिली है. वहीं, Baज़र, मॉड्यूल के दूसरे वर्शन को उसी कंपैटबिलिटी लेवल पर अपग्रेड करके, अनुमति वाले सबसे नए वर्शन पर अपग्रेड करता है. अगर कंपैटबिलिटी लेवल वाला ऐसा कोई भी वर्शन मौजूद नहीं है जिसकी अनुमति है, तो बेज़ल गड़बड़ी की सूचना देता है.
उदाहरण के लिए, अगर रिज़ॉल्यूशन से पहले, डिपेंडेंसी ग्राफ़ में 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_name~version
है (उदाहरण के लिए, bazel_skylib~1.0.3
). गैर-रजिस्ट्री ओवरराइड वाले मॉड्यूल के लिए, version
वाले हिस्से को स्ट्रिंग override
से बदलें. ध्यान दें कि कैननिकल नेम फ़ॉर्मैट कोई ऐसा एपीआई नहीं है जिस पर आपको भरोसा करना चाहिए. साथ ही, इसमें कभी भी बदलाव किया जा सकता है.
किसी मॉड्यूल को अपने डायरेक्ट डिपेंडेंट में मॉड्यूल का बैक अप लेने वाले रेपो का एपरपेरेंट नाम, डिफ़ॉल्ट तौर पर अपने मॉड्यूल का नाम सेट करता है. ऐसा तब तक होता है, जब तक bazel_dep
डायरेक्टिव की repo_name
एट्रिब्यूट में इसके बारे में कुछ और न बताया गया हो. ध्यान दें कि इसका मतलब है कि मॉड्यूल सिर्फ़ अपनी डायरेक्ट डिपेंडेंसी का पता लगा सकता है. यह ट्रांज़िटिव डिपेंडेंसी में बदलावों की वजह से गलती से होने वाली गड़बड़ियों से बचने में मदद करता है.
मॉड्यूल एक्सटेंशन किसी मॉड्यूल के दिखने वाले दायरे में अतिरिक्त डेटा स्टोर करने की सुविधा भी दे सकते हैं.