Bazel की मदद से प्रोग्राम बनाएं

समस्या की शिकायत करें सोर्स देखें

इस पेज पर Bazel के साथ प्रोग्राम बनाने, कमांड सिंटैक्स, और टारगेट पैटर्न सिंटैक्स के बारे में बताया गया है.

क्विकस्टार्ट

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

bazel help
                             [Bazel release bazel version]
Usage: bazel command options ...

उपलब्ध निर्देश

  • analyze-profile: बिल्ड प्रोफ़ाइल के डेटा का विश्लेषण करता है.
  • aquery: विश्लेषण के बाद ऐक्शन ग्राफ़ पर क्वेरी लागू करता है.
  • build: तय किए गए टारगेट बनाता है.
  • canonicalize-flags: Bazel फ़्लैग को कैननिकल बनाएं.
  • clean: आउटपुट फ़ाइलों को हटाता है और वैकल्पिक रूप से सर्वर को रोक देता है.
  • cquery: विश्लेषण के बाद डिपेंडेंसी ग्राफ़ क्वेरी लागू करता है.
  • dump: Bazel सर्वर प्रोसेस की अंदरूनी स्थिति को डंप करता है.
  • help: निर्देश या इंडेक्स के लिए, प्रिंट सहायता.
  • info: यह bazel सर्वर के बारे में रनटाइम की जानकारी दिखाता है.
  • fetch: टारगेट की सभी बाहरी डिपेंडेंसी फ़ेच करता है.
  • mobile-install: इससे मोबाइल डिवाइसों पर ऐप्लिकेशन इंस्टॉल किए जाते हैं.
  • query: डिपेंडेंसी ग्राफ़ क्वेरी चलाता है.
  • run: तय किए गए टारगेट को चलाता है.
  • shutdown: Bazel सर्वर बंद करता है.
  • test: यह, जांच के लिए तय किए गए टारगेट बनाता और चलाता है.
  • version: Bazel के वर्शन की जानकारी प्रिंट करता है.

सहायता पाना

  • bazel help command: command के लिए सहायता और विकल्प प्रिंट करें.
  • bazel helpstartup_options: Bazel को होस्ट करने वाली जेवीएम के लिए विकल्प.
  • bazel helptarget-syntax: टारगेट की जानकारी देने के लिए सिंटैक्स के बारे में बताता है.
  • bazel help info-keys: यह जानकारी निर्देश में इस्तेमाल की गई कुंजियों की सूची दिखाता है.

bazel टूल कई काम करता है, जिन्हें निर्देश कहा जाता है. सबसे ज़्यादा इस्तेमाल किए जाने वाले bazel build और bazel test हैं. bazel help का इस्तेमाल करके, ऑनलाइन सहायता मैसेज ब्राउज़ किए जा सकते हैं.

एक टारगेट बनाना

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

Bazel के साथ प्रोग्राम बनाने के लिए, bazel build टाइप करें. इसके बाद, वह टारगेट टाइप करें जिसे आपको बनाना है.

bazel build //foo

//foo बनाने का निर्देश देने के बाद, आपको इस तरह का आउटपुट दिखेगा:

INFO: Analyzed target //foo:foo (14 packages loaded, 48 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 9.905s, Critical Path: 3.25s
INFO: Build completed successfully, 6 total actions

सबसे पहले, Bazel आपके टारगेट के डिपेंडेंसी ग्राफ़ में, सभी पैकेज लोड करता है. इसमें डिक्लेयर की गई डिपेंडेंसी, टारगेट की BUILD फ़ाइल में सीधे तौर पर लिस्ट की गई फ़ाइलें, और ट्रांज़िट डिपेंडेंसी शामिल होती हैं. ये फ़ाइलें, आपके टारगेट की डिपेंडेंसी की BUILD फ़ाइलों में मौजूद होती हैं. सभी डिपेंडेंसी की पहचान करने के बाद, Bazel उनका विश्लेषण करता है, ताकि यह पता लगाया जा सके कि वे सही हैं या नहीं. इसके बाद, वह बनाने से जुड़ी कार्रवाइयां बनाता है. आख़िर में, Bazel बिल्ड के कंपाइलर और दूसरे टूल को चलाता है.

बिल्ड के एक्ज़ीक्यूशन के दौरान, Bazel मैसेज प्रिंट करता है. प्रोग्रेस मैसेज में, बिल्ड चरण (जैसे कि कंपाइलर या लिंकर) का मौजूदा चरण शुरू होता है. इसमें बिल्ड ऐक्शन की कुल संख्या में से पूरी हुई संख्या भी शामिल होती है. जैसे-जैसे बिल्ड शुरू होता है, वैसे-वैसे कुल कार्रवाइयों की संख्या बढ़ जाती है. जैसे-जैसे बेज़ेल पूरा ऐक्शन ग्राफ़ देखता है, वैसे-वैसे यह संख्या भी कुछ ही सेकंड में स्थिर हो जाती है.

बिल्ड के आखिर में, Bazel प्रिंट उन टारगेट को प्रिंट करता है जिनका अनुरोध किया गया था. भले ही, वे बनाए गए हों या नहीं. अगर है, तो आउटपुट फ़ाइलें कहां मिल सकती हैं. बिल्ड चलाने वाली स्क्रिप्ट इस आउटपुट को भरोसेमंद तरीके से पार्स कर सकती हैं. ज़्यादा जानकारी के लिए, --show_result देखें.

अगर वही निर्देश फिर से लिखा जाता है, तो बिल्ड ज़्यादा तेज़ी से पूरा होता है.

bazel build //foo
INFO: Analyzed target //foo:foo (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 0.144s, Critical Path: 0.00s
INFO: Build completed successfully, 1 total action

यह एक null build है. कोई बदलाव नहीं हुआ है, इसलिए फिर से लोड करने के लिए कोई पैकेज नहीं है और लागू करने के लिए कोई बिल्ड स्टेप नहीं है. अगर 'foo' या इसकी डिपेंडेंसी में कोई बदलाव होता है, तो Bazel कुछ बिल्ड ऐक्शन को फिर से चलाएगा या इंक्रीमेंटल बिल्ड को पूरा करेगा.

