GNU मेकफाइल नियम एकल स्रोत फ़ाइल से कुछ लक्ष्य बनाते हैं


105

मैं निम्नलिखित करने का प्रयास कर रहा हूं। एक कार्यक्रम है, इसे कॉल करें foo-bin, जो एकल इनपुट फ़ाइल में लेता है और दो आउटपुट फ़ाइलों को उत्पन्न करता है। इसके लिए गूंगा मेकफाइल नियम होगा:

file-a.out file-b.out: input.in
    foo-bin input.in file-a.out file-b.out

हालांकि, यह makeकिसी भी तरह से नहीं बताता है कि दोनों लक्ष्य एक साथ उत्पन्न होंगे। makeधारावाहिक में भागते समय यह ठीक है , लेकिन अगर कोई कोशिश करता है make -j16या समान रूप से पागल हो जाता है, तो परेशानी का कारण होगा ।

सवाल यह है कि क्या ऐसे मामले के लिए उचित मेकफाइल नियम लिखने का कोई तरीका मौजूद है? जाहिर है, यह एक DAG उत्पन्न करेगा, लेकिन किसी भी तरह से GNU मैनुअल यह निर्दिष्ट नहीं करता है कि इस मामले को कैसे संभाला जा सकता है।

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


जवाबों:


12

मैं इसे इस प्रकार हल करूंगा:

file-a.out: input.in
    foo-bin input.in file-a.out file-b.out   

file-b.out: file-a.out
    #do nothing
    noop

इस मामले में समानांतर बनाना 'ए' और 'बी' बनाना धारावाहिक होगा, लेकिन बी बनाने के बाद ऐसा कुछ भी नहीं होता है जिसमें कोई समय न लगे।


16
दरअसल, इससे एक समस्या है। यदि file-b.outकहा गया था और फिर रहस्यमय तरीके से हटा दिया गया था, तो makeइसे फिर से बनाने में असमर्थ file-a.outहोगा क्योंकि अभी भी मौजूद रहेगा। कोई विचार?
बनारस

यदि आप रहस्यमय तरीके से हटाते हैं file-a.outतो उपरोक्त समाधान अच्छी तरह से काम करता है। यह एक हैक का सुझाव देता है: जब केवल एक या बी मौजूद होता है, तो आश्रितों को इस तरह का आदेश दिया जाना चाहिए कि अनुपलब्ध फ़ाइल आउटपुट के रूप में प्रकट होती है input.in। कुछ $(widcard...)एस, $(filter...)एस, $(filter-out...)एस आदि, चाल करना चाहिए। ओह।
बोब्बोगो

3
PS foo-binस्पष्ट रूप से फ़ाइलों में से एक को पहले बनाएगा (माइक्रोसेकंड रिज़ॉल्यूशन का उपयोग करके), इसलिए आपको यह सुनिश्चित करना होगा कि आपकी फ़ाइलें मौजूद होने पर आपके पास सही निर्भरता क्रम हो।
बोब्बोगो

2
@ बोबोगो या तो, या आह्वान के touch file-a.outबाद दूसरे आदेश के रूप में जोड़ें foo-bin
कॉनर हैरिस

इसके लिए यहां एक संभावित सुधार है: touch file-b.outपहले नियम में दूसरे कमांड के रूप में जोड़ें और दूसरे नियम में कमांड को डुप्लिकेट करें। यदि फ़ाइल या तो गायब है या उससे अधिक पुरानी है input.in, तो कमांड को केवल एक बार निष्पादित किया जाएगा और इससे file-b.outअधिक हाल ही में दोनों फ़ाइलों को पुन: उत्पन्न करेगा file-a.out
चकरली

128

चाल कई लक्ष्यों के साथ एक पैटर्न नियम का उपयोग करने के लिए है। उस स्थिति में यह मान लेंगे कि दोनों लक्ष्य कमांड के एकल आह्वान द्वारा बनाए गए हैं।

all: file-a.out file-b.out
file-a% out file-b% out: input.in
    foo-bin input.in फ़ाइल-एक $ * आउट फ़ाइल-बी $ * बाहर

पैटर्न नियमों और सामान्य नियमों के बीच व्याख्या में यह अंतर बिल्कुल समझ में नहीं आता है, लेकिन यह इस तरह के मामलों के लिए उपयोगी है, और यह मैनुअल में प्रलेखित है।

