जीएनयू खराब क्यों है?

GNU मेक ऑटोमैटिक प्रोजेक्ट्स के निर्माण के लिए एक प्रसिद्ध उपयोगिता है। UNIX दुनिया में, यह इस कार्य के लिए वास्तविक मानक है। विंडोज डेवलपर्स के बीच इतना लोकप्रिय नहीं होने के बावजूद, इसने माइक्रोसॉफ्ट से एनएमके के रूप में इस तरह के एनालॉग के उभरने का नेतृत्व किया।

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

बेशक, निर्माण को स्वचालित करने के लिए केवल उपयोगिता नहीं है। मेक की सीमाओं को हटाने के लिए कई अन्य उपकरण बनाए गए हैं। उनमें से कुछ निश्चित रूप से मूल मेक से बेहतर हैं, लेकिन इससे मेक की लोकप्रियता प्रभावित नहीं हुई। इस दस्तावेज़ का उद्देश्य, सरल शब्दों में, मेक से जुड़ी कुछ समस्याओं के बारे में बात करना है, ताकि वे आपको आश्चर्यचकित न करें।

इस लेख के अधिकांश तर्क मूल UNIX मेक और GNU मेक से संबंधित हैं। चूंकि जीएनयू मेक आज सबसे अधिक संभावना है, जब हम मेक या "मेकफाइल्स" का उल्लेख करते हैं, तो हमारा मतलब होगा कि जीएनयू मेक।

लेख यह भी मानता है कि पाठक पहले से ही इस तरह की अवधारणाओं को "नियम", "लक्ष्य" और "निर्भरता" के रूप में समझने और समझने के लिए बुनियादी स्तर पर परिचित है।

भाषा डिजाइन


जिस किसी ने कभी भी मेकफाइल लिखा है, उसके सिंटैक्स की "ख़ासियत" में सबसे अधिक संभावना है: यह टैब का उपयोग करता है। कमांड की शुरुआत का वर्णन करने वाली प्रत्येक पंक्ति को टैब वर्ण से शुरू करना चाहिए। रिक्त स्थान फिट नहीं है - सिर्फ एक टैब। दुर्भाग्य से, यह मेक लैंग्वेज के अजीब पहलुओं में से एक है।

पुनरावर्ती बनाते हैं

जब एक नियम दूसरे मेक सेशन को बनाता है तो मेकफाइल नियमों को परिभाषित करते समय रिकर्सिव मेक एक सामान्य पैटर्न है। चूंकि प्रत्येक मेक सेशन केवल एक बार शीर्ष-स्तरीय मेकफाइल को पढ़ता है, यह कई उप-परियोजनाओं से मिलकर एक प्रोजेक्ट के लिए मेकफाइल का वर्णन करने का एक प्राकृतिक तरीका है।

"पुनरावर्ती बनाना" इतनी समस्याएं पैदा करता है कि एक लेख भी यह दिखाने के लिए लिखा गया है कि यह समाधान खराब क्यों है। यह कई कठिनाइयों (जिनमें से कुछ का उल्लेख नीचे किया गया है) की पहचान करता है, लेकिन मेकफाइल्स लिखना जो पुनरावृत्ति का उपयोग नहीं करते हैं, वास्तव में एक मुश्किल काम है।

पार्सर

प्रोग्रामिंग भाषाओं के अधिकांश पार्सर व्यवहार के समान पैटर्न का पालन करते हैं। शुरुआत में, स्रोत पाठ को "टोकन" या "स्कैन" में विभाजित किया जाता है, टिप्पणियों और रिक्त स्थान को बाहर निकाल दिया जाता है और इनपुट पाठ (काफी स्वतंत्र रूप में निर्दिष्ट) का अनुवाद "टोकन", जैसे "वर्ण", "पहचानकर्ता" और "आरक्षित शब्द" की एक धारा में किया जाता है। । टोकन की परिणामी धारा तब एक भाषा व्याकरण का उपयोग करके "पार्स" की जाती है जो यह निर्धारित करती है कि कौन से संयोजन और टोकन का क्रम सही है। अंत में, परिणामी "व्याकरण वृक्ष" की व्याख्या, संकलित आदि की जाती है।

मेक पार्सर इस मानक मॉडल का पालन नहीं करता है। आप एक ही समय में इसे निष्पादित किए बिना एक मेकफाइल पार्स नहीं कर सकते। परिवर्तनीय प्रतिस्थापन कहीं भी हो सकता है, और चूँकि आप किसी चर का मूल्य नहीं जानते हैं, आप पार्स करना जारी नहीं रख सकते। परिणामस्वरूप, एक अलग उपयोगिता लिखना एक बहुत ही गैर-तुच्छ कार्य है जो मेकफाइल्स को पार्स कर सकता है, क्योंकि आपको पूरी भाषा का कार्यान्वयन लिखना होगा।