कई टारगेट बनाना

Bazel बनाने के लिए, टारगेट तय करने के कई तरीके हैं. कुल मिलाकर, इन्हें टारगेट पैटर्न कहा जाता है. इस सिंटैक्स का इस्तेमाल build, test या query जैसे निर्देशों में किया जाता है.

लेबल का इस्तेमाल, अलग-अलग टारगेट के बारे में बताने के लिए किया जाता है, जैसे कि BUILD फ़ाइलों में डिपेंडेंसी का एलान करने के लिए. वहीं, Bazel के टारगेट पैटर्न में कई टारगेट तय किए जाते हैं. टारगेट पैटर्न, वाइल्डकार्ड का इस्तेमाल करके टारगेट के सेट के लिए लेबल सिंटैक्स का सामान्यीकरण होते हैं. सबसे आसान मामले में, कोई भी मान्य लेबल एक मान्य टारगेट पैटर्न होता है, जो ठीक एक टारगेट के सेट की पहचान करता है.

// से शुरू होने वाले सभी टारगेट पैटर्न को मौजूदा फ़ाइल फ़ोल्डर के हिसाब से हल किया जाता है.

//foo/bar:wiz सिर्फ़ एक टारगेट //foo/bar:wiz.
//foo/bar //foo/bar:bar के बराबर.
//foo/bar:all foo/bar पैकेज में मौजूद सभी नियम टारगेट.
//foo/... foo डायरेक्ट्री में शामिल सभी नियम, सभी पैकेज में टारगेट किए गए हैं.
//foo/...:all foo डायरेक्ट्री में शामिल सभी नियम, सभी पैकेज में टारगेट किए गए हैं.
//foo/...:* foo डायरेक्ट्री के नीचे मौजूद सभी पैकेज में मौजूद सभी टारगेट (नियम और फ़ाइलें).
//foo/...:all-targets foo डायरेक्ट्री के नीचे मौजूद सभी पैकेज में मौजूद सभी टारगेट (नियम और फ़ाइलें).
//... मुख्य डेटा स्टोर करने की जगह के पैकेज में सभी नियम टारगेट. इसमें डेटा स्टोर करने की बाहरी जगहों के टारगेट शामिल नहीं होते.
//:all अगर फ़ाइल फ़ोल्डर के रूट में `BUILD` फ़ाइल है, तो टॉप लेवल पैकेज में सभी नियम टारगेट.

// से शुरू नहीं होने वाले टारगेट पैटर्न को, मौजूदा वर्किंग डायरेक्ट्री के हिसाब से हल किया जाता है. ये उदाहरण, foo की वर्किंग डायरेक्ट्री के बारे में बताते हैं:

:foo //foo:foo के बराबर.
bar:wiz //foo/bar:wiz के बराबर.
bar/wiz इसके बराबर:
  • अगर foo/bar/wiz एक पैकेज है, तो //foo/bar/wiz:wiz
  • अगर foo/bar एक पैकेज है, तो //foo/bar:wiz
  • अन्य मामलों में //foo:bar/wiz
bar:all //foo/bar:all के बराबर.
:all //foo:all के बराबर.
...:all //foo/...:all के बराबर.
... //foo/...:all के बराबर.
bar/...:all //foo/bar/...:all के बराबर.

डिफ़ॉल्ट रूप से, डायरेक्ट्री सिमलिंक का इस्तेमाल बार-बार होने वाले टारगेट पैटर्न के लिए किया जाता है. उन नतीजों को छोड़कर जो आउटपुट बेस के नीचे होते हैं, जैसे कि सुविधा वाले सिमलिंक, जो फ़ाइल फ़ोल्डर की रूट डायरेक्ट्री में बनाए जाते हैं.

इसके अलावा, Bazel, किसी भी डायरेक्ट्री में बार-बार होने वाले टारगेट पैटर्न का आकलन करते समय सिमलिंक का पालन नहीं करता है. ऐसी डायरेक्ट्री जिसमें फ़ाइल का नाम इस तरह है:DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN

foo/..., पैकेज पर एक वाइल्डकार्ड है, जो डायरेक्ट्री foo (पैकेज पाथ के सभी रूट के लिए) के नीचे, सभी पैकेज बार-बार दिखाता है. :all, टारगेट पर एक वाइल्डकार्ड है. यह पैकेज में मौजूद सभी नियमों से मेल खाता है. foo/...:all की तरह, इन दोनों को एक साथ जोड़ा जा सकता है. जब दोनों वाइल्डकार्ड का इस्तेमाल किया जाता है, तो इसका नाम foo/... हो सकता है.

इसके अलावा, :* (या :all-targets) एक वाइल्डकार्ड है जो मेल खाने वाले पैकेज के हर टारगेट से मैच करता है. इसमें वे फ़ाइलें भी शामिल हैं जो आम तौर पर किसी नियम के तहत नहीं बनाई जातीं, जैसे कि java_binary नियमों से जुड़ी _deploy.jar फ़ाइलें.

इसका मतलब है कि :*, :all का सुपरसेट दिखाता है. हालांकि, यह भ्रम की स्थिति बना सकता है, लेकिन इस सिंटैक्स में आम तौर पर इस्तेमाल होने वाले बिल्ड के लिए, :all वाइल्डकार्ड का इस्तेमाल किया जा सकता है. ऐसा तब होता है, जब _deploy.jar जैसे बिल्डिंग टारगेट की ज़रूरत न हो.

इसके अलावा, Bazel, लेबल सिंटैक्स के लिए ज़रूरी कोलन के बजाय स्लैश का इस्तेमाल करने की अनुमति देता है. Bash फ़ाइल नाम एक्सपैंशन का इस्तेमाल करते समय, यह आम तौर पर सुविधाजनक होता है. उदाहरण के लिए, foo/bar/wiz, //foo/bar:wiz (अगर foo/bar पैकेज हो) या //foo:bar/wiz (अगर पैकेज foo हो) के बराबर है.

