एक्सटर्नल डिपेंडेंसी के बारे में खास जानकारी

Bazel, बाहरी डिपेंडेंसी के साथ-साथ सोर्स फ़ाइलों (टेक्स्ट और बाइनरी, दोनों) के साथ काम करता है. ये फ़ाइलें, आपके बिल्ड में इस्तेमाल की जाती हैं और आपके वर्कस्पेस से नहीं होती हैं. उदाहरण के लिए, वे GitHub repo में होस्ट किया गया कोई नियम सेट, Maven आर्टफ़ैक्ट या आपके मौजूदा वर्कस्पेस से बाहर आपकी लोकल मशीन पर मौजूद कोई डायरेक्ट्री हो सकती हैं.

इस दस्तावेज़ में, सिस्टम के बारे में खास जानकारी दी गई है. इसके बाद, कुछ कॉन्सेप्ट के बारे में ज़्यादा जानकारी दी गई है.

सिस्टम के बारे में खास जानकारी

Bazel का बाहरी डिपेंडेंसी सिस्टम, Bazel मॉड्यूल के आधार पर काम करता है. इनमें से हर मॉड्यूल, वर्शन वाला Bazel प्रोजेक्ट होता है. साथ ही, यह रिपॉज़िटरी (या रेपो) के आधार पर भी काम करता है. ये डायरेक्ट्री ट्री होते हैं, जिनमें सोर्स फ़ाइलें होती हैं.

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

module(name = "my-module", version = "1.0")

bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "platforms", version = "0.0.11")

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

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

मॉड्यूल, टैग नाम के कस्टम डेटा के बारे में भी बता सकते हैं. मॉड्यूल रिज़ॉल्यूशन के बाद, मॉड्यूल एक्सटेंशन इनका इस्तेमाल करते हैं, ताकि अतिरिक्त रिपॉज़िटरी तय की जा सकें. ये एक्सटेंशन, फ़ाइल I/O और नेटवर्क अनुरोध भेजने जैसी कार्रवाइयां कर सकते हैं. इनकी मदद से, Bazel अन्य पैकेज मैनेजमेंट सिस्टम के साथ इंटरैक्ट कर पाता है. साथ ही, Bazel मॉड्यूल से बनाए गए डिपेंडेंसी ग्राफ़ का पालन भी कर पाता है.

