यह दस्तावेज़, कोडबेस के बारे में जानकारी देता है. साथ ही, यह भी बताता है कि Basel की प्रोसेस कैसे हुई है. यह का मकसद उन लोगों के लिए है जो Basel में योगदान देना चाहते हैं, न कि असली उपयोगकर्ताओं के लिए.
परिचय
Basel का कोड बेस बड़ा है (~350KLOC प्रोडक्शन कोड और ~260 KLOC टेस्ट और कोई भी व्यक्ति पूरे लैंडस्केप से परिचित नहीं है: सभी को उनके बारे में बहुत कम लोग जानते हैं, लेकिन कुछ ही लोगों को यह पता है कि पहाड़ियों के बीच क्या मौजूद है दिशा-निर्देश.
ताकि यात्रा के बीच में लोगों के सामने आने के लिए, जंगल के अंधेरे में गायब होने का सीधा रास्ता गायब है, यह दस्तावेज़ कोडबेस का एक अवलोकन दें, ताकि हम इस पर काम कर रहे हैं.
Baज़ल के सोर्स कोड का सार्वजनिक वर्शन, GitHub में मौजूद है github.com/bazelbuild/bazel यह नहीं है "सच का स्रोत"; यह Google के इंटरनल सोर्स ट्री से आता है. में ऐसी अतिरिक्त सुविधा है जो Google के बाहर काम की नहीं है. कॉन्टेंट बनाने लंबे समय के लिए लक्ष्य, GitHub को सच का स्रोत बनाना है.
योगदानों को सामान्य GitHub पुल अनुरोध प्रणाली के ज़रिए स्वीकार किया जाता है, और किसी Googler की मदद से इंटरनल सोर्स ट्री में मैन्युअल तरीके से इंपोर्ट करें, फिर GitHub में फिर से एक्सपोर्ट किया जाएगा.
क्लाइंट/सर्वर आर्किटेक्चर
Basel का बड़ा हिस्सा एक सर्वर प्रोसेस में रहता है, जो बिल्ड के बीच रैम में बनी रहती है. इसकी मदद से, Basel को अलग-अलग बिल्ड के बीच स्थिति बनाए रखने में मदद मिलती है.
यही वजह है कि Basel कमांड लाइन में दो तरह के विकल्प होते हैं: स्टार्टअप और आदेश. इस तरह से किसी कमांड लाइन में:
bazel --host_jvm_args=-Xmx8G build -c opt //foo:bar
कुछ विकल्प (--host_jvm_args=
), चलाए जाने वाले निर्देश के नाम से पहले हैं
और कुछ (-c opt
) के बाद की हैं; पहले वाले प्रकार को "स्टार्टअप विकल्प" कहा जाता है और
पूरी तरह से सर्वर प्रक्रिया को प्रभावित करता है, जबकि बाद वाली प्रक्रिया में "कमांड
विकल्प", सिर्फ़ एक निर्देश पर असर डालता है.
हर सर्वर इंस्टेंस में एक ही असोसिएट किया गया फ़ाइल फ़ोल्डर (सोर्स का कलेक्शन होता है) और हर फ़ाइल फ़ोल्डर में आम तौर पर एक ही सर्वर इंस्टेंस. कस्टम आउटपुट बेस तय करके, इससे बचा जा सकता है ज़्यादा जानकारी के लिए, "डायरेक्ट्री लेआउट" सेक्शन देखें).
Basel को एक ऐसी ELF एक्ज़ीक्यूटेबल फ़ाइल के तौर पर डिस्ट्रिब्यूट किया गया है जो एक मान्य .zip फ़ाइल भी है.
bazel
टाइप करने पर, ऊपर दिए गए ELF को C++ में लागू कर दिया जाता है (
"क्लाइंट") को कंट्रोल मिलता है. यह इसका इस्तेमाल करके एक सही सर्वर प्रोसेस सेट अप करता है
इसके लिए, नीचे दिया गया तरीका अपनाएं:
- जांचता है कि क्या इसे पहले से ही अलग किया गया है. अगर ऐसा नहीं है, तो फिर भी कर लिया है. यह सर्वर को लागू करने का तरीका है.
- यह जांचता है कि क्या कोई ऐसा सर्वर इंस्टेंस मौजूद है जो चालू है: वह चल रहा है,
इसमें सही स्टार्टअप विकल्प हैं और यह सही फ़ाइल फ़ोल्डर डायरेक्ट्री का इस्तेमाल करता है. यह
$OUTPUT_BASE/server
डायरेक्ट्री को देखकर, चल रहे सर्वर को ढूंढता है जहां पोर्ट के साथ एक लॉक फ़ाइल हो, जिस पर सर्वर सुन रहा हो. - ज़रूरत पड़ने पर, पुरानी सर्वर प्रोसेस को बंद कर देता है
- ज़रूरत पड़ने पर, नई सर्वर प्रोसेस शुरू की जा सकती है
एक उपयुक्त सर्वर प्रक्रिया तैयार होने के बाद, चलाने की आवश्यकता यह आदेश है
gRPC इंटरफ़ेस के ज़रिए बताया जाता है. इसके बाद, Bagel के आउटपुट को वापस पाइप किया जाता है
तक ले जाना है. एक समय पर सिर्फ़ एक निर्देश चलाया जा सकता है. यह है
C++ में पुर्ज़ों के साथ और पुर्ज़ों में
Java. साथ में कई कमांड चलाने के लिए कुछ इन्फ़्रास्ट्रक्चर है,
क्योंकि bazel version
को किसी अन्य निर्देश के साथ नहीं चलाया जा सकता
शर्मिंदगी करता है. मुख्य ब्लॉकर, BlazeModule
की लाइफ़ साइकल है
BlazeRuntime
में कुछ राज्य हैं.
किसी निर्देश के खत्म होने पर, Basel का सर्वर, क्लाइंट को एग्ज़िट कोड भेजता है
वापस आना चाहिए. bazel run
को लागू करना एक दिलचस्प बात है:
इस कमांड का काम, बेज़ल अभी-अभी बनाए गए कुछ को चलाना है, लेकिन यह ऐसा नहीं कर सकता
को सर्वर प्रोसेस से हटा दिया जाता है, क्योंकि इसमें टर्मिनल नहीं होता. तो इसके बजाय यह बताता है कि
क्लाइंट को कौन-सी बाइनरी ujexec()
और कौन-से तर्क के साथ उसे सेट करना चाहिए.
जब कोई व्यक्ति Ctrl-C दबाता है, तो क्लाइंट इसे gRPC पर 'कॉल रद्द करें' में बदल देता है कनेक्शन, जो कमांड को जल्द से जल्द खत्म करने की कोशिश करता है. इसके बाद तीसरा Ctrl-C, क्लाइंट इसके बजाय सर्वर को SIGKILL भेजता है.
क्लाइंट का सोर्स कोड src/main/cpp
से कम है और इसके लिए इस्तेमाल किया जाने वाला प्रोटोकॉल
सर्वर से संपर्क src/main/protobuf/command_server.proto
में है .
सर्वर का मुख्य एंट्री पॉइंट BlazeRuntime.main()
और gRPC कॉल हैं
GrpcServerImpl.run()
की ओर से मैनेज किए जाते हैं.
डायरेक्ट्री का लेआउट
बिल्ड के दौरान Baज़ल, डायरेक्ट्री का कुछ मुश्किल सेट बना देता है. एक पूरा ब्यौरा आउटपुट डायरेक्ट्री लेआउट में उपलब्ध है.
"मुख्य डेटा स्टोर करने की जगह" वह सोर्स ट्री है जिसमें Basel का इस्तेमाल किया गया है. आम तौर पर, यह जिसे आपने सोर्स कंट्रोल से चेक आउट किया हो. इस डायरेक्ट्री का रूट यह है इसे "वर्कस्पेस रूट" कहा जाता है.
Basel का डेटा अपना पूरा डेटा "आउटपुट यूज़र रूट" में सेव होता है. आम तौर पर ऐसा होता है
$HOME/.cache/bazel/_bazel_${USER}
है, लेकिन इसे
--output_user_root
स्टार्टअप विकल्प.
"Android SDK के इंस्टॉल की संख्या" वह स्थान है जहां Basel को निकाला जाता है. यह काम अपने-आप हो जाता है
और प्रत्येक Basel वर्शन को
Android SDK के इंस्टॉल की संख्या. यह डिफ़ॉल्ट रूप से $OUTPUT_USER_ROOT/install
पर सेट है और इसे बदला जा सकता है
ऐसा करने के लिए, --install_base
कमांड लाइन इस्तेमाल करें.
"आउटपुट बेस" वह जगह है जहां Basel इंस्टेंस किसी खास
फ़ाइल फ़ोल्डर इस पर लिखता है. हर आउटपुट बेस में ज़्यादा से ज़्यादा एक Bagel सर्वर इंस्टेंस होता है
Chrome को डाउनलोड किया जा सकता है. आम तौर पर, यह $OUTPUT_USER_ROOT/<checksum of the path
to the workspace>
बजे होता है. इसे --output_base
स्टार्टअप विकल्प का इस्तेमाल करके बदला जा सकता है,
जो अन्य चीज़ों के साथ-साथ, उस सीमा से बाहर निकलने में भी उपयोगी होती है,
एक Basel इंस्टेंस किसी भी समय पर किसी भी फ़ाइल फ़ोल्डर में चल सकता है.
आउटपुट डायरेक्ट्री में दूसरी चीज़ों के साथ-साथ ये चीज़ें भी शामिल होती हैं:
- डेटा स्टोर करने की बाहरी जगहों को
$OUTPUT_BASE/external
पर फ़ेच किया गया. - exec रूट, एक डायरेक्ट्री जिसमें सभी सोर्स के सिमलिंक होते हैं
मौजूदा बिल्ड के लिए कोड. यह
$OUTPUT_BASE/execroot
में है. इस दौरान बिल्ड के लिए, काम करने वाली डायरेक्ट्री$EXECROOT/<name of main repository>
है. हम इसे$EXECROOT
में बदलने की योजना बना रहे हैं. हालांकि, यह लंबी अवधि वाली योजना के लिए इस्तेमाल किया जा सकता है, क्योंकि यह बहुत ही असंगत बदलाव है. - बिल्ड के दौरान बनाई गई फ़ाइलें.
किसी निर्देश को लागू करने की प्रोसेस
जब बेज़ल सर्वर को कंट्रोल मिल जाता है और उसे किसी ऐसे निर्देश के बारे में सूचना मिलती है जो लागू करते हैं, तो इवेंट का नीचे दिया गया क्रम होता है:
BlazeCommandDispatcher
को नए अनुरोध के बारे में सूचना दी गई है. यह तय करता है क्या कमांड को चलाने के लिए किसी फ़ाइल फ़ोल्डर की ज़रूरत है (इसके अलावा, तकरीबन उनका स्रोत कोड से कोई लेना-देना नहीं है, जैसे कि help) देखें और यह बताएं कि क्या कोई दूसरा निर्देश चल रहा है.सही निर्देश मिल गया है. हर निर्देश को इंटरफ़ेस पर लागू करना ज़रूरी है
BlazeCommand
और इसमें@Command
एनोटेशन होना चाहिए (यह एंटीपैटर्न है, तो अच्छा होगा कि अगर किसी कमांड की ज़रूरत के सारे मेटाडेटाBlazeCommand
पर बताए गए तरीके से बताया गया है)कमांड लाइन के विकल्पों को पार्स किया गया है. हर कमांड में अलग कमांड लाइन होती है विकल्पों की जानकारी होती है, जिनकी जानकारी
@Command
एनोटेशन में दी गई है.एक इवेंट बस बनाई गई. इवेंट बस, होने वाले इवेंट के लिए स्ट्रीम है बिल्ड के दौरान किया गया था. इनमें से कुछ को बिल्ड इवेंट प्रोटोकॉल के एआई का इस्तेमाल करके, दुनिया को यह बताया जा सकता है कि जाता है.
इससे निर्देश को कंट्रोल किया जा सकता है. सबसे दिलचस्प निर्देश वे होते हैं जो बिल्ड: बिल्ड, टेस्ट, रन, कवरेज वगैरह: यह फंक्शन,
BuildTool
ने लागू किया.कमांड लाइन पर टारगेट पैटर्न के सेट को पार्स किया गया है. साथ ही, वाइल्डकार्ड को इस तरह से पार्स किया गया है
//pkg:all
और//pkg/...
बंद हो गई हैं. इसे इसमें लागू किया जाता हैAnalysisPhaseRunner.evaluateTargetPatterns()
और Skyframe में इस हिसाब से बदलाव किया गयाTargetPatternPhaseValue
.लोडिंग/विश्लेषण का चरण, ऐक्शन ग्राफ़ बनाने के लिए चलाया जाता है. उन निर्देशों का एक साइक्लिक ग्राफ़ जिन्हें बिल्ड करने के लिए एक्ज़ीक्यूट किया जाना चाहिए).
प्रोग्राम चलाने का चरण पूरा होता है. इसका मतलब है कि सभी ज़रूरी कार्रवाई करने पर अनुरोध किए गए टॉप-लेवल टारगेट को बनाया जा सकता है और उन्हें चलाया जा सकता है.
कमांड लाइन के विकल्प
बेज़ल इनवोकेशन के लिए कमांड लाइन विकल्पों के बारे में
OptionsParsingResult
ऑब्जेक्ट, जिसके बदले में "option
क्लास" वैल्यू दी गई हैं. "विकल्प की क्लास" एक सब-क्लास है
OptionsBase
और कमांड लाइन के विकल्पों को एक साथ ग्रुप करता है, जो हर एक से जुड़े होते हैं
अन्य. उदाहरण के लिए:
- किसी प्रोग्रामिंग भाषा (
CppOptions
याJavaOptions
) से जुड़े विकल्प. येFragmentOptions
की सब-क्लास होनी चाहिए और आखिर में इन्हें रैप कर दिया जाता हैBuildOptions
ऑब्जेक्ट में डालें. - Baze, कार्रवाइयों को लागू करने के तरीके से जुड़े विकल्प (
ExecutionOptions
)
ये विकल्प इस तरह से डिज़ाइन किए जाते हैं कि विश्लेषण के दौरान उनका इस्तेमाल किया जा सके और (
Java में RuleContext.getFragment()
से या Starlark में ctx.fragments
के ज़रिए.
इनमें से कुछ (उदाहरण के लिए, C++ में स्कैन करना शामिल है या नहीं) को पढ़ा गया
है, लेकिन इसके लिए हमेशा साफ़ तौर पर प्लंबिंग की ज़रूरत होती है.
BuildConfiguration
उपलब्ध नहीं होगा. ज़्यादा जानकारी के लिए, देखें
सेक्शन "कॉन्फ़िगरेशन" पर जाएं.
चेतावनी: हम यह दिखाना चाहते हैं कि OptionsBase
इंस्टेंस में बदलाव नहीं किया जा सकता और
उनका इस तरह से इस्तेमाल करें (जैसे कि SkyKeys
का हिस्सा). हालांकि, ऐसा नहीं है और
उनमें बदलाव करना बेज़ल को आसान बनाने का एक अच्छा तरीका है.
डीबग करने के लिए. दुर्भाग्य से, उन्हें असल में नहीं बदला जा सकने वाला बनाना एक बड़ी कोशिश है.
(किसी और के लिए, निर्माण के तुरंत बाद FragmentOptions
में बदलाव करना
को उसका रेफ़रंस रखने का मौका मिलता है और equals()
या hashCode()
से पहले
कॉल किया जा सकता है.)
बेज़ल, विकल्प क्लास के बारे में ये जानकारी हासिल करते हैं:
- कुछ बेज़ल (
CommonCommandOptions
) में हार्ड वायर वाली हैं - हर Basel कमांड के लिए,
@Command
एनोटेशन से ConfiguredRuleClassProvider
से (ये कमांड लाइन के विकल्प से जुड़े हैं अलग-अलग प्रोग्रामिंग भाषाओं में)- स्टारलार्क के नियम अपने विकल्प भी तय कर सकते हैं (देखें यहां देखें)
Starlark के तय किए गए विकल्पों को छोड़कर, हर विकल्प
FragmentOptions
सब-क्लास जिसमें @Option
एनोटेशन है. इससे पता चलता है कि
कुछ सहायता टेक्स्ट के साथ कमांड लाइन विकल्प के नाम और टाइप के बारे में जानकारी.
कमांड लाइन विकल्प की वैल्यू का Java टाइप आम तौर पर, कुछ आसान होता है
(एक स्ट्रिंग, एक पूर्णांक, बूलियन, लेबल वगैरह). हालांकि, हम यह भी ध्यान रखते हैं कि
ज़्यादा मुश्किल टाइप के विकल्प; इस मामले में, रूपांतरण का काम
डेटा प्रकार की कमांड लाइन स्ट्रिंग
com.google.devtools.common.options.Converter
.
सोर्स ट्री, जैसा कि बेज़ल ने देखा
Basel, सॉफ़्टवेयर बनाने का कारोबार करती है. इसे पढ़कर और सोर्स कोड को समझने में मदद मिलती है. सोर्स कोड Basel की कुल वैल्यू इस पर काम करती है उसे "फ़ाइल फ़ोल्डर" कहा जाता है और उसे डेटा संग्रह स्थान, पैकेज, और नियम.
डेटा स्टोर करने की जगह
एक "डेटा स्टोर करने की जगह" एक सोर्स ट्री है, जिस पर डेवलपर काम करता है; यह आम तौर पर सिंगल प्रोजेक्ट को दिखाता है. बेज़ल के पूर्वज, ब्लेज़, एक मोनो रिपॉज़िटरी का संचालन करते थे, इसका मतलब है कि एक ऐसा सोर्स ट्री जिसमें बिल्ड को चलाने के लिए इस्तेमाल किए गए सभी सोर्स कोड शामिल होते हैं. इसके उलट, Basel का सोर्स कोड एक से ज़्यादा ऐसेट में काम करता है डेटा स्टोर करने की जगहें. जिस रिपॉज़िटरी से बेज़ल का शुरू किया गया है उसे "मुख्य रिपॉज़िटरी", बाकी फ़ाइलों को "एक्सटर्नल डेटा स्टोर करने की जगह" कहा जाता है.
डेटा स्टोर करने की जगह को रेपो सीमा वाली फ़ाइल (MODULE.bazel
, REPO.bazel
या
WORKSPACE
या WORKSPACE.bazel
) लेगसी कॉन्टेक्स्ट में होने चाहिए. कॉन्टेंट बनाने
मुख्य रेपो वह सोर्स ट्री है जहां से आप बेज़ल का इस्तेमाल कर रहे हैं. डेटा स्टोर करने की बाहरी जगहें
अलग-अलग तरीके से परिभाषित किया गया है; बाहरी डिपेंडेंसी देखें
ज़्यादा जानकारी के लिए, खास जानकारी देखें.
बाहरी डेटा स्टोर करने की जगहों का कोड इसके तहत सिमलिंक किया गया या डाउनलोड किया गया
$OUTPUT_BASE/external
.
बिल्ड को चलाते समय, पूरे सोर्स ट्री को एक साथ जोड़ना ज़रूरी है; यह
SymlinkForest
की मदद से किया जाता है, जो डेटा स्टोर करने की मुख्य जगह में मौजूद हर पैकेज को सिमलिंक करता है
$EXECROOT
और हर बाहरी रिपॉज़िटरी को $EXECROOT/external
या
$EXECROOT/..
.
पैकेज
हर रिपॉज़िटरी, पैकेज, संबंधित फ़ाइलों और
डिपेंडेंसी का एक स्पेसिफ़िकेशन. इन्हें इस नाम वाली फ़ाइल से तय किया जाता है
BUILD
या BUILD.bazel
. अगर दोनों मौजूद हों, तो Basel को BUILD.bazel
पसंद है; वजह
BUILD
फ़ाइलें अब भी स्वीकार क्यों की गई हैं
फ़ाइल नाम. हालांकि, यह आम तौर पर इस्तेमाल किया जाने वाला पाथ सेगमेंट साबित हुआ है. खास तौर पर,
होता है, जिसमें फ़ाइल के नाम केस-इनसेंसिटिव होते हैं.
पैकेज एक-दूसरे से अलग होते हैं: किसी पैकेज की BUILD
फ़ाइल में बदलाव किया जाता है
दूसरे पैकेज को बदलने की वजह नहीं बन सकती. BUILD
फ़ाइलों को जोड़ना या हटाना
_can _दूसरे पैकेज को बदल सकता है, क्योंकि बार-बार होने वाले ग्लॉब्स पैकेज की सीमाओं पर रुक जाते हैं
और इसलिए BUILD
फ़ाइल की मौजूदगी से बार-बार होने वाला इवेंट रुक जाता है.
BUILD
फ़ाइल के मूल्यांकन को "पैकेज लोडिंग" कहा जाता है. इसे लागू कर दिया गया है
क्लास PackageFactory
में, स्टारलार्क इंटरप्रेटर को कॉल करके और काम करती है
इसके लिए, उपलब्ध नियम क्लास के सेट की जानकारी होना ज़रूरी है. पैकेज का नतीजा
लोडिंग एक Package
ऑब्जेक्ट है. यह ज़्यादातर किसी स्ट्रिंग (
टारगेट में जोड़ी जाती है.
पैकेज लोड होने के दौरान जटिलता का एक बड़ा हिस्सा घबराहट में है: Basel का काम
हर सोर्स फ़ाइल को साफ़ तौर पर सूची में शामिल करना ज़रूरी है. इसके बजाय, वह ग्लोब चला सकती है
(जैसे कि glob(["**/*.java"])
). शेल के विपरीत, यह रिकर्सिव ग्लब का समर्थन करता है जो
सबडायरेक्ट्री के तौर पर (लेकिन सबपैकेज में नहीं). इसके लिए, आपको
फ़ाइल सिस्टम में समस्या हो सकती है और चूंकि यह धीमा हो सकता है, इसलिए हम
इसे साथ-साथ और जितना हो सके उतना बेहतर ढंग से चलाना.
ग्लोबिंग इन क्लास में लागू किया जाता है:
LegacyGlobber
, एक तेज़ और मज़ेदार SkyFrame, अज्ञात ग्लोबरSkyframeHybridGlobber
एक ऐसा वर्शन है जो SkyFrame का इस्तेमाल करता है और "SkyFrame के रीस्टार्ट होने" से बचने के लिए, लेगसी ग्लॉबर (नीचे बताया गया है)
Package
क्लास में कुछ ऐसे सदस्य हैं जिनका इस्तेमाल खास तौर पर किया जाता है
"external" को पार्स करें पैकेज (बाहरी डिपेंडेंसी से जुड़े) होते हैं और
के लिए उपयोगी साबित होते हैं. यह है
डिज़ाइन से जुड़ी गड़बड़ी है, क्योंकि सामान्य पैकेज के बारे में जानकारी देने वाले ऑब्जेक्ट में
किसी अन्य फ़ील्ड के बारे में जानकारी देते हैं. इनमें शामिल हैं:
- रिपॉज़िटरी की मैपिंग
- रजिस्टर किए गए टूलचेन
- रजिस्टर किए गए एक्ज़ीक्यूशन प्लैटफ़ॉर्म
आम तौर पर, "एक्सटर्नल" पार्स करने के बीच ज़्यादा अंतर होगा पैकेज
नियमित पैकेज को पार्स करने से रोक सकता है, ताकि Package
को
दोनों ज़रूरतों को पूरा करता हो. दुर्भाग्य से, ऐसा करना बहुत मुश्किल है, क्योंकि दोनों
एक-दूसरे से जुड़े हुए.
लेबल, टारगेट, और नियम
पैकेज में टारगेट होते हैं. इनके टाइप इस तरह के होते हैं:
- फ़ाइलें: ऐसी चीज़ें जो बिल्ड के इनपुट या आउटपुट होती हैं. तय सीमा में Baज़ल पार्लेंस, हम उन्हें आर्टफ़ैक्ट कहते हैं (इनकी चर्चा कहीं और की गई है). ऐसा हो सकता है कि सभी पक्षों को बिल्ड के दौरान बनाई गई फ़ाइलें टारगेट होती हैं; यह आम तौर पर होता है Baज़ल के पास कोई असोसिएटेड लेबल नहीं होना चाहिए.
- नियम: इनसे आउटपुट पाने के तरीकों के बारे में जानकारी मिलती है. वे
आम तौर पर, किसी प्रोग्रामिंग भाषा से जुड़ी होती हैं (जैसे कि
cc_library
,java_library
याpy_library
), लेकिन कुछ ऐसे भी हैं जो भाषा पर फ़ोकस नहीं करते (जैसे किgenrule
याfilegroup
) - पैकेज ग्रुप: किसको दिखे सेक्शन में इसके बारे में बताया गया है.
टारगेट के नाम को लेबल कहा जाता है. लेबल का सिंटैक्स यह है
@repo//pac/kage:name
, जहां repo
उस रिपॉज़िटरी का नाम है जिस पर लेबल
में, pac/kage
वह डायरेक्ट्री है जिसमें इसकी BUILD
फ़ाइल है और name
इसका पाथ है
वह फ़ाइल (अगर लेबल किसी सोर्स फ़ाइल का रेफ़रंस देता है), जो
पैकेज. कमांड लाइन पर टारगेट का रेफ़रंस देते समय, लेबल के कुछ हिस्से
छोड़ा जा सकता है:
- अगर रिपॉज़िटरी को हटा दिया गया है, तो लेबल को मुख्य डेटा स्टोर करने की जगह.
- अगर पैकेज वाला हिस्सा हटा दिया जाता है (जैसे कि
name
या:name
), तो लेबल हटा दिया जाता है मौजूदा वर्किंग डायरेक्ट्री के पैकेज में होना चाहिए (रिलेटिव पाथ अपलेवल के रेफ़रंस (..) शामिल करने की अनुमति नहीं है)
एक तरह के नियम (जैसे कि "C++ लाइब्रेरी") को "नियम क्लास" कहा जाता है. नियम की क्लास हो सकती हैं
या तो Starlark (rule()
फ़ंक्शन) या Java में (जिसे कॉल किया गया है)
"नेटिव नियम", RuleClass
टाइप करें). लंबे समय तक, हर भाषा के हिसाब से
नियम को Starlark में लागू किया जाएगा. हालांकि, कुछ लेगसी नियम फ़ैमिली (जैसे, Java का इस्तेमाल करके)
या C++) अभी भी Java में हैं.
Starlark नियम क्लास को BUILD
फ़ाइलों की शुरुआत में इंपोर्ट करना ज़रूरी है
load()
स्टेटमेंट का इस्तेमाल कर रहे हैं, जबकि Java नियम क्लास "सही तरीके से" हैं इसके ज़रिए ज्ञात
Basel, ऐसा इसलिए है, क्योंकि वे ConfiguredRuleClassProvider
के साथ रजिस्टर हैं.
नियम की क्लास में इस तरह की जानकारी होती है:
- इसके एट्रिब्यूट (जैसे,
srcs
,deps
): उनके टाइप, डिफ़ॉल्ट वैल्यू, सीमाएं, वगैरह. - अगर किसी एट्रिब्यूट की वैल्यू सबमिट की जाती है, तो उससे जुड़े कॉन्फ़िगरेशन ट्रांज़िशन और पहलू
- नियम को लागू करना
- ट्रांज़िटिव जानकारी देने वालों के लिए नियम "आम तौर पर" लागू होता है बनाता है
शब्दावली नोट: कोडबेस में, हम अक्सर "नियम" का इस्तेमाल करते हैं इसका मतलब है कि
नियम वाली क्लास से बनाया जाता है. हालांकि, स्टारलार्क और लोगों को दिखने वाले दस्तावेज़ों में,
"नियम" का इस्तेमाल खास तौर पर नियम की कैटगरी के बारे में बताने के लिए किया जाना चाहिए; टारगेट
यह सिर्फ़ एक "टारगेट" है. यह भी ध्यान रखें कि RuleClass
के पास "क्लास" है इसमें
नाम, नियम वर्ग और लक्ष्यों के बीच कोई Java इनहेरिटेंस संबंध नहीं है
उस प्रकार का हो सकता है.
स्काईफ़्रेम
Basel के इवैलुएशन फ़्रेमवर्क को Skyframe कहा जाता है. इसका मॉडल यह है कि बिल्ड के दौरान बनने वाली हर चीज़ को एक तय क्रम में व्यवस्थित किया जाता है. डेटा के किसी भी हिस्से से उसकी डिपेंडेंसी की ओर पॉइंट करते हुए किनारों वाला एक साइक्लिक ग्राफ़, यानी डेटा के ऐसे अन्य हिस्से जिन्हें इसे बनाने के लिए पता होना चाहिए.
ग्राफ़ में नोड को SkyValue
कहा जाता है और उनके नामों को
SkyKey
. दोनों में ही बदलाव नहीं किया जा सकता; सिर्फ़ नहीं बदले जा सकने वाले ऑब्जेक्ट होने चाहिए
पहुंच सकते हैं. यह इन् वैरिएंट आम तौर पर हमेशा मौजूद रहता है और अगर ऐसा नहीं होता
(जैसे कि व्यक्तिगत विकल्प क्लास BuildOptions
के लिए, जो इसका सदस्य है
BuildConfigurationValue
और उसका SkyKey
) हम पूरी कोशिश करते हैं कि उन्हें बदला न जाए
उन्हें बदलने के लिए भी कहा जाए, ताकि उन्हें सिर्फ़ देखा न जा सके.
इसके हिसाब से यह स्काईफ़्रेम में कंप्यूट किया गया हर चीज़ (जैसे कि
कॉन्फ़िगर किए गए टारगेट) भी नहीं बदले जा सकने वाले होने चाहिए.
स्काईफ़्रेम ग्राफ़ को देखने का सबसे आसान तरीका bazel dump
--skyframe=deps
को चलाना है. यह ग्राफ़ को एक लाइन में एक SkyValue
डंप कर देता है. यह सबसे अच्छा है
छोटे साइज़ के लिए करना होगा, क्योंकि यह बहुत बड़ा भी हो सकता है.
स्काईफ़्रेम, com.google.devtools.build.skyframe
पैकेज में मौजूद है. कॉन्टेंट बनाने
समान नाम वाले पैकेज com.google.devtools.build.lib.skyframe
में
पर लागू होता है. स्काईफ़्रेम के बारे में ज़्यादा जानकारी
यहां उपलब्ध है.
दिए गए SkyKey
का SkyValue
में आकलन करने के लिए, SkyFrame
कुंजी के टाइप के हिसाब से SkyFunction
. फ़ंक्शन के
जांच करनी है, तो यह
SkyFunction.Environment.getValue()
के अलग-अलग ओवरलोड. इसमें
उन डिपेंडेंसी को Skyframe के इंटरनल ग्राफ़ में रजिस्टर करने का साइड-इफ़ेक्ट, ताकि
किसी भी डिपेंडेंसी होने पर, Skyframe को फिर से फ़ंक्शन का आकलन करने के बारे में पता चल जाएगा
बदलें. दूसरे शब्दों में, Skyframe की कैश मेमोरी और इंक्रीमेंटल कंप्यूटेशन की सेवा
SkyFunction
और SkyValue
की जानकारी का स्तर.
जब भी SkyFunction
किसी ऐसी डिपेंडेंसी का अनुरोध करता है जो उपलब्ध नहीं है, तो getValue()
शून्य दिखाएगा. इसके बाद फ़ंक्शन को
इसकी वैल्यू शून्य है. आने वाले समय में, SkyFrame
उपलब्ध नहीं है, तो फ़ंक्शन को शुरुआत से रीस्टार्ट करें — सिर्फ़ यही
समय getValue()
कॉल गैर-शून्य नतीजे के साथ सफल होगा.
इसका नतीजा यह होता है कि SkyFunction
में की जाने वाली कोई भी कैलकुलेशन
आपको रीस्टार्ट करने से पहले यह प्रोसेस दोहरानी होगी. हालांकि, इसमें ऐसा काम शामिल नहीं है जो
डिपेंडेंसी SkyValues
का आकलन करें, जो कैश मेमोरी में सेव किया गया है. इसलिए, हम आम तौर पर
इस समस्या को हल कर रहे हैं.
- डिपेंडेंसी को बैच में तय करना (
getValuesAndExceptions()
का इस्तेमाल करके) रीस्टार्ट होने की संख्या सीमित कर दें. SkyValue
को अलग-अलग हिस्सों में बांटनाSkyFunction
का डेटा इकट्ठा किया जाता है, ताकि उनकी गिनती अलग से की जा सके और उन्हें कैश मेमोरी में सेव किया जा सके. यह इसे सोच-समझकर तैयार किया जाना चाहिए, क्योंकि इसमें मेमोरी बढ़ाने की क्षमता है इस्तेमाल.- रीस्टार्ट होने के बीच की स्थिति, या तो इस्तेमाल किया जा रहा है
SkyFunction.Environment.getState()
या ऐड-हॉक स्टैटिक कैश मेमोरी रखना "SkyFrame के पिछले हिस्से के पीछे". कॉम्प्लेक्स SkyFunctions, स्टेट मैनेजमेंट दो बार रीस्टार्ट होना मुश्किल हो सकता है, इसलिएStateMachine
को इस प्रोजेक्ट के लिए पेश किया गया था लॉजिकल कॉनज़ेंसी का एक स्ट्रक्चर्ड तरीका, जिसमें निलंबित करने के लिए बने हुक औरSkyFunction
में हैरारकी के हिसाब से कंप्यूटेशन फिर से शुरू कर सकें. उदाहरणःDependencyResolver#computeDependencies
संभावित रूप से बहुत बड़े सेट को कंप्यूट करने के लिएgetState()
के साथStateMachine
का इस्तेमाल करता है कॉन्फ़िगर किए गए टारगेट की डायरेक्ट डिपेंडेंसी का असर रीस्टार्ट हो सकते हैं.
बुनियादी तौर पर, Basel को इस तरह के समाधान की ज़रूरत होती है, क्योंकि सैकड़ों
के हज़ारों इन-फ़्लाइट स्काईफ़्रेम नोड सामान्य हैं, और Java का
लाइटवेट थ्रेड बेहतर परफ़ॉर्म नहीं करते
StateMachine
साल 2023 से लागू किया गया.
स्टारलार्क
Starlark डोमेन के हिसाब से एक खास भाषा है. लोग इसका इस्तेमाल कॉन्फ़िगर करने और उसका दायरा बढ़ाने के लिए करते हैं बेज़ल. इसे Python के प्रतिबंधित सबसेट के तौर पर माना गया है, जिसमें बहुत कम टाइप, कंट्रोल फ़्लो पर ज़्यादा पाबंदियां होती हैं. साथ ही, सबसे अहम बात यह है कि इनका इस्तेमाल न किया जा सकता है एक साथ पढ़ने की सुविधा को चालू करने की गारंटी देती है. यह ट्यूरिंग-पूर्ण नहीं है, क्योंकि कुछ (सभी नहीं) उपयोगकर्ताओं को सामान्य लक्ष्य हासिल करने से रोकने के लिए प्रोग्रामिंग टास्क होते हैं.
Starlark को net.starlark.java
पैकेज में लागू किया गया है.
इसमें स्वतंत्र रूप से, Go का इस्तेमाल किया जा सकता है
यहां पढ़ें. Java
Baज़ल में इस्तेमाल किया जाने वाला इंप्लिमेंटेशन फ़िलहाल एक इंटरप्रेटर है.
स्टारलार्क का इस्तेमाल कई तरह के कॉन्टेक्स्ट में किया जाता है. जैसे:
BUILD
फ़ाइलें. यहां नए बिल्ड टारगेट तय किए जाते हैं. स्टारलार्क इस कॉन्टेक्स्ट में चल रहे कोड के पास सिर्फ़BUILD
के कॉन्टेंट का ऐक्सेस होता है फ़ाइल और इससे लोड की गई.bzl
फ़ाइलें.MODULE.bazel
फ़ाइल. बाहरी डिपेंडेंसी यहां होती हैं तय किया गया है. इस कॉन्टेक्स्ट में चल रहे Starlark कोड का ऐक्सेस सीमित है की शर्तों को पूरा करते हैं..bzl
फ़ाइलें. यहां नए बिल्ड रूल, रेपो नियम, और मॉड्यूल मौजूद हैं एक्सटेंशन परिभाषित होते हैं. यहां Starlark कोड नए फ़ंक्शन तय कर सकता है और एक ही पैरामीटर को लोड कर सकता है अन्य.bzl
फ़ाइलों से.
BUILD
और .bzl
फ़ाइलों के लिए उपलब्ध बोलियां थोड़ी अलग हैं
क्योंकि वे अलग-अलग बातें ज़ाहिर करते हैं. अंतरों की एक सूची उपलब्ध है
यहां पढ़ें.
Starlark के बारे में ज़्यादा जानकारी यहां उपलब्ध है.
लोड होने/विश्लेषण का चरण
लोडिंग/विश्लेषण का चरण वह समय होता है जिसमें Ba ज़रिए, यह तय करता है कि उसे अपनी ज़रूरत के हिसाब से किन कार्रवाइयों की ज़रूरत है कोई खास नियम बनाएं. इसकी बुनियादी यूनिट "कॉन्फ़िगर किया गया टारगेट" है, जो कि (टारगेट, कॉन्फ़िगरेशन) जोड़ी है.
इसे "लोड करना/विश्लेषण का चरण" कहा जाता है क्योंकि इसे दो हिस्सों में बांटा जा सकता है अलग-अलग हिस्से, जिन्हें पहले क्रम से लगाया जाता था, लेकिन अब ये समय के साथ ओवरलैप हो सकते हैं:
- पैकेज लोड हो रहे हैं. इसका मतलब है कि
BUILD
फ़ाइलों कोPackage
ऑब्जेक्ट में बदला जा रहा है जो उनका प्रतिनिधित्व करते हैं - कॉन्फ़िगर किए गए टारगेट का विश्लेषण करने से लेकर, ऐक्शन ग्राफ़ बनाने के नियम
कॉन्फ़िगर किए गए टारगेट के ट्रांज़िटिव क्लोज़र में, कॉन्फ़िगर किया गया हर टारगेट नीचे से ऊपर की ओर विश्लेषण किया जाना चाहिए; यानी, लीफ़ नोड पहला, कमांड लाइन पर मौजूद. इनके विश्लेषण के लिए इनपुट कॉन्फ़िगर किए गए सिंगल टारगेट हैं:
- कॉन्फ़िगरेशन. ("वह नियम कैसे बनाएं"; उदाहरण के लिए, कमांड लाइन के विकल्प जैसी चीज़ें भी, जो उपयोगकर्ता चाहता है C++ कंपाइलर को भेजा जाता है)
- डायरेक्ट डिपेंडेंसी. अस्थायी जानकारी देने वाली सुविधाएं उपलब्ध हैं का विश्लेषण किया जा सकता है. उन्हें ऐसा कहा जाता है, क्योंकि वे "रोल-अप" कॉन्फ़िगर किए गए डोमेन के आखिरी हिस्से में मौजूद लक्षित करें, जैसे क्लासपाथ पर सभी .Jar फ़ाइलें या वे सभी .o फ़ाइलें जो C++ बाइनरी से लिंक होना ज़रूरी है)
- टारगेट. यह टारगेट पैकेज को लोड करने का नतीजा है शामिल है. नियमों के लिए, इसमें इसकी विशेषताएं शामिल होती हैं, जो आम तौर पर मायने रखता है.
- कॉन्फ़िगर किए गए टारगेट को लागू करना. नियमों के हिसाब से, इनमें से कोई एक विकल्प चुना जा सकता है स्टारलार्क या जावा में हो. गैर-नियम कॉन्फ़िगर किए गए सभी टारगेट लागू किए गए जावा में.
कॉन्फ़िगर किए गए टारगेट का विश्लेषण करने का आउटपुट नतीजा है:
- डेटा देने वाली ऐसी अस्थायी कंपनियां जो इस पर निर्भर अपने टारगेट को कॉन्फ़िगर कर सकती हैं ऐक्सेस
- यह कौन-कौनसी आर्टफ़ैक्ट बना सकता है और उन्हें बनाने के लिए क्या किया जा सकता है.
Java के नियमों के लिए ऑफ़र किया गया एपीआई RuleContext
है, जो
Starlark के नियमों का ctx
आर्ग्युमेंट. इसका एपीआई ज़्यादा असरदार है, लेकिन
समय है, तो Bad ThingsTM करना आसान है. उदाहरण के लिए, आप ऐसा कोड लिख सकते हैं जिसका समय या
स्पेस की जटिलता क्वाड्रेटिक (या इससे खराब) है. इससे बेज़ल सर्वर के क्रैश होने की वजह
Java का इस्तेमाल न करना या इन्वैरिएंट का उल्लंघन करना (जैसे, अनजाने में किसी
Options
इंस्टेंस या कॉन्फ़िगर किए गए टारगेट को म्यूटेबल बनाकर)
वह एल्गोरिदम जो कॉन्फ़िगर किए गए टारगेट की डायरेक्ट डिपेंडेंसी तय करता है
DependencyResolver.dependentNodeMap()
में रहते/रहती हैं.
कॉन्फ़िगरेशन
कॉन्फ़िगरेशन "कैसे" हैं जैसे: किस प्लैटफ़ॉर्म के लिए, किस तरह कमांड लाइन विकल्प वगैरह.
एक ही बिल्ड में कई कॉन्फ़िगरेशन के लिए एक ही टारगेट बनाया जा सकता है. यह उदाहरण के लिए, तब उपयोगी होता है, जब उसी कोड का इस्तेमाल किसी ऐसे टूल के लिए किया जाता है जो और जब हम क्रॉस-कंपाइलिंग कर रहे होते हैं या जब हम बढ़िया Android ऐप्लिकेशन बनाना (ऐसा ऐप्लिकेशन जिसमें एक से ज़्यादा सीपीयू के लिए नेटिव कोड हो आर्किटेक्चर)
सैद्धांतिक तौर पर, कॉन्फ़िगरेशन एक BuildOptions
इंस्टेंस है. हालांकि,
प्रैक्टिस, BuildOptions
को BuildConfiguration
ने रैप किया है, जो
कई तरह की सुविधाएं उपलब्ध कराई गई हैं. यह इसके ऊपर से फैलता है
नीचे वाला डिपेंडेंसी ग्राफ़. अगर यह बदलता है, तो बिल्ड
उनका फिर से विश्लेषण कर लिया है.
इसकी वजह से अनियमितताएं होती हैं, जैसे कि अगर उदाहरण के लिए, अनुरोध किए गए टेस्ट की संख्या बदल जाती है, भले ही सिर्फ़ टेस्ट टारगेट पर असर डालता है. कॉन्फ़िगरेशन को "काट-छांट" करने की हमारी योजना है. हालाँकि, यह अभी तक तैयार नहीं है).
जब किसी नियम को लागू करने के लिए कॉन्फ़िगरेशन का कोई हिस्सा ज़रूरी होता है, तो उसके लिए यह एलान करना ज़रूरी होता है
RuleClass.Builder.requiresConfigurationFragments()
का इस्तेमाल करके इसे इसकी परिभाषा में शामिल किया गया है
को अपनाएं. ये दोनों बातें, गलतियों से बचने के लिए हैं. जैसे, Java फ़्रैगमेंट का इस्तेमाल करने वाले Python के नियम और
कॉन्फ़िगरेशन में काट-छांट करने के लिए, ताकि Python के विकल्प बदलने पर, C++
टारगेट का फिर से विश्लेषण करने की ज़रूरत नहीं होती.
यह ज़रूरी नहीं है कि किसी नियम का कॉन्फ़िगरेशन उसके "पैरंट" के कॉन्फ़िगरेशन जैसा ही हो नियम. किसी डिपेंडेंसी एज में कॉन्फ़िगरेशन बदलने की प्रक्रिया को "कॉन्फ़िगरेशन ट्रांज़िशन". ऐसा दो जगहों पर हो सकता है:
- डिपेंडेंसी किनारे पर. ये ट्रांज़िशन यहां दिए गए हैं
Attribute.Builder.cfg()
औरRule
(जहां ट्रांज़िशन होता है) औरBuildOptions
(ओरिजनल कॉन्फ़िगरेशन) एक पर या ज़्यादाBuildOptions
(आउटपुट कॉन्फ़िगरेशन). - कॉन्फ़िगर किए गए टारगेट के किसी भी इनकमिंग किनारे पर. इनकी जानकारी यहां दी गई है
RuleClass.Builder.cfg()
.
सही क्लास TransitionFactory
और ConfigurationTransition
हैं.
कॉन्फ़िगरेशन ट्रांज़िशन का इस्तेमाल किया जाता है, उदाहरण के लिए:
- यह बताने के लिए कि बिल्ड के दौरान किसी खास डिपेंडेंसी का इस्तेमाल किया जाता है और वह इसलिए, इसे एक्ज़ीक्यूशन आर्किटेक्चर में बनाया जाना चाहिए
- यह एलान करने के लिए कि एक खास डिपेंडेंसी, एक से ज़्यादा आर्किटेक्चर (जैसे कि फ़ैट वाले Android APKs में नेटिव कोड के लिए)
अगर किसी कॉन्फ़िगरेशन ट्रांज़िशन के नतीजे में कई कॉन्फ़िगरेशन होते हैं, तो उसे स्प्लिट ट्रांज़िशन.
कॉन्फ़िगरेशन ट्रांज़िशन को Starlark में भी लागू किया जा सकता है (दस्तावेज़ यहां देखें)
ट्रांज़िटिव जानकारी देने वाली कंपनियां
ट्रांज़िटिव जानकारी देने वाली कंपनियां, कॉन्फ़िगर किए गए टारगेट के लिए एक तरीका (और _only _way) हैं कॉन्फ़िगर किए गए अन्य टारगेट के बारे में जानकारी देता है. इसकी वजह "ट्रांज़िव" उनके नाम पर यह है कि आम तौर पर यह एक तरह का रोल-अप होता है कॉन्फ़िगर किए गए टारगेट का अस्थायी तौर पर बंद होना.
आम तौर पर, Java की ट्रांज़िटिव जानकारी देने वाली कंपनियों के बीच 1:1 का तालमेल होता है
और Starlark शामिल (इसका अपवाद DefaultInfo
है जो
FileProvider
, FilesToRunProvider
, और RunfilesProvider
, क्योंकि वह एपीआई
को Java वन के डायरेक्ट ट्रांसलिट्रेशन से ज़्यादा स्टारलार्क-इश माना जाता है).
उनकी कुंजी, इनमें से एक चीज़ है:
- Java क्लास ऑब्जेक्ट. यह सिर्फ़ उन कंपनियों के लिए उपलब्ध है जो
Starlark से ऐक्सेस किए जा सकते हैं. ये सेवा देने वाली कंपनियां,
TransitiveInfoProvider
. - एक स्ट्रिंग. यह काफ़ी पुरानी है और इसकी सलाह बिलकुल नहीं दी जाती, क्योंकि
नाम का टकराव. संवेदनशील जानकारी देने वाली ऐसी कंपनियां, सीधे तौर पर इन कंपनियों की सब-क्लास हैं
build.lib.packages.Info
- सेवा देने वाली कंपनी का सिंबल. इसे
provider()
का इस्तेमाल करके, Starlark से बनाया जा सकता है फ़ंक्शन किया जाता है और यह सेवा देने वाली नई कंपनियां बनाने का सुझाया गया तरीका है. इसका चिह्न है इसे Java में,Provider.Key
इंस्टेंस से दिखाया जाता है.
Java में लागू की गई नई कंपनियों को BuiltinProvider
का इस्तेमाल करके लागू किया जाना चाहिए.
NativeProvider
के इस्तेमाल पर रोक लगा दी गई है (हमारे पास अब तक इसे हटाने का समय नहीं है) और
Starlark से TransitiveInfoProvider
सब-क्लास को ऐक्सेस नहीं किया जा सकता.
कॉन्फ़िगर किए गए टारगेट
कॉन्फ़िगर किए गए टारगेट, RuleConfiguredTargetFactory
के तौर पर लागू किए गए हैं. कई
Java में लागू की गई हर नियम की क्लास के लिए सब-क्लास. Starlark के कॉन्फ़िगर किए गए टारगेट
StarlarkRuleConfiguredTargetUtil.buildRule()
के ज़रिए बनाए गए हैं .
कॉन्फ़िगर की गई टारगेट फ़ैक्ट्री को RuleConfiguredTargetBuilder
का इस्तेमाल करना चाहिए,
अपनी रिटर्न वैल्यू तैयार करें. इसमें ये चीज़ें शामिल होती हैं:
- उनका
filesToBuild
, "इस नियम की फ़ाइलों के सेट का धुंधला कॉन्सेप्ट का प्रतिनिधित्व करता है." ये वे फ़ाइलें होती हैं जो कॉन्फ़िगर किए गए टारगेट के दौरान बनती हैं कमांड लाइन पर या जेनरूल के सोर्स में है. - उनकी रनफ़ाइल, सामान्य और डेटा.
- उनके आउटपुट ग्रुप. ये "फ़ाइलों के अन्य सेट" हैं तो नियम यह कर सकते हैं:
बिल्ड. उन्हें आउटपुट के तौर पर सेट किए गए
BUILD में फ़ाइलग्रुप नियम और Java में
OutputGroupInfo
प्रोवाइडर का इस्तेमाल करने के नियम.
रनफ़ाइल
कुछ बाइनरी को चलाने के लिए डेटा फ़ाइलों की ज़रूरत होती है. इसका एक कारगर उदाहरण ऐसे टेस्ट हैं जिनमें इनपुट फ़ाइलें. इसे बेज़ेल में "रनफ़ाइल" के सिद्धांत से दिखाया गया है. ऐप्लिकेशन "रनफ़ाइल ट्री" किसी खास बाइनरी की डेटा फ़ाइलों का डायरेक्ट्री ट्री है. इसे फ़ाइल सिस्टम में, अलग-अलग सिमलिंक के साथ सिमलिंक ट्री के तौर पर बनाया जाता है जो आउटपुट ट्री के सोर्स में मौजूद फ़ाइलों पर ले जाता हो.
रनफ़ाइल के सेट को Runfiles
इंस्टेंस के तौर पर दिखाया जाता है. यह सैद्धांतिक तौर पर
रनफ़ाइल ट्री में फ़ाइल के पाथ से Artifact
इंस्टेंस तक मैप करें
इसका प्रतिनिधित्व करता है. यह दो लोगों के लिए एक Map
से थोड़ा ज़्यादा जटिल है
वजहें:
- ज़्यादातर मामलों में, किसी फ़ाइल का रनफ़ाइल पाथ उसके एक्ज़ीकपाथ के जैसा ही होता है. हम इसका इस्तेमाल कुछ रैम बचाने के लिए करते हैं.
- रनफ़ाइल ट्री में कई तरह की लेगसी एंट्री हैं जिनके लिए ये भी ज़रूरी हैं दिखाया जाना है.
रनफ़ाइल RunfilesProvider
का इस्तेमाल करके इकट्ठा की जाती हैं: इस क्लास का एक इंस्टेंस
कॉन्फ़िगर की गई टारगेट (जैसे कि लाइब्रेरी) और उसके ट्रांज़िटिव रनफ़ाइल को दिखाती है
बंद कर देने की ज़रूरत होती है और उन्हें एक नेस्ट किए हुए सेट की तरह इकट्ठा किया जाता है (असल में, वे
लागू की गई फ़ाइलों को कवर के नीचे नेस्ट किए गए सेट का इस्तेमाल करके लागू किया जाता है): हर टारगेट, रनफ़ाइल को
अपनी डिपेंडेंसी का इस्तेमाल करता है, अपनी कुछ चीज़ें जोड़ता है. इसके बाद, मिलने वाले सेट को ऊपर की ओर भेजता है
. एक RunfilesProvider
इंस्टेंस में दो Runfiles
हैं
उदाहरण के लिए, ऐसा तब हो सकता है, जब नियम "data" के ज़रिए बनाया गया हो एट्रिब्यूट और
एक से दूसरी तरह की इनकमिंग डिपेंडेंसी के लिए एक. ऐसा इसलिए है, क्योंकि
डेटा एट्रिब्यूट के आधार पर निर्भर होने पर, कभी-कभी यह अलग रनफ़ाइल देता है
नहीं करना होगा. हम पुराने तरीकों से इस तरह का व्यवहार नहीं कर पाए
अभी तक हटा रहे हैं.
बाइनरी वाली रनफ़ाइल को RunfilesSupport
के इंस्टेंस के तौर पर दिखाया जाता है. यह
Runfiles
से अलग है, क्योंकि RunfilesSupport
की
बनाया जा रहा है (Runfiles
के उलट, जो सिर्फ़ एक मैपिंग है). यह
इसके लिए, इन अतिरिक्त कॉम्पोनेंट की ज़रूरत होती है:
- रनफ़ाइल मेनिफ़ेस्ट इनपुट. यह रनफ़ाइल ट्री. इसका इस्तेमाल रनफ़ाइल ट्री के कॉन्टेंट के लिए प्रॉक्सी के तौर पर किया जाता है और Basel का मानना है कि रनफ़ाइल ट्री तब ही बदलता है, जब में किए गए बदलावों के बारे में बताया है.
- आउटपुट रनफ़ाइल मेनिफ़ेस्ट. इसका इस्तेमाल ऐसी रनटाइम लाइब्रेरी में किया जाता है जो खास तौर पर Windows पर रनफ़ाइल ट्री हैंडल करती हैं, जो कभी-कभी काम नहीं करतीं सिम्बॉलिक लिंक.
- रनफ़ाइल मिडलमैन. रनफ़ाइल ट्री के मौजूद रहने के लिए, एक को बनाने के लिए कहा जाता है. ऑर्डर में शामिल है डिपेंडेंसी के किनारों की संख्या को कम करने के लिए, रनफ़ाइल मिडलमैन को का इस्तेमाल इन सभी चीज़ों के लिए किया जाता है.
- उस बाइनरी को चलाने के लिए कमांड लाइन आर्ग्युमेंट जिसकी फ़ाइल रन करती है
RunfilesSupport
ऑब्जेक्ट दिखाता है.
पक्ष
आसपेक्ट रेशियो "डिपेंडेंसी ग्राफ़ में कंप्यूटेशन" करने का तरीका है. वे हैं
Basel के उपयोगकर्ताओं के लिए बताया गया
यहां पढ़ें. बहुत अच्छा
प्रेरणा देने वाला उदाहरण प्रोटोकॉल बफ़र: proto_library
नियम को पता नहीं होना चाहिए
किसी खास भाषा के बारे में नहीं, बल्कि प्रोटोकॉल को लागू करने के लिए
किसी भी प्रोग्रामिंग में बफ़र मैसेज (प्रोटोकॉल बफ़र की "बेसिक यूनिट")
भाषा proto_library
नियम के साथ जोड़ी जानी चाहिए, ताकि अगर दो लक्ष्य
ऐसी भाषा एक ही प्रोटोकॉल बफ़र पर निर्भर होती है, इसलिए इसे सिर्फ़ एक बार बनाया जाता है.
कॉन्फ़िगर किए गए टारगेट की तरह ही, उन्हें Skyframe में SkyValue
के तौर पर दिखाया जाता है
उन्हें बनाने का तरीका, कॉन्फ़िगर किए गए टारगेट से काफ़ी मिलता-जुलता है
बनाया है: उनके पास ConfiguredAspectFactory
नाम की एक फ़ैक्ट्री क्लास है जिसमें
RuleContext
तक ऐक्सेस है, लेकिन कॉन्फ़िगर की गई टारगेट फ़ैक्ट्री के उलट, इसे यह भी पता है
कॉन्फ़िगर किए गए टारगेट और उसे सेवा देने वाली कंपनियों के बारे में जानकारी.
डिपेंडेंसी ग्राफ़ में नीचे बताए गए पहलुओं का सेट, हर एक के लिए तय किया गया है
एट्रिब्यूट की वैल्यू के तौर पर Attribute.Builder.aspects()
फ़ंक्शन का इस्तेमाल करें. कुछ ऐसे हैं
इस प्रोसेस में, गुमराह करने वाले नाम वाली क्लास:
AspectClass
आसपेक्ट रेशियो को लागू करना है. यह या तो Java में हो सकती है (इस मामले में यह एक सब-क्लास है) या Starlark में (इस मामले में यह एकStarlarkAspectClass
का इंस्टेंस). यहRuleConfiguredTargetFactory
.AspectDefinition
, पहलू की परिभाषा है; इसमें ये चीज़ें शामिल हैं को उपलब्ध कराने वाली कंपनियों के साथ काम करता है, लागू करें, जैसे कि सहीAspectClass
इंस्टेंस. यह समय हैRuleClass
के जैसा है.AspectParameters
किसी पक्ष को पैरामेटाइज़ करने का एक तरीका है डिपेंडेंसी ग्राफ़. फ़िलहाल, यह मैप को स्ट्रिंग करने के लिए स्ट्रिंग है. एक अच्छा उदाहरण यह इतना उपयोगी क्यों है कि प्रोटोकॉल बफ़र: अगर किसी भाषा में एक से ज़्यादा API (एपीआई) हैं, तो यह जानकारी कि किस एपीआई के लिए प्रोटोकॉल बफ़र बनाया जाना चाहिए नीचे की ओर डिपेंडेंसी ग्राफ़ के नीचे दिखेगा.Aspect
वह पूरा डेटा दिखाता है जो उस पहलू की गणना करने के लिए ज़रूरी है जो डिपेंडेंसी ग्राफ़ को नीचे की ओर ले जाता है. इसमें आसपेक्ट क्लास, इसका की परिभाषा और इसके पैरामीटर का पता लगाना है.RuleAspect
ऐसा फ़ंक्शन है जो तय करता है कि किसी नियम में क्या-क्या है प्रचार होना चाहिए. यहRule
है ->Aspect
फ़ंक्शन.
कुछ चीज़ों की उम्मीद से हटकर, पहलुओं को दूसरे पहलुओं के साथ जोड़ा जा सकता है;
उदाहरण के लिए, किसी Java IDE के लिए क्लासपाथ इकट्ठा करने वाला पहलू
क्लासपाथ पर सभी .jar फ़ाइलों के बारे में जानना चाहते हैं, लेकिन उनमें से कुछ
प्रोटोकॉल बफ़र. इस मामले में, IDE पहलू
(proto_library
नियम + Java प्रोटो आसपेक्ट रेशियो) जोड़ी.
पहलुओं के पहलुओं की जटिलता को कक्षा में कैप्चर किया जाता है
AspectCollection
.
प्लैटफ़ॉर्म और टूलचेन
Baज़र, मल्टी-प्लैटफ़ॉर्म बिल्ड के साथ काम करता है. इसका मतलब है कि ऐसी बिल्ड की सुविधा हर जगह उपलब्ध है ऐसे एक से ज़्यादा आर्किटेक्चर जहां बिल्ड ऐक्शन चलते हैं और जिनके लिए कई आर्किटेक्चर बनाए जाते हैं कौनसा कोड बनाया गया है. बेज़ल में इन आर्किटेक्चर को प्लैटफ़ॉर्म कहा जाता है बातचीत (पूरा दस्तावेज़ यहां देखें)
किसी प्लैटफ़ॉर्म के बारे में, कंस्ट्रेंट सेटिंग से की-वैल्यू मैपिंग की मदद से बताया जाता है (जैसे,
"सीपीयू आर्किटेक्चर" का सिद्धांत), ताकि वैल्यू को सीमित किया जा सके (जैसे, एक खास सीपीयू)
जैसे कि x86_64). हमारे पास एक "शब्दकोश" है कि सबसे ज़्यादा इस्तेमाल होने वाला कंस्ट्रेंट
@platforms
डेटा स्टोर करने की जगह में सेटिंग और वैल्यू.
टूलचेन का सिद्धांत इस बात से जुड़ा है कि किन प्लैटफ़ॉर्म पर बिल्ड किन प्लैटफ़ॉर्म पर चल रहा है और कौनसे प्लैटफ़ॉर्म टारगेट किए गए हैं, तो अलग-अलग कंपाइलर; उदाहरण के लिए, कोई C++ टूलचेन किसी और कुछ अन्य ओएस को टारगेट कर पाएं. बेज़ल को C++ तय करना ज़रूरी है कंपाइलर, जिसका इस्तेमाल सेट एक्ज़ीक्यूशन और टारगेट प्लैटफ़ॉर्म के आधार पर किया जाता है (टूलचेन के लिए दस्तावेज़ यहां देखें).
ऐसा करने के लिए, टूलचेन को लागू करने के सेट के साथ एनोटेट किया जाता है और टारगेट प्लैटफ़ॉर्म के कंस्ट्रेंट को टारगेट करने के लिए जो काम करते हैं. ऐसा करने के लिए, टूलचेन के दो हिस्से होते हैं:
toolchain()
का नियम, जो एक्ज़ीक्यूशन और टारगेट के सेट की जानकारी देता है टूलचेन टेक्नोलॉजी पर आधारित बिड के साथ काम करने वाली और यह बताती है कि यह टूलचेन है (अन्य टूल कोtoolchain_type()
नियम से दिखाया जाता है)- एक भाषा के हिसाब से नियम, जो असल टूलचेन के बारे में बताता है (जैसे कि
cc_toolchain()
)
यह इस तरह से किया जाता है, क्योंकि हमें हर
और किसी खास भाषा के हिसाब से समाधान करने के लिए,
*_toolchain()
नियमों में इससे ज़्यादा जानकारी है, इसलिए वे ज़्यादा
लोड होने में कितना समय लगेगा.
एक्ज़ीक्यूशन प्लैटफ़ॉर्म, इनमें से किसी एक तरीके से तय किए जाते हैं:
register_execution_platforms()
फ़ंक्शन का इस्तेमाल करके MODULE.bagel फ़ाइल में- --extra_execution_platforms कमांड लाइन का इस्तेमाल करके कमांड लाइन पर विकल्प
उपलब्ध एक्ज़ीक्यूशन प्लैटफ़ॉर्म के सेट का हिसाब
RegisteredExecutionPlatformsFunction
कॉन्फ़िगर किए गए टारगेट के लिए टारगेट प्लैटफ़ॉर्म इस हिसाब से तय किया जाता है
PlatformOptions.computeTargetPlatform()
यह प्लैटफ़ॉर्म की एक सूची है, क्योंकि
और आखिर में कई टारगेट प्लैटफ़ॉर्म को सपोर्ट करना हो, लेकिन उसे लागू नहीं किया गया हो
न करें.
कॉन्फ़िगर किए गए टारगेट के लिए इस्तेमाल किए जाने वाले टूलचेन के सेट को इस हिसाब से तय किया जाता है
ToolchainResolutionFunction
. यह इसका एक फ़ंक्शन है:
- रजिस्टर किए गए टूलचेन का सेट (MODULE.bazu फ़ाइल और कॉन्फ़िगरेशन)
- कॉन्फ़िगरेशन में, पसंद के मुताबिक एक्ज़ीक्यूशन और टारगेट प्लैटफ़ॉर्म
- कॉन्फ़िगर किए गए टारगेट के लिए ज़रूरी टूलचेन टाइप का सेट (
UnloadedToolchainContextKey)
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है - कॉन्फ़िगर किए गए टारगेट के लिए, एक्ज़ीक्यूशन करने वाले प्लैटफ़ॉर्म कंस्ट्रेंट का सेट (
exec_compatible_with
एट्रिब्यूट) और कॉन्फ़िगरेशन (--experimental_add_exec_constraints_to_targets
), मेंUnloadedToolchainContextKey
इसका नतीजा एक UnloadedToolchainContext
है, जो असल में इस क्वेरी का मैप है:
इस लेबल के लिए, टूलचेन टाइप (ToolchainTypeInfo
इंस्टेंस के तौर पर दिखाया जाता है)
चुना गया टूलचेन. इसे "अनलोड किया गया" कहा जाता है क्योंकि इसमें
सिर्फ़ उनके लेबल के हिसाब से होने चाहिए.
इसके बाद, ResolvedToolchainContext.load()
का इस्तेमाल करके टूलचेन को लोड किया जाता है
और कॉन्फ़िगर किए गए टारगेट को लागू करने में इसका इस्तेमाल किया जाता है.
हमारे पास एक लेगसी सिस्टम भी है, जो सिर्फ़ एक "होस्ट" पर निर्भर करता है
कॉन्फ़िगरेशन और टारगेट कॉन्फ़िगरेशन, जिन्हें अलग-अलग
कॉन्फ़िगरेशन फ़्लैग, जैसे कि --cpu
. हम धीरे-धीरे ऊपर बताए गए ऐप्लिकेशन पर स्विच कर रहे हैं
सिस्टम. वे ऐसे मामलों को हैंडल करने के लिए, जहां लोग लेगसी कॉन्फ़िगरेशन का इस्तेमाल करते हैं
हैं, जिन्हें हमने लागू किया है
प्लैटफ़ॉर्म मैपिंग
का इस्तेमाल किया जा सकता है.
उनका कोड PlatformMappingFunction
में है और गैर-स्टारलार्क "little" का इस्तेमाल करता है
भाषा".
कंस्ट्रेंट
कभी-कभी कोई व्यक्ति अपने टारगेट में से सिर्फ़ कुछ के साथ काम करना चाहता है प्लैटफ़ॉर्म. माफ़ करें, Baज़र के पास इस मकसद को पूरा करने के लिए कई तरीके हैं:
- नियम-विशिष्ट सीमाएं
environment_group()
/environment()
- प्लैटफ़ॉर्म के कंट्रोल
किसी खास नियम से जुड़ी पाबंदियों का इस्तेमाल, ज़्यादातर Google में Java के नियमों के लिए किया जाता है; वे हैं
बाहर जाते हैं और वे Basel में उपलब्ध नहीं हैं, लेकिन सोर्स कोड हो सकता है
शामिल करता है. इसे नियंत्रित करने वाली विशेषता को कहा जाता है
constraints=
एनवायरमेंट_ग्रुप() और एनवायरमेंट()
ये नियम, लेगसी तकनीक हैं. इनका ज़्यादा से ज़्यादा इस्तेमाल नहीं किया जाता.
सभी बिल्ड नियम बता सकते हैं कि कौनसे "एनवायरमेंट" बनाए जा सकते हैं, जहां
"एनवायरमेंट" environment()
नियम का उदाहरण है.
किसी नियम के लिए काम करने वाले एनवायरमेंट तय करने के कई तरीके हैं:
restricted_to=
एट्रिब्यूट का इस्तेमाल करके. यह सबसे आसान प्रोसेस है, स्पेसिफ़िकेशन; यह एनवायरमेंट के उन सटीक सेट के बारे में बताता है जो नियम में काम करता है इस समूह के लिए.compatible_with=
एट्रिब्यूट का इस्तेमाल करके. इससे एनवायरमेंट को नियम के तौर पर सेट किया जाता है "मानक" के अलावा का समर्थन करता है जो इन एनवायरमेंट की मदद से काम करते हैं डिफ़ॉल्ट.- पैकेज-लेवल एट्रिब्यूट
default_restricted_to=
औरdefault_compatible_with=
. environment_group()
नियमों में डिफ़ॉल्ट स्पेसिफ़िकेशन के ज़रिए. कई एनवायरमेंट, थीम के हिसाब से मिलते-जुलते ग्रुप के ग्रुप से जुड़ा होता है. जैसे, "सीपीयू" आर्किटेक्चर", "JDK वर्शन" या "मोबाइल ऑपरेटिंग सिस्टम"). कॉन्टेंट बनाने एनवायरमेंट ग्रुप की परिभाषा में, इनमें से कौनसे एनवायरमेंट शामिल हैं "डिफ़ॉल्ट" पर सेट होना चाहिए अगर नियम, शर्तें,restricted_to=
/environment()
एट्रिब्यूट. ऐसा नियम एट्रिब्यूट सभी डिफ़ॉल्ट को इनहेरिट करते हैं.- नियम की कैटगरी डिफ़ॉल्ट के ज़रिए. यह सभी के लिए ग्लोबल डिफ़ॉल्ट को बदल देता है
दिए गए नियम की क्लास के इंस्टेंस. उदाहरण के लिए, इसका इस्तेमाल यह बनाने के लिए किया जा सकता है
हर इंस्टेंस साफ़ तौर पर दिए बिना, जांच किए जा सकने वाले सभी
*_test
नियम एलान करेंगे.
environment()
को सामान्य नियम के तौर पर लागू किया गया है, जबकि environment_group()
दोनों Target
की सब-क्लास है, लेकिन Rule
(EnvironmentGroup
) नहीं है और
ऐसा फ़ंक्शन जो Starlark में डिफ़ॉल्ट रूप से उपलब्ध होता है
(StarlarkLibrary.environmentGroup()
) जो आखिर में एक छोटा नाम बनाता है
टारगेट. यह चक्रीय डिपेंडेंसी से बचने के लिए किया जाता है. इसकी वजह यह है कि हर
एनवायरमेंट को यह बताना ज़रूरी है कि वह किस एनवायरमेंट ग्रुप से जुड़ा है और हर
एनवायरमेंट ग्रुप को अपने डिफ़ॉल्ट एनवायरमेंट का एलान करना होगा.
बिल्ड को एक खास एनवायरमेंट तक सीमित किया जा सकता है. इसके लिए,
--target_environment
कमांड लाइन का विकल्प.
कंस्ट्रेंट जांच को लागू करने की सेटिंग
RuleContextConstraintSemantics
और TopLevelConstraintSemantics
.
प्लैटफ़ॉर्म के कंट्रोल
मौजूदा "आधिकारिक" यह बताने का तरीका कि टारगेट कौनसे प्लैटफ़ॉर्म के साथ काम करता है इसके लिए, उन कंस्ट्रेंट का इस्तेमाल किया जाता है जिनका इस्तेमाल टूलचेन और प्लैटफ़ॉर्म के लिए किया जाता है. पुल के अनुरोध की समीक्षा की जा रही है #10945.
किसको दिखाई दे
अगर कई डेवलपर (जैसे, Google) के साथ मिलकर बड़े कोड बेस पर काम किया जाता है, तो आप इस बात का ध्यान रखना चाहते हैं कि आपके कोड. अगर ऐसा नहीं है, तो हाइरम के नियम के मुताबिक, लोग ऐसी गतिविधियों पर भरोसा करेंगे जिन्हें आपने लागू किया है विवरण.
बेज़ल इसका समर्थन विज़िबिलिटी नाम की तकनीक की मदद से करता है: आपके पास यह एलान करने का विकल्प है कि कोई विशेष लक्ष्य केवल visibility एट्रिब्यूट का इस्तेमाल करके सबमिट करें. यह एट्रिब्यूट थोड़ा खास है, क्योंकि इसमें लेबल की एक लिस्ट होती है, लेकिन ये लेबल किसी पॉइंटर के बजाय, पैकेज नामों पर पैटर्न के तौर पर एन्कोड कर सकते हैं तय करें. (हां, यह डिज़ाइन में एक गलती है.)
यह इन जगहों पर लागू किया जाता है:
RuleVisibility
इंटरफ़ेस, 'किसको दिखे' सेटिंग का एलान करता है. यह काम कर सकता है एक स्थायी (पूरी तरह से सार्वजनिक या पूरी तरह से निजी) या लेबल की सूची होनी चाहिए.- लेबल या तो पैकेज समूह (पैकेज की पहले से तय सूची) का संदर्भ दे सकते हैं,
पैकेज (
//pkg:__pkg__
) या पैकेज के सब-ट्री (//pkg:__subpackages__
). यह कमांड लाइन सिंटैक्स से अलग है. जो//pkg:*
या//pkg/...
का इस्तेमाल करता है. - पैकेज ग्रुप को उनके टारगेट (
PackageGroup
) के तौर पर लागू किया गया है और कॉन्फ़िगर किया गया टारगेट (PackageGroupConfiguredTarget
). हम शायद अगर हम चाहें, तो इनकी जगह आसान नियमों का इस्तेमाल कर सकते हैं. उनका लॉजिक लागू किया जाता है इसकी मदद से:PackageSpecification
, जो एक पैटर्न, जैसे कि//pkg/...
;PackageGroupContents
, जो इसके समान है किसी एकpackage_group
केpackages
एट्रिब्यूट को; औरPackageSpecificationProvider
, जोpackage_group
और यह ट्रांज़िट स्थिति वालाincludes
है. - विज़िबिलिटी लेबल की सूचियों को डिपेंडेंसी में तब बदला जाता है, जब
DependencyResolver.visitTargetVisibility
और कुछ अन्य कई तरह के प्रॉडक्ट जगहें. - असली जांच इतने समय में की जाती है
CommonPrerequisiteValidator.validateDirectPrerequisiteVisibility()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
नेस्ट किए गए सेट
कॉन्फ़िगर किया गया टारगेट, अक्सर अपनी डिपेंडेंसी से फ़ाइलों के सेट को एग्रीगेट करता है, अपनी क्वेरी जोड़ता है और एग्रीगेट सेट को ट्रांज़िटिव जानकारी देने वाले में रैप कर देता है, कॉन्फ़िगर किए गए टारगेट से भी ऐसा ही हो सकता है. उदाहरण:
- बिल्ड के लिए इस्तेमाल की जाने वाली C++ हेडर फ़ाइलें
- ऐसी ऑब्जेक्ट फ़ाइलें जो
cc_library
के ट्रांज़िटिव क्लोज़र दिखाती हैं - .Jer फ़ाइलों का वह सेट जिसे Java नियम के लिए, क्लासपाथ पर होना चाहिए कंपाइल या रन
- Python नियमों के ट्रांज़िटिव क्लोज़र में Python फ़ाइलों का सेट
उदाहरण के लिए, List
या Set
का इस्तेमाल करके अगर हमने यह आसान तरीके से किया, तो आखिर में
द्विघात मेमोरी का इस्तेमाल: अगर N नियमों की शृंखला है और हर नियम एक
फ़ाइल होगी, तो हमारे पास 1+2+...+N क्लेक्शन सदस्य होंगे.
इस समस्या से निपटने के लिए, हमने
NestedSet
. यह एक ऐसा डेटा स्ट्रक्चर है जिसमें अन्य NestedSet
शामिल होते हैं
अपने अलग-अलग इंस्टेंस और कुछ सदस्यों के लिए उपलब्ध होता है. इससे, एक तय किया हुआ असाइकिक ग्राफ़ बनता है
सेट का. इन्हें बदला नहीं जा सकता और सदस्यों को फिर से इस्तेमाल किया जा सकता है. हम तय करते हैं:
एक से ज़्यादा बार सामान लौटाने का ऑर्डर (NestedSet.Order
): पहले से ऑर्डर करना, बाद में ऑर्डर करना, टोपोलॉजिकल
(नोड हमेशा अपने पूर्वजों के बाद आता है) और "कोई परवाह नहीं करें, लेकिन इसे
एक जैसा अनुभव देगा".
स्टारलार्क में इसी डेटा स्ट्रक्चर को depset
कहा जाता है.
आर्टफ़ैक्ट और कार्रवाइयां
असल बिल्ड में कमांड का एक सेट होता है, जिसे जनरेट करने के लिए चलाना ज़रूरी होता है
उपयोगकर्ता की पसंद का आउटपुट. निर्देशों को इन इंस्टेंस के तौर पर दिखाया जाता है:
क्लास Action
और फ़ाइलों को क्लास के इंस्टेंस के तौर पर दिखाया जाता है
Artifact
. उन्हें द्विपक्षीय, निर्देशित, असाइकिक ग्राफ़ में व्यवस्थित किया जाता है, जिसे
"ऐक्शन ग्राफ़".
आर्टफ़ैक्ट दो तरह के होते हैं: सोर्स आर्टफ़ैक्ट (एक उपलब्ध आर्टफ़ैक्ट बेज़ल (जिन्हें पूरा करना ज़रूरी है) और जनरेट किए गए आर्टफ़ैक्ट बनाया गया है). प्रॉडक्ट से मिले आर्टफ़ैक्ट कई तरह के हो सकते हैं:
- **सामान्य आर्टफ़ैक्ट. **ये कंप्यूटिंग करके अप-टू-डेट होते हैं अपने चेकसम को, शॉर्टकट के तौर पर mtime के साथ; हम फ़ाइल की जांच नहीं करते, अगर उसकी ctime नहीं बदला है.
- सिमलिंक से जुड़े ऐसे आर्टफ़ैक्ट जो ठीक नहीं हुए हैं. इनके ज़रिए अप-टू-डेट होने की जांच की जाती है Readlink() को कॉल किया जा रहा है. आम तौर पर इस्तेमाल होने वाले आर्टफ़ैक्ट के उलट, आम तौर पर ये आर्टफ़ैक्ट, सिमलिंक. आम तौर पर इसका इस्तेमाल तब किया जाता है, जब कोई व्यक्ति कुछ फ़ाइलों को किसी तरह का संग्रह.
- पेड़ों से जुड़े आर्टफ़ैक्ट. ये सिंगल फ़ाइलें नहीं, बल्कि डायरेक्ट्री ट्री हैं. वे
फ़ाइलों के सेट और उनके
कॉन्टेंट. इन्हें
TreeArtifact
के तौर पर दिखाया जाता है. - कॉन्सटेंट मेटाडेटा के आर्टफ़ैक्ट. इन आर्टफ़ैक्ट में किए जाने वाले बदलाव फिर से बनाएं. इसका इस्तेमाल खास तौर पर बिल्ड स्टैंप की जानकारी के लिए किया जाता है. हमें क्योंकि मौजूदा समय बदल चुका है.
इसकी कोई बुनियादी वजह नहीं है कि सोर्स आर्टफ़ैक्ट, ट्री आर्टफ़ैक्ट नहीं हो सकते या
अभी तक हल नहीं किए गए सिमलिंक आर्टफ़ैक्ट
हालांकि -- BUILD
फ़ाइल में सोर्स डायरेक्ट्री का रेफ़रंस देना इनमें से एक है
बेज़ल के साथ, लंबे समय से चली आ रही गलत जानकारी से जुड़ी कुछ समस्याएं हैं; हमारे पास
इस तरह के काम को लागू करना
BAZEL_TRACK_SOURCE_DIRECTORIES=1
जेवीएम प्रॉपर्टी)
Artifact
में एक बड़ा नाम बिचौलिए होता है. इन्हें Artifact
से दिखाया जाता है
इंस्टेंस जो MiddlemanAction
के आउटपुट हैं. इनका इस्तेमाल इन कामों में किया जाता है
कुछ चीज़ों का खास ध्यान रखें:
- बिचौलिए को एग्रीगेट किया जाता है. आर्टफ़ैक्ट को एक ग्रुप में रखा जाता है. ऐसा इसलिए कि अगर बहुत सारी कार्रवाइयों में इनपुट के एक जैसे बड़े सेट का इस्तेमाल किया जाता है, तो हमारे पास N*M नहीं होता निर्भरता के किनारे, सिर्फ़ N+M (उन्हें नेस्ट किए गए सेट से बदला जा रहा है)
- शेड्यूल करने वाले डिपेंडेंसी बिचौलिए, यह पक्का करते हैं कि कोई कार्रवाई दूसरी से पहले चले.
ज़्यादातर इनका इस्तेमाल लिंटिंग के लिए किया जाता है. साथ ही, C++ कंपाइलेशन के लिए भी किया जाता है (देखें
ज़्यादा जानकारी के लिए
CcCompilationContext.createMiddleman()
) - रनफ़ाइल मिडलमैन का इस्तेमाल यह पक्का करने के लिए किया जाता है कि रनफ़ाइल ट्री मौजूद है या नहीं इसके लिए, अलग से आउटपुट मेनिफ़ेस्ट और हर सिंगल आर्टफ़ैक्ट होता है, जिसे रनफ़ाइल ट्री के ज़रिए रेफ़र किया जाता है.
कार्रवाइयों को एक निर्देश के रूप में सबसे अच्छी तरह समझा जाता है, जिसे चलाने की ज़रूरत होती है, वातावरण की ज़रूरत होती है और उससे मिलने वाले आउटपुट का सेट मिलता है. मुख्य रूप से, यहां दी गई चीज़ें किसी कार्रवाई की जानकारी के कॉम्पोनेंट:
- वह कमांड लाइन जिसे चलाना है
- इसके लिए ज़रूरी इनपुट आर्टफ़ैक्ट
- वे एनवायरमेंट वैरिएबल जिन्हें सेट करना ज़रूरी है
- ऐसी व्याख्याएं जो उस परिवेश (जैसे कि प्लैटफ़ॉर्म) की जानकारी देती हैं, जिसमें उसे चलाना आवश्यक है \
कुछ अन्य खास मामले भी होते हैं, जैसे कि ऐसी फ़ाइल लिखना जिसका कॉन्टेंट
सबसे ज़्यादा जानकारी है. ये AbstractAction
की सब-क्लास हैं. ज़्यादातर कार्रवाइयाँ ये हैं
SpawnAction
या StarlarkAction
(समान रूप से, उन्हें ऐसा नहीं होना चाहिए
अलग-अलग क्लास के लिए काम करता है), हालांकि Java और C++ के पास उनके अपने ऐक्शन टाइप हैं
(JavaCompileAction
, CppCompileAction
, और CppLinkAction
).
आखिर में हम सब कुछ SpawnAction
में ले जाना चाहते हैं; JavaCompileAction
है
काफ़ी करीब है, लेकिन .d फ़ाइल पार्सिंग और
स्कैनिंग शामिल है.
कार्रवाई ग्राफ़ में ज़्यादातर "एम्बेड किया गया" होता है में डाल सकते हैं: सैद्धांतिक रूप से,
किसी कार्रवाई के लागू होने को
ActionExecutionFunction
. किसी ऐक्शन ग्राफ़ डिपेंडेंसी एज से किसी
स्काईफ़्रेम डिपेंडेंसी एज की जानकारी इसमें दी गई है
ActionExecutionFunction.getInputDeps()
और Artifact.key()
में से कुछ
स्काईफ़्रेम के किनारों की संख्या कम रखने के लिए ऑप्टिमाइज़ेशन:
- हासिल किए गए आर्टफ़ैक्ट के अपने
SkyValue
नहीं होते हैं. इसके बजाय,Artifact.getGeneratingActionKey()
का इस्तेमाल उसे जनरेट करने वाला ऐक्शन - नेस्ट किए गए सेट की अपनी स्काईफ़्रेम कुंजी होती है.
शेयर की गई कार्रवाइयां
कुछ कार्रवाइयां, कॉन्फ़िगर किए गए कई टारगेट से जनरेट होती हैं; स्टारलार्क के नियम सीमित है, क्योंकि वे सिर्फ़ यहां दी गई कार्रवाइयों को कॉन्फ़िगरेशन और उनके पैकेज के अनुसार तय की जाती है (लेकिन फिर भी, एक ही पैकेज में मौजूद नियमों में टकराव हो सकता है), लेकिन Java में लागू किए गए नियम, से मिले आर्टफ़ैक्ट को कहीं भी ऐक्सेस किया जा सकता है.
इसे एक गलत सुविधा माना जाता है, लेकिन इससे छुटकारा पाना वाकई मुश्किल है क्योंकि इससे निष्पादन समय में भारी बचत होती है, उदाहरण के लिए, सोर्स फ़ाइल को किसी तरह प्रोसेस करना होगा और वह फ़ाइल एक से ज़्यादा नियम (हैंडवेव-हैंडवेव) के लिए एक से ज़्यादा नियम सेट किए जा सकते हैं. इसके लिए कुछ रैम की लागत आती है: हर एक रैम शेयर की गई कार्रवाई के इंस्टेंस को मेमोरी में अलग से सेव करना होगा.
अगर दो कार्रवाइयां एक ही आउटपुट फ़ाइल जनरेट करती हैं, तो वे बिलकुल एक जैसी होनी चाहिए:
एक जैसे इनपुट, एक जैसे आउटपुट हों और एक ही कमांड लाइन को चलाते हों. यह
इक्वलेंस रिलेशन को Actions.canBeShared()
में लागू किया गया है और यह
और हर कार्रवाई को देखकर, विश्लेषण और उसे लागू करने के चरणों के बीच की पुष्टि करें.
इसे SkyframeActionExecutor.findAndStoreArtifactConflicts()
में लागू किया गया है
और बेज़ेल की उन चंद जगहों में से एक है जहां "दुनिया भर के" का व्यू
बिल्ड.
एक्ज़ीक्यूशन का चरण
ऐसा तब होता है, जब बेज़ल असल में बिल्ड ऐक्शन चलाना शुरू कर देता है, जैसे कि ऐसे निर्देश जो आउटपुट देती है.
विश्लेषण के चरण में बेज़ल सबसे पहले यह पता लगा लेते हैं कि
कलाकृतियां बनाने की ज़रूरत है. इसके लिए लॉजिक को कोड में बदला गया है
TopLevelArtifactHelper
; मोटे तौर पर कहें, तो यह filesToBuild
कमांड लाइन पर कॉन्फ़िगर किए गए टारगेट और खास आउटपुट के कॉन्टेंट
यह बताने के लिए समूह का इस्तेमाल करें कि "अगर यह टारगेट कमांड पर है
इन आर्टफ़ैक्ट को बनाना चाहते हैं".
अगला चरण एक्ज़ीक्यूशन रूट बनाना है. ऐसा इसलिए हुआ, क्योंकि Baज़र के पास पढ़ने का विकल्प होता है
फ़ाइल सिस्टम (--package_path
) में अलग-अलग जगहों के सोर्स पैकेज हैं,
इसमें पूरे सोर्स ट्री के साथ स्थानीय तौर पर एक्ज़ीक्यूट की जाने वाली कार्रवाइयां दी जानी चाहिए. यह है
इसे SymlinkForest
क्लास की मदद से मैनेज किया जाता है. साथ ही, यह हर टारगेट का ध्यान रखता है
इसका इस्तेमाल, विश्लेषण के चरण में किया जाता है. साथ ही, एक ऐसा सिंगल डायरेक्ट्री ट्री तैयार किया जाता है जो सिमलिंक करता है
इस्तेमाल किए गए टारगेट के साथ हर पैकेज को उसकी असल जगह से हटा दिया जाता है. दूसरा विकल्प यह होता
निर्देशों का सही पाथ पास करें (--package_path
को ध्यान में रखते हुए).
ऐसा करना ज़रूरी नहीं है, क्योंकि:
- जब किसी पैकेज को पैकेज पाथ से दूसरी जगह ले जाया जाता है, तब यह ऐक्शन कमांड लाइन बदल देता है किसी अन्य में जाना (यह आम बात है)
- अगर किसी कार्रवाई को अगर कार्रवाई के बजाय किसी दूसरी जगह से चलाया जाता है, तो इससे अलग कमांड लाइन दिखती है यह स्थानीय तौर पर चलता है
- इसे इस्तेमाल किए जा रहे टूल के लिए खास तौर पर कमांड लाइन ट्रांसफ़ॉर्मेशन की ज़रूरत होती है (Java classpath और C++ में शामिल पाथ के बीच के अंतर पर ध्यान दें)
- किसी कार्रवाई की कमांड लाइन बदलने से उसकी ऐक्शन कैश एंट्री अमान्य हो जाती है
--package_path
को धीरे-धीरे और धीरे-धीरे बंद किया जा रहा है
इसके बाद, बेज़ल ऐक्शन ग्राफ़ (द्विपक्षीय, निर्देशित ग्राफ़) को ट्रैक करना शुरू कर देता है
इसमें कार्रवाइयां और उनके इनपुट और आउटपुट आर्टफ़ैक्ट शामिल होते हैं.
हर कार्रवाई के एक्ज़ीक्यूशन को SkyValue
के इंस्टेंस से दिखाया जाता है
क्लास ActionExecutionValue
.
चूंकि कोई कार्रवाई चलाना महंगा है, इसलिए हमारे पास कैश मेमोरी की कुछ परतें होती हैं, जो स्काईफ़्रेम के पीछे काम करेगा:
ActionExecutionFunction.stateMap
में ऐसा डेटा मौजूद है जिससे SkyFrame को रीस्टार्ट किया जाता हैActionExecutionFunction
में से सस्ता- लोकल ऐक्शन कैश मेमोरी में, फ़ाइल सिस्टम की स्थिति के बारे में डेटा मौजूद होता है
- रिमोट एक्ज़ीक्यूशन सिस्टम में आम तौर पर अपनी कैश मेमोरी भी होती है
लोकल ऐक्शन कैश
यह कैश मेमोरी, Skyframe के पीछे मौजूद लेयर है; भले ही, किसी कार्रवाई की वजह से फिर से लागू होता है, तो लोकल ऐक्शन कैश में यह अब भी हिट हो सकता है. यह लोकल फ़ाइल सिस्टम की स्थिति दिखाता है और यह डिस्क में सीरियल होता है. इसका मतलब है कि जब कोई नया Basel सर्वर शुरू करता है, तो उसे लोकल ऐक्शन कैश मेमोरी मिल सकती है हिट के साथ यहां दिए गए लिंक में बदलाव कर सकते हैं, भले ही स्काईफ़्रेम ग्राफ़ खाली हो.
इस कैश मेमोरी में, इस तरीके का इस्तेमाल करके हिट के लिए जांच की जाती है
ActionCacheChecker.getTokenIfNeedToExecute()
अपने नाम के उलट, यह निकाले गए आर्टफ़ैक्ट के पाथ से लेकर गतिविधि के बारे में बताया. इस कार्रवाई का ब्यौरा है:
- इसकी इनपुट और आउटपुट फ़ाइलों का सेट और उनके चेकसम
- इसकी "ऐक्शन बटन" होती है, जो आम तौर पर वह कमांड लाइन होती है जिसे एक्ज़ीक्यूट किया गया था, लेकिन
आम तौर पर, उन सभी चीज़ों का प्रतिनिधित्व करता है जिन्हें
इनपुट फ़ाइलें (जैसे,
FileWriteAction
के लिए, यह डेटा का चेकसम है) जिसे लिखा गया है)
इसमें बेहद एक्सपेरिमेंटल "टॉप-डाउन ऐक्शन कैश" भी है जो अब भी कम है डेवलपमेंट, जो ट्रांज़िटिव हैश का इस्तेमाल करके कैश मेमोरी में जाने से बचाता है बार.
इनपुट डिस्कवरी और इनपुट की काट-छांट
कुछ कार्रवाइयां सिर्फ़ इनपुट का सेट रखने से ज़्यादा मुश्किल होती हैं. इसमें बदलाव किसी कार्रवाई के इनपुट का सेट दो तरह के होते हैं:
- कोई कार्रवाई लागू होने से पहले नए इनपुट खोज सकती है या तय कर सकती है कि कुछ
इसके लिए इनपुट ज़रूरी नहीं हैं. कैननिकल का उदाहरण C++ है.
जहां इस बारे में सोच-समझकर अनुमान लगाना बेहतर होगा कि कौन सी हेडर फ़ाइल C++
फ़ाइल अपने अंतरंग रूप से बंद होने का संदर्भ देती है, ताकि हम
रिमोट एक्ज़िक्यूटर को फ़ाइल भेजें; इसलिए, हमारे पास यह विकल्प है कि आप
हेडर फ़ाइल को "इनपुट" के तौर पर रखता है, लेकिन सोर्स फ़ाइल को ट्रांज़िट के लिए स्कैन करता है
हेडर फ़ाइलों को शामिल करें और सिर्फ़ उन हेडर फ़ाइलों को ऐसे इनपुट के तौर पर मार्क करें
#include
स्टेटमेंट में बताया गया है (हम बढ़ा-चढ़ाकर पेश करते हैं, ताकि हमें एक पूरा C प्रीप्रोसेसर लागू करें) यह विकल्प फ़िलहाल "गलत" बेज़ल में है और सिर्फ़ Google में इसका इस्तेमाल किया जाता है. - किसी कार्रवाई के दौरान यह महसूस हो सकता है कि कुछ फ़ाइलों का इस्तेमाल नहीं किया गया था. तय सीमा में C++ में, इसे ".d फ़ाइलें" कहा जाता है: कंपाइलर बताता है कि कौनसी हेडर फ़ाइलें का इस्तेमाल कुछ चीज़ों के लिए किया जाता है. ऐसा इसलिए भी किया जाता है, ताकि Make की तुलना में बेज़ल इस तथ्य का इस्तेमाल करते हैं. इससे बेहतर शामिल करें स्कैनर की तुलना में अनुमान लगाया जाता है, क्योंकि यह कंपाइलर पर निर्भर करता है.
इन्हें कार्रवाई पर दिए गए तरीकों का इस्तेमाल करके लागू किया जाता है:
Action.discoverInputs()
पर कॉल किया गया है. इसकी वैल्यू, नेस्ट की गई वैल्यू में दी गई होनी चाहिए ऐसे दस्तावेज़ जिन्हें सबमिट करना ज़रूरी है. ये सोर्स आर्टफ़ैक्ट होने चाहिए ताकि कार्रवाई ग्राफ़ में कोई भी डिपेंडेंसी एज न हो जिसमें कॉन्फ़िगर किए गए टारगेट ग्राफ़ में मिलता-जुलता है.Action.execute()
को कॉल करके यह कार्रवाई की जाती है.Action.execute()
के खत्म होने पर, इस कार्रवाई को कॉल किया जा सकता हैAction.updateInputs()
ने बेज़ल को बताया कि इसके सभी इनपुट की ज़रूरत नहीं है. इससे इंक्रीमेंटल बिल्ड गलत हो सकता है, अगर इस्तेमाल किया गया इनपुट 'इस्तेमाल नहीं किया गया' के तौर पर मार्क किया गया.
जब किसी ऐक्शन कैश मेमोरी से, नए ऐक्शन इंस्टेंस पर हिट मिलता है (जैसे, बनाया गया
एक सर्वर के रीस्टार्ट हो जाने के बाद), Basel updateInputs()
को खुद कॉल करता है, ताकि
इनपुट की खोज और पहले की गई काट-छांट से मिले नतीजों के बारे में पता चलता है.
Starlark की कार्रवाइयों की मदद से, कुछ इनपुट को 'इस्तेमाल नहीं किया गया' के तौर पर एलान किया जा सकता है
इसका unused_inputs_list=
आर्ग्युमेंट इस्तेमाल किया जा रहा है
ctx.actions.run()
.
कार्रवाइयां करने के अलग-अलग तरीके: रणनीतियां/ActionContexts
कुछ कार्रवाइयां अलग-अलग तरीकों से चलाई जा सकती हैं. उदाहरण के लिए, कमांड लाइन
जिन्हें स्थानीय तौर पर, स्थानीय तौर पर, लेकिन कई तरह के सैंडबॉक्स में या किसी दूसरी जगह से चलाया जा सकता है. कॉन्टेंट बनाने
इसे धारण करने वाली अवधारणा को ActionContext
(या Strategy
कहा जाता है, क्योंकि
नाम बदलने के साथ ही सफलतापूर्वक आधा काम किया गया...)
किसी कार्रवाई कॉन्टेक्स्ट का लाइफ़ साइकल इस तरह से होता है:
- ऑटोमेशन लागू होने का चरण शुरू होने पर,
BlazeModule
इंस्टेंस पूछा जाता है कार्रवाई के बारे में है. ऐसा इसके कंस्ट्रक्टर में होता हैExecutionTool
. कार्रवाई के कॉन्टेक्स्ट के टाइप की पहचान JavaClass
की मदद से की जाती है जोActionContext
के सब-इंटरफ़ेस को दिखाता है और इंटरफ़ेस करें, जिसे कार्रवाई संदर्भ को लागू करना होगा. - उपलब्ध विकल्पों में से कार्रवाई का सही संदर्भ चुना जाता है और
ActionExecutionContext
औरBlazeExecutor
पर फ़ॉरवर्ड किया गया . ActionExecutionContext.getContext()
का इस्तेमाल करके कार्रवाइयों का अनुरोध करना औरBlazeExecutor.getStrategy()
(असल में ऐसा करने का सिर्फ़ एक ही तरीका होना चाहिए इसे...)
रणनीतियां अपना काम करने के लिए, दूसरी रणनीतियों का इस्तेमाल कर सकती हैं; किसी गड़बड़ी की वजह से उदाहरण के लिए, जो डाइनैमिक रणनीति में स्थानीय और रिमोट, दोनों तरीकों से कार्रवाइयां शुरू करती है तो जो भी पहले खत्म होता है उसका उपयोग करता है.
एक ध्यान देने वाली रणनीति वह है जो कर्मचारियों के काम करने की स्थायी प्रक्रिया को लागू करती है
(WorkerSpawnStrategy
). आइडिया यह है कि कुछ टूल को शुरू करने में ज़्यादा समय लगता है
और इसलिए, इसे ऐक्शन के बीच में फिर से इस्तेमाल किया जाना चाहिए, न कि इसके लिए फिर से शुरू किया जाना चाहिए
हर कार्रवाई (यह संभावित रूप से सटीक होने की समस्या का प्रतिनिधित्व करती है, क्योंकि बेज़ल
वर्कर प्रोसेस के वादे पर निर्भर करता है कि वह
अलग-अलग अनुरोधों के बीच स्थिति)
टूल बदलने पर, वर्कर प्रोसेस को फिर से शुरू करना होगा. चाहे एक कर्मचारी
का पता लगाने के लिए, इस्तेमाल किए जाने वाले टूल के लिए चेकसम की गणना की जाती है
WorkerFilesHash
. इसके लिए, हमें यह पता होना चाहिए कि कार्रवाई के किस इनपुट से
किसी टूल का हिस्सा होते हैं और जो इनपुट दिखाते हैं; इसे क्रिएटर तय करता है
कार्रवाई की हैं: Spawn.getToolFiles()
और Spawn
की रनफ़ाइल
टूल के हिस्से के तौर पर गिना जाता है.
रणनीतियों (या कार्रवाई से जुड़े कॉन्टेक्स्ट!) के बारे में ज़्यादा जानकारी:
- कार्रवाइयों के लिए अलग-अलग रणनीतियों के बारे में जानकारी उपलब्ध है यहां पढ़ें.
- डाइनैमिक रणनीति के बारे में जानकारी, जहां हम दोनों ऐक्शन करते हैं स्थानीय तौर पर या किसी दूसरे डिवाइस से देखा जा सकता है कि यहां पढ़ें.
- स्थानीय तौर पर कार्रवाइयों को लागू करने से जुड़ी बारीकियों के बारे में जानकारी उपलब्ध है यहां पढ़ें.
स्थानीय संसाधन मैनेजर
Baze एक साथ कई कार्रवाइयां कर सकता है. लोकल ऐक्शन की संख्या अलग-अलग कामों के लिए एक साथ चलने चाहिए: जितने ज़्यादा रिसॉर्स होंगे, कार्रवाई ज़रूरी है, इससे बचने के लिए कम इंस्टेंस एक ही समय पर चलने चाहिए लोकल मशीन को ओवरलोड कर रहा है.
इसे क्लास ResourceManager
में लागू किया गया है: हर कार्रवाई
के रूप में आवश्यक स्थानीय संसाधनों का एक अनुमान दिखाया गया है.
ResourceSet
इंस्टेंस (सीपीयू और रैम). इसके बाद, जब कार्रवाई से जुड़े कॉन्टेक्स्ट के आधार पर कोई कार्रवाई की जाती है
जिसे स्थानीय संसाधनों की ज़रूरत है, वे ResourceManager.acquireResources()
को कॉल करते हैं
साथ ही, उन्हें तब तक ब्लॉक किया जाता है, जब तक ज़रूरी संसाधन उपलब्ध नहीं होते.
स्थानीय संसाधन प्रबंधन का ज़्यादा विस्तृत विवरण उपलब्ध है यहां पढ़ें.
आउटपुट डायरेक्ट्री का स्ट्रक्चर
हर कार्रवाई के लिए, आउटपुट डायरेक्ट्री में एक अलग जगह होनी चाहिए, जहां वह इसके आउटपुट. आम तौर पर, हासिल किए गए आर्टफ़ैक्ट की जगह इस तरह होती है:
$EXECROOT/bazel-out/<configuration>/bin/<package>/<artifact name>
किसी खास डायरेक्ट्री से जुड़ी डायरेक्ट्री का नाम कॉन्फ़िगरेशन निर्धारित है? पसंद की दो प्रॉपर्टी अलग-अलग होती हैं:
- अगर एक ही बिल्ड में दो कॉन्फ़िगरेशन हो सकते हैं, तो उनमें ये ज़रूर होने चाहिए ताकि दोनों के पास समान वर्शन का अपना वर्शन हो कार्रवाई; नहीं तो, अगर दोनों कॉन्फ़िगरेशन किसी की पंक्ति में कोई कार्रवाई की है, जो एक ही आउटपुट फ़ाइल बनाती है, तो Baज़र को यह पता नहीं होता कि वह चुनने के लिए कार्रवाई ("कार्रवाई से जुड़ी समस्या")
- अगर दो कॉन्फ़िगरेशन "मोटे तौर पर" दिखाते हैं साथ ही, उन्हें यह भी लगता है कि एक ही नाम हो, ताकि एक पर की गई कार्रवाइयों को दूसरे के लिए फिर से इस्तेमाल किया जा सके, कमांड लाइन मेल खाती हैं: उदाहरण के लिए, कमांड लाइन के विकल्पों में बदलाव करके Java कंपाइलर का इस्तेमाल करने पर, C++ कंपाइलर फिर से नहीं चलाई जानी चाहिए.
अब तक हम इस समस्या को हल करने के लिए कोई ऐसा सैद्धांतिक तरीका नहीं बना पाए हैं कॉन्फ़िगरेशन ट्रिमिंग की समस्या से मेल खाती है. एक लंबी चर्चा में से विकल्प उपलब्ध हैं यहां पढ़ें. मुख्य समस्या वाले क्षेत्र स्टारलार्क नियम हैं (जिनके लेखक आम तौर पर बेज़ल से अच्छी तरह वाकिफ़ हैं) और पहलू, जो ऐसी चीज़ों का स्पेस है, जो "एक जैसा" आउटपुट फ़ाइल में सेव किया जाएगा.
मौजूदा तरीका यह है कि कॉन्फ़िगरेशन के लिए पाथ सेगमेंट यह है
कई सफ़िक्स के साथ <CPU>-<compilation mode>
को जोड़ा गया है, ताकि कॉन्फ़िगरेशन
Java में लागू किए गए ट्रांज़िशन की वजह से कोई समस्या नहीं होती है. इसके अलावा,
Starlark कॉन्फ़िगरेशन ट्रांज़िशन के सेट का चेकसम जोड़ा गया है, ताकि उपयोगकर्ता
कार्रवाई से जुड़े विवाद पैदा नहीं किए जा सकते. यह एकदम सही से बहुत दूर है. इसे इसमें लागू किया जाता है
OutputDirectories.buildMnemonic()
और हर कॉन्फ़िगरेशन फ़्रैगमेंट पर निर्भर करता है
आउटपुट डायरेक्ट्री के नाम में अपना हिस्सा जोड़कर.
जांच
Basel के पास, दौड़ने की जांचों को बेहतर तरीके से इस्तेमाल करने की सुविधा है. यह इनके साथ काम करता है:
- दूर से टेस्ट करना (अगर रिमोट एक्ज़ीक्यूशन बैकएंड उपलब्ध है)
- एक साथ कई बार टेस्ट करना (डिफ़्लेकिंग या टाइम इकट्ठा करने के लिए) डेटा)
- शार्डिंग टेस्ट (एक से ज़्यादा प्रोसेस पर एक ही टेस्ट में टेस्ट केस को अलग करना स्पीड के लिए)
- फ़्लेकी टेस्ट फिर से चलाना
- टेस्ट को टेस्ट सुइट में ग्रुप करना
टेस्ट, नियमित तौर पर कॉन्फ़िगर किए गए टारगेट होते हैं. इनमें TestProvider की जानकारी होती है, जो टेस्ट कैसे चलाया जाना चाहिए:
- ऐसे आर्टफ़ैक्ट जिनकी इमारत की जांच में टेस्ट कराया गया. यह "कैश मेमोरी" में
स्टेटस" ऐसी फ़ाइल जिसमें एक सीरियल
TestResultData
मैसेज है - टेस्ट कितनी बार चलाया जाना चाहिए
- उन शार्ड की संख्या जिन्हें टेस्ट में बांटा जाना चाहिए
- टेस्ट को चलाने के तरीके से जुड़े कुछ पैरामीटर, जैसे कि टेस्ट का टाइम आउट
यह तय करना कि कौनसे टेस्ट किए जाएं
कौनसे टेस्ट चलाए जाएं, यह तय करना एक लंबी प्रक्रिया है.
पहला, टारगेट पैटर्न पार्स करने के दौरान, टेस्ट सुइट को बार-बार बड़ा किया जाता है. कॉन्टेंट बनाने
TestsForTargetPatternFunction
में एक्सपैंशन लागू किया गया है. कुछ हद तक
हैरान करने वाली बात यह है कि अगर कोई टेस्ट सुइट, टेस्ट किसी भी टेस्ट का एलान नहीं करता है, तो
सभी टेस्ट को अपने पैकेज में शामिल करना चाहिए. इसे Package.beforeBuild()
में लागू करता है
सुइट के नियमों की जांच करने के लिए, $implicit_tests
नाम का इंप्लिसिट एट्रिब्यूट जोड़ना.
इसके बाद, साइज़, टैग, टाइम आउट, और भाषा के हिसाब से टेस्ट,
कमांड लाइन विकल्प. इसे TestFilter
में लागू किया गया है और इसे यहां से कॉल किया जाता है
टारगेट पार्स करने के दौरान TargetPatternPhaseFunction.determineTests()
और
नतीजा TargetPatternPhaseValue.getTestsToRunLabels()
में डाला जाता है. वजह
नियम के जिन एट्रिब्यूट को फ़िल्टर किया जा सकता है उन्हें कॉन्फ़िगर नहीं किया जा सकता
विश्लेषण चरण से पहले होता है, इसलिए कॉन्फ़िगरेशन
उपलब्ध हैं.
इसके बाद, इसे BuildView.createResult()
: टारगेट में प्रोसेस किया जाता है
विश्लेषण को फ़िल्टर करके बाहर कर दिया जाता है और टेस्ट को खास और
कुछ खास टेस्ट भी शामिल हैं. इसके बाद, उसे AnalysisResult
में डाला जाता है. इसका मतलब है कि
ExecutionTool
को पता है कि कौनसी जांच की जानी चाहिए.
इस विस्तृत प्रक्रिया में थोड़ी पारदर्शिता लाने के लिए, tests()
क्वेरी ऑपरेटर (TestsFunction
में लागू किया गया) की मदद से यह बताया जा सकता है कि कौनसे टेस्ट
जब कमांड लाइन पर कोई खास टारगेट तय किया गया हो, तब ट्रिगर होते हैं. यह समय है
यह सुविधा फिर से लागू की गई है. इसलिए, हो सकता है कि यह ऊपर दी गई जानकारी से अलग हो
अलग-अलग तरीके से मैनेज किया जा सकता है.
चल रही जांच
कैश मेमोरी की स्थिति बताने वाले आर्टफ़ैक्ट के लिए अनुरोध करके, जांच की जाती है. इसके बाद
नतीजे के तौर पर TestRunnerAction
लागू करता है, जो आखिर में
TestActionContext
को --test_strategy
कमांड लाइन विकल्प से चुना गया, जो
अनुरोध किए गए तरीके से जांच करता है.
टेस्ट एक ऐसे प्रोटोकॉल के मुताबिक किए जाते हैं जिसमें एनवायरमेंट वैरिएबल का इस्तेमाल होता है ताकि टेस्ट को यह बताया जा सके कि उनसे क्या उम्मीद की जाती है. बेज़ल क्या है, इसकी पूरी जानकारी टेस्ट से क्या उम्मीद की जा सकती है और Basel के टेस्ट उपलब्ध होने से क्या उम्मीद की जा सकती है यहां पढ़ें. इस आसान शब्दों में, 0 का एग्ज़िट कोड 0 का मतलब है, सफलता, किसी और का मतलब फ़ेलियर है.
कैश मेमोरी की स्थिति वाली फ़ाइल के अलावा, हर जांच की प्रक्रिया में
फ़ाइलें शामिल हैं. उन्हें "टेस्ट लॉग की डायरेक्ट्री" में रखा जाता है जिस सबडायरेक्ट्री को
टारगेट कॉन्फ़िगरेशन की आउटपुट डायरेक्ट्री का testlogs
:
test.xml
, JUnit शैली की एक्सएमएल फ़ाइल, जिसमें अलग-अलग टेस्ट केस की जानकारी दी गई है टेस्ट शार्डtest.log
, टेस्ट का कंसोल आउटपुट. stdout और stderr ये नहीं हैं अलग किया है.test.outputs
, "एलान नहीं की गई आउटपुट डायरेक्ट्री"; इसका इस्तेमाल टेस्ट में किया जाता है जो टर्मिनल पर प्रिंट किए गए कॉन्टेंट के साथ-साथ फ़ाइलें आउटपुट करना चाहते हों.
टेस्ट निष्पादन के दौरान दो चीज़ें हो सकती हैं, जो इस दौरान नहीं हो सकती नियमित लक्ष्य बनाना: विशेष परीक्षण निष्पादन और आउटपुट स्ट्रीमिंग.
कुछ जांचों को खास मोड में किया जाना चाहिए. उदाहरण के लिए, ये जांच के साथ
टेस्ट किए जा सकते हैं. इसके लिए, tags=["exclusive"]
को
या --test_strategy=exclusive
की मदद से टेस्ट करने के लिए, नियम की जांच करें . हर खास ऑफ़र
की जांच एक अलग Skyframe शुरू करने से की जाती है, जिसमें
"मुख्य" के बाद परीक्षण करें बिल्ड. इसे इसमें लागू किया जाता है
SkyframeExecutor.runExclusiveTest()
.
सामान्य कार्रवाइयों के उलट, जिनके टर्मिनल आउटपुट को कार्रवाई के समय डंप कर दिया जाता है
खत्म हो जाता है, तो उपयोगकर्ता स्ट्रीम किए जाने वाले टेस्ट के आउटपुट का अनुरोध कर सकता है, ताकि
लंबे समय से चल रहे टेस्ट की प्रोग्रेस के बारे में जानकारी पाएं. यह इस नियम के तहत तय किया जाता है:
--test_output=streamed
कमांड लाइन विकल्प और इसमें खास टेस्ट शामिल है
ताकि अलग-अलग टेस्ट के नतीजे एक ही जगह पर न आएं.
इसे सही नाम वाली StreamedTestOutput
क्लास में लागू किया जाता है और यह इनके काम करता है:
विचाराधीन परीक्षण की test.log
फ़ाइल में मतदान परिवर्तन और नए डंप करना
बाइट उसी टर्मिनल पर ले जाएं जहां Basel का नियम होता है.
लागू किए गए टेस्ट के नतीजे, इवेंट बस में उपलब्ध हैं. इसके लिए,
अलग-अलग इवेंट (जैसे TestAttempt
, TestResult
या TestingCompleteEvent
).
उन्हें बिल्ड इवेंट प्रोटोकॉल में भेजा जाता है और उन्हें कंसोल में भेज दिया जाता है
AggregatingTestListener
का बनाया.
कवरेज कलेक्शन
कवरेज को फ़ाइलों में LCOV फ़ॉर्मैट में की जाने वाली जांचों से रिपोर्ट किया जाता है
bazel-testlogs/$PACKAGE/$TARGET/coverage.dat
कवरेज इकट्ठा करने के लिए, हर जांच को एक ऐसी स्क्रिप्ट में रैप किया जाता है जिसे
collect_coverage.sh
यह स्क्रिप्ट कवरेज कलेक्शन को चालू करने के लिए टेस्ट एनवायरमेंट को सेट अप करती है और यह तय करें कि कवरेज रनटाइम के हिसाब से कवरेज फ़ाइलें कहां लिखी गई हों. इसके बाद, यह टेस्ट करता है. टेस्ट में कई सबप्रोसेस चल सकती हैं. इनमें ये शामिल हैं कई अलग-अलग प्रोग्रामिंग भाषाओं में लिखे गए पार्ट के लिए (अलग-अलग कवरेज कलेक्शन रनटाइम). रैपर स्क्रिप्ट रूपांतरण के लिए ज़िम्मेदार होती है होने पर, फ़ाइलों को LCOV फ़ॉर्मैट में बदल देगा और उन्हें एक साथ मर्ज कर देगा फ़ाइल से लिए जाते हैं.
collect_coverage.sh
का इंटरपोजिशन, टेस्ट रणनीतियों और
इस फ़ंक्शन को टेस्ट के इनपुट में collect_coverage.sh
को शामिल करना ज़रूरी है. यह है
इंप्लिसिट एट्रिब्यूट :coverage_support
से पूरा किया जाता है, जिसका समाधान
कॉन्फ़िगरेशन फ़्लैग --coverage_support
का मान (देखें
TestConfiguration.TestOptions.coverageSupport
)
कुछ भाषाएं ऑफ़लाइन इंस्ट्रुमेंटेशन करती हैं, जिसका मतलब है कि कवरेज कंपाइलेशन के समय इंस्ट्रुमेंटेशन जोड़ा जाता है (जैसे कि C++) और दूसरे इंस्ट्रुमेंट ऑनलाइन करते हैं इंस्ट्रुमेंटेशन का मतलब है कि कवरेज इंस्ट्रुमेंटेशन को निष्पादन के समय जोड़ा जाता है समय.
दूसरा अहम सिद्धांत है बेसलाइन कवरेज. यह एक लाइब्रेरी का कवरेज है,
बाइनरी का इस्तेमाल करें या टेस्ट करें कि उसमें कोई कोड नहीं चला है या नहीं. इसके समाधान की समस्या यह है कि अगर
की जांच कवरेज के रूप में करना है, तो
सभी टेस्ट का कवरेज होता है, क्योंकि हो सकता है कि बाइनरी में ऐसा कोड हो जो
का इस्तेमाल किसी भी टेस्ट में किया जा सकता है. इसलिए, हम ऐसा करते हैं कि हर
बाइनरी, जिसमें सिर्फ़ ऐसी फ़ाइलें होती हैं जिनके लिए हम कवरेज को बिना किसी कवर किए इकट्ठा करते हैं
लाइन. टारगेट के लिए बेसलाइन कवरेज फ़ाइल यह है
bazel-testlogs/$PACKAGE/$TARGET/baseline_coverage.dat
यह भी जनरेट होता है
परीक्षणों के अलावा बाइनरी और लाइब्रेरी के लिए भी उपलब्ध है, यदि आप
बेज़ल के लिए --nobuild_tests_only
फ़्लैग.
बेसलाइन कवरेज अभी उपलब्ध नहीं है.
हर नियम के लिए कवरेज कलेक्शन के लिए, हम फ़ाइलों के दो ग्रुप ट्रैक करते हैं: इंस्ट्रुमेंटेशन वाली फ़ाइलें और इंस्ट्रुमेंटेशन मेटाडेटा फ़ाइलों का सेट.
इंस्ट्रुमेंट वाली फ़ाइलों का सेट, इंस्ट्रुमेंट में सेव की गई फ़ाइलों का सेट है. इसके लिए ऑनलाइन कवरेज रनटाइम, इसका इस्तेमाल रनटाइम के दौरान यह तय करने के लिए किया जा सकता है कि किन फ़ाइलों को वाद्ययंत्र. इसका इस्तेमाल बेसलाइन कवरेज को लागू करने के लिए भी किया जाता है.
इंस्ट्रुमेंटेशन मेटाडेटा फ़ाइलों का सेट, उन अतिरिक्त फ़ाइलों का सेट है जिनकी एक टेस्ट को ज़रूरत होती है ताकि Basel की ज़रूरत के मुताबिक LCOV फ़ाइलें जनरेट की जा सकें. व्यावहारिक तौर पर इसमें यह शामिल है रनटाइम के हिसाब से बनाई गई फ़ाइलें; उदाहरण के लिए, gcc, कंपाइलेशन के दौरान .gcno फ़ाइलें बनाता है. इन्हें टेस्ट ऐक्शन के इनपुट के सेट में जोड़ा जाता है. हालांकि, ऐसा सिर्फ़ तब होगा, जब कवरेज मोड चालू किया गया.
कवरेज को इकट्ठा किया जा रहा है या नहीं, यह
BuildConfiguration
. यह उपयोगी है क्योंकि यह परीक्षण को बदलने का आसान तरीका है
के हिसाब से देख सकते हैं, लेकिन इसका मतलब यह भी है कि अगर
यह बिट फ़्लिप कर दिया जाता है, तो सभी टारगेट का फिर से विश्लेषण करने की ज़रूरत होती है (कुछ भाषाएं, जैसे
C++ के लिए, कंपाइलर के अलग-अलग विकल्पों की ज़रूरत होती है, ताकि कवरेज इकट्ठा किया जा सके.
यह इस समस्या को कुछ हद तक कम कर देता है. इसलिए, फिर से विश्लेषण करने की ज़रूरत होती है).
कवरेज से जुड़ी सहायता फ़ाइलें, सीधे तौर पर लेबल पर निर्भर करती हैं ताकि उन्हें शुरू करने की नीति से बदला जा सके. इस नीति की मदद से, जिनमें बेज़ल के अलग-अलग वर्शन के बीच फ़र्क़ होता है. आम तौर पर, ये को हटा दिया जाएगा और हमने उनमें से एक के आधार पर मानकों का पालन किया.
हम एक "कवरेज रिपोर्ट" भी जनरेट करते हैं इस सुविधा की मदद से,
बेज़ल इन्वेशन पर हर टेस्ट को शामिल करता है. इसे यह कंपनी मैनेज करती है
CoverageReportActionFactory
और BuildView.createResult()
से कॉल की जाती है . यह
:coverage_report_generator
पर देखकर, अपनी ज़रूरत के हिसाब से टूल को ऐक्सेस कर सकता है
पहले जांच की जाने वाली विशेषता का इस्तेमाल करें.
क्वेरी इंजन
बेज़ेल के पास छोटी सी भाषा अलग-अलग ग्राफ़ के बारे में कई चीज़ें पूछी जाती थीं. नीचे दिए गए क्वेरी के प्रकार ये रहे:
bazel query
का इस्तेमाल, टारगेट ग्राफ़ की जांच करने के लिए किया जाता हैbazel cquery
का इस्तेमाल, कॉन्फ़िगर किए गए टारगेट ग्राफ़ की जांच करने के लिए किया जाता हैbazel aquery
का इस्तेमाल, ऐक्शन ग्राफ़ की जांच करने के लिए किया जाता है
ये सभी पैरामीटर, AbstractBlazeQueryEnvironment
सब-क्लास की मदद से लागू किए जाते हैं.
QueryFunction
को सब-क्लास करके, अन्य क्वेरी फ़ंक्शन पूरे किए जा सकते हैं
को अपनाएं. क्वेरी के नतीजों को इकट्ठा करने के बजाय, उन्हें स्ट्रीम करने की अनुमति देने के लिए
इस डेटा स्ट्रक्चर के हिसाब से, QueryFunction
को query2.engine.Callback
पास किया जाता है, जो
इसे ऐसे परिणामों के लिए कॉल करता है जिन्हें वह वापस पाना चाहता है.
कोई क्वेरी कई तरीकों से जनरेट हो सकती है: लेबल, लेबल, और नियम
क्लास, एक्सएमएल, प्रोटोबफ़ वगैरह. ये इसकी सब-क्लास के रूप में लागू किए जाते हैं
OutputFormatter
.
कुछ क्वेरी आउटपुट फ़ॉर्मैट की खास ज़रूरत यह है कि (प्रोटो, बिलकुल) पैकेज लोडिंग से मिलने वाली _all _जानकारी को निकालना ज़रूरी है, आउटपुट में अंतर किया जा सकता है और यह पता लगाया जा सकता है कि कोई खास टारगेट बदला है या नहीं. इसलिए, एट्रिब्यूट की वैल्यू को क्रम से लगाया जाना चाहिए. इसलिए, ऐसे बहुत कम एट्रिब्यूट टाइप हैं जिनके एट्रिब्यूट में स्टारलार्क जटिल नहीं होता वैल्यू. आम तौर पर, समस्या का हल यह है कि एक लेबल का इस्तेमाल किया जाए. साथ ही, उस लेबल वाले नियम की जानकारी डालें. यह समाधान बहुत संतोषजनक नहीं है बहुत अच्छा होगा कि इस ज़रूरत को हटा दिया जाए.
मॉड्यूल सिस्टम
Basel में मॉड्यूल जोड़कर उसे बढ़ाया जा सकता है. हर मॉड्यूल को सब-क्लास होना चाहिए
BlazeModule
(यह नाम बेज़ेल के इतिहास का एक प्रतीक है, जो पहले होता था
ब्लेज़ कहा जाता है) और जिन प्रोग्राम के पास
कोई निर्देश देते हैं.
ज़्यादातर इनका इस्तेमाल "नॉन-कोर" के अलग-अलग हिस्सों को लागू करने के लिए किया जाता है फ़ंक्शन की ज़रूरत के हिसाब से Basel के कुछ वर्शन (जैसे, जिसका हम Google में इस्तेमाल करते हैं) को:
- रिमोट एक्ज़िक्यूशन सिस्टम के लिए इंटरफ़ेस
- नए निर्देश
BlazeModule
में मिलने वाले एक्सटेंशन पॉइंट का सेट कुछ हद तक मुश्किल होता है. ये काम न करें
इसे अच्छे डिज़ाइन सिद्धांतों के उदाहरण के रूप में इस्तेमाल करें.
इवेंट बस
ब्लेज़ मॉड्यूल्स का बज़ेल के बाकी देशों से संपर्क करने का मुख्य तरीका एक इवेंट बस है
(EventBus
): बेज़ल के अलग-अलग हिस्सों के हर बिल्ड के लिए एक नया इंस्टेंस बनाया गया है
इसमें इवेंट पोस्ट कर सकते हैं और मॉड्यूल उन इवेंट के लिए लिसनर को रजिस्टर कर सकता है
दिलचस्पी है. उदाहरण के लिए, नीचे दी गई चीज़ें इवेंट के तौर पर दिखाई जाती हैं:
- बनाए जाने वाले बिल्ड टारगेट की सूची तय कर दी गई है
TargetParsingCompleteEvent
- टॉप-लेवल कॉन्फ़िगरेशन के बारे में बताया गया है
BuildConfigurationEvent
- टारगेट बनाया गया था या हो गया है या नहीं (
TargetCompleteEvent
) - टेस्ट किया गया (
TestAttempt
,TestSummary
)
इनमें से कुछ इवेंट, बेज़ल के बाहर इस दौर में दिखाए जाते हैं
इवेंट प्रोटोकॉल बनाएं
(वे BuildEvent
s हैं). इससे न सिर्फ़ BlazeModule
, बल्कि अन्य चीज़ों की भी अनुमति मिलती है
के लिए डिज़ाइन किया है. उन्हें इस तरह से ऐक्सेस किया जा सकता है:
ऐसी फ़ाइल जिसमें प्रोटोकॉल संदेश या Basel होता है, वह सर्वर से कनेक्ट हो सकती है (
बिल्ड इवेंट सर्विस) का इस्तेमाल करें.
इसे build.lib.buildeventservice
और
build.lib.buildeventstream
Java पैकेज.
डेटा स्टोर करने की बाहरी जगहें
वहीं Basel को मूल रूप से मोनो रिपॉज़िटरी (एक ही सोर्स) में इस्तेमाल करने के लिए डिज़ाइन किया गया था ट्री जिसमें वह सब कुछ है जिसे बनाने की ज़रूरत है), बेज़ल एक ऐसी दुनिया में रहता है जहां ज़रूरी नहीं है कि यह हमेशा सही हो. "डेटा स्टोर करने की बाहरी जगहें" एक ऐब्स्ट्रैक्ट है, जिसका इस्तेमाल इन दो दुनिया को जोड़ने का काम कर सकते हैं: ये ऐसे कोड का प्रतिनिधित्व करते हैं जो बिल्ड के लिए ज़रूरी है लेकिन मुख्य सोर्स ट्री में नहीं है.
Workspace फ़ाइल
डेटा स्टोर करने की बाहरी जगहों का सेट, Workspace फ़ाइल को पार्स करके तय किया जाता है. उदाहरण के लिए, इस तरह का एलान:
local_repository(name="foo", path="/foo/bar")
डेटा स्टोर करने की जगह में, @foo
नाम के नतीजे उपलब्ध हैं. यह कहां मिलता है
मुश्किल यह है कि Starlark फ़ाइलों में डेटा स्टोर करने के नए नियम तय किए जा सकते हैं
फिर, नए Starlark कोड को लोड करने के लिए इसका इस्तेमाल किया जा सकता है. इसका इस्तेमाल नए
डेटा स्टोर करने के नियम वगैरह...
इस मामले को संभालने के लिए, Workspace फ़ाइल को पार्स करना (
WorkspaceFileFunction
) को load()
की मदद से, अलग-अलग हिस्सों में बांटा गया है
स्टेटमेंट. डेटा समूह को WorkspaceFileKey.getIndex()
और
इंडेक्स X तक WorkspaceFileFunction
की गणना करने का मतलब है कि
Xth load()
स्टेटमेंट.
डेटा स्टोर करने की जगहें फ़ेच की जा रही हैं
डेटा स्टोर करने की जगह का कोड Basel के लिए उपलब्ध होने से पहले, यह ज़रूरी है कि
फ़ेच किया गया. इसके परिणाम में बेज़ल इसके तहत एक डायरेक्ट्री बना देता है
$OUTPUT_BASE/external/<repository name>
.
डेटा स्टोर करने की जगह को फ़ेच करने के लिए, यह तरीका अपनाएं:
PackageLookupFunction
को लगता है कि इसे रिपॉज़िटरी की ज़रूरत है. साथ ही, यहSkyKey
के तौर परRepositoryName
, जोRepositoryLoaderFunction
को शुरू करता हैRepositoryLoaderFunction
अनुरोध को इस नंबर पर फ़ॉरवर्ड करता हैRepositoryDelegatorFunction
(कोड के हिसाब से यह सही है) अगर स्काईफ़्रेम रीस्टार्ट हो जाता है, तो चीज़ों को फिर से डाउनलोड करने से बचना चाहिए. हालांकि, यह ज़रूरी नहीं है कि तर्क के साथ जवाब देना)RepositoryDelegatorFunction
, डेटा स्टोर करने की जगह के उस नियम का पता लगा लेता है जिसके लिए उसने कहा था डेटा फ़ेच करने के लिए, Workspace फ़ाइल के अलग-अलग हिस्सों को तब तक दोहराएं, जब तक अनुरोध करने का अनुरोध न किया जाए डेटा स्टोर करने की जगह मिल गई है- सही
RepositoryFunction
मिलता है, जो डेटा स्टोर करने की जगह को लागू करता है fetching; वह डेटा स्टोर करने की जगह का Starlark इस्तेमाल या Java में, डेटा स्टोर करने की जगहों के लिए हार्ड कोड किया गया मैप
कैश मेमोरी की अलग-अलग लेयर होती हैं, क्योंकि रिपॉज़िटरी को फ़ेच करना बहुत मुश्किल होता है महंगा:
- डाउनलोड की गई फ़ाइलों के लिए एक कैश मेमोरी उपलब्ध होती है, जो उनके चेकसम के हिसाब से होती है
(
RepositoryCache
). इसके लिए चेकसम उपलब्ध होना ज़रूरी है workspace फ़ाइल ट्रांसफ़र की जा सकती है, लेकिन यह हर्मेटिकिटी के लिए भी अच्छा है. इसे इसने शेयर किया है एक ही वर्कस्टेशन पर काम कर रहा है, जिसका वे इस्तेमाल कर रहे हैं. - "मार्कर फ़ाइल" को
$OUTPUT_BASE/external
के तहत हर डेटा स्टोर करने की जगह के लिए लिखा गया है जिसमें उस नियम का चेकसम शामिल हो जिसका इस्तेमाल उसे फ़ेच करने के लिए किया गया था. अगर बेज़ल सर्वर रीस्टार्ट होता है, लेकिन चेकसम बदलता नहीं है. इसलिए, इसे फिर से फ़ेच नहीं किया जाता. यहRepositoryDelegatorFunction.DigestWriter
में लागू किया गया है . --distdir
कमांड लाइन विकल्प, ऐसी दूसरी कैश मेमोरी तय करता है जिसका इस्तेमाल इन कामों के लिए किया जाता है डाउनलोड करने के लिए आर्टफ़ैक्ट देख सकते हैं. यह एंटरप्राइज़ सेटिंग में काम का है जहां बेज़ल को इंटरनेट से कोई भी चीज़ फ़ेच नहीं करनी चाहिए. यह हैDownloadManager
ने लागू किया .
रिपॉज़िटरी डाउनलोड होने के बाद, उसमें मौजूद आर्टफ़ैक्ट को सोर्स माना जाता है
आर्टफ़ैक्ट. इससे समस्या हो सकती है, क्योंकि बज़ल आम तौर पर अप-टू-डेट जानकारी की जांच करते हैं
सोर्स आर्टफ़ैक्ट को देखने का मौका मिलता है. इसके लिए, उन्हें stat() को कॉल किया जाता है और ये आर्टफ़ैक्ट
अमान्य हो सकता है. ऐसा तब होता है, जब डेटा स्टोर करने की जगह की परिभाषा में बदलाव होता है. इसलिए,
बाहरी डेटा स्टोर करने की जगह में आर्टफ़ैक्ट के FileStateValue
को इस पर निर्भर होना चाहिए
डेटा स्टोर करने की जगह भी तय कर सकता है. इसे ExternalFilesHelper
मैनेज करता है.
डेटा स्टोर करने की जगह की मैपिंग
ऐसा हो सकता है कि एक से ज़्यादा रिपॉज़िटरी एक ही रिपॉज़िटरी पर निर्भर करना चाहें,
लेकिन अलग-अलग वर्शन में (यह "डायमंड डिपेंडेंसी
सवाल). उदाहरण के लिए, अगर बिल्ड में अलग-अलग डेटा स्टोर करने की जगहों में दो बाइनरी हों
अगर Guava पर निर्भर है, तो शायद वे दोनों लेबल के साथ Guava से जुड़े होंगे
जो @guava//
से शुरू होगा और इसके अलग-अलग वर्शन के तौर पर दिखेगा.
इसलिए, Basel को बाहरी रिपॉज़िटरी लेबल को फिर से मैप करने की अनुमति देता है, ताकि
स्ट्रिंग @guava//
एक Guava रिपॉज़िटरी (जैसे कि @guava1//
) को
एक बाइनरी और दूसरी गुआवा रिपॉज़िटरी (जैसे, @guava2//
) की रिपॉज़िटरी
डेटा स्टोर करने की जगह इकट्ठा करें.
इसके अलावा, इसका इस्तेमाल डायमंड जोड़ने के लिए भी किया जा सकता है. अगर रिपॉज़िटरी (डेटा स्टोर करने की जगह)
यह @guava1//
पर निर्भर करता है. दूसरा, रिपॉज़िटरी मैपिंग के आधार पर @guava2//
पर निर्भर करता है
इससे दोनों डेटा स्टोर करने की जगह को फिर से मैप किया जा सकता है, ताकि वह कैननिकल @guava//
डेटा स्टोर करने की जगह का इस्तेमाल कर सके.
मैपिंग को वर्कस्पेस फ़ाइल में repo_mapping
एट्रिब्यूट के तौर पर बताया गया है
की अलग-अलग रिपॉज़िटरी की परिभाषाएं हैं. इसके बाद, यह Skyframe में
WorkspaceFileValue
, जहां यह मिलता है:
Package.Builder.repositoryMapping
का इस्तेमाल, लेबल की वैल्यू को बदलने के लिए किया जाता है इसके ज़रिए पैकेज में नियमों की विशेषताएंRuleClass.populateRuleAttributeValues()
Package.repositoryMapping
का इस्तेमाल, विश्लेषण के चरण में किया जाता है (इसके लिए लोडिंग में पार्स नहीं किए गए$(location)
जैसी चीज़ों को ठीक किया जा रहा है फ़ेज़)- load() स्टेटमेंट में लेबल का समाधान करने के लिए
BzlLoadFunction
JNI बिट
Basel का सर्वर ज़्यादातर Java में लिखा जाता है. हालांकि, वे हिस्से अपवाद हैं जब हमने इसे लागू किया था, तो Java अपने आप नहीं कर सकता या अपने आप नहीं कर सकता. यह फ़ाइल सिस्टम, प्रोसेस कंट्रोल और बहुत कुछ है.
C++ कोड, src/main/native और Java की क्लास में नेटिव के साथ रहता है तरीके हैं:
NativePosixFiles
औरNativePosixFileSystem
ProcessUtils
WindowsFileOperations
औरWindowsFileProcesses
com.google.devtools.build.lib.platform
कंसोल आउटपुट
कंसोल आउटपुट को छोड़ देना एक आसान बात है, लेकिन यह एक साथ कई काम करता है कई प्रोसेस (कभी-कभी रिमोट तरीके से), बारीक कैश मेमोरी में सेव होना, इसका टर्मिनल आउटपुट अच्छा और रंग-बिरंगा है. साथ ही, लंबे समय तक चलने वाला सर्वर छोटा नहीं है.
क्लाइंट से RPC कॉल आने के तुरंत बाद, दो RpcOutputStream
stdout और stderr के लिए, इंस्टेंस बनाए जाते हैं जो
उन्हें क्लाइंट को भेजना है. इसके बाद, इन्हें OutErr
(stdout, stderr) एट्रिब्यूट में रैप किया जाता है
जोड़ें). कंसोल पर प्रिंट करने की ज़रूरत की सभी चीज़ों को इनके ज़रिए भेजा जाता है
स्ट्रीम. इसके बाद, ये स्ट्रीम Google News
BlazeCommandDispatcher.execExclusively()
.
आउटपुट, डिफ़ॉल्ट रूप से एएनएसआई एस्केप सीक्वेंस के साथ प्रिंट होता है. जब ये शर्तें
(--color=no
) की ज़रूरत है, तो उन्हें AnsiStrippingOutputStream
हटा दिया जाएगा. तय सीमा में
इसके अलावा, System.out
और System.err
को इन आउटपुट स्ट्रीम पर रीडायरेक्ट किया जाता है.
ऐसा इसलिए है, ताकि डीबग करने की जानकारी को
System.err.println()
और फिर भी क्लाइंट के टर्मिनल आउटपुट में पहुंचते हैं
(जो सर्वर से अलग है). इस बात का ध्यान रखा जाता है कि अगर प्रोसेस
बाइनरी आउटपुट देता है (जैसे कि bazel query --output=proto
), stdout पर कोई म्यूजिंग नहीं
होता है.
छोटे मैसेज (गड़बड़ियां, चेतावनियां वगैरह)
EventHandler
इंटरफ़ेस. विशेष रूप से, ये पोस्ट किए जाने वाले पोस्ट से अलग होते हैं
EventBus
(यह भ्रम की स्थिति पैदा करता है). हर Event
में एक EventKind
है (गड़बड़ी,
चेतावनी, जानकारी वगैरह) और वे एक Location
(जिस जगह पर यह मौजूद है) हो सकते हैं
वह सोर्स कोड जिसकी वजह से इवेंट हुआ).
कुछ EventHandler
लागू करने की सुविधा, उन्हें मिलने वाले इवेंट को सेव करती है. इसका इस्तेमाल किया गया
कई तरह की कैश मेमोरी में सेव होने की वजह से होने वाली जानकारी को यूज़र इंटरफ़ेस (यूआई) में फिर से चलाने के लिए,
उदाहरण के लिए, कैश मेमोरी में सेव किए गए कॉन्फ़िगर किए गए टारगेट से मिलने वाली चेतावनियां.
कुछ EventHandler
ऐसे इवेंट पोस्ट करने की भी अनुमति देते हैं जिनमें बाद में
इवेंट बस (सामान्य Event
वहां _नहीं _दिखते हैं). ये हैं
ExtendedEventHandler
को लागू करना और इनका मुख्य इस्तेमाल, कैश मेमोरी में सेव की गई फ़ाइलों को फिर से चलाने के लिए करना होता है
EventBus
इवेंट. ये EventBus
इवेंट, Postable
को लागू करते हैं. हालांकि, लागू नहीं होते
EventBus
पर पोस्ट की जाने वाली हर जानकारी इस इंटरफ़ेस को लागू करना ज़रूरी है;
जिन्हें ExtendedEventHandler
की कैश मेमोरी में सेव किया गया हो (यह अच्छा और
ज़्यादातर चीज़ें काम करती हैं; हालांकि, इसे लागू नहीं किया जाता)
टर्मिनल आउटपुट, UiEventHandler
के ज़रिए ज़्यादातर उत्सर्जन करता है, जो कि
सभी फ़ैंसी आउटपुट फ़ॉर्मैटिंग और प्रोग्रेस रिपोर्टिंग के लिए ज़िम्मेदार है
करता है. इसके दो इनपुट होते हैं:
- इवेंट बस
- इवेंट स्ट्रीम, रिपोर्टर के ज़रिए इसमें शामिल हुई
कमांड एक्ज़ीक्यूशन करने वाली मशीनरी से सिर्फ़ एक डायरेक्ट कनेक्शन होता है. उदाहरण के लिए,
Basel) की स्ट्रीम को क्लाइंट को आरपीसी स्ट्रीम से कनेक्ट करने के लिए, Reporter.getOutErr()
से भेजा जाता है.
इससे इन स्ट्रीम को सीधे तौर पर ऐक्सेस किया जा सकता है. इसका इस्तेमाल सिर्फ़ तब किया जाता है, जब किसी निर्देश की ज़रूरत हो
बड़ी मात्रा में संभावित बाइनरी डेटा (जैसे bazel query
) डंप करने के लिए.
प्रोफ़ाइलिंग बेज़ल
Basel एक तेज़ है. Basel का उपकरण भी धीमा होता है, क्योंकि बिल्ड में
की एक सीमा है. इस कारण, Basel ने एक प्रोफ़ाइलर को शामिल किया है, जिसे
का इस्तेमाल प्रोफ़ाइल बिल्ड और खुद Basel को बनाने में किया था. इसे ऐसी क्लास में लागू किया जाता है जिसे
इसका सही नाम Profiler
है. यह सुविधा डिफ़ॉल्ट रूप से चालू होती है. हालांकि, यह सिर्फ़ रिकॉर्ड करती है
डेटा को छोटा किया जाना चाहिए, ताकि उसका ओवरहेड सहन किया जा सके; कमांड लाइन
--record_full_profiler_data
अपनी हर चीज़ रिकॉर्ड कर सकता है.
इससे Chrome के प्रोफ़ाइलर फ़ॉर्मैट में एक प्रोफ़ाइल बनती है; यह Chrome में सबसे अच्छा दिखता है. यह डेटा मॉडल, टास्क स्टैक का है: इसमें टास्क शुरू किया जा सकता है और टास्क खत्म किए जा सकते हैं. उन्हें एक-दूसरे में सही तरीके से नेस्ट किया जाना चाहिए. हर Java थ्रेड को टास्क का स्टैक बनाया गया. TODO: यह कार्रवाइयों के साथ कैसे काम करता है और लगातार आगे बढ़ने वाली स्टाइल?
प्रोफ़ाइलर BlazeRuntime.initProfiler()
में शुरू और बंद हो गया है और
BlazeRuntime.afterCommand()
और लंबे समय तक लाइव रहने की कोशिश करता है
ताकि हम हर चीज़ की प्रोफ़ाइल बना सकें. प्रोफ़ाइल में कुछ जोड़ने के लिए,
Profiler.instance().profile()
को कॉल करें. यह एक Closeable
दिखाता है, जो बंद है
टास्क के खत्म होने के बारे में बताता है. संसाधनों की मदद से, आज़माने में सबसे अच्छा है
स्टेटमेंट.
हम MemoryProfiler
में अल्पमेंटरी मेमोरी प्रोफ़ाइलिंग भी करते हैं. यह हमेशा चालू रहता है
और यह ज़्यादातर हीप के ज़्यादा से ज़्यादा साइज़ और जीसी बिहेवियर को रिकॉर्ड करता है.
बेज़ल टेस्टिंग
Basel के पास दो मुख्य तरह के टेस्ट होते हैं: इनमें से एक में Ba पैकेज को "ब्लैक बॉक्स" के तौर पर देखा जाता है और जो सिर्फ़ विश्लेषण वाले चरण को पूरा करते हैं. हम पहले के वर्शन को "इंटिग्रेशन टेस्ट" कहते हैं और बाद वाले "यूनिट टेस्ट" में शामिल हैं, हालांकि वे इंटिग्रेशन टेस्ट की तरह होते हैं वे कम जुड़े हुए हैं. हमारे पास कुछ असल यूनिट टेस्ट भी हैं, जहां वे ज़रूरी है.
इंटिग्रेशन टेस्ट दो तरह के होते हैं:
- इन्हें लागू करने के लिए, बैश टेस्ट फ़्रेमवर्क का इस्तेमाल किया जाता है.
src/test/shell
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है - Java में लागू की गई सुविधाएं. ये इसकी सब-क्लास के रूप में लागू किए जाते हैं
BuildIntegrationTestCase
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
इंटिग्रेशन की जांच करने के लिए, BuildIntegrationTestCase
पसंदीदा फ़्रेमवर्क है
ज़्यादातर जांच स्थितियों के लिए अच्छी तरह से तैयार रहता है. यह एक Java फ़्रेमवर्क है, इसलिए यह
कई सामान्य डेवलपमेंट के साथ डीबग करने और आसान इंटिग्रेशन उपलब्ध कराता है
टूल. यहां BuildIntegrationTestCase
क्लास के कई उदाहरण दिए गए हैं
बेज़ल रिपॉज़िटरी.
विश्लेषण जांच को BuildViewTestCase
की सब-क्लास के तौर पर लागू किया जाता है. कई
स्क्रैच फ़ाइल सिस्टम का इस्तेमाल करके, BUILD
फ़ाइलों को लिखा जा सकता है. इसके बाद, अलग-अलग हेल्पर बनाया जा सकता है
तरीके, कॉन्फ़िगर किए गए टारगेट के लिए अनुरोध कर सकते हैं, कॉन्फ़िगरेशन को बदल सकते हैं, और
कई चीज़ों के बारे में बात करते हैं.