कई Bazel कमांड, टारगेट पैटर्न की सूची को आर्ग्युमेंट के तौर पर स्वीकार करते हैं और वे सभी प्रीफ़िक्स निगेशन ऑपरेटर - के मुताबिक होते हैं. इसका इस्तेमाल पिछले तर्कों के तय सेट में से टारगेट के एक सेट को घटाने के लिए किया जा सकता है. ध्यान दें कि इसका मतलब है कि ऑर्डर मायने रखता है. उदाहरण के लिए,

bazel build foo/... bar/...

इसका मतलब है "foo से नीचे के सभी टारगेट बनाएं और bar से नीचे के सभी टारगेट बनाएं", जबकि

bazel build -- foo/... -foo/bar/...

इसका मतलब है "foo/bar में मौजूद विकल्पों को छोड़कर foo के नीचे के सभी टारगेट बनाएं". - से शुरू होने वाले बाद के तर्कों को अतिरिक्त विकल्पों के तौर पर न मानने के लिए -- तर्क की ज़रूरत होती है.

हालांकि, यह बताना ज़रूरी है कि इस तरह से टारगेट को घटाने पर इस बात की गारंटी नहीं दी जाएगी कि वे नहीं बनाए गए हैं. इसकी वजह यह हो सकती है कि वे ऐसे टारगेट पर निर्भर हों जिन्हें घटाया नहीं गया था. उदाहरण के लिए, अगर कोई ऐसा टारगेट //foo:all-apis है जो //foo/bar:api पर निर्भर करता था, तो बाद वाले टारगेट को पहले वाले टारगेट के हिस्से के तौर पर बनाया जाएगा.

tags = ["manual"] वाले टारगेट, वाइल्डकार्ड टारगेट पैटर्न (..., :*, :all वगैरह) में शामिल नहीं किए जाते, जब उन्हें bazel build और bazel test जैसे निर्देशों में बताया जाता है. हालांकि, ये नेगेटिव वाइल्डकार्ड टारगेट पैटर्न में शामिल होते हैं. यही वजह है कि इन्हें घटा दिया जाता है. अगर आपको बैज़ल से टेस्ट टारगेट बनाना/जांच कराना है, तो आपको कमांड लाइन पर साफ़ तौर पर टारगेट पैटर्न के साथ टेस्ट टारगेट के बारे में बताना चाहिए. वहीं दूसरी ओर, bazel query अपने-आप ऐसी कोई फ़िल्टर नहीं करता (इससे bazel query का मकसद काम नहीं करेगा).

बाहरी डिपेंडेंसी फ़ेच की जा रही हैं

डिफ़ॉल्ट रूप से, Bazel बिल्ड के दौरान बाहरी डिपेंडेंसी को डाउनलोड और सिमलिंक करेगा. हालांकि, ऐसा हो सकता है कि ऐसा तब हो, जब आपको यह जानना हो कि नई बाहरी डिपेंडेंसी कब जोड़ी गई हैं या आपको डिपेंडेंसी "प्री-फ़ेच" करनी है. जैसे, फ़्लाइट से पहले, जहां आप ऑफ़लाइन होंगे. अगर आपको बिल्ड के दौरान नई डिपेंडेंसी जोड़ने से रोकना है, तो --fetch=false फ़्लैग तय करें. ध्यान दें कि यह फ़्लैग सिर्फ़ रिपॉज़िटरी के नियमों पर लागू होता है, जो लोकल फ़ाइल सिस्टम में किसी डायरेक्ट्री की जानकारी नहीं देता. उदाहरण के लिए, local_repository, new_local_repository, और Android SDK और एनडीके डेटा स्टोर करने की जगह के नियमों में किए गए बदलाव, हमेशा लागू होंगे. भले ही, वैल्यू --fetch कुछ भी हो.

अगर आप बिल्ड के दौरान फ़ेच करने की अनुमति नहीं देते और Bazel को नई बाहरी डिपेंडेंसी मिलती हैं, तो आपका बिल्ड काम नहीं करेगा.

bazel fetch चलाकर, डिपेंडेंसी को मैन्युअल तरीके से फ़ेच किया जा सकता है. अगर बिल बनाने के दौरान फ़ेच करने की अनुमति नहीं दी जाती है, तो आपको bazel fetch चलाना होगा:

  • पहली बार बनाने से पहले.
  • नई बाहरी डिपेंडेंसी जोड़ने के बाद.

एक बार चलाए जाने के बाद, आपको इसे तब तक फिर से नहीं चलाना चाहिए, जब तक कि WORKSPACE फ़ाइल में बदलाव न किया जाए.

fetch, डिपेंडेंसी पाने के लिए टारगेट की सूची लेता है. उदाहरण के लिए, इससे //foo:bar और //bar:baz बनाने के लिए ज़रूरी डिपेंडेंसी फ़ेच होंगी:

bazel fetch //foo:bar //bar:baz

किसी फ़ाइल फ़ोल्डर के लिए सभी बाहरी डिपेंडेंसी फ़ेच करने के लिए, इसे चलाएं:

bazel fetch //...

Bazel 7 या उसके बाद के वर्शन का इस्तेमाल करने पर, अगर आपने Bzlmod चालू किया हुआ है, तो ब्राउज़र पर

bazel fetch

अगर आपके पास अपने वर्कस्पेस रूट में इस्तेमाल किए जा रहे सभी टूल (लाइब्रेरी जार से लेकर JDK तक) हैं, तो आपको bazel फ़ेच चलाने की ज़रूरत नहीं है. हालांकि, अगर फ़ाइल फ़ोल्डर डायरेक्ट्री के बाहर किसी और का इस्तेमाल किया जा रहा है, तो Bazel चलने से पहले bazel fetch अपने-आप चलेगा bazel build.

डेटा स्टोर करने की जगह की कैश मेमोरी

