टास्क-आधारित बिल्ड सिस्टम

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

इस पेज पर, टास्क पर आधारित बिल्ड सिस्टम और उनके काम करने के तरीके के बारे में बताया गया है. साथ ही, इसमें कुछ ऐसी जटिलताओं के बारे में भी बताया गया है जो टास्क पर आधारित सिस्टम से हो सकती हैं. शेल स्क्रिप्ट के बाद, काम पर आधारित बिल्ड सिस्टम इमारत बनाने के अगले तार्किक विकास हैं.

टास्क-आधारित बिल्ड सिस्टम को समझना

टास्क पर आधारित सिस्टम में, काम की बुनियादी इकाई ही टास्क होती है. हर टास्क एक स्क्रिप्ट होती है जो किसी भी तरह के लॉजिक को अपना सकती है. साथ ही, टास्क दूसरे टास्क को डिपेंडेंसी के तौर पर बताते हैं जो उनके पहले चलने चाहिए. आज-कल चीटियों, Maven, Gradle, Grunt, और Rake जैसे बड़े सिस्टम इस्तेमाल किए जाते हैं. शेल स्क्रिप्ट के बजाय, ज़्यादातर आधुनिक बिल्ड सिस्टम में बिल्ड फ़ाइलें बनाने के लिए इंजीनियर की ज़रूरत होती है. यह बिल्ड को चलाने का तरीका बताता है.

इस उदाहरण को चीट मैनुअल से लें:

<project name="MyProject" default="dist" basedir=".">
   <description>
     simple example build file
   </description>
   <!-- set global properties for this build -->
   <property name="src" location="src"/>
   <property name="build" location="build"/>
   <property name="dist" location="dist"/>

   <target name="init">
     <!-- Create the time stamp -->
     <tstamp/>
     <!-- Create the build directory structure used by compile -->
     <mkdir dir="${build}"/>
   </target>
   <target name="compile" depends="init"
       description="compile the source">
     <!-- Compile the Java code from ${src} into ${build} -->
     <javac srcdir="${src}" destdir="${build}"/>
   </target>
   <target name="dist" depends="compile"
       description="generate the distribution">
     <!-- Create the distribution directory -->
     <mkdir dir="${dist}/lib"/>
     <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
     <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
   </target>
   <target name="clean"
       description="clean up">
     <!-- Delete the ${build} and ${dist} directory trees -->
     <delete dir="${build}"/>
     <delete dir="${dist}"/>
   </target>
</project>

buildfile, एक्सएमएल में लिखी जाती है और टास्क की सूची (एक्सएमएल में <target> टैग) के साथ बिल्ड के बारे में कुछ आसान मेटाडेटा तय करती है. (चींटी लक्ष्य शब्द का इस्तेमाल टास्क को दिखाने के लिए करती है, और यह टास्क शब्द का इस्तेमाल करके कमांड बताती है.) हर टास्क, Ant से तय किए गए संभावित निर्देशों की सूची बनाता है. इनमें, डायरेक्ट्री बनाना और मिटाना, javac चलाना, और JAR फ़ाइल बनाना शामिल है. किसी भी तरह के तर्क को शामिल करने के लिए, निर्देशों के इस सेट को उपयोगकर्ता की ओर से दिए गए प्लग-इन से बढ़ाया जा सकता है. हर टास्क उन टास्क के बारे में भी बता सकता है जो डिपेंडेंसी के आधार पर निर्भर होते हैं. जैसा कि चित्र 1 में दिखाया गया है, ये डिपेंडेंसी पानी का ग्राफ़ बनाती हैं.

एक्रिलिक ग्राफ़, जो डिपेंडेंसी दिखा रहा है

चित्र 1. डिपेंडेंसी दिखाने वाला असाइकलिक ग्राफ़

उपयोगकर्ता, Ant के कमांड-लाइन टूल को टास्क बनाकर बिल्ड करते हैं. उदाहरण के लिए, जब कोई उपयोगकर्ता ant dist टाइप करता है, तब Ant ये कार्रवाइयां करता है:

  1. मौजूदा डायरेक्ट्री में build.xml नाम की फ़ाइल लोड करता है और उसे पार्स करता है, ताकि इमेज 1 में दिखाया जा सके.
  2. कमांड लाइन पर दिए गए dist नाम के टास्क की खोज करता है और इससे पता चलता है कि यह compile नाम के टास्क पर निर्भर है.
  3. compile नाम के टास्क को ढूंढने पर, उसे पता चलता है कि यह init नाम के टास्क पर निर्भर है.
  4. init नाम के टास्क की मदद से उसे पता चलता है कि वह कोई डिपेंडेंसी नहीं है.
  5. init टास्क में तय किए गए निर्देशों को लागू करता हो.
  6. हालांकि, compile टास्क में बताए गए निर्देशों का पालन करता है. इसके लिए, यह पक्का करें कि उस टास्क की सभी डिपेंडेंसी चला दी गई हों.
  7. हालांकि, dist टास्क में बताए गए निर्देशों का पालन करता है. इसके लिए, यह पक्का करें कि उस टास्क की सभी डिपेंडेंसी चला दी गई हों.

