डिपेंडेंसी

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

किसी टारगेट की सीधी डिपेंडेंसी वे अन्य टारगेट होते हैं जिन तक डिपेंडेंसी ग्राफ़ में, एक पाथ से पहुंचा जा सकता है. किसी टारगेट की ट्रांज़िटिव डिपेंडेंसी वे टारगेट होते हैं जिन पर वह ग्राफ़ में किसी भी पाथ के ज़रिए निर्भर करता है.

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

असल और डिक्लेयर की गई डिपेंडेंसी

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

अगर X के पैकेज में, X से Y तक कोई डिपेंडेंसी एज है, तो कहा जाता है कि टारगेट X की डिक्लेयर की गई डिपेंडेंसी, टारगेट Y है.

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