इसके अलावा, भाषा में टोकन में कोई स्पष्ट विभाजन नहीं है। उदाहरण के लिए, देखते हैं कि अल्पविराम कैसे संसाधित किया जाता है।

कभी-कभी अल्पविराम एक लाइन का हिस्सा होता है और इसकी कोई विशेष स्थिति नहीं होती है:
X = y,z 


कभी-कभी एक अल्पविराम उन रेखाओं को अलग कर देता है जिनकी तुलना यदि कथन में की जाती है :
 ifeq ($(X),$(Y)) 


कभी-कभी एक अल्पविराम समारोह के तर्क को अलग करता है:
 $(filter %.c,$(SRC_FILES)) 


लेकिन कभी-कभी, फ़ंक्शन तर्कों के बीच भी, अल्पविराम लाइन का हिस्सा होता है:
 $(filter %.c,ac bc c.cpp d,ec) 

(चूंकि filter केवल दो मापदंडों को स्वीकार करता है, अंतिम अल्पविराम एक नया पैरामीटर नहीं जोड़ता है; यह दूसरे वर्ण के पात्रों में से एक बन जाता है)

रिक्त स्थान समान अस्पष्ट नियमों का पालन करते हैं। कभी-कभी अंतराल को ध्यान में रखा जाता है, कभी-कभी नहीं। लाइनें उद्धरण चिह्नों में संलग्न नहीं हैं, इस वजह से, यह स्पष्ट रूप से स्पष्ट नहीं है कि कौन से स्थान महत्वपूर्ण हैं। एक "सूची" (केवल लाइनें मौजूद) के रूप में इस तरह के डेटा प्रकार की कमी के कारण, रिक्त स्थान को सूची आइटम के लिए विभाजक के रूप में उपयोग किया जाना चाहिए। नतीजतन, यह तर्क की अत्यधिक जटिलता की ओर जाता है, उदाहरण के लिए, यदि फ़ाइल नाम में बस एक स्थान होता है।

निम्नलिखित उदाहरण रिक्त स्थान को संभालने के लिए भ्रमित तर्क को दर्शाता है। एक चर बनाने के लिए अस्पष्ट चाल की आवश्यकता होती है जो किसी स्थान के साथ समाप्त होती है। (आमतौर पर लाइनों के सिरों पर रिक्त स्थान एक पार्सर द्वारा फेंक दिए जाते हैं, लेकिन यह पहले होता है, लेकिन चर के प्रतिस्थापन के बाद नहीं।)
 NOTHING := SPACE := $(NOTHING) $(NOTHING) CC_TARGET_PREFIX := -o$(SPACE) #       $(CC_TARGET_PREFIX)$@ 


और हमने सिर्फ अल्पविराम और रिक्त स्थान को छुआ। केवल कुछ ही लोग मेक पार्सर की पेचीदगियों को समझते हैं।

Uninitialized और पर्यावरण चर।

यदि मेकफाइल में अनइंस्टाल्यूटेड वैरिएबल एक्सेस किया गया है, तो त्रुटि की रिपोर्ट नहीं करें। इसके बजाय, उसे इस चर का मान उसी नाम के वातावरण चर से प्राप्त होता है। यदि समान नाम वाला एक पर्यावरण चर नहीं पाया जाता है, तो यह माना जाता है कि मान एक रिक्त स्ट्रिंग होगा।

इससे दो तरह की समस्याएं होती हैं। सबसे पहले, टाइपोस पकड़े नहीं जाते हैं और त्रुटियों को नहीं माना जाता है (आप ऐसी स्थितियों के लिए चेतावनी जारी करने के लिए मजबूर कर सकते हैं, लेकिन यह व्यवहार डिफ़ॉल्ट रूप से अक्षम है, और कभी-कभी जानबूझकर उपयोग नहीं किए गए चर)। दूसरा - पर्यावरण चर अप्रत्याशित रूप से आपके मेकअप के कोड को प्रभावित कर सकता है। आप यह सुनिश्चित नहीं कर सकते हैं कि उपयोगकर्ता द्वारा कौन से चर निर्धारित किए जा सकते हैं, इसलिए, विश्वसनीयता के लिए, आपको उन्हें संदर्भित करने या += माध्यम से जोड़ने से पहले सभी चर को आरंभ करना होगा +=

" export FOO=1 ; make कॉल करें" के साथ " make FOO=1 " के रूप में बुलाया जाने पर मेक के व्यवहार के बीच एक भ्रामक अंतर भी होता है। पहले मामले में, FOO = 0 में लाइन का कोई प्रभाव नहीं है! इसके बजाय, आपको override FOO = 0 लिखना चाहिए।

