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 जैसे कई प्रोजेक्ट में अलग-अलग वर्शनिंग सिस्टम का इस्तेमाल किया जाता है. 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 मॉड्यूल सिस्टम में पेश किए गए Minimal Version Selection (MVS) एल्गोरिदम का इस्तेमाल करता है. 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
कमांड का इस्तेमाल करें. इससे आपको रिपॉज़िटरी के किसी सेट के लिए, नामों को कैननिकल नामों से मैप करने की सुविधा मिलती है.
मॉड्यूल एक्सटेंशन, मॉड्यूल के दिखने वाले स्कोप में अतिरिक्त रिपॉज़िटरी भी जोड़ सकते हैं.