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 रजिस्ट्री खोजी जाती है. रजिस्ट्री, डिपेंडेंसी की MODULE.bazel
फ़ाइलें उपलब्ध कराती है. इससे Bazel को वर्शन रिज़ॉल्यूशन करने से पहले, ट्रांज़िटिव डिपेंडेंसी ग्राफ़ को पूरा खोजने में मदद मिलती है.
वर्शन रिज़ॉल्यूशन के बाद, हर मॉड्यूल के लिए एक वर्शन चुना जाता है. इसके बाद, Bazel हर मॉड्यूल के लिए एक रिपॉज़िटरी तय करने का तरीका जानने के लिए, रजिस्ट्री से फिर से संपर्क करता है. इसका मतलब है कि हर डिपेंडेंसी मॉड्यूल के सोर्स को कैसे फ़ेच किया जाना चाहिए. ज़्यादातर मामलों में, ये सिर्फ़ इंटरनेट से डाउनलोड किए गए और निकाले गए संग्रह होते हैं.
मॉड्यूल में, टैग नाम के डेटा के कस्टमाइज़ किए गए हिस्से भी शामिल किए जा सकते हैं. मॉड्यूल रिज़ॉल्यूशन के बाद, मॉड्यूल एक्सटेंशन इनका इस्तेमाल करते हैं, ताकि अन्य रिपॉज़िटरी तय किए जा सकें. ये एक्सटेंशन, फ़ाइल I/O और नेटवर्क अनुरोध भेजने जैसी कार्रवाइयां कर सकते हैं. इनकी मदद से, Bazel को अन्य पैकेज मैनेजमेंट सिस्टम के साथ इंटरैक्ट करने की अनुमति मिलती है. साथ ही, Bazel मॉड्यूल से बनाए गए डिपेंडेंसी ग्राफ़ का भी सम्मान किया जाता है.
तीन तरह के रिपॉज़िटरी -- मुख्य रिपॉज़िटरी (यह वह सोर्स ट्री है जिसमें काम किया जा रहा है), ट्रांज़िशन डेपेंडेंसी मॉड्यूल दिखाने वाले रिपॉज़िटरी, और मॉड्यूल एक्सटेंशन से बनाए गए रिपॉज़िटरी -- एक साथ मिलकर वर्कस्पेस बनाते हैं.
बाहरी रिपॉज़िटरी (मुख्य रिपॉज़िटरी नहीं) को मांग पर फ़ेच किया जाता है. उदाहरण के लिए, जब उन्हें BUILD फ़ाइलों में लेबल (जैसे, @repo//pkg:target
) से रेफ़र किया जाता है.
फ़ायदे
Bazel का बाहरी डिपेंडेंसी सिस्टम कई तरह के फ़ायदे देता है.
डिपेंडेंसी का अपने-आप हल होना
- डेटरमिनिस्टिक वर्शन रिज़ॉल्यूशन: Bazel, वर्शन रिज़ॉल्यूशन के लिए डेटरमिनिस्टिक MVS एल्गोरिदम का इस्तेमाल करता है. इससे, कॉन्फ़िगरेशन में होने वाली गड़बड़ियों को कम करने के साथ-साथ, डायमंड डिपेंडेंसी से जुड़ी समस्याओं को हल करने में मदद मिलती है.
- डिपेंडेंसी को आसानी से मैनेज करना:
MODULE.bazel
सिर्फ़ डायरेक्ट डिपेंडेंसी का एलान करता है. ट्रांज़िशन डिपेंडेंसी अपने-आप हल हो जाती हैं. इससे प्रोजेक्ट की डिपेंडेंसी के बारे में साफ़ तौर पर जानकारी मिलती है. - सख्त डिपेंडेंसी की जानकारी दिखना: सिर्फ़ डायरेक्ट डिपेंडेंसी दिखती हैं. इससे यह पक्का होता है कि डिपेंडेंसी सही है और उसका अनुमान लगाया जा सकता है.
ईकोसिस्टम इंटिग्रेशन
- Bazel सेंट्रल रजिस्ट्री: यह एक ऐसी रिपॉज़िटरी है जिसमें आम तौर पर इस्तेमाल होने वाली डिपेंडेंसी को Bazel मॉड्यूल के तौर पर खोजा और मैनेज किया जा सकता है.
- Bazel के अलावा दूसरे प्रोजेक्ट का इस्तेमाल करना: जब किसी ऐसे प्रोजेक्ट (आम तौर पर C++ लाइब्रेरी) को Bazel के लिए अडैप्ट किया जाता है और उसे BCR में उपलब्ध कराया जाता है जो Bazel के अलावा किसी और टूल का इस्तेमाल करता है, तो इससे पूरी कम्यूनिटी के लिए इंटिग्रेशन को आसान बना दिया जाता है. साथ ही, कस्टम BUILD फ़ाइलों के डुप्लीकेट होने और उनमें होने वाली गड़बड़ियों को भी खत्म किया जाता है.
- भाषा के हिसाब से पैकेज मैनेजर के साथ यूनिफ़ाइड इंटिग्रेशन: नियमों के सेट, Bazel के अलावा अन्य डिपेंडेंसी के लिए, बाहरी पैकेज मैनेजर के साथ इंटिग्रेशन को आसान बनाते हैं. इनमें ये शामिल हैं:
- Maven के लिए rules_jvm_external,
- PyPi के लिए rules_python,
- Go मॉड्यूल के लिए bazel-gazelle,
- कार्गो के लिए rules_rust.
बेहतर सुविधाएं
- मॉड्यूल एक्सटेंशन:
use_repo_rule
और मॉड्यूल एक्सटेंशन की सुविधाओं की मदद से, कस्टम रिपॉज़िटरी नियमों और रिज़ॉल्यूशन लॉजिक का आसानी से इस्तेमाल किया जा सकता है. इससे, Bazel के अलावा किसी अन्य डेपेंडेंसी को भी शामिल किया जा सकता है. bazel mod
कमांड: सब-कमांड की मदद से, बाहरी डिपेंडेंसी की जांच करने के बेहतर तरीके मिलते हैं. आपको पता है कि बाहरी डिपेंडेंसी को कैसे तय किया जाता है और यह कहां से आती है.- वेंडर मोड: ऑफ़लाइन बिल्ड की सुविधा के लिए, ज़रूरी बाहरी डिपेंडेंसी को पहले से फ़ेच करें.
- Lockfile: लॉकफ़ाइल, बिल्ड को दोबारा बनाने की सुविधा को बेहतर बनाती है और डिपेंडेंसी को हल करने की प्रोसेस को तेज़ करती है.
- (आने वाला समय) बीसीआर के सोर्स की पुष्टि करने वाले एटेस्टेशन: डिपेंडेंसी के सोर्स की पुष्टि करके, सप्लाई चेन की सुरक्षा को बेहतर बनाएं.
कॉन्सेप्ट
इस सेक्शन में, बाहरी डिपेंडेंसी से जुड़े कॉन्सेप्ट के बारे में ज़्यादा जानकारी दी गई है.
मॉड्यूल
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 को रिपॉज़िटरी को मैटीरियलाइज़ करने का तरीका बताता है. उदाहरण के लिए, "किसी यूआरएल से zip संग्रह डाउनलोड करना और उसे निकालना", "किसी Maven आर्टफ़ैक्ट को फ़ेच करना और उसे java_import
टारगेट के तौर पर उपलब्ध कराना" या "किसी स्थानीय डायरेक्ट्री को सिंबल लिंक करना". हर repo को, सही संख्या में आर्ग्युमेंट के साथ repo नियम को कॉल करके तय किया जाता है.
खुद के रिपॉज़िटरी नियम लिखने के तरीके के बारे में ज़्यादा जानकारी के लिए, रिपॉज़िटरी के नियम देखें.
अब तक, सबसे सामान्य रिपॉज़िटरी नियम ये हैं:
http_archive
, जो किसी यूआरएल से संग्रह डाउनलोड करता है और उसे निकालता है. दूसरा,
local_repository
, जो पहले से ही Bazel रिपॉज़िटरी वाली किसी स्थानीय डायरेक्ट्री को लिंक करता है.
कोई रिपॉज़िटरी फ़ेच करना
किसी रिपॉज़िटरी से जुड़ा रिपॉज़िटरी नियम चलाकर, उसे लोकल डिस्क पर उपलब्ध कराने की कार्रवाई. किसी वर्कस्पेस में तय किए गए रिपॉज़िटरी, फ़ेच किए जाने से पहले लोकल डिस्क पर उपलब्ध नहीं होते.
आम तौर पर, Bazel किसी रिपॉज़िटरी को सिर्फ़ तब फ़ेच करता है, जब उसे रिपॉज़िटरी से कुछ चाहिए हो और रिपॉज़िटरी को पहले से फ़ेच न किया गया हो. अगर किसी रिपॉज़िटरी को पहले ही फ़ेच किया जा चुका है, तो Bazel उसे फिर से सिर्फ़ तब फ़ेच करता है, जब उसकी डेफ़िनिशन बदल जाती है.
fetch
कमांड का इस्तेमाल, किसी भी बिल्ड को पूरा करने के लिए किसी रिपॉज़िटरी, टारगेट या सभी ज़रूरी रिपॉज़िटरी के लिए, पहले से फ़ेच करने की प्रोसेस शुरू करने के लिए किया जा सकता है. इस सुविधा की मदद से, --nofetch
विकल्प का इस्तेमाल करके ऑफ़लाइन बिल्ड किए जा सकते हैं.
--fetch
विकल्प, नेटवर्क ऐक्सेस को मैनेज करने के लिए इस्तेमाल किया जाता है. इसकी डिफ़ॉल्ट वैल्यू 'सही' है.
हालांकि, अगर इसे 'गलत है' (--nofetch
) पर सेट किया जाता है, तो कमांड, डिपेंडेंसी के कैश मेमोरी में सेव किए गए किसी भी वर्शन का इस्तेमाल करेगा. अगर कोई वर्शन मौजूद नहीं है, तो कमांड काम नहीं करेगा.
फ़ेच को कंट्रोल करने के बारे में ज़्यादा जानकारी के लिए, फ़ेच करने के विकल्प देखें.
डायरेक्ट्री का लेआउट
फ़ेच होने के बाद, रिपॉज़िटरी को आउटपुट बेस में मौजूद सबडायरेक्ट्री external
में, उसके कैननिकल नाम के नीचे देखा जा सकता है.
कैननिकल नाम canonical_name
वाले रिपॉज़िटरी का कॉन्टेंट देखने के लिए, यह कमांड चलाएं:
ls $(bazel info output_base)/external/ canonical_name
REPO.bazel फ़ाइल
REPO.bazel
फ़ाइल का इस्तेमाल, उस डायरेक्ट्री ट्री की सबसे ऊपरी सीमाओं को मार्क करने के लिए किया जाता है जो किसी रिपॉज़िटरी का हिस्सा होता है. रिपॉज़िटरी की बाउंड्री फ़ाइल के तौर पर काम करने के लिए, इसमें कुछ भी शामिल करने की ज़रूरत नहीं है. हालांकि, इसका इस्तेमाल रिपॉज़िटरी में मौजूद सभी बिल्ड टारगेट के लिए कुछ सामान्य एट्रिब्यूट तय करने के लिए भी किया जा सकता है.
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
फ़ाइलों से मिलता-जुलता है. इसमें बिल्ड नियमों के बजाय, repo नियमों का इस्तेमाल किया जाता है.
यहां दिया गया स्निपेट, WORKSPACE
फ़ाइल में http_archive
repo नियम का इस्तेमाल करने का उदाहरण है:
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
फ़ाइलों कोload
नहीं कर सकते. इसलिए, इन प्रोजेक्ट को इस "deps" मैक्रो में अपनी ट्रांज़िशन डिपेंडेंसी तय करनी होगी या उपयोगकर्ता को कई लेयर वाले "deps" मैक्रो को कॉल करके इस समस्या को हल करना होगा. - Bazel,
WORKSPACE
फ़ाइल का क्रम से आकलन करता है. इसके अलावा, यूआरएल के साथhttp_archive
का इस्तेमाल करके, डिपेंडेंसी की जानकारी दी जाती है. इसमें वर्शन की जानकारी नहीं होती. इसका मतलब है कि डायमंड डिपेंडेंसी के मामले में, वर्शन रिज़ॉल्यूशन करने का कोई भरोसेमंद तरीका नहीं है. जैसे,A
,B
औरC
पर निर्भर करता है.B
औरC
, दोनोंD
के अलग-अलग वर्शन पर निर्भर करते हैं.
- इसकी कुछ समस्याएं हैं: मैक्रो,
WORKSPACE की कमियों की वजह से, मॉड्यूल पर आधारित नए सिस्टम (कोडनेम "Bzlmod") ने धीरे-धीरे Bazel 6 और 9 के बीच, लेगसी WORKSPACE सिस्टम की जगह ले ली. Bzlmod पर माइग्रेट करने का तरीका जानने के लिए, Bzlmod पर माइग्रेट करने से जुड़ी गाइड पढ़ें.