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

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

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

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

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: 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

यह शून्य बिल्ड है. कोई बदलाव न होने की वजह से, रीफ़्रेश करने के लिए कोई पैकेज नहीं है और न ही कोई बिल्ड चरण है. अगर '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 की तरह काम करता है.

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

इसके अलावा, ऐसी किसी भी डायरेक्ट्री में बार-बार लागू होने वाले टारगेट पैटर्न का आकलन करते समय, Bagel सिमलिंक को फ़ॉलो नहीं करता है जिसमें इस तरह की फ़ाइल होती है: 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 जैसे टारगेट बनाने की ज़रूरत न हो.

इसके अलावा, लेबल सिंटैक्स के लिए ज़रूरी कोलन के बजाय, स्लैश का इस्तेमाल किया जा सकता है. 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 पर निर्भर होते हैं, तो बाद वाला टारगेट पिछले टारगेट के हिस्से के तौर पर बनाया जाएगा.

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

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

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

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

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

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

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

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

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

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

bazel fetch //...

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

रिपॉज़िटरी कैश मेमोरी

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

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

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

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

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

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

एयरगैप वाले एनवायरमेंट में Bazel को चलाना

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

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

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

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

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

इसके बाद, ऐसा टारबॉल बनाएं जिसमें उस खास बेज़ेल वर्शन के लिए इंप्लिसिट रनटाइम डिपेंडेंसी मौजूद हों:

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

कई वजहों से, अनुरोध के कॉन्फ़िगरेशन से कोई अलग एक्ज़िक्यूटिव कॉन्फ़िगरेशन चुनना बेहतर होता है. सबसे ज़रूरी बात:

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

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

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

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

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

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

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

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

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

एक जैसी सुविधाएं और इंंक्रीमेंटल बिल्ड बनाना

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

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

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

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

जैसा कि सभी गारंटी के साथ किया जाता है, इसके कुछ अच्छे प्रिंट भी आते हैं: Basel के साथ स्थिर स्थिति में रहने के कुछ जाने-पहचाने तरीके हैं. हम इस बात की गारंटी नहीं देते कि हम इस तरह की समस्याओं की जांच करने की कोशिश करेंगे.

अगर आपको कभी Babel के साथ स्थिर और असंगत स्थिति का पता चलता है, तो कृपया एक बग की रिपोर्ट करें.

सैंडबॉक्स में चलाने की सुविधा

Bazel, सैंडबॉक्स का इस्तेमाल करके यह पक्का करता है कि कार्रवाइयां सही तरीके से और पूरी तरह से पूरी हों. Bazel, सैंडबॉक्स में स्पैन (आम तौर पर: कार्रवाइयां) चलाता है. इन सैंडबॉक्स में, सिर्फ़ उन फ़ाइलों का कम से कम सेट होता है जिनकी ज़रूरत टूल को अपना काम करने के लिए होती है. फ़िलहाल, सैंडबॉक्सिंग की सुविधा, CONFIG_USER_NS विकल्प चालू होने पर, Linux 3.12 या इसके बाद के वर्शन पर काम करती है. साथ ही, यह 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 डिस्ट्रिब्यूशन इस्तेमाल किया है, ताकि हम इसकी जांच कर सकें और अगली रिलीज़ में इसे ठीक कर सकें.

बिल्ड के चरण

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

लोडिंग का चरण

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

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

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

विश्लेषण का फ़ेज़

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

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

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

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

लागू करने का फ़ेज़

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