सशर्त अभिव्यक्ति सिंटैक्स

मेकफाइल भाषा की मुख्य कमियों में से एक है सशर्त अभिव्यक्तियों के लिए इसका सीमित समर्थन (विशेष रूप से, स्टेटमेंट, क्रॉस-प्लेटफ़ॉर्म मेकफाइल्स लिखने के लिए महत्वपूर्ण हैं)। नए संस्करणों के पहले से ही " अगर " सिंटैक्स के लिए समर्थन शामिल है। बेशक, यदि कथन में केवल चार मूल विकल्प हैं: ifeq, ifneq, ifdef और ifndef । यदि आपकी स्थिति अधिक जटिल है और "और / या / नहीं" के लिए जाँच की आवश्यकता है, तो आपको अधिक बोझिल कोड लिखना होगा।

मान लें कि हमें लिनक्स / x86 को लक्ष्य प्लेटफ़ॉर्म के रूप में परिभाषित करने की आवश्यकता है। निम्नलिखित हैक उसके सरोगेट के साथ "और" स्थिति को बदलने का सामान्य तरीका है:
 ifeq ($(TARGET_OS)-$(TARGET_CPU),linux-x86) foo = bar endif 


स्थिति "या" इतनी सरल नहीं होगी। मान लें कि हमें x86 या x86_64 को परिभाषित करने की आवश्यकता है, और " foo = bar " के बजाय हमारे पास 10+ लाइनों के लिए कोड है और हम इसे डुप्लिकेट नहीं करना चाहते हैं। हमारे पास कई विकल्प हैं, जिनमें से प्रत्येक खराब है:
 # ,   ifneq (,$(filter x86 x86_64,$(TARGET_CPU)) foo = bar endif # ,    ifeq ($(TARGET_CPU),x86) TARGET_CPU_IS_X86 := 1 else ifeq ($(TARGET_CPU),x86_64) TARGET_CPU_IS_X86 := 1 else TARGET_CPU_IS_X86 := 0 endif ifeq ($(TARGET_CPU_IS_X86),1) foo = bar endif 


यदि भाषा पूर्ण वाक्यविन्यास का समर्थन करती है, तो मेकफाइल्स के कई स्थानों को सरल बनाया जा सकता है।

दो प्रकार के चर

मेक में दो तरह के वेरिएबल असाइनमेंट हैं। " : = " अभिव्यक्ति का मूल्यांकन तुरंत दाईं ओर करता है। सामान्य " = " अभिव्यक्ति का मूल्यांकन बाद में करता है जब चर का उपयोग किया जाता है। पहला विकल्प अधिकांश अन्य प्रोग्रामिंग भाषाओं में उपयोग किया जाता है और, एक नियम के रूप में, विशेष रूप से अधिक प्रभावी है, अगर अभिव्यक्ति की गणना करना मुश्किल है। दूसरा विकल्प, बेशक, ज्यादातर मेकफाइल्स में उपयोग किया जाता है।

" = " का उपयोग करने के उद्देश्य उद्देश्य हैं (आस्थगित संगणना के साथ)। लेकिन अक्सर आप अधिक सटीक मेकफाइल आर्किटेक्चर का उपयोग करके इससे छुटकारा पा सकते हैं। यहां तक ​​कि प्रदर्शन के मुद्दे पर विचार किए बिना, आस्थगित गणना मेकफाइल कोड को पढ़ने और समझने में अधिक कठिन बनाते हैं।

आमतौर पर, आप शुरू से अंत तक एक कार्यक्रम पढ़ सकते हैं - उसी क्रम में जिसमें इसे निष्पादित किया जाता है, और यह जान लें कि किसी भी समय यह किस स्थिति में है। आस्थगित गणना के साथ, आप प्रोग्राम में आगे क्या होता है, यह जाने बिना एक चर का मूल्य नहीं जान सकते। एक चर अपने मान को अप्रत्यक्ष रूप से बदल सकता है, बिना इसे सीधे बदले। यदि आप "डिबग आउटपुट" का उपयोग करते हुए मेकफाइल में त्रुटियों को देखने का प्रयास करते हैं, उदाहरण के लिए:
 $(warning VAR=$(VAR)) 
... आपको वह नहीं मिल सकता है जिसकी आपको आवश्यकता है।

पैटर्न प्रतिस्थापन और फ़ाइल खोज

