Bazel मॉड्यूल

Bazel मॉड्यूल , Bazel का एक ऐसा प्रोजेक्ट होता है जिसके कई वर्शन हो सकते हैं. इनमें से हर वर्शन, उन अन्य मॉड्यूल के बारे में मेटाडेटा पब्लिश करता है जिन पर वह निर्भर करता है. यह, डिपेंडेंसी मैनेजमेंट के अन्य सिस्टम में मौजूद जाने-पहचाने कॉन्सेप्ट जैसा ही है. जैसे, Maven आर्टफ़ैक्ट, npm पैकेज, Go मॉड्यूल या Cargo क्रेट.

किसी मॉड्यूल के रेपो रूट में MODULE.bazel फ़ाइल होनी चाहिए. यह फ़ाइल, WORKSPACE फ़ाइल के बगल में होती है. यह फ़ाइल, मॉड्यूल का मेनिफ़ेस्ट होती है. इसमें मॉड्यूल का नाम, वर्शन, सीधे तौर पर निर्भरता की सूची, और अन्य जानकारी होती है. यहां एक बुनियादी उदाहरण दिया गया है:

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 डिफ़ॉल्ट रूप से हर मॉड्यूल का एक वर्शन चुनता है इस्तेमाल करने के लिए. 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 इंस्टेंस पर Label.repo_name का इस्तेमाल करें. जैसे, Label("@bazel_skylib").repo_name. * रनफ़ाइल ढूंढते समय, $(rlocationpath ...) या @bazel_tools//tools/{bash,cpp,java}/runfiles में मौजूद रनफ़ाइल लाइब्रेरी में से किसी एक का इस्तेमाल करें. इसके अलावा, नियमसेट rules_foo के लिए, @rules_foo//foo/runfiles में मौजूद रनफ़ाइल लाइब्रेरी में से किसी एक का इस्तेमाल करें. * Bazel के साथ इंटरैक्ट करते समय, किसी बाहरी टूल का इस्तेमाल करें. जैसे, आईडीई या लैंग्वेज सर्वर. इसके लिए, दिए गए रिपॉज़िटरी के सेट के लिए, नामों को कैननिकल नामों से मैप करने की जानकारी पाने के लिए, bazel mod dump_repo_mapping कमांड का इस्तेमाल करें.

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