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

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

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

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

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

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

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

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

सहायता पाना

  • bazel help command: command के लिए, प्रिंट से जुड़ी सहायता और विकल्प.
  • bazel helpstartup_options: Bazel को होस्ट करने वाले JVM के विकल्प.
  • 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 प्रिंट के टारगेट का अनुरोध किया गया. भले ही, वे बनाए गए हों या नहीं. अगर हां, तो आउटपुट फ़ाइलें कहां मिलेंगी. बिल्ड चलाने वाली स्क्रिप्ट, इस आउटपुट को भरोसेमंद तरीके से पार्स कर सकती हैं. ज़्यादा जानकारी के लिए, --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 के नीचे के सभी टारगेट बनाएं. ये टारगेट foo/bar से नीचे के नहीं हैं. (- से शुरू होने वाले बाद के तर्कों को अतिरिक्त विकल्पों के तौर पर इंटरप्रेट करने से रोकने के लिए -- तर्क ज़रूरी है.)

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

अगर bazel build और bazel test जैसे निर्देशों में tags = ["manual"] के टारगेट शामिल किए जाते हैं, तो उन्हें वाइल्डकार्ड टारगेट पैटर्न (..., :*, :all वगैरह) में शामिल नहीं किया जाता. हालांकि, इन्हें नेगेटिव वाइल्डकार्ड टारगेट पैटर्न में शामिल किया जाता है. यही वजह है कि इन्हें घटा दिया जाता है. अगर आपको Bazel को टेस्ट करना है, तो आपको कमांड लाइन में ऐसे टेस्ट टारगेट के बारे में साफ़ तौर पर टारगेट पैटर्न के साथ बताना चाहिए. इसके उलट, 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.1 या इसके बाद के वर्शन के साथ, अगर आपने Bzlmod चालू किया हुआ है, तो इसे चलाकर सभी बाहरी डिपेंडेंसी भी फ़ेच की जा सकती हैं

bazel fetch

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

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

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

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

डिस्ट्रिब्यूशन फ़ाइलों की डायरेक्ट्री

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

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

हालांकि, फ़ाइल के नाम की शर्त ठीक करने के लिए ज़रूरी नहीं है, लेकिन यह अलग-अलग डायरेक्ट्री के लिए कैंडिडेट फ़ाइलों की संख्या कम कर देती है. इस तरह से, डिस्ट्रिब्यूशन फ़ाइलों की डायरेक्ट्री तय करने से काम आसान हो जाता है. भले ही, ऐसी डायरेक्ट्री में फ़ाइलों की संख्या बढ़ जाए.

हवा में घुमने वाले माहौल में बैजल की दौड़

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

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

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

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

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

  • क्रॉसटूल (--crosstool_top) के उसी वर्शन का इस्तेमाल करें जो अनुरोध के कॉन्फ़िगरेशन में बताया गया है. ऐसा तब तक करें, जब तक --host_crosstool_top न बताया गया हो.
  • --cpu के लिए --host_cpu के मान का इस्तेमाल करें (डिफ़ॉल्ट: k8).
  • इन विकल्पों के लिए वही वैल्यू इस्तेमाल करें जो अनुरोध के कॉन्फ़िगरेशन में दी गई हैं: --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 समय कम होता है.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

सैंडबॉक्स की गई एक्ज़ीक्यूशन

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

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

Google Kubernetes Engine क्लस्टर नोड या 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 कमांड के लिए एक अच्छा आधार बनता है. यह टूल, लोडिंग फ़ेज़ के ऊपर लागू होता है.

प्लान लागू करने का चरण

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