तीन तरह के रेपो -- मुख्य रेपो (यह वह सोर्स ट्री है जिसमें काम किया जा रहा है), ट्रांज़िटिव डिपेंडेंसी मॉड्यूल को दिखाने वाले रेपो, और मॉड्यूल एक्सटेंशन से बनाए गए रेपो -- मिलकर वर्कस्पेस बनाते हैं. बाहरी रेपो (मुख्य रेपो के अलावा अन्य रेपो) को मांग पर फ़ेच किया जाता है. उदाहरण के लिए, जब BUILD फ़ाइलों में लेबल (जैसे कि @repo//pkg:target) के ज़रिए उनका रेफ़रंस दिया जाता है.

फ़ायदे

Bazel का बाहरी डिपेंडेंसी सिस्टम कई तरह के फ़ायदे देता है.

अपने-आप डिपेंडेंसी हल करने की सुविधा

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

ईकोसिस्टम इंटिग्रेशन

  • Bazel Central Registry: यह एक सेंट्रल रिपॉज़िटरी है. इसकी मदद से, Bazel मॉड्यूल के तौर पर सामान्य डिपेंडेंसी का पता लगाया जा सकता है और उन्हें मैनेज किया जा सकता है.
  • Bazel के अलावा अन्य टूल का इस्तेमाल करके बनाए गए प्रोजेक्ट को Bazel के साथ इंटिग्रेट करना: जब Bazel के अलावा अन्य टूल का इस्तेमाल करके बनाए गए प्रोजेक्ट (आम तौर पर, C++ लाइब्रेरी) को Bazel के साथ इंटिग्रेट किया जाता है और उसे BCR में उपलब्ध कराया जाता है, तो यह पूरी कम्यूनिटी के लिए इसके इंटिग्रेशन की प्रोसेस को आसान बना देता है. साथ ही, इससे एक ही काम को बार-बार करने की ज़रूरत नहीं पड़ती और कस्टम BUILD फ़ाइलों के टकराव की समस्या भी खत्म हो जाती है.
  • भाषा के हिसाब से पैकेज मैनेजर के साथ यूनिफ़ाइड इंटिग्रेशन: नियमों के सेट, Bazel के अलावा अन्य डिपेंडेंसी के लिए बाहरी पैकेज मैनेजर के साथ इंटिग्रेशन को आसान बनाते हैं. इनमें ये शामिल हैं:

बेहतर सुविधाएं

  • मॉड्यूल एक्सटेंशन: use_repo_rule और मॉड्यूल एक्सटेंशन की सुविधाओं की मदद से, कस्टम रिपॉज़िटरी के नियमों और रिज़ॉल्यूशन लॉजिक का फ़्लेक्सिबल तरीके से इस्तेमाल किया जा सकता है. इससे, Bazel के अलावा किसी अन्य डिपेंडेंसी को जोड़ा जा सकता है.
  • bazel mod Command: इस सब-कमांड की मदद से, बाहरी डिपेंडेंसी की जांच करने के लिए बेहतर तरीके उपलब्ध होते हैं. आपको यह पता होता है कि बाहरी डिपेंडेंसी को कैसे तय किया जाता है और वह कहां से आती है.
  • वेंडर मोड: ऑफ़लाइन बिल्ड को आसान बनाने के लिए, उन बाहरी डिपेंडेंसी को पहले से फ़ेच करें जिनकी आपको ज़रूरत है.
  • लॉकफ़ाइल: लॉकफ़ाइल से, बिल्ड को फिर से बनाने की प्रोसेस बेहतर होती है. साथ ही, इससे डिपेंडेंसी को हल करने की प्रोसेस तेज़ होती है.
  • (आने वाला समय) बीसीआर की उत्पत्ति की पुष्टि करने वाले अटेस्टेशन: डिपेंडेंसी की उत्पत्ति की पुष्टि करके, सप्लाई चेन की सुरक्षा को बेहतर बनाएं.

कॉन्सेप्ट

इस सेक्शन में, बाहरी डिपेंडेंसी से जुड़े कॉन्सेप्ट के बारे में ज़्यादा जानकारी दी गई है.

मॉड्यूल

Bazel प्रोजेक्ट, जिसके कई वर्शन हो सकते हैं. इनमें से हर वर्शन, अन्य मॉड्यूल पर निर्भर हो सकता है.

Bazel के लोकल वर्कस्पेस में, किसी मॉड्यूल को रिपॉज़िटरी के तौर पर दिखाया जाता है.

ज़्यादा जानकारी के लिए, Bazel मॉड्यूल देखें.

रिपॉज़िटरी

एक डायरेक्ट्री ट्री, जिसकी रूट डायरेक्ट्री में बाउंड्री मार्कर फ़ाइल मौजूद है. इसमें ऐसी सोर्स फ़ाइलें शामिल हैं जिनका इस्तेमाल Bazel बिल्ड में किया जा सकता है. इसे अक्सर छोटा करके सिर्फ़ repo कहा जाता है.

रेपो बाउंड्री मार्कर फ़ाइल MODULE.bazel (यह बताती है कि यह रेपो, Bazel मॉड्यूल को दिखाता है), REPO.bazel (नीचे देखें) या लेगसी कॉन्टेक्स्ट में, WORKSPACE या WORKSPACE.bazel हो सकती है. किसी भी रेपो बाउंड्री मार्कर फ़ाइल से, रेपो की बाउंड्री का पता चलेगा. इस तरह की कई फ़ाइलें, किसी डायरेक्ट्री में एक साथ मौजूद हो सकती हैं.

मुख्य रिपॉज़िटरी

वह रिपॉज़िटरी जिसमें Bazel का मौजूदा कमांड चलाया जा रहा है.

मुख्य रिपॉज़िटरी के रूट को वर्कस्पेस रूट भी कहा जाता है.

Workspace

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

ध्यान दें कि पहले "रिपॉज़िटरी" और "वर्कस्पेस" के कॉन्सेप्ट को एक ही माना जाता था. "वर्कस्पेस" शब्द का इस्तेमाल अक्सर मुख्य रिपॉज़िटरी के लिए किया जाता था. कभी-कभी इसे "रिपॉज़िटरी" के पर्यायवाची के तौर पर भी इस्तेमाल किया जाता था.

कैननिकल रिपॉज़िटरी का नाम

वह नाम जिससे किसी रिपॉज़िटरी को हमेशा ऐक्सेस किया जा सकता है. किसी वर्कस्पेस के संदर्भ में, हर रिपॉज़िटरी का एक ही कैननिकल नाम होता है. किसी रिपॉज़िटरी में मौजूद टारगेट का कैननिकल नाम canonical_name है. इसे @@canonical_name//package:target लेबल से ऐक्सेस किया जा सकता है. ध्यान दें कि इसमें दो बार @ का इस्तेमाल किया गया है.

मुख्य रिपॉज़िटरी का कैननिकल नाम हमेशा खाली स्ट्रिंग होता है.

रिपॉज़िटरी का नाम

वह नाम जिससे किसी अन्य रिपॉज़िटरी के संदर्भ में किसी रिपॉज़िटरी को ऐक्सेस किया जा सकता है. इसे किसी रिपॉज़िटरी का "निकनेम" माना जा सकता है: कैननिकल नाम michael वाली रिपॉज़िटरी का नाम, रिपॉज़िटरी alice के संदर्भ में mike हो सकता है. हालांकि, रिपॉज़िटरी bob के संदर्भ में इसका नाम mickey हो सकता है. इस मामले में, michael में मौजूद किसी टारगेट को alice के कॉन्टेक्स्ट में लेबल @mike//package:target से ऐक्सेस किया जा सकता है. ध्यान दें कि यहां सिर्फ़ एक @ है.

इसके उलट, इसे रिपॉज़िटरी मैपिंग के तौर पर समझा जा सकता है: हर रिपो, "रिपो का नाम" से "कैननिकल रिपो का नाम" तक की मैपिंग बनाए रखता है.

रिपॉज़िटरी का नियम

डेटाबेस की परिभाषाओं के लिए स्कीमा. इससे Bazel को यह पता चलता है कि डेटाबेस को कैसे बनाया जाए. उदाहरण के लिए, यह "किसी यूआरएल से ज़िप संग्रह डाउनलोड करें और उसे निकालें", "किसी Maven आर्टफ़ैक्ट को फ़ेच करें और उसे java_import टारगेट के तौर पर उपलब्ध कराएं" या सिर्फ़ "किसी स्थानीय डायरेक्ट्री को सिमलिंक करें" हो सकता है. हर रेपो को सही संख्या में आर्ग्युमेंट के साथ रेपो के नियम को कॉल करके तय किया जाता है.

अपने रिपॉज़िटरी के नियम लिखने के तरीके के बारे में ज़्यादा जानने के लिए, रिपॉज़िटरी के नियम देखें.

सबसे ज़्यादा इस्तेमाल किए जाने वाले रेपो नियम ये हैं: http_archive, जो किसी यूआरएल से संग्रह डाउनलोड करता है और उसे निकालता है. इसके अलावा, local_repository, जो पहले से मौजूद Bazel रिपॉज़िटरी वाली लोकल डायरेक्ट्री को सिमलंक करता है.

किसी रिपॉज़िटरी को फ़ेच करना

किसी repo से जुड़े repo के नियम को लागू करके, repo को लोकल डिस्क पर उपलब्ध कराने की कार्रवाई. वर्कस्पेस में तय की गई रिपॉज़िटरी, फ़ेच किए जाने से पहले लोकल डिस्क पर उपलब्ध नहीं होती हैं.

आम तौर पर, Bazel किसी repo को सिर्फ़ तब फ़ेच करता है, जब उसे repo से किसी चीज़ की ज़रूरत होती है और repo को पहले से फ़ेच नहीं किया गया होता है. अगर रिपो को पहले ही फ़ेच किया जा चुका है, तो Bazel उसे सिर्फ़ तब फिर से फ़ेच करता है, जब उसकी डेफ़िनिशन में बदलाव हुआ हो.

fetch कमांड का इस्तेमाल, किसी रिपॉज़िटरी, टारगेट या सभी ज़रूरी रिपॉज़िटरी के लिए प्री-फ़ेच शुरू करने के लिए किया जा सकता है, ताकि कोई भी बिल्ड किया जा सके. इस सुविधा की मदद से, --nofetch विकल्प का इस्तेमाल करके ऑफ़लाइन बिल्ड बनाए जा सकते हैं.

--fetch विकल्प का इस्तेमाल, नेटवर्क ऐक्सेस को मैनेज करने के लिए किया जाता है. इसकी डिफ़ॉल्ट वैल्यू, सही है. हालांकि, जब इसे गलत (--nofetch) पर सेट किया जाता है, तो कमांड, डिपेंडेंसी के किसी भी कैश मेमोरी वर्शन का इस्तेमाल करेगी. अगर कोई भी वर्शन मौजूद नहीं है, तो कमांड काम नहीं करेगी.

डेटा फ़ेच करने की प्रोसेस को कंट्रोल करने के बारे में ज़्यादा जानने के लिए, डेटा फ़ेच करने के विकल्प देखें.

डायरेक्ट्री का लेआउट

फ़ेच करने के बाद, रिपो को आउटपुट बेस में external सबडायरेक्ट्री में, इसके कैननिकल नाम से देखा जा सकता है.

कैननिकल नाम canonical_name के साथ रेपो का कॉन्टेंट देखने के लिए, यह कमांड चलाएं:

ls $(bazel info output_base)/external/ canonical_name 

REPO.bazel फ़ाइल

REPO.bazel फ़ाइल का इस्तेमाल, डायरेक्ट्री ट्री की सबसे ऊपरी सीमा को मार्क करने के लिए किया जाता है. यह डायरेक्ट्री ट्री, किसी रेपो को बनाता है. इसे repo boundary फ़ाइल के तौर पर इस्तेमाल करने के लिए, इसमें कुछ भी शामिल करने की ज़रूरत नहीं है. हालांकि, इसका इस्तेमाल repo में मौजूद सभी बिल्ड टारगेट के लिए, कुछ सामान्य एट्रिब्यूट तय करने के लिए भी किया जा सकता है.

REPO.bazel फ़ाइल का सिंटैक्स, BUILD फ़ाइलों के जैसा ही होता है. हालांकि, इसमें load स्टेटमेंट इस्तेमाल नहीं किए जा सकते. repo() फ़ंक्शन, BUILD फ़ाइलों में package() फ़ंक्शन के जैसे ही आर्ग्युमेंट स्वीकार करता है. वहीं, package() पैकेज में मौजूद सभी बिल्ड टारगेट के लिए सामान्य एट्रिब्यूट तय करता है. इसी तरह, repo() रिपो में मौजूद सभी बिल्ड टारगेट के लिए सामान्य एट्रिब्यूट तय करता है.

उदाहरण के लिए, अपनी रेपो में मौजूद सभी टारगेट के लिए एक सामान्य लाइसेंस तय किया जा सकता है. इसके लिए, आपको यह REPO.bazel फ़ाइल बनानी होगी:

repo(
    default_package_metadata = ["//:my_license"],
)

लेगसी WORKSPACE सिस्टम

Bazel के पुराने वर्शन (9.0 से पहले) में, बाहरी डिपेंडेंसी को WORKSPACE (या WORKSPACE.bazel) फ़ाइल में रेपो तय करके शामिल किया जाता था. इस फ़ाइल का सिंटैक्स, BUILD फ़ाइलों के सिंटैक्स जैसा ही है. इसमें बिल्ड के नियमों के बजाय, रेपो के नियमों का इस्तेमाल किया जाता है.

नीचे दिया गया स्निपेट, WORKSPACE फ़ाइल में http_archive repo rule का इस्तेमाल करने का उदाहरण है:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "foo",
    urls = ["https://example.com/foo.zip"],
    sha256 = "c9526390a7cd420fdcec2988b4f3626fe9c5b51e2959f685e8f4d170d1a9bd96",
)

