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

Bazel, एक्सटर्नल डिपेंडेंसी, सोर्स फ़ाइलों (टेक्स्ट और बाइनरी, दोनों) के साथ काम करता है. इनका इस्तेमाल आपके बिल्ड में किया जाता है . हालांकि, ये आपके वर्कस्पेस से नहीं होती हैं. उदाहरण के लिए, ये GitHub रेपो में होस्ट किया गया कोई नियम सेट, 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 में सिर्फ़ डायरेक्ट डिपेंडेंसी की जानकारी होती है. वहीं, ट्रांज़िटिव डिपेंडेंसी अपने-आप हल हो जाती हैं, इससे प्रोजेक्ट की डिपेंडेंसी की बेहतर खास जानकारी मिलती है.
  • डिपेंडेंसी की विज़िबिलिटी को सीमित करना: सिर्फ़ डायरेक्ट डिपेंडेंसी दिखती हैं. इससे यह पक्का होता है कि डिपेंडेंसी सही हैं और उनके बारे में अनुमान लगाया जा सकता है.

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

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

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

कॉन्सेप्ट

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

मॉड्यूल

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

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

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

रिपॉज़िटरी

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

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

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

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

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

वर्कस्पेस

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

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

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

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

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

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

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

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

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

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

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

सबसे आम रेपो नियम, 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() फ़ंक्शन, package() फ़ंक्शन फ़ाइलों में BUILD के जैसे ही आर्ग्युमेंट लेता है. जहां package() पैकेज में मौजूद सभी बिल्ड टारगेट के लिए सामान्य एट्रिब्यूट तय करता है, वहीं repo() रेपो में मौजूद सभी बिल्ड टारगेट के लिए ऐसा ही करता है.

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

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

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

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

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

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

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