इस ट्रिक का उपयोग किसी भी संख्या में आउटपुट फाइल के लिए किया जा सकता है, जब तक कि उनके नाम के लिए कुछ सामान्य विकल्प नहीं हैं % मिलान के हो। (इस मामले में सामान्य विकल्प है ""।


3
यह वास्तव में सही नहीं है। आपका नियम निर्दिष्ट करता है कि इन पैटर्नों से मेल खाने वाली फाइलें इनपुट.इन से निर्दिष्ट कमांड का उपयोग करके बनाई जाती हैं, लेकिन कहीं भी यह नहीं कहती हैं कि वे एक साथ बनाई गई हैं। यदि आप वास्तव में इसे समानांतर में makeचलाते हैं, तो एक ही कमांड को एक साथ दो बार चलाएंगे।
18:25

47
कृपया यह कहने से पहले प्रयास करें कि यह काम नहीं करता ;-) GNU मेनुअल मैनुअल कहता है: "पैटर्न नियमों में एक से अधिक लक्ष्य हो सकते हैं। सामान्य नियमों के विपरीत, यह एक ही पूर्वापेक्षाओं और आदेशों के साथ कई अलग-अलग नियमों के रूप में कार्य नहीं करता है। यदि एक पैटर्न नियम में कई लक्ष्य होते हैं, यह जानते हैं कि नियम के आदेश सभी लक्ष्यों को बनाने के लिए जिम्मेदार हैं। सभी लक्ष्यों को बनाने के लिए आदेशों को केवल एक बार निष्पादित किया जाता है। " gnu.org/software/make/manual/make.html#Pattern-Intro
slowdog

4
लेकिन क्या होगा अगर मेरे पास पूरी तरह से अलग इनपुट हैं ?, foo.in और bar.src कहें। क्या पैटर्न नियम खाली पैटर्न मैच का समर्थन करता है?
grwlf

2
@dcow: आउटपुट फ़ाइलों को केवल एक सबरिंग (यहां तक ​​कि एक एकल वर्ण, जैसे ""।, पर्याप्त है) को आवश्यक रूप से एक उपसर्ग साझा करने की आवश्यकता नहीं है। यदि कोई सामान्य विकल्प नहीं है, तो आप एक मध्यवर्ती लक्ष्य का उपयोग कर सकते हैं जैसा कि रिकार्ड और डीमर द्वारा वर्णित है। @grwlf: अरे, इसका मतलब है कि "foo.in" और "bar.src" किया जा सकता है: foo%in bar%src: input.in:-)
slowdog

1
यदि संबंधित आउटपुट फ़ाइलों को चर में रखा जाता है तो नुस्खा को इस प्रकार लिखा जा सकता है: $(subst .,%,$(varA) $(varB)): input.in(GNU मेक 4.1 के साथ परीक्षण किया गया)।
स्टीफनक्ट

55

ऐसा करने के लिए कोई भी सहज तरीका नहीं है, लेकिन दो अच्छे वर्कअराउंड हैं।

सबसे पहले, यदि शामिल लक्ष्यों में एक सामान्य स्टेम है, तो आप उपसर्ग नियम (GNU मेक के साथ) का उपयोग कर सकते हैं। यदि आप निम्न नियम को ठीक करना चाहते हैं:

object.out1 object.out2: object.input
    foo-bin object.input object.out1 object.out2

आप इसे इस तरह लिख सकते हैं:

%.out1 %.out2: %.input
    foo-bin $*.input $*.out1 $*.out2

(पैटर्न-नियम चर $ * का उपयोग करना, जो पैटर्न के मिलान वाले भाग के लिए खड़ा है)

यदि आप गैर-जीएनयू को लागू करने के लिए पोर्टेबल होना चाहते हैं या यदि आपकी फ़ाइलों को एक पैटर्न नियम से मिलान करने के लिए नाम नहीं दिया जा सकता है, तो एक और तरीका है:

file-a.out file-b.out: input.in.intermediate ;

.INTERMEDIATE: input.in.intermediate
input.in.intermediate: input.in
    foo-bin input.in file-a.out file-b.out

यह बताता है कि इनपुट.in.interate बनाने से पहले मौजूद नहीं होगा, इसलिए इसकी अनुपस्थिति (या इसका टाइमस्टैम्प) foo-bin को सहजता से चलाने का कारण नहीं होगा। और चाहे फ़ाइल-a.out या फ़ाइल-b.out या दोनों आउट-ऑफ़-डेट (इनपुट.in के सापेक्ष) हों, फू-बिन केवल एक बार चलाया जाएगा। आप .INTERMEDIATE के बजाय .SECONDARY का उपयोग कर सकते हैं, जो एक काल्पनिक फ़ाइल नाम input.in.intermediate को हटाने का निर्देश नहीं देगा। यह विधि समानांतर मेक बिल्ड के लिए भी सुरक्षित है।

