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