डिपेंडेंसी

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

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

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

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

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

अगर 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();
}
      
एलान किया गया डिपेंडेंसी ग्राफ़, जिसमें 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 से कनेक्ट नहीं होता है, जिससे 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 लेबल का रेफ़रंस दिया जा सकता है.

फ़ाइलें बनाएं किसे दिखे