Bazel एक ही फ़ाइल को कई बार फ़ेच करने से रोकने की कोशिश करता है. भले ही, अलग-अलग फ़ाइल फ़ोल्डर में एक ही फ़ाइल की ज़रूरत हो या फिर किसी बाहरी डेटा स्टोर करने की जगह की परिभाषा बदल गई हो, लेकिन डाउनलोड करने के लिए उसी फ़ाइल को अब भी फ़ेच किया जा रहा हो. ऐसा करने के लिए, bazel, रिपॉज़िटरी कैश में डाउनलोड की गई सभी फ़ाइलों को कैश मेमोरी में सेव करता है, जो डिफ़ॉल्ट रूप से ~/.cache/bazel/_bazel_$USER/cache/repos/v1/ पर मौजूद होती है. --repository_cache विकल्प का इस्तेमाल करके जगह की जानकारी बदली जा सकती है. इस कैश मेमोरी को, bazel के सभी फ़ाइल फ़ोल्डर और इंस्टॉल किए गए वर्शन के बीच शेयर किया जाता है. कैश मेमोरी से एक एंट्री तब ली जाती है, जब Bazel यह पक्का कर लेता है कि उसके पास सही फ़ाइल की कॉपी है. इसका मतलब है कि अगर डाउनलोड अनुरोध में बताई गई फ़ाइल का SHA256 योग है और उस हैश वाली फ़ाइल कैश में है. इसलिए, हर बाहरी फ़ाइल के लिए हैश तय करना न सिर्फ़ सुरक्षा के लिहाज़ से अच्छा आइडिया है; बल्कि इससे ग़ैर-ज़रूरी डाउनलोड से भी बचा जा सकता है.

कैश मेमोरी में हर बार हिट होने पर, कैश मेमोरी में मौजूद फ़ाइल में बदलाव करने का समय अपडेट किया जाता है. इस तरह कैश डायरेक्ट्री में किसी फ़ाइल के आखिरी इस्तेमाल का पता आसानी से लगाया जा सकता है. उदाहरण के लिए, कैश मेमोरी को मैन्युअल तरीके से मिटाना. कैश मेमोरी कभी भी अपने-आप नहीं हटती, क्योंकि इसमें किसी ऐसी फ़ाइल की कॉपी हो सकती है जो अब अपस्ट्रीम उपलब्ध नहीं है.

[अब काम नहीं करती] डिस्ट्रिब्यूशन फ़ाइलों की डायरेक्ट्री

अब काम नहीं करता: ऑफ़लाइन बिल्ड पाने के लिए, रिपॉज़िटरी कैश मेमोरी का इस्तेमाल करना बेहतर होता है.

ग़ैर-ज़रूरी डाउनलोड से बचने के लिए, डिस्ट्रिब्यूशन डायरेक्ट्री एक और Bazel तरीका है. Bazel, डेटा स्टोर करने की जगह की कैश मेमोरी से पहले, डिस्ट्रिब्यूशन डायरेक्ट्री खोजता है. दोनों में मुख्य अंतर यह है कि डिस्ट्रिब्यूशन डायरेक्ट्री के लिए मैन्युअल तैयारी करनी पड़ती है.

--distdir=/path/to-directory विकल्प का इस्तेमाल करके, फ़ाइलों को फ़ेच करने के बजाय, उन्हें ढूंढने के लिए अतिरिक्त रीड-ओनली डायरेक्ट्री तय की जा सकती है. ऐसी डायरेक्ट्री से कोई फ़ाइल तब ली जाती है, जब फ़ाइल का नाम यूआरएल के बेस नाम के बराबर हो. साथ ही, फ़ाइल का हैश भी डाउनलोड के अनुरोध में बताए गए हैश के बराबर हो. यह सिर्फ़ तब काम करता है, जब फ़ाइल हैश के बारे में WORKSPACE एलान में बताया गया हो.

हालांकि, सुधार के लिए फ़ाइल के नाम की शर्त ज़रूरी नहीं है, लेकिन यह हर खास डायरेक्ट्री के लिए कैंडिडेट फ़ाइलों की संख्या को एक कर देती है. इस तरह से, डिस्ट्रिब्यूशन फ़ाइलों की डायरेक्ट्री तय करने से वह काम करती है. भले ही, उस डायरेक्ट्री में फ़ाइलों की संख्या बढ़ जाए.

हवा वाली जगह पर Bazel चलाना

पहली बार चलाते समय, Bazel के बाइनरी साइज़ को छोटा रखने के लिए, Bazel की इंप्लिसिट डिपेंडेंसी, नेटवर्क पर फ़ेच की जाती है. इन इंप्लिसिट डिपेंडेंसी में ऐसे टूलचेन और नियम होते हैं जो सभी के लिए ज़रूरी नहीं होते. उदाहरण के लिए, Android टूल को सिर्फ़ Android प्रोजेक्ट बनाते समय ही अनबंडल किया जाता है और फ़ेच किया जाता है.

हालांकि, इन इंप्लिसिट डिपेंडेंसी से Bazel को एयर-गैप वाले माहौल में चलाते समय समस्याएं हो सकती हैं. भले ही, आपने अपनी सभी बाहरी डिपेंडेंसी के तौर पर वेंडर को मैनेज किया हो. इसे ठीक करने के लिए, डेटा स्टोर करने की जगह की कैश मेमोरी (Bzel 7 या उसके बाद के वर्शन के साथ) या डिस्ट्रिब्यूशन डायरेक्ट्री (7 से पहले Bazel के साथ) तैयार की जा सकती है. इसमें नेटवर्क ऐक्सेस वाली मशीन पर ये डिपेंडेंसी होती हैं. इसके बाद, उन्हें ऑफ़लाइन तरीके से एयरगैप वाले एनवायरमेंट में ट्रांसफ़र किया जा सकता है.

डेटा स्टोर करने की जगह की कैश मेमोरी (Bazel 7 या उसके बाद के वर्शन के साथ)