कुछ नियम फ़ाइल नाम के मुख्य भाग (एक्सटेंशन के बिना) को इंगित करने के लिए% साइन का उपयोग करते हैं - ताकि दूसरों के लिए कुछ फ़ाइलों को बनाने के लिए नियम निर्धारित किया जा सके। उदाहरण के लिए, एक्सटेंशन फ़ाइल के साथ ऑब्जेक्ट फ़ाइल में .c फ़ाइलों को संकलित करने के लिए " % .o:% .c " नियम।

माना कि हमें एक ऑब्जेक्ट फ़ाइल foo.o बनाने की आवश्यकता है, लेकिन स्रोत फ़ाइल foo.c वर्तमान निर्देशिका में कहीं नहीं है। Make में एक vpath निर्देश है जो उसे बताता है कि ऐसी फ़ाइलों को कहाँ देखना है। दुर्भाग्य से, अगर निर्देशिका में foo.c नाम की एक निर्देशिका दो बार मिली है, तो गलत फ़ाइल का चयन कर सकते हैं।

मेकफाइल्स के लिए निम्न मानक प्रोग्रामिंग पैटर्न विफल हो जाता है यदि दो स्रोत फ़ाइलों का नाम एक ही है (लेकिन एक अलग एक्सटेंशन के साथ) और एक साथ झूठ बोलें। समस्या यह है कि रूपांतरण "स्रोत फ़ाइल नाम => ऑब्जेक्ट फ़ाइल नाम" कुछ जानकारी खो देता है, लेकिन मेक'आ डिज़ाइन को इसके लिए रिवर्स मैपिंग करने की आवश्यकता होती है।

 O_FILES := $(patsubst %.c,%.o,$(notdir $(C_FILES))) vpath %.c $(sort $(dir $(C_FILES))) $(LIB): $(O_FILES) 


और अन्य गायब विशेषताएं

मेक किसी भी डेटा प्रकारों को नहीं जानता है - बस तार। कोई बूलियन प्रकार, सूची, शब्दकोश।
"गुंजाइश" की कोई अवधारणा नहीं है। सभी चर वैश्विक हैं।
चक्रों के लिए समर्थन सीमित है। $ (foreach) कई बार अभिव्यक्ति का मूल्यांकन करेगा और परिणामों को संयोजित करेगा, लेकिन आप $ (foreach) का उपयोग नहीं कर सकते हैं, उदाहरण के लिए, नियमों का एक समूह।
उपयोगकर्ता-परिभाषित फ़ंक्शंस मौजूद हैं, लेकिन फ़ॉरच के समान सीमाएं हैं। वे केवल चर प्रतिस्थापन के साथ सौदा कर सकते हैं और पूरी तरह से भाषा वाक्यविन्यास का उपयोग नहीं कर सकते हैं या नई निर्भरता बना सकते हैं।

विश्वसनीयता


मेक की विश्वसनीयता कम है, खासकर बड़ी परियोजनाओं या वृद्धिशील संकलन पर। कभी-कभी असेंबली एक अजीब त्रुटि के साथ क्रैश हो जाती है, और आपको "मैजिक मंत्र" का उपयोग करना होगा जैसे कि साफ करें और आशा करें कि सब कुछ तय हो गया है। कभी-कभी (अधिक खतरनाक स्थिति) सब कुछ अच्छा लगता है, लेकिन कुछ को फिर से तैयार नहीं किया गया है और आपका आवेदन शुरू होने के बाद दुर्घटनाग्रस्त हो जाएगा।

गुम निर्भरताएँ

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

"अंतिम फ़ाइल संशोधन के समय" लेबल का उपयोग करना

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

कमांड लाइन विकल्प

जब एक प्रोग्राम का पैरामीटर स्ट्रिंग बदलता है, तो इसके परिणाम भी बदल सकते हैं (उदाहरण के लिए, एक बदलाव -Doption, जिसे C प्रीप्रोसेसर को पास किया जाता है)। मेक इस मामले में recompile नहीं करेगा, जिसके परिणामस्वरूप गलत इंटरमीडिएट recompilation होगा।

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

पर्यावरण चर और उन पर निर्भरता का विरासत

न केवल प्रत्येक पर्यावरण चर एक परिवर्तनशील चर बन जाता है, बल्कि ये चर हर कार्यक्रम के लिए पारित किए जाते हैं जो रन बनाते हैं। चूंकि प्रत्येक उपयोगकर्ता का पर्यावरण चर का अपना सेट होता है, इसलिए एक ही असेंबली चलाने वाले दो उपयोगकर्ता अलग-अलग परिणाम प्राप्त कर सकते हैं।
बाल प्रक्रिया में पारित किसी भी पर्यावरण चर को बदलने से इसका उत्पादन बदल सकता है। यही है, ऐसी स्थिति को एक पुनर्निर्माण शुरू करना चाहिए, लेकिन मेक नहीं होगा।

