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

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

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

  • मॉड्यूल एक्सटेंशन: 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 हो सकता है. इस मामले में, michael में मौजूद किसी टारगेट को alice के कॉन्टेक्स्ट में लेबल @mike//package:target से ऐक्सेस किया जा सकता है. ध्यान दें कि यहां सिर्फ़ एक @ है.

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

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

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

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

सबसे ज़्यादा इस्तेमाल किए जाने वाले repo नियम ये हैं: 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.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 पर माइग्रेट करने से जुड़ी गाइड पढ़ें.