Bazel मॉड्यूल

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).

इस वजह से, Bzlmod SemVer स्पेसिफ़िकेशन के ज़्यादा आसान वर्शन का इस्तेमाल करता है. इन दोनों में ये अंतर हैं:

  • SemVer के मुताबिक, वर्शन के "रिलीज़" हिस्से में तीन सेगमेंट होने चाहिए: MAJOR.MINOR.PATCH. Bazel में, इस शर्त को आसान बना दिया गया है, ताकि किसी भी संख्या में सेगमेंट बनाए जा सकें.
  • SemVer में, "रिलीज़" वाले हिस्से के हर सेगमेंट में सिर्फ़ अंक होने चाहिए. Bazel में, अक्षरों को भी अनुमति देने के लिए इसे कम कर दिया गया है. साथ ही, तुलना के सिमैंटिक, "prerelease" वाले हिस्से में मौजूद "पहचानकर्ताओं" से मेल खाते हैं.
  • इसके अलावा, मेजर, माइनर, और पैच वर्शन में बढ़ोतरी के सिमैंटिक लागू नहीं किए जाते.

कोई भी मान्य 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 या नया वर्शन मौजूद हो, हम उन्हें नहीं चुनते. एमवीएस का इस्तेमाल करने से, वर्शन चुनने की ऐसी प्रोसेस बनती है जो हाई-फ़िडेलिटी और दोहराई जा सकने वाली होती है.

हटाए गए वर्शन

अगर किसी वर्शन का इस्तेमाल नहीं करना चाहिए, तो रजिस्ट्री उसे yanked के तौर पर मार्क कर सकती है. जैसे, सुरक्षा से जुड़े जोखिम की आशंकाओं के लिए. किसी मॉड्यूल के यैंक किए गए वर्शन को चुनने पर, Bazel एक गड़बड़ी दिखाता है. इस गड़बड़ी को ठीक करने के लिए, या तो नए वर्शन में अपग्रेड करें या --allow_yanked_versions फ़्लैग का इस्तेमाल करके, हटाए गए वर्शन को अनुमति दें.

बदली गई कीमत

Bazel मॉड्यूल रिज़ॉल्यूशन के व्यवहार में बदलाव करने के लिए, MODULE.bazel फ़ाइल में ओवरराइड तय करें. सिर्फ़ रूट मॉड्यूल के ओवरराइड लागू होते हैं. अगर किसी मॉड्यूल का इस्तेमाल डिपेंडेंसी के तौर पर किया जाता है, तो उसके ओवरराइड को अनदेखा कर दिया जाता है.

हर ओवरराइड को किसी मॉड्यूल के नाम के लिए तय किया जाता है. इससे डिपेंडेंसी ग्राफ़ में मौजूद उसके सभी वर्शन पर असर पड़ता है. हालांकि, सिर्फ़ रूट मॉड्यूल के ओवरराइड लागू होते हैं, लेकिन ये ऐसे ट्रांज़िटिव डिपेंडेंसी के लिए हो सकते हैं जिन पर रूट मॉड्यूल सीधे तौर पर निर्भर नहीं होता.

एक वर्शन को ओवरराइड करना

single_version_override कई कामों के लिए इस्तेमाल किया जाता है:

  • version एट्रिब्यूट की मदद से, किसी डिपेंडेंसी को किसी खास वर्शन पर पिन किया जा सकता है. भले ही, डिपेंडेंसी ग्राफ़ में डिपेंडेंसी के किसी भी वर्शन का अनुरोध किया गया हो.
  • registry एट्रिब्यूट की मदद से, इस डिपेंडेंसी को किसी खास रजिस्ट्री से लाने के लिए मजबूर किया जा सकता है. इसके लिए, सामान्य रजिस्ट्री चुनने की प्रोसेस को फ़ॉलो करने की ज़रूरत नहीं होती.
  • patch* एट्रिब्यूट की मदद से, डाउनलोड किए गए मॉड्यूल पर लागू करने के लिए पैच का सेट तय किया जा सकता है.

ये सभी एट्रिब्यूट ज़रूरी नहीं हैं. इन्हें एक-दूसरे के साथ मिलाकर इस्तेमाल किया जा सकता है.

कई वर्शन को ओवरराइड करना

multiple_version_override को यह तय करने के लिए सेट किया जा सकता है कि एक ही मॉड्यूल के कई वर्शन, हल किए गए डिपेंडेंसी ग्राफ़ में एक साथ मौजूद रह सकें.

अगर डिपेंडेंसी ग्राफ़ में एक ही मॉड्यूल के कई वर्शन मौजूद हैं, तो 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.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 का इस्तेमाल करें.यह इंस्टेंस, रेपो के नाम से दी गई लेबल स्ट्रिंग से बनाया गया है. उदाहरण के लिए, 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 कमांड का इस्तेमाल करें. इससे, रिपॉज़िटरी के किसी सेट के लिए, दिखने वाले नामों से कैननिकल नामों की मैपिंग मिलती है.

मॉड्यूल एक्सटेंशन की मदद से, मॉड्यूल के स्कोप में अतिरिक्त रिपॉज़िटरी भी जोड़ी जा सकती हैं.