पहली पंक्ति में अर्धविराम महत्वपूर्ण है। यह उस नियम के लिए एक खाली नुस्खा बनाता है, ताकि यह जानें कि हम वास्तव में फ़ाइल-a.out और file-b.out को अपडेट करते रहेंगे (धन्यवाद @siulkiulki और अन्य जिन्होंने इसे इंगित किया था)


7
अच्छा, loooks .INTERMEDIATE दृष्टिकोण ठीक काम करता है। समस्या का वर्णन करते हुए स्वचालित लेख भी देखें (लेकिन वे इसे पोर्टेबल तरीके से हल करने का प्रयास करते हैं)।
grwlf

3
यह सबसे अच्छा जवाब है जो मैंने इसे देखा। ब्याज के अलावा अन्य विशेष लक्ष्य भी हो सकते हैं: gnu.org/software/make/manual/html_node/Special-Targets.html
Arne Babenhauserheide

2
@deemer यह OSX पर मेरे लिए काम नहीं कर रहा है। (जीएनयू मेक 3.81)
जॉर्ज बुकरन

2
मैं यह कोशिश कर रहा हूं, और मैंने समानांतर बिल्ड के साथ सूक्ष्म समस्याओं पर ध्यान दिया है --- निर्भरता हमेशा मध्यवर्ती लक्ष्य के पार ठीक से नहीं फैलती है (हालांकि -j1बिल्ड के साथ सबकुछ ठीक है)। इसके अलावा, अगर मेकफाइल बाधित हो जाता है और यह input.in.intermediateफ़ाइल को चारों ओर छोड़ देता है तो यह वास्तव में भ्रमित हो जाता है।
डेविड ने

3
इस उत्तर में एक त्रुटि है। यो पूरी तरह से काम कर समानांतर बनाता है। तुम बस बदलने के लिए file-a.out file-b.out: input.in.intermediateकरने के लिए file-a.out file-b.out: input.in.intermediate ;देखें stackoverflow.com/questions/37873522/... अधिक जानकारी के लिए।
शुलकिलुलकी

10

यह @ डीमर के दूसरे उत्तर पर आधारित है जो पैटर्न नियमों पर निर्भर नहीं करता है, और यह एक समस्या को हल करता है जिसे मैं वर्कअराउंड के नेस्टेड उपयोगों के साथ अनुभव कर रहा था।

file-a.out file-b.out: input.in.intermediate
    @# Empty recipe to propagate "newness" from the intermediate to final targets

.INTERMEDIATE: input.in.intermediate
input.in.intermediate: input.in
    foo-bin input.in file-a.out file-b.out

मैंने इसे @ डीमर के उत्तर के लिए एक टिप्पणी के रूप में जोड़ा होगा, लेकिन मैं ऐसा नहीं कर सकता क्योंकि मैंने केवल यह खाता बनाया है और इसकी कोई प्रतिष्ठा नहीं है।

स्पष्टीकरण: खाली नुस्खा आदेश मेक निशान के लिए उचित बहीखाता करने के लिए अनुमति देने के लिए की जरूरत है file-a.outऔर file-b.outफिर से बनाया गया होने के रूप में। यदि आपके पास अभी तक एक और मध्यवर्ती लक्ष्य है जो निर्भर करता है file-a.out, तो मेक बाहरी मध्यवर्ती का निर्माण नहीं करने का दावा करेगा, दावा करता है:

No recipe for 'file-a.out' and no prerequisites actually changed.
No need to remake target 'file-a.out'.

9

जीएनयू मेक 4.3 (19 जनवरी, 2020) के बाद आप "स्पष्ट लक्ष्य" का उपयोग कर सकते हैं। बदलें :के साथ &:

file-a.out file-b.out &: input.in
    foo-bin input.in file-a.out file-b.out

GNU मेक की NEWS फाइल कहती है:

नई सुविधा: स्पष्ट लक्षित समूह

पैटर्न नियमों में हमेशा नुस्खा के एकल आह्वान के साथ कई लक्ष्य उत्पन्न करने की क्षमता होती है। अब यह घोषित करना संभव है कि एक स्पष्ट नियम एक एकल आह्वान के साथ कई लक्ष्य बनाता है। इसका उपयोग करने के लिए, नियम में ":" टोकन को "&:" से बदलें। इस विशेषता का पता लगाने के लिए .FEATURES विशेष चर में 'समूहीकृत-लक्ष्य' की खोज करें। कार्यान्वयन काज़ कल्हेकु <kaz@kylheku.com> द्वारा योगदान दिया गया


2