इस स्निपेट में ऐसे रेपो के बारे में बताया गया है जिसका कैननिकल नाम foo है. WORKSPACEसिस्टम में, डिफ़ॉल्ट रूप से किसी रेपो का कैननिकल नाम ही, अन्य सभी रेपो के लिए उसका नाम होता है.

WORKSPACE फ़ाइलों में उपलब्ध फ़ंक्शन की पूरी सूची देखें.

WORKSPACE सिस्टम की कमियां

WORKSPACE सिस्टम को लॉन्च करने के बाद, उपयोगकर्ताओं ने कई समस्याएं बताईं. जैसे:

  • Bazel, किसी भी डिपेंडेंसी की WORKSPACE फ़ाइलों का आकलन नहीं करता. इसलिए, सभी ट्रांज़िटिव डिपेंडेंसी को मुख्य रेपो की WORKSPACE फ़ाइल में तय किया जाना चाहिए. इसके अलावा, डायरेक्ट डिपेंडेंसी को भी तय किया जाना चाहिए.
  • इससे बचने के लिए, प्रोजेक्ट में "deps.bzl" पैटर्न का इस्तेमाल किया जाता है. इसमें वे एक मैक्रो तय करते हैं, जो कई रिपॉज़िटरी तय करता है. साथ ही, उपयोगकर्ताओं से इस मैक्रो को अपनी WORKSPACE फ़ाइलों में कॉल करने के लिए कहता है.
    • इसकी अपनी समस्याएं हैं: मैक्रो, load अन्य .bzl फ़ाइलें नहीं कर सकते. इसलिए, इन प्रोजेक्ट को इस "deps" मैक्रो में अपनी ट्रांज़िटिव डिपेंडेंसी तय करनी होती है. इसके अलावा, इस समस्या को हल करने के लिए, उपयोगकर्ता को कई लेयर वाले "deps" मैक्रो को कॉल करना होता है.
    • Bazel, WORKSPACE फ़ाइल का आकलन क्रम से करता है. इसके अलावा, यूआरएल के साथ http_archive का इस्तेमाल करके डिपेंडेंसी तय की जाती हैं. इसमें वर्शन की कोई जानकारी नहीं होती. इसका मतलब है कि डायमंड डिपेंडेंसी (A, B और C पर निर्भर करता है; B और C, दोनों D के अलग-अलग वर्शन पर निर्भर करते हैं) के मामले में, वर्शन रिज़ॉल्यूशन करने का कोई भरोसेमंद तरीका नहीं है.

WORKSPACE की कमियों की वजह से, Bazel 6 और 9 के बीच, मॉड्यूल पर आधारित नए सिस्टम (कोडनेम "Bzlmod") ने धीरे-धीरे लेगसी WORKSPACE सिस्टम की जगह ले ली. Bzlmod पर माइग्रेट करने का तरीका जानने के लिए, Bzlmod पर माइग्रेट करने से जुड़ी गाइड पढ़ें.