एकाधिक समवर्ती सत्र

यदि आप एक ही समय में एक ही निर्देशिका में बनाने के दो उदाहरण चलाते हैं, तो वे उसी फ़ाइलों को संकलित करने का प्रयास करते समय टकराएंगे। सबसे अधिक संभावना है, उनमें से एक (या यहां तक ​​कि दोनों) दुर्घटनाग्रस्त हो जाएगा।

पुनर्निर्माण के दौरान फ़ाइलों का संपादन।

यदि आपने फ़ाइल को चलाने के दौरान संपादित और सहेजा है, तो परिणाम की भविष्यवाणी नहीं की जा सकती है। शायद मेक इन बदलावों को सही ढंग से उठाएगा, या शायद नहीं, और आपको फिर से बनाने की आवश्यकता होगी। या, यदि आप भाग्य से बाहर हैं, तो बचत ऐसे क्षण में हो सकती है कि कुछ लक्ष्यों के पुनर्निर्माण की आवश्यकता होगी, लेकिन बाद में रन बनाने के लिए यह नहीं मिलेगा।

अनावश्यक फ़ाइलों को हटा दें

मान लीजिए कि आपकी परियोजना ने मूल रूप से फ़ाइल foo.c का उपयोग किया है, लेकिन बाद में इस फ़ाइल को परियोजना से और मेकफाइल से हटा दिया गया। अस्थायी ऑब्जेक्ट फ़ाइल foo.o रहेगी। यह आमतौर पर स्वीकार्य है, लेकिन ऐसी फाइलें समय के साथ जमा हो सकती हैं और कभी-कभी समस्याएं पैदा कर सकती हैं। उदाहरण के लिए, वे गलती से vpath खोज के दौरान चुने जा सकते हैं। एक अन्य उदाहरण: मान लीजिए कि असेंबली के दौरान पहले से निर्मित फ़ाइलों में से एक को अब संस्करण नियंत्रण प्रणाली में डाल दिया गया है। इस फ़ाइल को बनाने वाला नियम भी मेकफ़ाइल से हटा दिया गया है। हालांकि, संस्करण नियंत्रण प्रणालियां आमतौर पर फाइलों को अधिलेखित नहीं करती हैं यदि वे देखते हैं कि समान नाम वाली गैर-संस्करण वाली फ़ाइल पहले से मौजूद है (कुछ महत्वपूर्ण को हटाने के डर से)। यदि आपने ऐसी त्रुटि के बारे में संदेश पर ध्यान नहीं दिया, तो इस फ़ाइल को मैन्युअल रूप से नहीं हटाया और फिर से स्रोत निर्देशिका को अपडेट नहीं किया, तो आप इस फ़ाइल के पुराने संस्करण का उपयोग करेंगे।

फ़ाइल का नाम सामान्यीकरण

आप एक ही फाइल को विभिन्न रास्तों का उपयोग करके एक्सेस कर सकते हैं। यहां तक ​​कि हार्ड और प्रतीकात्मक लिंक को ध्यान में रखे बिना, foo.c, .foo.c, ../bar/foo.c, /home/user/bar/foo.c उसी फ़ाइल को इंगित कर सकते हैं। उन्हें उचित तरीके से संभालना चाहिए, हालांकि वह नहीं करता है।
समस्या विंडोज के तहत और भी खराब है, जहां फाइल सिस्टम संवेदनशील नहीं है।

एक बाधित या असफल पुनर्निर्माण के परिणाम

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

उत्पादकता


परियोजना का आकार बढ़ने पर मेक का प्रदर्शन अच्छी तरह से (गैर-रैखिक रूप से) पैमाने पर नहीं होता है।

वृद्धिशील विधानसभा प्रदर्शन

