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