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 को होस्ट करने वाले 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 प्रोग्रेस मैसेज प्रिंट करता है. प्रोग्रेस मैसेज में, मौजूदा बिल्ड स्टेप (जैसे, कंपाइलर या लिंकर) शामिल होता है. साथ ही, इसमें बिल्ड की कुल कार्रवाइयों में से पूरी हो चुकी कार्रवाइयों की संख्या भी शामिल होती है. बिल्ड शुरू होने पर, कार्रवाइयों की कुल संख्या अक्सर बढ़ जाती है, क्योंकि 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:wiz अगर foo/bar/wiz एक पैकेज है
  • //foo/bar:wiz अगर foo/bar एक पैकेज है
  • अन्य मामलों में //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 पर निर्भर करता है, तो //foo/bar:api को //foo:all-apis के हिस्से के तौर पर बनाया जाएगा.

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

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

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

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

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

  • पहली बार ऐप्लिकेशन बनाने से पहले.
  • बाहरी तौर पर निर्भर रहने वाली कोई नई सुविधा जोड़ने के बाद.

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

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

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

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

bazel fetch //...

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

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

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

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

इंक्रीमेंटल रीबिल्ड की सुविधा सही तरीके से काम करती है

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

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

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

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

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

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

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

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

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

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

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

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

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

सैंडबॉक्स में कोड एक्ज़ीक्यूट करना

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

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

कुछ प्लैटफ़ॉर्म, जैसे कि 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, विश्लेषण करने वाले टूल के लिए एक अच्छा प्लैटफ़ॉर्म बन जाता है. जैसे, Bazel की query कमांड. यह कमांड, लोडिंग फ़ेज़ के ऊपर लागू की जाती है.

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

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