Bazel मॉड्यूल

समस्या की शिकायत करें स्रोत देखें

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

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

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

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

Bazel का नेटवर्क बहुत अलग है. हमारे प्रोजेक्ट में अलग-अलग वर्शनिंग स्कीम इस्तेमाल होती हैं. SemVer, अब तक सबसे ज़्यादा लोकप्रिय है. हालांकि, कुछ ऐसे प्रोजेक्ट भी हैं जिनमें अलग-अलग स्कीम का इस्तेमाल किया गया है. जैसे, Abseil, जिसके वर्शन तारीख के हिसाब से होते हैं, जैसे कि 20210324.2).

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

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

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

वर्शन चुनना

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

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

यांकेड वर्शन

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

कम्पैटबिलटी लेवल

Go में, पुराने सिस्टम के साथ काम करने की सुविधा के बारे में एमवीएस का अनुमान काम करता है. ऐसा इसलिए है, क्योंकि यह किसी मॉड्यूल के पिछले वर्शन के साथ काम न करने वाले वर्शन को एक अलग मॉड्यूल की तरह देखता है. 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_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). ध्यान दें कि कैननिकल नाम का फ़ॉर्मैट ऐसा एपीआई नहीं है जिस पर आपको निर्भर रहना चाहिए और किसी भी समय बदला जा सकता है. कैननिकल नाम को हार्ड-कोड करने के बजाय, इसे सीधे Bazel से पाने के लिए, ऐसे तरीके का इस्तेमाल करें जो इस सुविधा के साथ काम करता हो: * 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 के लिए, $(rlocationpath ...) या किसी रनफ़ाइल लाइब्रेरी का इस्तेमाल करें. * IDE या भाषा सर्वर जैसे किसी बाहरी टूल की मदद से Bazel के साथ इंटरैक्ट करते समय, bazel mod dump_repo_mapping कमांड का इस्तेमाल करें. इससे डेटा स्टोर करने की जगहों के दिए गए सेट के लिए, साफ़ तौर पर दिखने वाले नामों से कैननिकल नामों तक मैपिंग की जा सकती है.

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