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

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

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

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

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

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

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

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

सहायता पाना

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

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

एक टारगेट बनाया जा रहा है

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

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

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

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

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

बिल्ड के आखिर में, Basel के जिन टारगेट का अनुरोध किया गया था उनके लिए अनुरोध किया गया था, भले ही उन्हें बनाया गया हो या नहीं. अगर ऐसा है, तो आउटपुट फ़ाइलें कहां मिल सकती हैं. बिल्ड चलाने वाली स्क्रिप्ट इस आउटपुट को भरोसेमंद तरीके से पार्स कर सकती हैं. ज़्यादा जानकारी के लिए --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' या इसकी डिपेंडेंसी में कुछ बदलाव होता है, तो Basel, बिल्ड से जुड़ी कुछ कार्रवाइयों को फिर से लागू करेगा या इंक्रीमेंटल बिल्ड पूरा करेगा.

कई लक्ष्य बनाना

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

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

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

//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 की तरह काम करता है.

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

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

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

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 वगैरह) में शामिल नहीं किए जाते (लेकिन उन्हें नेगेटिव वाइल्डकार्ड टारगेट पैटर्न में शामिल किया गया है, यानी उन्हें घटा दिया जाएगा). अगर आप चाहते हैं कि Basel को वे बिल्ड/टेस्ट कर सकें, तो आपको कमांड लाइन पर ऐसे टेस्ट टारगेट के बारे में साफ़ तौर पर टारगेट पैटर्न के साथ बताना चाहिए. वहीं दूसरी ओर, bazel query अपने-आप इस तरह की कोई फ़िल्टर लागू नहीं करता (इससे bazel query का मकसद खत्म हो जाएगा).

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

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

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

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

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

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

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

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

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

bazel fetch //...

अगर आपने Bzlmod वर्शन चालू किया हुआ है, तो Basel 7.1 या उसके बाद के वर्शन के साथ, सभी एक्सटर्नल डिपेंडेंसी भी फ़ेच की जा सकती हैं.

bazel fetch

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

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

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

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

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

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

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

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

एयरगेप्ड माहौल में बेज़ल दौड़ते हुए

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

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

डिस्ट्रिब्यूशन डायरेक्ट्री बनाने के लिए, --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

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

build --distdir=path/to/directory

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

एक जैसा अनुभव देकर, अपने कॉन्टेंट को लगातार बेहतर बनाएं

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

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

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

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

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

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

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

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

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

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

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

बिल्ड के चरण

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

लोड होने का चरण

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

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

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

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

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

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

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

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

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

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