आप उम्मीद कर सकते हैं कि किसी परियोजना को फिर से तैयार करना उन लक्ष्यों की संख्या के लिए आनुपातिक लगता है जिन्हें फिर से बनाने की आवश्यकता है। दुर्भाग्य से, ऐसा नहीं है।
इस तथ्य के कारण कि वृद्धिशील असेंबली का परिणाम हमेशा आत्मविश्वास को प्रेरित नहीं करता है, उपयोगकर्ताओं को अधिक या कम नियमित रूप से पूर्ण पुनर्निर्माण करना चाहिए, कभी-कभी यदि आवश्यक हो (यदि कुछ नहीं जा रहा है, तो साफ करने की कोशिश करें , बनाओ ), और कभी-कभी लगातार (व्यामोह के कारण) )। यह सुनिश्चित होना बेहतर है और जोखिम के मुकाबले पूर्ण पुनर्निर्माण के लिए प्रतीक्षा करें कि कुछ हिस्सा स्रोत के साथ सिंक से बाहर है।
फ़ाइल का "अंतिम संशोधित समय" फ़ाइल की सामग्री को बदले बिना बदल सकता है। इससे अनावश्यक पुन: टिप्पणियां होती हैं।
खराब लिखे गए मेकफाइल में बहुत अधिक निर्भरताएं हो सकती हैं, इस वजह से, लक्ष्यों को तब भी बदला जा सकता है, भले ही इसकी (वास्तविक) निर्भरताएं बदल नहीं गई हों। "फोनी" लक्ष्यों का गलत उपयोग त्रुटियों का एक अन्य स्रोत है (ऐसे लक्ष्यों को हमेशा पुनर्गठन किया जाना चाहिए)।
यहां तक ​​कि अगर आपके मेकफाइल्स में त्रुटियां नहीं हैं, और आपके वृद्धिशील बिल्ड बिल्कुल विश्वसनीय हैं, तो प्रदर्शन सही नहीं है। मान लीजिए कि आपने एक बड़ी परियोजना में .c फ़ाइलों (हेडर फ़ाइल नहीं) में से एक को संपादित किया है। यदि आप प्रोजेक्ट के मूल में टाइप करते हैं, तो मेक को सभी मेकफाइल्स को पार्स करना होगा, कई बार खुद को इनवॉइस करना होगा, और यह पता लगाने के लिए सभी निर्भरता से गुजरना होगा कि क्या उन्हें पुनर्निर्माण की आवश्यकता है। कंपाइलर का प्रारंभ समय कुल समय की तुलना में काफी कम हो सकता है।

पुनरावर्ती बनाने और प्रदर्शन

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

समानांतर मेक

मेक का "समानांतर लॉन्च" गति में एक बड़ी वृद्धि का वादा करता है, खासकर आधुनिक प्रोसेसर पर कई कोर के साथ। दुर्भाग्य से, वास्तविकता वादों से दूर है।
"समानांतर मेक" का पाठ आउटपुट पढ़ना मुश्किल है। यह देखना मुश्किल है कि कौन सी चेतावनी / लाइन / आदि। किस टीम को संदर्भित करता है जब एक साथ कई प्रक्रियाएं एक ही वातावरण में काम करती हैं।
समानांतर मेक निर्भरता के सही विनिर्देश के प्रति विशेष रूप से संवेदनशील है। यदि दो नियम निर्भरता के माध्यम से नहीं जुड़े हैं, तो मान लें कि उन्हें किसी भी क्रम में लागू किया जा सकता है। जब एक एकल मेक को बुलाया जाता है, तो इसका व्यवहार पूर्वानुमेय होता है: यदि A B और C पर निर्भर करता है, तो पहले B का निर्माण किया जाएगा, फिर C, फिर A का, निश्चित रूप से C से B के निर्माण का अधिकार होगा, लेकिन (क्रमबद्ध तरीके से) आदेश को परिभाषित किया गया है। ।
समानांतर मोड में, बी और सी (लेकिन आवश्यक नहीं हैं) समानांतर में बनाए जा सकते हैं। यदि C (वास्तव में) B पर निर्भर करता है, लेकिन यह निर्भरता मेकफाइल में नहीं लिखी गई है, तो C के निर्माण में सबसे अधिक संभावना असफल होगी (लेकिन आवश्यक नहीं, विशिष्ट समय के आधार पर)।
समानांतर बनाना मेकफाइल्स में गुम निर्भरता के मुद्दों को चुटकियों में खत्म कर देता है। यह अपने आप में एक अच्छी बात है। वे अन्य समस्याओं को जन्म देते हैं, और यह बहुत अच्छा है कि आप उन्हें पकड़ सकते हैं और उन्हें ठीक कर सकते हैं। लेकिन व्यवहार में, बड़ी परियोजनाओं पर, एक समानांतर मेक का उपयोग करने का परिणाम निराशाजनक है।
एक पुनरावर्ती मेक के साथ समानांतर मेक की बातचीत मुश्किल है। प्रत्येक मेक'आ सत्र स्वतंत्र होता है, अर्थात प्रत्येक अपने कार्य को स्वतंत्र रूप से दूसरों के समानांतर करने का प्रयास करता है और पूर्ण निर्भरता वाले पेड़ का सामान्य विचार नहीं रखता है। हमें विश्वसनीयता और प्रदर्शन के बीच समझौता करना चाहिए। एक ओर, हम असेंबली को केवल एक मेकफाइल के नहीं, बल्कि अन्य सभी मेकफाइल्स के समानांतर बनाना चाहते हैं। लेकिन, चूंकि मेक क्रॉस-मेकफाइल निर्भरता के बारे में नहीं जानता है, पूरी तरह से उप-समानांतर काम नहीं करता है।
कुछ उप-मेक को समानांतर में चलाया जा सकता है, अन्य को सीरियल मोड में चलाया जाना चाहिए। इन निर्भरताओं को निर्दिष्ट करना असुविधाजनक है, और उनमें से कुछ को छोड़ना बहुत आसान है। मेकफाइल ट्री को पार्स करने के एक विश्वसनीय क्रमिक तरीके से लौटने और एक समय में केवल एकल मेकफाइल्स को समानांतर करने का प्रलोभन है, लेकिन यह विशेष रूप से वृद्धिशील बिल्ड के साथ समग्र प्रदर्शन को कम करता है।

