Bazel मॉड्यूल

7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

मॉड्यूल के रेपो रूट में, 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 फ़ाइलों में मौजूद निर्देशों की पूरी सूची देखें.

मॉड्यूल रिज़ॉल्यूशन करने के लिए, Basel, रूट मॉड्यूल की MODULE.bazel फ़ाइल को पढ़कर शुरुआत करता है. इसके बाद, Basel रजिस्ट्री से किसी भी डिपेंडेंसी की MODULE.bazel फ़ाइल के लिए तब तक बार-बार अनुरोध करता है, जब तक कि वह पूरा डिपेंडेंसी ग्राफ़ को नहीं खोज लेता.

इसके बाद, Bazel डिफ़ॉल्ट रूप से इस्तेमाल करने के लिए, हर मॉड्यूल का एक वर्शन चुनें. Baज़र, हर मॉड्यूल के लिए रेपो की जानकारी देता है और रजिस्ट्री के बारे में दोबारा सलाह लेता है. इससे यह पता चलता है कि हर मॉड्यूल के लिए, रिपो की जानकारी कैसे तय की जाती है.

वर्शन का फ़ॉर्मैट

Bazel का एक अलग ईकोसिस्टम है और प्रोजेक्ट, वर्शनिंग की अलग-अलग स्कीम का इस्तेमाल करते हैं. अब तक SemVer सबसे लोकप्रिय है. हालांकि, Abseil जैसी अलग-अलग स्कीम का इस्तेमाल करने वाले लोकप्रिय प्रोजेक्ट भी हैं. इनके वर्शन, तारीख पर आधारित होते हैं, जैसे कि 20210324.2).

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

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

SemVer का कोई भी मान्य वर्शन, Bazel मॉड्यूल का मान्य वर्शन होता है. इसके अलावा, दो SemVer वर्शन a और b a < b की तुलना सिर्फ़ तब करते हैं, जब उनकी तुलना बज़ल मॉड्यूल वर्शन के रूप में की जाती है और दोनों वर्शन एक ही होते हैं.

वर्शन चुनना

डायमंड डिपेंडेंसी की समस्या पर विचार करें. यह वर्शन वाले डिपेंडेंसी मैनेजमेंट स्पेस में एक आम समस्या है. मान लें कि आपके पास डिपेंडेंसी ग्राफ़ है:

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

हटाए गए वर्शन

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

काम करने का लेवल

Go में, पुराने सिस्टम के साथ काम करने की सुविधा के बारे में MVS का अनुमान इसलिए काम करता है, क्योंकि यह किसी मॉड्यूल के पुराने वर्शन के साथ काम न करने वाले वर्शन को एक अलग मॉड्यूल की तरह समझता है. SemVer के हिसाब से, इसका मतलब है कि A 1.x और A 2.x को अलग-अलग मॉड्यूल माना जाता है. साथ ही, ये दोनों एक साथ, डिपेंडेंसी ग्राफ़ में मौजूद हो सकते हैं. ऐसा, Go में पैकेज पाथ में मेजर वर्शन को कोड में बदलकर किया जाता है, ताकि संकलन के समय या लिंक करने के समय कोई विरोध न हो.

हालांकि, Basel को इस तरह की गारंटी नहीं दी जा सकती, इसलिए उसे पुराने सिस्टम के साथ काम न करने वाले वर्शन का पता लगाने के लिए, "मेजर वर्शन" नंबर की ज़रूरत होती है. इस नंबर को काम करने का लेवल कहा जाता है. यह हर मॉड्यूल के वर्शन के module() डायरेक्टिव में बताया जाता है. इस जानकारी की मदद से, Bazel गड़बड़ी का मैसेज दिखा सकता है. ऐसा तब होता है, जब उसे पता चलता है कि रिज़ॉल्व किए गए डिपेंडेंसी ग्राफ़ में, एक ही मॉड्यूल के अलग-अलग वर्शन मौजूद हैं और उनका काम करने का तरीका अलग-अलग है.

ओवरराइड

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

इसके अलावा, उपयोगकर्ता registry एट्रिब्यूट का इस्तेमाल करके भी रजिस्ट्री को बदल सकते हैं. यह एक वर्शन में बदलाव की तरह ही होता है.

रजिस्ट्री के अलावा अन्य ओवरराइड

नॉन-रजिस्ट्री ओवरराइड की वजह से, वर्शन रिज़ॉल्यूशन से मॉड्यूल पूरी तरह हट जाता है. Basel, इन MODULE.bazel फ़ाइलों के लिए रजिस्ट्री से अनुरोध नहीं करता. इसके बजाय, वह रेपो से अनुरोध करता है.

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

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