डेटा स्टोर करने की जगह की कैश मेमोरी को तैयार करने के लिए, --repository_cache फ़्लैग का इस्तेमाल करें. आपको हर नए Bazel बाइनरी वर्शन के लिए, एक बार ऐसा करना होगा. ऐसा इसलिए, क्योंकि हर रिलीज़ के लिए इंप्लिसिट डिपेंडेंसी अलग हो सकती हैं.

इन डिपेंडेंसी को आपके एयरगैप वाले एनवायरमेंट के बाहर लाने के लिए, सबसे पहले एक खाली वर्कस्पेस बनाएं:

mkdir empty_workspace && cd empty_workspace
touch MODULE.bazel
touch WORKSPACE

पहले से मौजूद Bzlmod डिपेंडेंसी फ़ेच करने के लिए, चलाएं

bazel fetch --repository_cache="path/to/repository/cache"

अगर आप अब भी पहले से मौजूद वर्कस्पेस डिपेंडेंसी को फ़ेच करने के लिए, लेगसी वर्कस्पेस फ़ाइल का इस्तेमाल कर रहे हैं, तो

bazel sync --repository_cache="path/to/repository/cache"

आखिर में, जब आप एयरगैप वाले माहौल में Bazel का इस्तेमाल करते हैं, तो उसी --repository_cache फ़्लैग को पास करें. सुविधा के लिए, इसे .bazelrc एंट्री के तौर पर जोड़ा जा सकता है:

common --repository_cache="path/to/repository/cache"

इसके अलावा, आपको स्थानीय तौर पर BCR का क्लोन बनाना होगा और --registry फ़्लैग का इस्तेमाल करना होगा, ताकि Bazel को इंटरनेट के ज़रिए BCR ऐक्सेस करने से रोका जा सके. ऐसा स्थानीय कॉपी को पॉइंट करने के लिए किया जा सकता है. अपने .bazelrc में यह लाइन जोड़ें:

common --registry="path/to/local/bcr/registry"
डिस्ट्रिब्यूशन डायरेक्ट्री (7 से पहले Bazel के साथ)

डिस्ट्रिब्यूशन डायरेक्ट्री बनाने के लिए, --distdir फ़्लैग का इस्तेमाल करें. आपको हर नए Bazel बाइनरी वर्शन के लिए, एक बार ऐसा करना होगा. ऐसा इसलिए, क्योंकि हर रिलीज़ के लिए इंप्लिसिट डिपेंडेंसी अलग हो सकती हैं.

इन डिपेंडेंसी को अपने एयरगैप वाले एनवायरमेंट के बाहर बनाने के लिए, सबसे पहले Bazel सोर्स ट्री के सही वर्शन पर चेकआउट करें:

git clone https://github.com/bazelbuild/bazel "$BAZEL_DIR"
cd "$BAZEL_DIR"
git checkout "$BAZEL_VERSION"

इसके बाद, उस Bazel वर्शन के लिए इंप्लिसिट रनटाइम डिपेंडेंसी वाला टारबॉल बनाएं:

bazel build @additional_distfiles//:archives.tar

इस टारबॉल को एक ऐसी डायरेक्ट्री में एक्सपोर्ट करें जिसे आपके एयरगैप वाले माहौल में कॉपी किया जा सके. --strip-components फ़्लैग पर ध्यान दें, क्योंकि --distdir डायरेक्ट्री नेस्टिंग लेवल के साथ काफ़ी मुश्किल हो सकती है:

tar xvf bazel-bin/external/additional_distfiles/archives.tar \
  -C "$NEW_DIRECTORY" --strip-components=3

आखिर में, जब एयरगैप वाले माहौल में Bazel का इस्तेमाल किया जाता है, तो डायरेक्ट्री की जानकारी देने वाले --distdir फ़्लैग को पास करें. सुविधा के लिए, इसे .bazelrc एंट्री के तौर पर जोड़ा जा सकता है:

build --distdir=path/to/directory

कॉन्फ़िगरेशन और क्रॉस-कंपाइलेशन बनाएं

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

किसी भी बिल्ड में, एक से ज़्यादा कॉन्फ़िगरेशन हो सकते हैं. क्रॉस-कंपाइल का इस्तेमाल करें, जिसमें 64-बिट आर्किटेक्चर के लिए //foo:bin एक्ज़ीक्यूटेबल बनाई गई हो, लेकिन आपका वर्कस्टेशन 32-बिट मशीन हो. साफ़ तौर पर, बिल्ड के लिए //foo:bin बनाने के लिए ऐसे टूलचेन की ज़रूरत होगी जो 64-बिट एक्ज़िक्यूटेबल बना सके. हालांकि, बिल्ड सिस्टम को खुद ही टूल बनाने के दौरान इस्तेमाल किए जाने वाले अलग-अलग टूल भी बनाने होंगे. उदाहरण के लिए, ऐसे टूल जो सोर्स से बनाए जाते हैं और बाद में जनरेट किए जाते हैं. इन्हें आपके वर्कस्टेशन पर चलाने के लिए बनाया जाना चाहिए. इस तरह, हम दो कॉन्फ़िगरेशन की पहचान कर सकते हैं: एक्सपेरिमेंट कॉन्फ़िगरेशन, जिसका इस्तेमाल बिल्ड के दौरान चलने वाले टूल बनाने में किया जाता है और टारगेट कॉन्फ़िगरेशन (या कॉन्फ़िगरेशन का अनुरोध) किया जाता है. हालांकि, उस शब्द के कई मतलब होने पर भी हम अक्सर "टारगेट कॉन्फ़िगरेशन" कहते हैं. इसका इस्तेमाल उस बाइनरी को बनाने में किया जाता है जिसका आपने अनुरोध किया था.