Microsoft Visual C ++ के लिए स्वचालित निर्भरता पीढ़ी

कई संकलक, जैसे जीसीसी, मेक द्वारा समझे गए प्रारूप में निर्भरता की जानकारी प्रदान कर सकते हैं। दुर्भाग्य से, Microsoft Visual C ++ ऐसा नहीं करता है। इसमें एक विशेष कुंजी / शो-इंक शामिल है , लेकिन इस जानकारी को मेकए प्रारूप में अनुवाद करने के लिए एक अतिरिक्त स्क्रिप्ट की आवश्यकता होती है। इसके लिए प्रत्येक C- फाइल पर एक अलग स्क्रिप्ट चलाने की आवश्यकता होती है। चल रहा है, उदाहरण के लिए, प्रत्येक फ़ाइल के लिए पायथन दुभाषिया एक त्वरित ऑपरेशन नहीं है।

इनलाइन नियम

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

अन्य


अन्य मेक नोट्स भी हैं जो पिछली श्रेणियों में नहीं आते हैं।

मौन सोना है

एरिक रेमंड के अनुसार, “सबसे पुराना और अयोग्य UNIX विश्व डिजाइन नियमों में से एक यह है कि यदि किसी कार्यक्रम में दिलचस्प या अप्रत्याशित कहने के लिए कुछ नहीं है, तो उसे चुप रहना चाहिए। अच्छी तरह से व्यवहार किए गए कार्यक्रम अपने काम को विनीत रूप से करते हैं, कम से कम आवश्यक ध्यान और चिंता के साथ। मौन सोना है। ” मेक इस नियम का पालन नहीं करता है।
जब आप रन बनाते हैं, तो इसके लॉग में सभी कमांड्स होते हैं जो इसे चलाता है और सब कुछ जो ये कमांड स्टैडआउट और स्टेडर को देते हैं। यह बहुत ज्यादा है। इस धारा में महत्वपूर्ण चेतावनियाँ / त्रुटियाँ डूब जाती हैं, और पाठ को अक्सर इतनी जल्दी प्रदर्शित किया जाता है कि यह अपठनीय हो जाता है।
आप मेक-अप चलाकर इस आउटपुट को बहुत कम कर सकते हैंलेकिन यह डिफ़ॉल्ट व्यवहार नहीं है। इसके अलावा, कोई मध्यवर्ती विकल्प नहीं है, जिसमें यह प्रदर्शित किया जाता है कि यह अब क्या कर रहा है - कमांड लाइन टाइप किए बिना।

बहुउद्देशीय नियम

कुछ उपयोगिताओं उनके काम के परिणामस्वरूप एक से अधिक फ़ाइल उत्पन्न करती हैं। लेकिन नियम बनाना केवल एक उद्देश्य हो सकता है। यदि आप ऐसी अतिरिक्त फ़ाइल पर एक अलग निर्भरता लिखने का प्रयास करते हैं, तो दो नियमों के बीच संबंध का पता नहीं लगा सकते।

चेतावनी जो त्रुटियां होनी चाहिए

प्रिंट को चेतावनी दें, लेकिन अगर यह चक्रीय निर्भरता का पता लगाता है तो रोक नहीं सकता है। यह, सबसे अधिक संभावना है, मेकफाइल में एक गंभीर त्रुटि को इंगित करता है, लेकिन इस स्थिति को मामूली उपद्रव के रूप में मूल्यांकन करता है।
इसी तरह, प्रिंट को चेतावनी दें (और आगे काम करना जारी रखें) यदि यह पता लगाता है कि दो नियम हैं जो एक लक्ष्य बनाने का वर्णन करते हैं। वह बस उनमें से एक को अनदेखा करता है। और फिर से - यह मेकफाइल में एक गंभीर बग है, लेकिन ऐसा नहीं लगता है।

