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

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

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

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

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

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

कॉन्सेप्ट

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

मॉड्यूल

यह 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 हैं. 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 पर माइग्रेट करने से जुड़ी गाइड पढ़ें.