आम तौर पर, ऐसी कई लाइब्रेरी होती हैं जो अनुरोध किए गए बिल्ड टारगेट (//foo:bin) और एक या एक से ज़्यादा एक्ज़ीक्यूट टूल के लिए ज़रूरी शर्तें होती हैं. उदाहरण के लिए, कुछ बेस लाइब्रेरी. ऐसी लाइब्रेरी दो बार बनाई जानी चाहिए, एक बार एक्ज़ीक्यूट कॉन्फ़िगरेशन के लिए, और एक बार टारगेट कॉन्फ़िगरेशन के लिए. Bazel इस बात का ध्यान रखता है कि दोनों वैरिएंट बनाए जाएं और बनाई गई फ़ाइलों को किसी भी रुकावट से बचाने के लिए, अलग रखा जाए. आम तौर पर, ऐसे टारगेट एक साथ बनाए जा सकते हैं, क्योंकि वे एक-दूसरे से अलग होते हैं. अगर आपको प्रोग्रेस से जुड़े मैसेज दिखते हैं, जिनसे पता चलता है कि कोई टारगेट दो बार बनाया जा रहा है, तो इस बारे में यही वजह होगी.

exec कॉन्फ़िगरेशन, टारगेट कॉन्फ़िगरेशन से इस तरह लिया गया है:

  • क्रॉसटूल (--crosstool_top) के उसी वर्शन का इस्तेमाल करें जो अनुरोध के कॉन्फ़िगरेशन में बताया गया है. ऐसा तब तक करें, जब तक --host_crosstool_top तय न किया गया हो.
  • --cpu (डिफ़ॉल्ट: k8) के लिए, --host_cpu की वैल्यू का इस्तेमाल करें.
  • इन विकल्पों की वही वैल्यू इस्तेमाल करें जो अनुरोध के कॉन्फ़िगरेशन में दी गई हैं: --compiler, --use_ijars और अगर --host_crosstool_top का इस्तेमाल किया गया है, तो कॉन्फ़िगरेशन के लिए क्रॉस-टूल (--compiler को अनदेखा करते हुए) में default_toolchain खोजने के लिए --host_cpu की वैल्यू का इस्तेमाल किया जाता है.
  • --javabase के लिए --host_javabase के मान का उपयोग करें
  • --java_toolchain के लिए --host_java_toolchain के मान का उपयोग करें
  • C++ कोड (-c opt) के लिए, ऑप्टिमाइज़ किए गए बिल्ड इस्तेमाल करें.
  • डीबग करने से जुड़ी कोई जानकारी जनरेट नहीं करता (--copt=-g0).
  • एक्ज़ीक्यूटेबल और शेयर की गई लाइब्रेरी से डीबग की जानकारी हटाएं (--strip=always).
  • सभी पाई गई फ़ाइलों को एक ऐसी खास जगह पर रखें जो किसी भी संभावित अनुरोध कॉन्फ़िगरेशन के लिए इस्तेमाल की जाने वाली जगह से अलग हो.
  • बिल्ड डेटा से बाइनरी पर स्टैंप लागू होने से रोकें (--embed_* विकल्प देखें).
  • बाकी सभी वैल्यू अपने डिफ़ॉल्ट पर सेट रहती हैं.

अनुरोध के कॉन्फ़िगरेशन से किसी अलग exec कॉन्फ़िगरेशन को चुनने को प्राथमिकता देने की कई वजहें हो सकती हैं. सबसे ज़रूरी:

सबसे पहले, स्ट्रिप्ड और ऑप्टिमाइज़ की गई बाइनरी का इस्तेमाल करने से, टूल को लिंक करने और उन्हें लागू करने में लगने वाला समय, टूल के लिए डिस्क के स्टोरेज, और डिस्ट्रिब्यूटेड बिल्ड में नेटवर्क I/O में लगने वाले समय को कम किया जाता है.

दूसरा, सभी बिल्ड में एक्ज़ीक्यूट को अलग करके और कॉन्फ़िगरेशन के लिए अनुरोध करके, आप बहुत ही महंगे रीबिल से बचते हैं. इसकी वजह यह है कि अनुरोध के कॉन्फ़िगरेशन (जैसे कि लिंकर के विकल्पों को बदलना) में छोटे-मोटे बदलाव किए जा सकते हैं, जैसा कि पहले ही बताया गया है.

सही इंक्रीमेंटल रीबिल

Bazel प्रोजेक्ट का एक मुख्य लक्ष्य यह पक्का करना है कि कैंपेन को सही तरीके से फिर से बनाया जाए. पिछले बिल्ड टूल, खास तौर पर 'मेक' पर आधारित टूल, इंक्रीमेंटल बिल्ड लागू करते समय कई अनचाहे अनुमान लगाते हैं.

पहला, फ़ाइलों के टाइमस्टैंप एक ही समय पर बढ़ते हैं. यह एक सामान्य मामला है, फिर भी इस तरह की गलती को नज़रअंदाज़ करना बहुत आसान है. फ़ाइल को पहले के वर्शन के साथ सिंक करने से, फ़ाइल में बदलाव करने में लगने वाला समय कम हो जाता है; मेक-आधारित सिस्टम फिर से नहीं बनेंगे.

आम तौर पर, बनाएं तो फ़ाइलों में हुए बदलावों का पता लगाता है, लेकिन यह निर्देशों में हुए बदलावों का पता नहीं लगाता. अगर बिल्ड चरण में कंपाइलर को पास किए गए विकल्पों में बदलाव किया जाता है, तो 'मेक', कंपाइलर को फिर से नहीं चलाएगा. साथ ही, make clean का इस्तेमाल करके पिछले बिल्ड के अमान्य आउटपुट मैन्युअल तरीके से खारिज करना ज़रूरी है.

साथ ही, उस सबप्रोसेस के अपनी आउटपुट फ़ाइल में लिखना शुरू करने के बाद, मेक अपनी किसी एक सबप्रोसेस के असफल होने पर भी उसे सुरक्षित नहीं रखता है. हालांकि मेक का मौजूदा निष्पादन विफल हो जाएगा, लेकिन बाद में मेक का अनुरोध करने पर यह अनजाने में यह मान लिया जाएगा कि छोटी की गई आउटपुट फ़ाइल मान्य है (क्योंकि यह उसके इनपुट से नई है) और उसे फिर से बनाया नहीं जाएगा. इसी तरह, अगर 'बनाएं' प्रोसेस बंद हो जाती है, तो भी ऐसी ही स्थिति हो सकती है.

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

सही इंक्रीमेंटल बिल्ड के उपयोगकर्ताओं को यह फ़ायदा होता है: भ्रम की वजह से, समय की बचत होती है. (साथ ही, make clean इस्तेमाल करने की वजह से फिर से बनाने में लगने वाला समय कम हुआ, चाहे यह ज़रूरी हो या पहले से ही.)

कंसिस्टेंसी और इंक्रीमेंटल बिल्ड बनाना

औपचारिक तौर पर, जब सभी आउटपुट फ़ाइलें मौजूद होती हैं और उनका कॉन्टेंट सही होता है, तो हम बिल्ड की स्थिति को एक जैसा के तौर पर परिभाषित करते हैं. किसी सोर्स फ़ाइल में बदलाव करने पर, बिल्ड की स्थिति असमान मानी जाती है. यह तब तक एक जैसी रहती है, जब तक कि बिल्ड टूल को पूरा नहीं चलाया जाता. हम इस स्थिति को अस्थिरता के तौर पर बताते हैं, क्योंकि यह कुछ समय के लिए होता है. साथ ही, बिल्ड टूल इस्तेमाल करके एक जैसा बदलाव किया जा सकता है.

एक और तरह की गड़बड़ी है, जो जानलेवा है: एक जैसा असमानता. अगर बिल्ड एक स्थिर स्थिति में पहुंच जाता है, तो बिल्ड टूल को बार-बार लागू करने पर भी कॉन्फ़िगरेशन पहले जैसा नहीं होता है: बिल्ड "अटके" हो जाता है, और आउटपुट गलत रहते हैं. स्थिर और असमान स्थिति, मेक (और दूसरे बिल्ड टूल) के उपयोगकर्ताओं की make clean टाइप की मुख्य वजह हैं. यह पता करना कि बिल्ड टूल इस तरीके से काम नहीं कर रहा है (और फिर उससे ठीक हो रहा है) समय और बहुत परेशान करने वाला हो सकता है.

सैद्धान्तिक तौर पर, एक जैसा बिल्ड पाने का सबसे आसान तरीका यह है कि आप पिछले सभी बिल्ड आउटपुट का इस्तेमाल न कर दें और फिर से शुरुआत करें: हर बिल्ड को एक साफ़ बिल्ड बनाएं. यह बेशक ज़्यादा समय लेने वाला है. हालांकि, ऐसा रिलीज़ इंजीनियर के मामले में नहीं होता. इसलिए, इसे इस्तेमाल करने के लिए यह ज़रूरी है कि बिल्ड टूल, एक जैसी बनावट से समझौता किए बिना, इंक्रीमेंटल बिल्ड करने के लिए तैयार हो.

इंक्रीमेंटल डिपेंडेंसी का सही विश्लेषण करना काफ़ी मुश्किल होता है. जैसा कि ऊपर बताया गया है, कई अन्य बिल्ड टूल, इंक्रीमेंटल (बढ़ने वाले) बिल्ड के दौरान, लगातार एक जैसी स्थिति से बचने में खराब परफ़ॉर्म करते हैं. इसके उलट, Bazel नीचे दी गई गारंटी देता है: बिल्ड टूल को लागू करने के दौरान, जिस दौरान आपने कोई बदलाव नहीं किया है, उसके बाद बिल्ड एक जैसा रहेगा. (अगर बिल्ड के दौरान अपनी सोर्स फ़ाइलों में बदलाव किया जाता है, तो Bazel इस बात की कोई गारंटी नहीं देता कि वर्शन एक जैसा है. हालांकि, इससे इस बात की गारंटी ज़रूर मिलती है कि अगले बिल्ड के नतीजे, एक जैसा व्यवहार करें.)

जैसा कि सभी गारंटी के साथ किया जाता है, यहां कुछ ज़रूरी बातें भी बताई गई हैं: Bazel का इस्तेमाल करने के कुछ तरीके हैं. हम इंंक्रीमेंटल डिपेंडेंसी विश्लेषण में गड़बड़ियों का पता लगाने की जान-बूझकर की गई समस्याओं की जांच करने की गारंटी नहीं देंगे. हालांकि, हम इसकी जांच करेंगे और बिल्ड टूल के सामान्य या "उचित" इस्तेमाल की वजह से होने वाली सभी स्थिर स्थितियों को ठीक करने की पूरी कोशिश करेंगे.

अगर आपको कभी भी Bazel के साथ ऐसी गड़बड़ी का पता चलता है जिसमें बदलाव नहीं होता है, तो कृपया गड़बड़ी की शिकायत करें.

सैंडबॉक्स की मदद से एक्ज़ीक्यूशन

Bazel सैंडबॉक्स का इस्तेमाल करके यह गारंटी देता है कि कार्रवाइयां हर्मेटिक तरीके से और सही तरीके से चलें. Bazel सैंडबॉक्स में spawns (कम बोलने में: कार्रवाइयां) चलाता है. इनमें उन फ़ाइलों का कम से कम सेट होता है जिनकी टूल को टूल को काम करने के लिए ज़रूरत होती है. फ़िलहाल, सैंडबॉक्स की सुविधा, Linux 3.12 या इसके बाद के वर्शन पर काम करती है. इसमें CONFIG_USER_NS का विकल्प चालू है. यह macOS 10.11 या इसके बाद के वर्शन पर भी काम करता है.

अगर आपका सिस्टम सैंडबॉक्सिंग की सुविधा नहीं देता है, तो Bazel एक चेतावनी प्रिंट करेगा. यह आपको इस बात की चेतावनी देगा कि बिल्ड, हेर्मेटिक होने की गारंटी नहीं हैं और यह होस्ट सिस्टम पर अनजान तरीकों से असर डाल सकता है. इस चेतावनी को बंद करने के लिए, Bazel को --ignore_unsupported_sandboxing फ़्लैग भेजा जा सकता है.

Google Cubernetes इंजन क्लस्टर नोड या Debian जैसे कुछ प्लैटफ़ॉर्म पर, सुरक्षा से जुड़ी समस्याओं की वजह से, उपयोगकर्ता नेमस्पेस डिफ़ॉल्ट रूप से बंद कर दिए जाते हैं. इसकी जांच, फ़ाइल में देखकर की जा सकती है /proc/sys/kernel/unprivileged_userns_clone: अगर यह मौजूद है और इसमें 0 है, तो उपयोगकर्ता नेमस्पेस को sudo sysctl kernel.unprivileged_userns_clone=1 की मदद से चालू किया जा सकता है.

कुछ मामलों में, सिस्टम सेटअप की वजह से Bazel सैंडबॉक्स, नियमों को लागू नहीं कर पाता है. आम तौर पर, इसका लक्षण namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory से मिलता-जुलता कोई मैसेज न देने पर होता है. ऐसी स्थिति में, --strategy=Genrule=standalone वाले जेन नियमों के लिए सैंडबॉक्स और --spawn_strategy=standalone वाले दूसरे नियमों के लिए, सैंडबॉक्स को बंद करें. साथ ही, कृपया हमारे समस्या वाले ट्रैकर पर गड़बड़ी की शिकायत करें. साथ ही, बताएं कि आप Linux का कौनसा डिस्ट्रिब्यूशन इस्तेमाल कर रहे हैं. इससे हम इसकी जांच कर पाएंगे और आपको आने वाली प्रोसेस में सुधार कर पाएंगे.

बिल्ड के चरण

Bazel में, बिल्ड तीन अलग-अलग चरणों में होता है. उपयोगकर्ता के तौर पर, इनके बीच के अंतर को समझने से, बिल्ड को कंट्रोल करने वाले विकल्पों के बारे में अहम जानकारी मिलती है (नीचे देखें).

लोड होने का फ़ेज़

लोडिंग में, शुरुआती टारगेट के लिए सभी ज़रूरी BUILD फ़ाइलें और डिपेंडेंसी के उनकी ट्रांज़िटिव बंद होने वाली फ़ाइलें लोड की जाती हैं, पार्स की जाती हैं, आकलन की जाती हैं, और कैश मेमोरी में सेव की जाती हैं.

Bazel सर्वर शुरू होने के बाद, पहले बिल्ड के लिए, आम तौर पर लोड होने के चरण में फ़ाइल सिस्टम से लोड होने वाली BUILD फ़ाइलें होती हैं. बाद के बिल्ड में, खास तौर पर अगर BUILD फ़ाइल में कोई बदलाव नहीं होता है, तो लोडिंग बहुत जल्दी होती है.

इस चरण के दौरान रिपोर्ट की गई गड़बड़ियों में ये शामिल हैं: पैकेज नहीं मिला, टारगेट नहीं मिला, BUILD फ़ाइल में लेक्सिकल और व्याकरण से जुड़ी गड़बड़ियां, और आकलन से जुड़ी गड़बड़ियां.

विश्लेषण का चरण

दूसरे चरण, विश्लेषण में, हर बिल्ड नियम का सिमैंटिक विश्लेषण और पुष्टि करना, बिल्ड डिपेंडेंसी ग्राफ़ बनाना, और यह तय करना शामिल होता है कि बिल्ड के हर चरण में क्या करना है.

लोड होने की तरह, विश्लेषण की पूरी प्रक्रिया में भी कई सेकंड लगते हैं. हालांकि, Bazel, डिपेंडेंसी ग्राफ़ को एक बिल्ड से अगले बिल्ड में कैश मेमोरी में सेव करता है. साथ ही, यह सिर्फ़ उसकी प्रोसेस का विश्लेषण करता है. इससे ऐसे मामले में, जब पैकेज में पिछले बिल्ड के बाद से कोई बदलाव नहीं होता है, तो इंक्रीमेंटल बिल्ड बहुत तेज़ी से बढ़ सकते हैं.

इस चरण में रिपोर्ट की गई गड़बड़ियों में ये शामिल हैं: गलत डिपेंडेंसी, किसी नियम के अमान्य इनपुट, और किसी नियम पर आधारित सभी गड़बड़ी के मैसेज.

लोडिंग और विश्लेषण की प्रोसेस तेज़ है, क्योंकि इस चरण में Bazel गैर-ज़रूरी फ़ाइल I/O का इस्तेमाल नहीं करता. यह काम पूरा होने का पता लगाने के लिए, सिर्फ़ BUILD फ़ाइलें पढ़ता है. इसे डिज़ाइन करने के मकसद से डिज़ाइन किया गया है. इससे Bazel, विश्लेषण करने वाले टूल के लिए एक अच्छा आधार बन जाता है. जैसे, Bazel की query कमांड, जिसे लोड होने के चरण के ऊपर लागू किया जाता है.

स्क्रिप्ट रन करने का चरण

बिल्ड का तीसरा और आखिरी चरण एक्ज़ीक्यूशन होता है. इस फ़ेज़ से यह पक्का होता है कि बिल्ड में हर चरण के आउटपुट अपने इनपुट के मुताबिक हैं. ज़रूरत के हिसाब से कंपाइलेशन/लिंकिंग/वगैरह टूल को फिर से चालू करता है. इस चरण में बिल्ड का ज़्यादातर समय खर्च होता है. बड़े बिल्ड के लिए, इसमें कुछ सेकंड से लेकर एक घंटे से ज़्यादा का समय लग सकता है. इस चरण के दौरान रिपोर्ट की गई गड़बड़ियों में ये शामिल हैं: सोर्स फ़ाइल का उपलब्ध न होना, बिल्ड ऐक्शन के दौरान लागू किए गए टूल में गड़बड़ियां या आउटपुट का अनुमानित सेट न बना पाना.