अगर B
की ज़रूरत A
को बिल्ड या एक्ज़ीक्यूशन के समय होती है, तो टारगेट A
, टारगेट B
पर A
निर्भर करता है. इस पर निर्भर करता है संबंध, टारगेट पर डायरेक्टेड एसाइक्लिक ग्राफ़ (डीएजी) बनाता है. इसे डिपेंडेंसी ग्राफ़ कहा जाता है.
किसी टारगेट की सीधी डिपेंडेंसी, ऐसे दूसरे टारगेट होते हैं जिन तक डिपेंडेंसी ग्राफ़ में लंबाई 1 वाले पाथ से पहुंचा जा सकता है. किसी टारगेट की ट्रांज़िटिव डिपेंडेंसी वे टारगेट होते हैं जिन पर वह ग्राफ़ के किसी भी पाथ के ज़रिए निर्भर करता है.
दरअसल, बिल्ड के संदर्भ में दो डिपेंडेंसी ग्राफ़ होते हैं. एक असल डिपेंडेंसी का ग्राफ़ होता है और दूसरा डिक्लेयर की गई डिपेंडेंसी का ग्राफ़ होता है. ज़्यादातर समय, दोनों ग्राफ़ इतने मिलते-जुलते होते हैं कि यह अंतर बताने की ज़रूरत नहीं होती. हालांकि, नीचे दी गई चर्चा के लिए यह अंतर जानना ज़रूरी है.
असल और बताई गई डिपेंडेंसी
अगर X
को सही तरीके से बनाने के लिए, Y
का मौजूद होना, बनाया जाना, और अप-टू-डेट होना ज़रूरी है, तो इसका मतलब है कि टारगेट X
, टारगेट Y
पर असल में निर्भर है. बनाया गया का मतलब है कि उसे जनरेट किया गया है, प्रोसेस किया गया है, कंपाइल किया गया है, लिंक किया गया है, संग्रहित किया गया है, कंप्रेस किया गया है, लागू किया गया है या बिल्ड के दौरान होने वाले किसी अन्य तरह के टास्क को पूरा किया गया है.
अगर X
के पैकेज में X
से Y
तक डिपेंडेंसी एज है, तो इसका मतलब है कि टारगेट X
में टारगेट Y
पर डिपेंडेंसी का एलान किया गया है.
सही बिल्ड के लिए, असल डिपेंडेंसी A का ग्राफ़, डिक्लेयर की गई डिपेंडेंसी D के ग्राफ़ का सबग्राफ़ होना चाहिए. इसका मतलब है कि A में सीधे तौर पर जुड़े नोड x --> y
का हर पेयर, D में भी सीधे तौर पर जुड़ा होना चाहिए. यह कहा जा सकता है कि D, A का ओवरएप्रोक्सिमेशन है.
BUILD
फ़ाइल लिखने वालों को, हर नियम के लिए बिल्ड सिस्टम की सभी असल डायरेक्ट डिपेंडेंसी के बारे में साफ़ तौर पर बताना होगा. इससे ज़्यादा नहीं.
इस सिद्धांत का पालन न करने पर, अनडिफ़ाइंड व्यवहार होता है: बिल्ड फ़ेल हो सकता है. हालांकि, इससे भी बुरी बात यह है कि बिल्ड, कुछ पिछली कार्रवाइयों या टारगेट की ट्रांज़िटिव डिपेंडेंसी पर निर्भर हो सकता है. Bazel, डिपेंडेंसी के मौजूद न होने की जांच करता है और गड़बड़ियों की रिपोर्ट करता है. हालांकि, ऐसा हो सकता है कि सभी मामलों में यह जांच पूरी न हो.
आपको परोक्ष रूप से इंपोर्ट की गई हर चीज़ को लिस्ट करने की कोशिश नहीं करनी चाहिए. भले ही, एक्ज़ीक्यूशन के समय A
को इसकी ज़रूरत हो.
टारगेट X
को बनाते समय, बिल्ड टूल X
की सभी ट्रांज़िटिव क्लोज़र डिपेंडेंसी की जांच करता है. इससे यह पक्का किया जाता है कि उन टारगेट में हुए किसी भी बदलाव को फ़ाइनल नतीजे में शामिल किया गया है. साथ ही, ज़रूरत के मुताबिक इंटरमीडिएट को फिर से बनाया जाता है.
डिपेंडेंसी के ट्रांज़िटिव नेचर की वजह से, आम तौर पर एक गलती होती है. कभी-कभी, किसी फ़ाइल में मौजूद कोड, अप्रत्यक्ष डिपेंडेंसी से मिले कोड का इस्तेमाल कर सकता है. यह ट्रांज़िटिव है, लेकिन डिपेंडेंसी ग्राफ़ में डायरेक्ट एज नहीं है. BUILD
फ़ाइल में, इनडायरेक्ट डिपेंडेंसी नहीं दिखती हैं. नियम सीधे तौर पर सेवा देने वाली कंपनी पर निर्भर नहीं करता है. इसलिए, बदलावों को ट्रैक करने का कोई तरीका नहीं है. इसे यहां दिए गए उदाहरण की टाइमलाइन में दिखाया गया है:
1. डिपेंडेंसी के एलान में बताई गई डिपेंडेंसी, असल डिपेंडेंसी से मेल खाती हों
शुरुआत में, सब कुछ ठीक से काम करता है. पैकेज a
में मौजूद कोड, पैकेज b
में मौजूद कोड का इस्तेमाल करता है.
पैकेज b
में मौजूद कोड, पैकेज c
में मौजूद कोड का इस्तेमाल करता है. इसलिए, a
ट्रांज़िटिव रूप से c
पर निर्भर करता है.
a/BUILD |
b/BUILD |
---|---|
rule( name = "a", srcs = "a.in", deps = "//b:b", ) |
rule( name = "b", srcs = "b.in", deps = "//c:c", ) |
a / a.in |
b / b.in |
import b; b.foo(); |
import c; function foo() { c.bar(); } |
|
|
डिपेंडेंसी के बारे में दी गई जानकारी, असल डिपेंडेंसी से ज़्यादा है. सब ठीक है.
2. ऐसी डिपेंडेंसी जोड़ना जिसके बारे में बताया न गया हो
जब कोई व्यक्ति a
में ऐसा कोड जोड़ता है जो c
पर सीधे तौर पर असल निर्भरता बनाता है, लेकिन उसे बिल्ड फ़ाइल a/BUILD
में शामिल करना भूल जाता है, तो एक संभावित खतरा पैदा हो जाता है.
a / a.in |
|
---|---|
import b; import c; b.foo(); c.garply(); |
|
|
|
डिपेंडेंसी के बारे में दी गई जानकारी, अब असल डिपेंडेंसी से ज़्यादा नहीं है.
यह ठीक से बन सकता है, क्योंकि दोनों ग्राफ़ के ट्रांज़िटिव क्लोज़र बराबर हैं,
लेकिन इससे एक समस्या छिप जाती है: a
की c
पर एक असल, लेकिन बिना बताई गई डिपेंडेंसी है.
3. डिपेंडेंसी के बताए गए और असल ग्राफ़ के बीच अंतर
यह समस्या तब सामने आती है, जब कोई व्यक्ति b
को इस तरह से रीफ़ैक्टर करता है कि यह अब c
पर निर्भर न रहे. इससे अनजाने में a
टूट जाता है. इसमें उस व्यक्ति की कोई गलती नहीं होती.
b/BUILD |
|
---|---|
rule( name = "b", srcs = "b.in", deps = "//d:d", ) |
|
b / b.in |
|
import d; function foo() { d.baz(); } |
|
|
|
डिपेंडेंसी ग्राफ़ में बताई गई डिपेंडेंसी, अब असल डिपेंडेंसी से कम हैं. भले ही, ट्रांज़िटिव डिपेंडेंसी को बंद कर दिया गया हो. इसलिए, हो सकता है कि बिल्ड पूरा न हो पाए.
इस समस्या से बचा जा सकता था. इसके लिए, यह पक्का करना ज़रूरी था कि चरण 2 में a
से c
तक की असल डिपेंडेंसी को BUILD
फ़ाइल में सही तरीके से बताया गया हो.
डिपेंडेंसी के टाइप
ज़्यादातर बिल्ड नियमों में, अलग-अलग तरह की सामान्य डिपेंडेंसी के बारे में बताने के लिए तीन एट्रिब्यूट होते हैं: srcs
, deps
, और data
. इनके बारे में यहां बताया गया है. ज़्यादा जानकारी के लिए, सभी नियमों के लिए सामान्य एट्रिब्यूट देखें.
कई नियमों में, नियम के हिसाब से तय की गई डिपेंडेंसी के लिए अतिरिक्त एट्रिब्यूट भी होते हैं. उदाहरण के लिए, compiler
या resources
. इनके बारे में ज़्यादा जानकारी, बिल्ड एनसाइक्लोपीडिया में दी गई है.
srcs
डिपेंडेंसी
ऐसी फ़ाइलें जिनका इस्तेमाल सीधे तौर पर नियम या ऐसे नियमों के ज़रिए किया जाता है जो सोर्स फ़ाइलें आउटपुट करते हैं.
deps
डिपेंडेंसी
अलग-अलग कंपाइल किए गए मॉड्यूल की ओर इशारा करने वाला नियम. इनमें हेडर फ़ाइलें, सिंबल, लाइब्रेरी, डेटा वगैरह शामिल होते हैं.
data
डिपेंडेंसी
किसी बिल्ड टारगेट को सही तरीके से चलाने के लिए, कुछ डेटा फ़ाइलों की ज़रूरत पड़ सकती है. ये डेटा फ़ाइलें, सोर्स कोड नहीं हैं. इनसे टारगेट बनाने के तरीके पर कोई असर नहीं पड़ता. उदाहरण के लिए, यूनिट टेस्ट में किसी फ़ंक्शन के आउटपुट की तुलना किसी फ़ाइल के कॉन्टेंट से की जा सकती है. यूनिट टेस्ट बनाते समय, आपको फ़ाइल की ज़रूरत नहीं होती. हालांकि, टेस्ट चलाने के लिए इसकी ज़रूरत होती है. यह बात, एक्ज़ीक्यूशन के दौरान लॉन्च किए गए टूल पर भी लागू होती है.
बिल्ड सिस्टम, टेस्ट को एक अलग डायरेक्ट्री में चलाता है. इसमें सिर्फ़ वे फ़ाइलें उपलब्ध होती हैं जिन्हें data
के तौर पर लिस्ट किया गया है. इसलिए, अगर किसी बाइनरी/लाइब्रेरी/टेस्ट को चलाने के लिए कुछ फ़ाइलों की ज़रूरत है, तो उन्हें data
में बताएं. इसके अलावा, उन्हें शामिल करने वाले बिल्ड नियम के बारे में भी बताया जा सकता है. उदाहरण के लिए:
# I need a config file from a directory named env:
java_binary(
name = "setenv",
...
data = [":env/default_env.txt"],
)
# I need test data from another directory
sh_test(
name = "regtest",
srcs = ["regtest.sh"],
data = [
"//data:file1.txt",
"//data:file2.txt",
...
],
)
ये फ़ाइलें, रिलेटिव पाथ path/to/data/file
का इस्तेमाल करके उपलब्ध हैं. टेस्ट में, इन फ़ाइलों का रेफ़रंस देने के लिए, टेस्ट के सोर्स डायरेक्ट्री के पाथ और वर्कस्पेस के हिसाब से पाथ को जोड़ा जा सकता है. उदाहरण के लिए, ${TEST_SRCDIR}/workspace/path/to/data/file
.
डायरेक्ट्री को रेफ़र करने के लिए लेबल का इस्तेमाल करना
हमारी BUILD
फ़ाइलों को देखते समय, आपको दिख सकता है कि कुछ data
लेबल, डायरेक्ट्री के बारे में बताते हैं. ये लेबल, इन उदाहरणों की तरह /.
या /
पर खत्म होते हैं. आपको इनका इस्तेमाल नहीं करना चाहिए:
इसका सुझाव नहीं दिया जाता —
data = ["//data/regression:unittest/."]
इसका सुझाव नहीं दिया जाता —
data = ["testdata/."]
इसका सुझाव नहीं दिया जाता —
data = ["testdata/"]
यह तरीका आसान लगता है. खास तौर पर, टेस्ट के लिए, क्योंकि इससे टेस्ट को डायरेक्ट्री में मौजूद सभी डेटा फ़ाइलों का इस्तेमाल करने की अनुमति मिलती है.
हालांकि, ऐसा न करें. बदलाव के बाद, सही इंक्रीमेंटल रीबिल्ड (और टेस्ट को फिर से लागू करना) पक्का करने के लिए, बिल्ड सिस्टम को उन सभी फ़ाइलों के बारे में पता होना चाहिए जो बिल्ड (या टेस्ट) के लिए इनपुट हैं. किसी डायरेक्ट्री को तय करने पर, बिल्ड सिस्टम सिर्फ़ तब फिर से बिल्ड करता है, जब डायरेक्ट्री में बदलाव होता है. जैसे, फ़ाइलें जोड़ने या मिटाने की वजह से. हालांकि, यह सिस्टम अलग-अलग फ़ाइलों में किए गए बदलावों का पता नहीं लगा पाता, क्योंकि उन बदलावों से डायरेक्ट्री पर कोई असर नहीं पड़ता.
बिल्ड सिस्टम के इनपुट के तौर पर डायरेक्ट्री तय करने के बजाय, आपको उनमें मौजूद फ़ाइलों का सेट गिनना चाहिए. इसके लिए, साफ़ तौर पर या glob()
फ़ंक्शन का इस्तेमाल करें. (glob()
को बार-बार दोहराने के लिए, **
का इस्तेमाल करें.)
सुझाया गया —
data = glob(["testdata/**"])
हालांकि, कुछ ऐसे मामले होते हैं जिनमें डायरेक्ट्री लेबल का इस्तेमाल करना ज़रूरी होता है.
उदाहरण के लिए, अगर testdata
डायरेक्ट्री में ऐसी फ़ाइलें हैं जिनके नाम लेबल सिंटैक्स के मुताबिक नहीं हैं, तो फ़ाइलों को साफ़ तौर पर गिनने या glob()
फ़ंक्शन का इस्तेमाल करने पर, अमान्य लेबल वाली गड़बड़ी दिखती है. इस मामले में, आपको डायरेक्ट्री लेबल का इस्तेमाल करना होगा. हालांकि, ऊपर बताई गई गलत रीबिल्ड से जुड़े जोखिम के बारे में ध्यान रखें.
अगर आपको डायरेक्ट्री लेबल का इस्तेमाल करना है, तो ध्यान रखें कि रिलेटिव ../
पाथ का इस्तेमाल करके, पैरंट पैकेज को रेफ़र नहीं किया जा सकता. इसके बजाय, //data/regression:unittest/.
जैसे ऐब्सलूट पाथ का इस्तेमाल करें.
अगर किसी बाहरी नियम (जैसे कि टेस्ट) को एक से ज़्यादा फ़ाइलों का इस्तेमाल करना है, तो उसे साफ़ तौर पर यह बताना होगा कि वह उन सभी फ़ाइलों पर निर्भर है. filegroup()
का इस्तेमाल करके, BUILD
फ़ाइल में फ़ाइलों को एक साथ ग्रुप किया जा सकता है:
filegroup(
name = 'my_data',
srcs = glob(['my_unittest_data/*'])
)
इसके बाद, अपने टेस्ट में डेटा डिपेंडेंसी के तौर पर लेबल my_data
का रेफ़रंस दिया जा सकता है.
BUILD फ़ाइलें | विज़िबिलिटी |