निर्देशिका बनाना

अलग-अलग निर्देशिकाओं में अलग-अलग कॉन्फ़िगरेशन के लिए आउटपुट फ़ाइलों को रखना बहुत सुविधाजनक है, और जब आप कॉन्फ़िगरेशन बदलते हैं तो आपको पूरी परियोजना को फिर से बनाने की आवश्यकता नहीं होगी। उदाहरण के लिए, आप "डीबग" बायनेरिज़ को "डीबग" निर्देशिका में रख सकते हैं और इसी तरह "रिलीज़" कॉन्फ़िगरेशन के लिए। लेकिन इससे पहले कि आप इन निर्देशिकाओं में फाइलें डालना शुरू करें, आपको उन्हें बनाना होगा।
यह बहुत अच्छा होगा यदि इसे स्वचालित रूप से किया जाए - जाहिर है कि लक्ष्य का निर्माण करना असंभव है यदि इसकी निर्देशिका अभी तक मौजूद नहीं है - लेकिन यह नहीं है। प्रत्येक नियम में mkdir -p $ (dir $ @)
को कॉल करना बहुत व्यावहारिक नहीं है । यह अक्षम है, और इसके अलावा, आपको त्रुटि को अनदेखा करना चाहिए यदि निर्देशिका पहले से मौजूद है। आप इस तरह से समस्या को हल करने की कोशिश कर सकते हैं:

 debug/%.o: %.c debug $(CC) -c $< -o $@ debug: mkdir $@ 


यह व्यावहारिक लगता है - यदि "डीबग" मौजूद नहीं है, तो डीबग / foo.o संकलित करने से पहले इसे बनाएं। लेकिन केवल दिखता है। एक निर्देशिका में एक नई फ़ाइल बनाने से इस निर्देशिका का "अंतिम संशोधन समय" बदल जाता है। मान लीजिए हम दो फाइल संकलित कर रहे हैं - डिबग / foo.o और डीबग / बार ।o. डीबग / बार बनाना। डिबग डायरेक्टरी के संशोधन समय को बदल देगा। अब यह डीबग / foo.o बनाने के समय की तुलना में नया हो जाएगा, अर्थात, अगली बार जब हम कॉल करते हैं, तो डिबग / foo.o फ़ाइल अनावश्यक रूप से पुन: उपयोग की जाएगी। और अगर पुरानी फ़ाइल को हटाने और एक नया बनाने (और मौजूदा फ़ाइल को अधिलेखित करके नहीं) द्वारा पुन: निर्माण किया जाता है, तो आपको अनावश्यक पुनर्मूल्यांकन की अंतहीन श्रृंखला मिल जाएगी।
समाधान फ़ाइल पर एक निर्भरता बनाने के लिए है (उदाहरण के लिए, डिबग / डमीटेक्स्ट), और निर्देशिका पर नहीं। इसके लिए मेकफाइल ( टच डिबग / डमीटैक्स ) में अतिरिक्त क्रियाओं की आवश्यकता होती है , और स्वचालित रूप से इंटरमीडिएट फ़ाइलों को हटाने की क्षमता के साथ संघर्ष हो सकता है। और यदि आप प्रत्येक लक्ष्य के लिए इस अतिरिक्त निर्भरता (dummy.txt पर) को निर्दिष्ट करने में सावधान नहीं हैं, तो जब आप समानांतर में बनाते हैं तो आपको समस्याएं होंगी।

निष्कर्ष


बनाओ कई दोषों के साथ एक लोकप्रिय उपयोगिता है। यह आपके जीवन को सरल बना सकता है, या इसे जटिल बना सकता है। यदि आप एक बड़े सॉफ्टवेयर उत्पाद पर काम कर रहे हैं, तो आपको बनाने के लिए अन्य विकल्पों पर विचार करना चाहिए। यदि आपको केवल मेक का उपयोग करना चाहिए, तो आपको इसकी कमियों के बारे में पता होना चाहिए।

पुनश्च: उपरोक्त सभी इस लेख का अनुवाद है लंबे समय से मैं एक ही विषय पर एक विषय लिखने जा रहा था, लेकिन लेख पढ़ने के बाद, मुझे एहसास हुआ कि अनुवाद करना बेहतर होगा। लेखक के सभी तर्क "मेकअप-विशिष्ट" नहीं हैं (और कुछ आम तौर पर इस तरह की सभी उपयोगिताओं के लिए लागू होते हैं), लेकिन सभी प्रोग्रामर जिन्हें इसे अपने काम में उपयोग करना है, उन्हें अलग-अलग रेक को जानने और समझने की आवश्यकता है।

Source: https://habr.com/ru/post/In138682/


All Articles