यह मेरा इसे करने का तरीका है। पहले मैं हमेशा व्यंजनों से पूर्व-आवश्यकताएँ अलग करता हूं। फिर इस मामले में नुस्खा करने के लिए एक नया लक्ष्य।

all: file-a.out file-b.out #first rule

file-a.out file-b.out: input.in

file-a.out file-b.out: dummy-a-and-b.out

.SECONDARY:dummy-a-and-b.out
dummy-a-and-b.out:
    echo creating: file-a.out file-b.out
    touch file-a.out file-b.out

पहली बार:
1. हम फ़ाइल-a.out बनाने की कोशिश करते हैं, लेकिन डमी-ए-और-बी.आउट को पहले करने की आवश्यकता होती है, इसलिए डमी-ए-और-बी-रेसिपी चलाते हैं।
2. हम फ़ाइल-b.out बनाने की कोशिश करते हैं, डमी-ए-और-b.out अद्यतित है।

दूसरा और बाद का समय:
1. हम फ़ाइल-ए.आउट बनाने की कोशिश करते हैं: पूर्वापेक्षाओं को देखते हैं, सामान्य पूर्वापेक्षाएँ अद्यतित हैं, द्वितीयक पूर्वापेक्षाएँ इतनी उपेक्षित हैं।
2. हम फ़ाइल-b.out बनाने की कोशिश करते हैं: पूर्वापेक्षाओं को देखते हैं, सामान्य पूर्वापेक्षाएँ अद्यतित हैं, द्वितीयक पूर्वापेक्षाएँ इतनी अनदेखी हैं।


1
यह टूट गया है। यदि आप file-b.outमेक को हटाते हैं, तो उसे फिर से बनाने का कोई तरीका नहीं है।
बोब्बोगो

सभी को जोड़ा गया: फ़ाइल- a.out फ़ाइल- b.out प्रथम नियम के रूप में। जैसे कि फ़ाइल-b.out को हटा दिया गया था, और मेक को बिना किसी लक्ष्य के चलाया गया था, कमांड लाइन पर, इसे फिर से नहीं बनाया जाएगा।
सीटीएल-अल्ट-डेलोर

@bobogo फिक्स्ड: ऊपर टिप्पणी देखें।
ctrl-alt-delor

0

@ डीमर के उत्तर के विस्तार के रूप में, मैंने इसे एक फ़ंक्शन में सामान्यीकृत किया है।

sp :=
sp +=
inter = .inter.$(subst $(sp),_,$(subst /,_,$1))

ATOMIC=\
    $(eval s1=$(strip $1)) \
    $(eval target=$(call inter,$(s1))) \
    $(eval $(s1): $(target) ;) \
    $(eval .INTERMEDIATE: $(target) ) \
    $(target)

$(call ATOMIC, file-a.out file-b.out): input.in
    foo-bin input.in file-a.out file-b.out

टूट - फूट:

$(eval s1=$(strip $1))

पहली दलील से किसी भी अग्रणी / अनुगामी व्हाट्सएप को स्ट्रीप करें

$(eval target=$(call inter,$(s1)))

मध्यवर्ती लक्ष्य के रूप में उपयोग करने के लिए एक अद्वितीय मूल्य के लिए एक चर लक्ष्य बनाएं। इस स्थिति के लिए, मान .inter.file-a.out_file-b.out होगा।

$(eval $(s1): $(target) ;)

प्रतिपादक के रूप में अद्वितीय लक्ष्य के साथ आउटपुट के लिए एक खाली नुस्खा बनाएं।

$(eval .INTERMEDIATE: $(target) ) 

एक मध्यवर्ती के रूप में अद्वितीय लक्ष्य घोषित करें।

$(target)

अद्वितीय लक्ष्य के संदर्भ में समाप्त करें ताकि इस फ़ंक्शन का उपयोग सीधे एक नुस्खा में किया जा सके।

यह भी ध्यान दें कि यहां से निष्कासन का उपयोग होता है क्योंकि eval का विस्तार कुछ भी नहीं है इसलिए फ़ंक्शन का पूर्ण विस्तार सिर्फ अद्वितीय लक्ष्य है।

जीएनयू मेक में एटॉमिक रूल्स को क्रेडिट देना चाहिए जो इस फंक्शन से प्रेरित है।


0

make -jNउपयोग के साथ कई आउटपुट के साथ एक नियम के समानांतर कई निष्पादन को रोकने के लिए .NOTPARALLEL: outputs। आपके मामले में :

.NOTPARALLEL: file-a.out file-b.out

file-a.out file-b.out: input.in
    foo-bin input.in file-a.out file-b.out
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.