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 के अलावा किसी अन्य प्रोजेक्ट (आम तौर पर C++ लाइब्रेरी) को Bazel के लिए तैयार किया जाता है और उसे BCR में उपलब्ध कराया जाता है, तो इससे पूरी कम्यूनिटी के लिए उसका इंटिग्रेशन आसान हो जाता है. साथ ही, डुप्लीकेट फ़ाइलें बनाने और कस्टम BUILD फ़ाइलों के टकराव की समस्या खत्म हो जाती है.
- भाषा के हिसाब से पैकेज मैनेजर के साथ यूनिफ़ाइड इंटिग्रेशन: नियमों के सेट, Bazel के अलावा अन्य डिपेंडेंसी के लिए बाहरी पैकेज मैनेजर के साथ इंटिग्रेशन को आसान बनाते हैं. इनमें ये शामिल हैं:
- Maven के लिए, rules_jvm_external
- PyPi के लिए rules_python,
- Go मॉड्यूल के लिए, bazel-gazelle
- Cargo के लिए rules_rust.
बेहतर सुविधाएं
- मॉड्यूल एक्सटेंशन:
use_repo_ruleऔर मॉड्यूल एक्सटेंशन की सुविधाओं की मदद से, कस्टम रिपॉज़िटरी के नियमों और रिज़ॉल्यूशन लॉजिक का फ़्लेक्सिबल तरीके से इस्तेमाल किया जा सकता है. इससे, Bazel के अलावा किसी भी अन्य डिपेंडेंसी को जोड़ा जा सकता है. bazel modकमांड: इस सब-कमांड की मदद से, बाहरी डिपेंडेंसी की जांच करने के लिए बेहतर तरीके मिलते हैं. आपको यह पता होता है कि बाहरी डिपेंडेंसी को कैसे तय किया जाता है और वह कहां से आती है.- वेंडर मोड: ऑफ़लाइन बिल्ड को आसान बनाने के लिए, उन बाहरी डिपेंडेंसी को पहले से फ़ेच करें जिनकी आपको ज़रूरत है.
- लॉकफ़ाइल: लॉकफ़ाइल से, बिल्ड को फिर से बनाने की प्रोसेस बेहतर होती है. साथ ही, इससे डिपेंडेंसी को हल करने की प्रोसेस तेज़ होती है.
- (आने वाला समय) बीसीआर की उत्पत्ति की पुष्टि करने वाले दस्तावेज़: डिपेंडेंसी की पुष्टि की गई उत्पत्ति की जानकारी देकर, सप्लाई चेन की सुरक्षा को बेहतर बनाएं.
कॉन्सेप्ट
इस सेक्शन में, बाहरी डिपेंडेंसी से जुड़े कॉन्सेप्ट के बारे में ज़्यादा जानकारी दी गई है.
मॉड्यूल
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 हो सकता है. इस मामले में, alice के कॉन्टेक्स्ट में, michael के अंदर मौजूद किसी टारगेट को लेबल @mike//package:target से ऐक्सेस किया जा सकता है (ध्यान दें कि यहां सिर्फ़ एक @ है).
इसके उलट, इसे रिपॉज़िटरी मैपिंग के तौर पर समझा जा सकता है: हर रिपो, "रिपॉज़िटरी का असली नाम" से "रिपॉज़िटरी का आधिकारिक नाम" तक की मैपिंग बनाए रखता है.
रिपॉज़िटरी का नियम
डेटाबेस की परिभाषाओं के लिए स्कीमा. इससे Bazel को यह पता चलता है कि डेटाबेस को कैसे बनाया जाए. उदाहरण के लिए, यह "किसी यूआरएल से ज़िप संग्रह डाउनलोड करें और उसे निकालें", "किसी Maven आर्टफ़ैक्ट को फ़ेच करें और उसे java_import टारगेट के तौर पर उपलब्ध कराएं" या सिर्फ़ "किसी स्थानीय डायरेक्ट्री को सिमलंक करें" हो सकता है. हर रिपो को सही संख्या में आर्ग्युमेंट के साथ रिपो के नियम को कॉल करके तय किया जाता है.
अपने रिपॉज़िटरी के नियम लिखने के बारे में ज़्यादा जानने के लिए, रिपॉज़िटरी के नियम देखें.
सबसे ज़्यादा इस्तेमाल किए जाने वाले रेपो नियम ये हैं: http_archive, जो किसी यूआरएल से संग्रह डाउनलोड करता है और उसे निकालता है. इसके अलावा, local_repository, जो पहले से मौजूद Bazel रिपॉज़िटरी वाली लोकल डायरेक्ट्री को सिमलंक करता है.
किसी रिपॉज़िटरी को फ़ेच करना
किसी repo से जुड़े repo के नियम को लागू करके, repo को लोकल डिस्क पर उपलब्ध कराने की कार्रवाई. वर्कस्पेस में तय की गई रिपॉज़िटरी, फ़ेच किए जाने से पहले लोकल डिस्क पर उपलब्ध नहीं होती हैं.
आम तौर पर, Bazel किसी repo को सिर्फ़ तब फ़ेच करता है, जब उसे repo से किसी चीज़ की ज़रूरत होती है और repo को पहले से फ़ेच नहीं किया गया होता है. अगर रिपो को पहले ही फ़ेच किया जा चुका है, तो Bazel उसे सिर्फ़ तब फिर से फ़ेच करता है, जब उसकी डेफ़िनिशन में बदलाव हुआ हो.
fetch कमांड का इस्तेमाल, किसी रिपॉज़िटरी, टारगेट या सभी ज़रूरी रिपॉज़िटरी के लिए प्री-फ़ेच शुरू करने के लिए किया जा सकता है, ताकि कोई भी बिल्ड किया जा सके. इस सुविधा की मदद से, --nofetch विकल्प का इस्तेमाल करके ऑफ़लाइन बिल्ड बनाए जा सकते हैं.
--fetch विकल्प का इस्तेमाल, नेटवर्क ऐक्सेस को मैनेज करने के लिए किया जाता है. इसकी डिफ़ॉल्ट वैल्यू, सही पर सेट होती है.
हालांकि, जब इसे 'गलत है' (--nofetch) पर सेट किया जाता है, तो कमांड, डिपेंडेंसी के किसी भी कैश मेमोरी वर्शन का इस्तेमाल करेगी. अगर कोई भी वर्शन मौजूद नहीं है, तो कमांड काम नहीं करेगी.
डेटा फ़ेच करने की प्रोसेस को कंट्रोल करने के बारे में ज़्यादा जानने के लिए, डेटा फ़ेच करने के विकल्प देखें.
डायरेक्ट्री का लेआउट
फ़ेच करने के बाद, रिपो को output base में 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 पर माइग्रेट करने से जुड़ी गाइड पढ़ें.