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

Bazel, बाहरी डिपेंडेंसी के साथ काम करता है. ये ऐसी सोर्स फ़ाइलें (टेक्स्ट और बाइनरी, दोनों) होती हैं जिनका इस्तेमाल आपके बिल्ड में किया जाता है, लेकिन ये आपके वर्कस्पेस से नहीं होती हैं. उदाहरण के लिए, वे GitHub repo में होस्ट किया गया कोई नियम सेट, Maven आर्टफ़ैक्ट या आपकी लोकल मशीन पर मौजूद कोई ऐसी डायरेक्ट्री हो सकती हैं जो आपके मौजूदा वर्कस्पेस से बाहर हो.

Bazel 6.0 के मुताबिक, Bazel के साथ बाहरी डिपेंडेंसी मैनेज करने के दो तरीके हैं: पहला, रिपॉज़िटरी पर फ़ोकस करने वाला WORKSPACE सिस्टम और दूसरा, मॉड्यूल पर फ़ोकस करने वाला नया MODULE.bazel सिस्टम. इसे Bzlmod नाम दिया गया है और यह --enable_bzlmod फ़्लैग के साथ चालू होता है. इन दोनों सिस्टम का इस्तेमाल एक साथ किया जा सकता है. हालांकि, Bazel के आने वाले वर्शन में WORKSPACE सिस्टम की जगह Bzlmod का इस्तेमाल किया जाएगा. माइग्रेट करने के तरीके के बारे में जानने के लिए, Bzlmod माइग्रेशन गाइड देखें.

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

कॉन्सेप्ट

रिपॉज़िटरी

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

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

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

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

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

Workspace

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

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

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

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

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

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

किसी अन्य रिपॉज़िटरी के संदर्भ में, रिपॉज़िटरी का वह नाम जिससे उसे ऐक्सेस किया जा सकता है. इसे किसी रिपॉज़िटरी का "निकनेम" माना जा सकता है: कैननिकल नाम michael वाली रिपॉज़िटरी का नाम, रिपॉज़िटरी alice के संदर्भ में mike हो सकता है. हालांकि, रिपॉज़िटरी bob के संदर्भ में इसका नाम mickey हो सकता है. इस मामले में, alice के कॉन्टेक्स्ट में, michael के अंदर मौजूद किसी टारगेट को लेबल @mike//pac/kage: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() उपलब्ध होता है. repo(), BUILD फ़ाइलों में package() फ़ंक्शन के जैसे ही आर्ग्युमेंट लेता है. वहीं, package(), पैकेज में मौजूद सभी बिल्ड टारगेट के लिए सामान्य एट्रिब्यूट तय करता है. इसी तरह, repo(), रिपॉज़िटरी में मौजूद सभी बिल्ड टारगेट के लिए ऐसा करता है.

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

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

Bzlmod की मदद से बाहरी डिपेंडेंसी मैनेज करना

Bzlmod, बाहरी डिपेंडेंसी का नया सबसिस्टम है. यह सीधे तौर पर repo definitions के साथ काम नहीं करता. इसके बजाय, यह मॉड्यूल से डिपेंडेंसी ग्राफ़ बनाता है. साथ ही, ग्राफ़ के ऊपर एक्सटेंशन चलाता है और इसके हिसाब से रिपॉज़िटरी तय करता है.

Bazel मॉड्यूल, Bazel प्रोजेक्ट होता है. इसके कई वर्शन हो सकते हैं. हर वर्शन, उन अन्य मॉड्यूल के बारे में मेटाडेटा पब्लिश करता है जिन पर वह निर्भर होता है. किसी मॉड्यूल में, उसके रेपो रूट पर MODULE.bazel फ़ाइल होनी चाहिए. यह फ़ाइल, WORKSPACE फ़ाइल के बगल में होनी चाहिए. यह फ़ाइल, मॉड्यूल का मेनिफ़ेस्ट है. इसमें मॉड्यूल का नाम, वर्शन, डिपेंडेंसी की सूची, और अन्य जानकारी शामिल होती है. यहां एक सामान्य उदाहरण दिया गया है:

module(name = "my-module", version = "1.0")

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

किसी मॉड्यूल को सिर्फ़ अपनी डायरेक्ट डिपेंडेंसी की सूची बनानी चाहिए. Bzlmod, Bazel रजिस्ट्री में इनकी जानकारी ढूंढता है. डिफ़ॉल्ट रूप से, यह Bazel Central Registry होती है. रजिस्ट्री, डिपेंडेंसी की MODULE.bazel फ़ाइलें उपलब्ध कराती है. इससे Bazel को वर्शन रिज़ॉल्यूशन करने से पहले, पूरे ट्रांज़िटिव डिपेंडेंसी ग्राफ़ का पता लगाने में मदद मिलती है.

वर्शन तय होने के बाद, जिसमें हर मॉड्यूल के लिए एक वर्शन चुना जाता है, Bazel हर मॉड्यूल के लिए रेपो को कैसे तय किया जाए, यह जानने के लिए रजिस्ट्री से फिर से सलाह लेता है. ज़्यादातर मामलों में, http_archive का इस्तेमाल किया जाता है.

मॉड्यूल, टैग नाम के कस्टम डेटा के बारे में भी बता सकते हैं. मॉड्यूल रिज़ॉल्यूशन के बाद, मॉड्यूल एक्सटेंशन इनका इस्तेमाल करते हैं, ताकि अतिरिक्त रिपॉज़िटरी तय की जा सकें. इन एक्सटेंशन में repo rules जैसी सुविधाएं होती हैं. इनकी मदद से, फ़ाइल I/O और नेटवर्क अनुरोध भेजने जैसी कार्रवाइयां की जा सकती हैं. इनकी मदद से, Bazel को अन्य पैकेज मैनेजमेंट सिस्टम के साथ इंटरैक्ट करने की अनुमति मिलती है. साथ ही, Bazel मॉड्यूल से बनाए गए डिपेंडेंसी ग्राफ़ का पालन किया जाता है.

WORKSPACE का इस्तेमाल करके, रीपो को परिभाषित करें

पहले, 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 की कमियों की वजह से, Bzlmod आने वाले समय में Bazel की रिलीज़ में, लेगसी WORKSPACE सिस्टम की जगह लेगा. Bzlmod पर माइग्रेट करने का तरीका जानने के लिए, कृपया Bzlmod माइग्रेशन गाइड पढ़ें.