आखिर में, dist टास्क चलाते समय चींटी ने जो कोड चलाया था वह इन शेल स्क्रिप्ट के बराबर है:

./createTimestamp.sh
mkdir build/
javac src/* -d build/
mkdir -p dist/lib/
jar cf dist/lib/MyProject-$(date --iso-8601).jar build/*

जब सिंटैक्स को साफ़ कर दिया जाता है, तो buildfile और बिल्ड स्क्रिप्ट बहुत अलग नहीं होती हैं. हालांकि, हमने पहले ही काफ़ी कुछ हासिल कर लिया है. हम दूसरी डायरेक्ट्री में नई बिल्डफ़ाइल बना सकते हैं और उन्हें एक साथ जोड़ सकते हैं. हम आसानी से ऐसे नए टास्क जोड़ सकते हैं जो मौजूदा टास्क पर निर्भर करते हैं. साथ ही, उन्हें आर्बिट्रेरी और कॉम्प्लेक्स तरीके से भी जोड़ा जा सकता है. हमें ant कमांड-लाइन टूल में से सिर्फ़ एक टास्क का नाम पास करना होता है. इससे हमें वे सभी चीज़ें पता चलती हैं जो चालू करने की ज़रूरत होती है.

चींटी, सॉफ़्टवेयर का एक पुराना हिस्सा है. इसे मूल रूप से साल 2000 में रिलीज़ किया गया था. मेवन और Gradle जैसे दूसरे टूल ने बीच के सालों में, Ant टूल को बेहतर बनाया है. इन बदलावों के लिए, एक्सटर्नल डिपेंडेंसी के अपने-आप मैनेजमेंट की सुविधा और बिना किसी एक्सएमएल के क्लीनर सिंटैक्स का इस्तेमाल किया गया है. हालांकि, इन नए सिस्टम का काम करने का तरीका एक जैसा ही रहता है: ये इंजीनियर को एक स्क्रिप्ट बनाने और मॉड्यूलर तरीके से, स्क्रिप्ट को एक स्क्रिप्ट के तौर पर लिखने की सुविधा देते हैं. साथ ही, इन टास्क को पूरा करने और इनके बीच डिपेंडेंसी मैनेज करने के टूल भी मुहैया कराते हैं.

टास्क-आधारित बिल्ड सिस्टम का डार्क साइड

ये टूल, इंजीनियर को हर स्क्रिप्ट को एक टास्क की तरह तय करने देते हैं. ये टूल बहुत ज़्यादा कारगर होते हैं. इनकी मदद से, टीम के लोग उन सभी कामों को पूरा कर सकते हैं जिनकी आपने कल्पना की थी. हालांकि, इसमें कमियां हैं और टास्क को ध्यान में रखकर बनाए गए बिल्ड सिस्टम पर काम करना मुश्किल हो सकता है. इसकी वजह यह है कि इनका इस्तेमाल करने से बिल्ड की स्क्रिप्ट ज़्यादा जटिल हो जाती है. ऐसे सिस्टम की समस्या यह है कि वे असल में इंजीनियरों को बहुत ज़्यादा पावर देते हैं और सिस्टम के लिए ज़रूरी पावर नहीं है. सिस्टम को इस बात की जानकारी नहीं है कि स्क्रिप्ट क्या कर रही है. इस वजह से परफ़ॉर्मेंस पर असर पड़ता है, क्योंकि इसे बनाने और शेड्यूल बनाने से जुड़ी बातों में अंतर होना चाहिए. सिस्टम के पास इस बात की पुष्टि करने का कोई तरीका नहीं है कि हर स्क्रिप्ट वही काम कर रही है जो उसे करनी चाहिए. इसलिए, स्क्रिप्ट मुश्किल हो जाती हैं. इस वजह से, उन्हें डीबग करने की ज़रूरत नहीं पड़ती.

बिल्ड स्टेप को पैरलल करने में मुश्किल

आधुनिक डेवलपमेंट वर्कस्टेशन काफ़ी अहम हैं. इनमें कई कोर हैं, जो कई बिल्ड स्टेप को साथ-साथ चालू कर सकते हैं. हालांकि, टास्क पर आधारित सिस्टम आम तौर पर, काम को एक साथ पूरा करने में असमर्थ होते हैं, भले ही उन्हें ऐसा करने में कोई परेशानी न हो. मान लें कि टास्क A, टास्क B और C पर निर्भर करता है. टास्क B और C एक-दूसरे पर निर्भर नहीं होते, इसलिए क्या उन्हें एक ही समय पर चलाना सुरक्षित है, ताकि सिस्टम तेज़ी से टास्क A पर जाए? अगर वे एक ही संसाधन को नहीं छूते, तो. लेकिन शायद नहीं—हो सकता है कि वे दोनों एक ही फ़ाइल का इस्तेमाल करके अपनी स्थितियों को ट्रैक करें और एक ही समय में उन्हें चलाने से समस्या पैदा हो जाए. आम तौर पर, सिस्टम को इसके बारे में पता होने का कोई तरीका नहीं है. इसलिए, इसमें इन समस्याओं की संभावना हो (जो बहुत कम होती है, लेकिन डीबग करने में मुश्किल होती है). इसके अलावा, पूरे बिल्ड को एक ही प्रोसेस में सिंगल थ्रेड पर चलाने तक सीमित करना भी ज़रूरी होता है. यह किसी दमदार डेवलपर मशीन का बहुत बड़ा नुकसान हो सकता है. इससे, उस बिल्ड को कई मशीनों में बांटने की संभावना खत्म हो जाती है.

इंक्रीमेंटल बिल्ड चलाने में परेशानी

एक अच्छे बिल्ड सिस्टम की मदद से इंजीनियर भरोसेमंद तरीके से इंक्रीमेंटल बिल्ड कर सकते हैं, जैसे कि एक छोटे से बदलाव के लिए, पूरे कोडबेस को फिर से स्क्रैच से बनाने की ज़रूरत नहीं होती. यह खास तौर पर तब ज़रूरी होता है, जब बिल्ड सिस्टम धीमा हो और ऊपर बताई गई वजहों से, बिल्ड के चरणों को एक साथ न कर पाए. लेकिन माफ़ करें, काम करने वाले बिल्ड सिस्टम को भी यहां दिक्कत आती है. टास्क से कुछ भी किया जा सकता है, इसलिए यह देखने का कोई तरीका नहीं है कि वे पहले ही किए जा चुके हैं. कई टास्क, बस सोर्स फ़ाइलों का एक सेट लेते हैं और बाइनरी का एक सेट बनाने के लिए कंपाइलर दिखाते हैं. इसलिए, अगर मूल सोर्स फ़ाइलों में कोई बदलाव नहीं किया गया है, तो उन्हें फिर से चलाने की ज़रूरत नहीं है. हालांकि, बिना किसी अतिरिक्त जानकारी के, सिस्टम यह नहीं कह सकता कि शायद टास्क ऐसी फ़ाइल डाउनलोड करता है जो बदल सकती है. इसके अलावा, यह एक ऐसा टाइमस्टैंप भी लिख सकता है जो हर बार चलने पर अलग हो सकता है. सुधार की गारंटी के लिए, सिस्टम को हर बिल्ड के दौरान हर टास्क को फिर से चलाना होगा. कुछ बिल्डिंग सिस्टम, इंक्रीमेंटल बिल्ड को चालू करने की कोशिश करते हैं. इसके लिए, इंजीनियर उन शर्तों की जानकारी देते हैं जिनके तहत टास्क को फिर से चलाने की ज़रूरत होती है. कभी-कभी यह संभव होता है, लेकिन अक्सर यह दिखाई देने से ज़्यादा जटिल समस्या होती है. उदाहरण के लिए, C++ जैसी भाषाओं में जो फ़ाइलों को सीधे दूसरी फ़ाइलों में शामिल करने की अनुमति देती हैं, इनपुट सोर्स को पार्स किए बिना उन फ़ाइलों के पूरे सेट को तय नहीं किया जा सकता जिन्हें बदलावों के लिए देखा जाना चाहिए. इंजीनियर अक्सर शॉर्टकट लेने लगते हैं. इन शॉर्टकट की वजह से, कभी-कभी इस्तेमाल होने वाले टास्क की संभावना बढ़ जाती है और उन्हें परेशानी होती है. भले ही, टास्क के नतीजों का इस्तेमाल दोबारा न करना पड़े. जब ऐसा अक्सर होता है, तो इंजीनियर को हर नई इमारत को बनाने से पहले उसकी सफ़ाई करने की आदत पड़ जाती है, ताकि उसकी स्थिति अपडेट हो सके. साथ ही, यह पक्का हो जाता है कि शुरुआत में इंक्रीमेंटल बिल्ड का मकसद पीछे ही रहे. किसी काम को फिर से करने की ज़रूरत का पता लगाना बेहद मुश्किल होता है. यह काम मशीन की मदद से इंसानाें की तुलना में बेहतर तरीके से किया जा सकता है.

स्क्रिप्ट के रखरखाव और डीबग करने में परेशानी

आखिर में, टास्क-आधारित बिल्ड सिस्टम से लगाई गई बिल्ड स्क्रिप्ट को समझना अक्सर मुश्किल होता है. हालांकि, अक्सर स्क्रिप्ट को जांच के लिए कम जगह मिलती है, लेकिन बिल्ड कोड सिस्टम की तरह ही होते हैं और गड़बड़ियों को छिपाने के लिए ये आसान जगहें हैं. यहां उन गड़बड़ियों के कुछ उदाहरण दिए गए हैं जो आम तौर पर टास्क पर आधारित बिल्ड सिस्टम के साथ काम करते समय होते हैं:

  • टास्क A, आउटपुट के तौर पर किसी खास फ़ाइल को बनाने के लिए टास्क B के हिसाब से काम करता है. टास्क B के मालिक को यह पता नहीं होता कि दूसरे टास्क उसका इस्तेमाल करते हैं. इसलिए, वे उसे किसी दूसरी जगह पर आउटपुट देने के लिए बदलते हैं. इसका पता तब तक नहीं लगाया जा सकता, जब तक कि कोई व्यक्ति टास्क A को चलाने की कोशिश नहीं करता और उसे यह पता नहीं चलता कि वह पूरा नहीं हो सका.
  • टास्क A, टास्क B पर निर्भर करता है. टास्क C पर निर्भर करता है, जहां टास्क A के लिए ज़रूरी आउटपुट के तौर पर एक खास फ़ाइल बनाई जा रही है. टास्क B का मालिक यह तय करता है कि उसे अब टास्क C पर निर्भर नहीं रहना चाहिए, क्योंकि इसकी वजह से टास्क A नाकाम हो जाता है, जबकि टास्क B को टास्क C की परवाह नहीं है!
  • नए टास्क के डेवलपर ने गलती से, टास्क चलाने वाली मशीन के बारे में एक अनुमान लगाया, जैसे कि किसी टूल की जगह या खास एनवायरमेंट वैरिएबल की वैल्यू. टास्क उनकी मशीन पर काम करता है, लेकिन जब भी कोई अन्य डेवलपर उसे आज़माता है, तो यह काम नहीं कर पाता.
  • किसी टास्क में कोई ऐसा कॉम्पोनेंट होता है जो तय नहीं होता है. उदाहरण के लिए, इंटरनेट से फ़ाइल डाउनलोड करना या बिल्ड में टाइमस्टैंप जोड़ना. अब, हर बार जब लोग बिल्ड चलाएंगे, तो उन्हें अलग-अलग नतीजे मिल सकते हैं. इसका मतलब है कि हमेशा एक-दूसरे के बिल्ड होने पर या किसी ऑटोमेटेड बिल्ड सिस्टम में होने वाली खराबी को ठीक करके, इंजीनियर उन्हें ठीक नहीं कर पाएंगे.
  • एक से ज़्यादा डिपेंडेंसी वाले टास्क, रेस कंडीशन बना सकते हैं. अगर टास्क A, टास्क B और टास्क C, दोनों पर निर्भर करता है और टास्क B और C एक ही फ़ाइल में बदलाव करते हैं, तो टास्क A के नतीजे अलग-अलग होते हैं. यह इस बात पर निर्भर करता है कि टास्क B और C में से कौनसा टास्क पहले पूरा होता है.

यहां बताई गई, टास्क पर आधारित फ़्रेमवर्क में इन परफ़ॉर्मेंस, सुधार या रखरखाव की समस्याओं को हल करने का कोई खास मकसद नहीं है. जब तक डेवलपर बिल्ड के दौरान चलने वाले आर्बिट्रेरी कोड लिख पाते हैं, तब तक सिस्टम में यह जानकारी नहीं दी जा सकती कि बिल्ड को हमेशा सही तरीके से और तेज़ी से चलाया जा सकता है या नहीं. इस समस्या को हल करने के लिए, हमें कुछ इंजीनियर के हाथ से कुछ अधिकार वापस लेना होगा और फिर से सिस्टम को कंट्रोल करने का काम करना होगा. साथ ही, सिस्टम की भूमिका की ज़रूरत को भी पूरा करना होगा, न कि सिर्फ़ टास्क पूरे करना और आर्टफ़ैक्ट बनाना.

इससे हमें ब्लेज़ और बेज़ल जैसे आर्टफ़ैक्ट पर आधारित बिल्ड सिस्टम बनाने में मदद मिली.