डिपेंडेंसी

7.3 · 7.2 · 7.1 · 7.0 · 6.5

अगर A को बिल्ड या रन करने के समय B की ज़रूरत है, तो टारगेट A, टारगेट B पर निर्भर करता है. इस पर निर्भर करता है संबंध, टारगेट पर डायरेक्टेड ऐसाइक्लिक ग्राफ़ (डीएजी) बनाता है. इसे डिपेंडेंसी ग्राफ़ कहा जाता है.

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

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

असल और बताई गई डिपेंडेंसी

टारगेट X, टारगेट Y पर असल में निर्भर होता है. ऐसा तब होता है, जब X को सही तरीके से बनाने के लिए Y मौजूद, बनाया गया हो, और अप-टू-डेट हो. बिल्ट का मतलब, जनरेट, प्रोसेस, कंपाइल, लिंक, संग्रहित, कंप्रेस, या किसी भी तरह के ऐसे टास्क से हो सकता है जो आम तौर पर बिल्ड के दौरान होते हैं.

अगर X के पैकेज में, 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();
}
      
a, b, और c को जोड़ने वाले ऐरो के साथ डिक्लेयर्ड डिपेंडेंसी ग्राफ़
एलान किया गया डिपेंडेंसी ग्राफ़
डिपेंडेंसी ग्राफ़, जो एलान किए गए डिपेंडेंसी ग्राफ़ से मेल खाता है. इस ग्राफ़ में a, b, और c ऐरो के निशान हैं
असल डिपेंडेंसी ग्राफ़

एलान की गई डिपेंडेंसी, असल डिपेंडेंसी से ज़्यादा होती हैं. सब ठीक है.

2. ऐसी डिपेंडेंसी जोड़ना जिसकी जानकारी नहीं दी गई है

जब कोई व्यक्ति a में ऐसा कोड जोड़ता है जिससे c पर सीधे असल डिपेंडेंसी बनती है, लेकिन उसे बिल्ड फ़ाइल a/BUILD में एलान करना भूल जाता है, तो एक छिपे हुए खतरे का सामना करना पड़ता है.

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
a, b, और c को जोड़ने वाले ऐरो के साथ डिक्लेयर्ड डिपेंडेंसी ग्राफ़
एलान किया गया डिपेंडेंसी ग्राफ़
a, b, और c को जोड़ने वाले ऐरो के साथ असल डिपेंडेंसी ग्राफ़. अब एक ऐरो, A को C से भी जोड़ता है. यह, एलान किए गए डिपेंडेंसी ग्राफ़ से मेल नहीं खाता
असल डिपेंडेंसी ग्राफ़

डिक्लेयर्ड डिपेंडेंसी, अब असल डिपेंडेंसी से ज़्यादा नहीं होती हैं. ऐसा हो सकता है कि यह ठीक से बन जाए, क्योंकि दोनों ग्राफ़ के ट्रांज़िशन क्लोज़र बराबर हैं. हालांकि, इससे एक समस्या छिप जाती है: 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();
      }
      
a और b को जोड़ने वाले ऐरो के साथ डिक्लेयर्ड डिपेंडेंसी ग्राफ़.
                  b अब c से कनेक्ट नहीं होता, जिससे a का c से कनेक्शन टूट जाता है
एलान किया गया डिपेंडेंसी ग्राफ़
असल डिपेंडेंसी ग्राफ़, जो b और c से कनेक्ट दिखाता है,
                  लेकिन b अब c से कनेक्ट नहीं होता
असल डिपेंडेंसी ग्राफ़

डिपेंडेंसी ग्राफ़ में अब असल डिपेंडेंसी के बारे में कम जानकारी दी गई है. भले ही, ट्रांज़िटिव तौर पर डिपेंडेंसी बंद हो गई हों, लेकिन बिल्ड पूरा न हो पाने की संभावना है.

यह पक्का करके इस समस्या को टाला जा सकता था कि दूसरे चरण में लॉन्च की गई 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 का इस्तेमाल करके उपलब्ध हैं. टेस्ट में, इन फ़ाइलों का रेफ़रंस देने के लिए, टेस्ट की सोर्स डायरेक्ट्री और Workspace के हिसाब से पाथ को जोड़ें. उदाहरण के लिए, ${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/. जैसे ऐब्सलूट पाथ का इस्तेमाल करें.

किसी भी बाहरी नियम, जैसे कि टेस्ट के लिए कई फ़ाइलों का इस्तेमाल करना ज़रूरी है. इसलिए, यह साफ़ तौर पर बताया जाना चाहिए कि वह उन सभी पर निर्भर है. BUILD फ़ाइल में, फ़ाइलों को एक साथ ग्रुप करने के लिए filegroup() का इस्तेमाल किया जा सकता है:

filegroup(
        name = 'my_data',
        srcs = glob(['my_unittest_data/*'])
)

इसके बाद, अपने टेस्ट में डेटा डिपेंडेंसी के तौर पर लेबल my_data का रेफ़रंस दिया जा सकता है.

BUILD फ़ाइलें किसको दिखे