मेकफाइल, हेडर निर्भरता


97

मान लीजिए कि मेरे पास नियम के साथ एक बदलाव है

%.o: %.c
 gcc -Wall -Iinclude ...

मैं चाहता हूं कि जब भी हेडर फ़ाइल में बदलाव हो, तो .o को फिर से बनाया जाए। निर्भरता की सूची पर काम करने के बजाय, जब भी कोई हेडर फ़ाइल में /includeपरिवर्तन करता है, तो डायर की सभी वस्तुओं को फिर से बनाया जाना चाहिए।

मैं इसे बदलने के लिए नियम को बदलने का एक अच्छा तरीका नहीं सोच सकता, मैं सुझाव के लिए खुला हूं। यदि शीर्षकों की सूची को हार्ड-कोडित नहीं किया जाना है तो बोनस अंक


नीचे अपना उत्तर लिखने के बाद मैंने संबंधित सूची में देखा और पाया: stackoverflow.com/questions/297514/… जो एक डुप्लिकेट प्रतीत होता है। क्रिस डोड का जवाब मेरे लिए बराबर है, हालांकि यह एक अलग नामकरण सम्मेलन का उपयोग करता है।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली

जवाबों:


116

यदि आप एक GNU संकलक का उपयोग कर रहे हैं, तो संकलक आपके लिए निर्भरता की एक सूची को इकट्ठा कर सकता है। मेकफाइल टुकड़ा:

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ -MF  ./.depend;

include .depend

या

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ > ./.depend;

include .depend

SRCSआपकी फ़ाइलों की स्रोत सूची की ओर इशारा करने वाला चर कहां है।

उपकरण भी है makedepend, लेकिन मैंने इसे कभी पसंद नहीं कियाgcc -MM


2
मुझे यह ट्रिक पसंद है, लेकिन मैं dependकेवल तभी चला सकता हूं जब स्रोत फाइलें बदल गई हों? यह हर बार की परवाह किए बिना चलाने के लिए लगता है ...
पीछा

2
@ कोच: ठीक है, मैंने गलती से ऑब्जेक्ट फ़ाइलों पर निर्भरता बना ली है, जब यह स्पष्ट रूप से स्रोतों पर होना चाहिए और दो लक्ष्यों के लिए निर्भरता का क्रम भी गलत था। यही मुझे स्मृति से टाइप करने के लिए मिलता है। अब इसे आजमाओ।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

4
क्या हर फ़ाइल को किसी उपसर्ग से पहले जोड़ने का तरीका यह है कि यह किसी अन्य निर्देशिका में है जैसे build/file.o?
रियाद

मैंने SRCS को OBJECTS में बदल दिया, जहाँ OBJECTS मेरी * .o फ़ाइलों की एक सूची है। ऐसा लगता है कि हर बार चलने से निर्भरता को रोका जा सकता है और केवल हेडर फ़ाइलों में परिवर्तन को भी पकड़ा है। यह पिछली टिप्पणियों के लिए काउंटर लगता है..क्या मुझे कुछ याद आ रहा है?
BigBrownBear00

2
अर्धविराम क्यों आवश्यक है? अगर मैं कोशिश करता हूं कि इसके बिना, या -MF के साथ। / निर्भर नहीं अंतिम तर्क, यह केवल $ (SRCS) में अंतिम फ़ाइल की निर्भरता को बचाता है।
हमोद

72

अधिकांश उत्तर आश्चर्यजनक रूप से जटिल या गलत हैं। हालाँकि सरल और मजबूत उदाहरण कहीं और पोस्ट किए गए हैं [ कोडरेव्यू ]। निश्चित रूप से ग्नू प्रीप्रोसेसर द्वारा उपलब्ध कराए गए विकल्प थोड़े भ्रमित करने वाले हैं। हालाँकि, बिल्ड टारगेट से सभी डाइरेक्टरीज़ -MMको डॉक्यूमेंट किया गया है, न कि बग [ gpp ]:

डिफ़ॉल्ट रूप से सीपीपी मुख्य इनपुट फ़ाइल का नाम लेता है, किसी भी निर्देशिका घटकों को हटाता है और किसी भी फ़ाइल प्रत्यय जैसे '.c' को हटाता है , और प्लेटफ़ॉर्म के सामान्य ऑब्जेक्ट प्रत्यय को जोड़ता है।

(कुछ नया) -MMDविकल्प शायद वही है जो आप चाहते हैं। एक पूर्णता का एक उदाहरण के लिए जो कई src dirs का समर्थन करता है और कुछ टिप्पणियों के साथ dirs का निर्माण करता है। बिल्ड डायर के बिना एक सरल संस्करण के लिए [ कोडरेव्यू ] देखें ।

CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow

# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build

# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)

# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)

# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)

# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
    # Create build directories - same structure as sources.
    mkdir -p $(@D)
    # Just link all the object files.
    $(CXX) $(CXX_FLAGS) $^ -o $@

# Include all .d files
-include $(DEP)

# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
    mkdir -p $(@D)
    # The -MMD flags additionaly creates a .d file with
    # the same name as the .o file.
    $(CXX) $(CXX_FLAGS) -MMD -c $< -o $@

.PHONY : clean
clean :
    # This should remove all generated files.
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

यह विधि काम करती है क्योंकि यदि किसी एकल लक्ष्य के लिए कई निर्भरता रेखाएँ हैं, तो निर्भरताएँ बस शामिल हो जाती हैं, जैसे:

a.o: a.h
a.o: a.c
    ./cmd

के बराबर है:

a.o: a.c a.h
    ./cmd

जैसा कि उल्लेख किया गया है: एकल लक्ष्य के लिए मेकफाइल कई निर्भरता लाइनें?


1
मुझे यह समाधान पसंद है। मैं निर्भर कमांड बनाने के लिए टाइप नहीं करना चाहता। उपयोगी !!
रॉबर्ट

1
OBJ वैरिएबल वैल्यू में एक स्पेलिंग एरर है: CPPCPPS
ctrucza

1
यह मेरा पसंदीदा उत्तर है; आपके लिए +1। इस पृष्ठ पर केवल यही एक है जो समझ में आता है, और कवर करता है (मैं क्या देख सकता हूं) सभी स्थितियों में जहां पुनर्संयोजन आवश्यक है (अनावश्यक संकलन से बचना, अभी तक पर्याप्त)
जोस्ट

1
बॉक्स से बाहर, यह मेरे लिए हेडर का पता लगाने में विफल रहा, भले ही hpp और cpp दोनों एक ही dir पर हों।
विलास

1
यदि आपके पास अपनी स्रोत फ़ाइलें ( a.cpp, b.cpp) हैं ./src/, तो क्या वह प्रतिस्थापन नहीं होगा $(OBJ)=./build/src/a.o ./build/src/b.o?
गाल्वा

26

जैसा कि मैंने यहाँ पोस्ट किया है gcc निर्भरता बना सकता है और एक ही समय में संकलित कर सकता है:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

'-MF' पैरामीटर निर्भरता को संग्रहीत करने के लिए एक फ़ाइल निर्दिष्ट करता है।

'-Include' के आरंभ में डैश, .d फ़ाइल को मौजूद नहीं होने पर जारी रखने के लिए कहता है (जैसे पहले संकलन पर)।

ध्यान दें -o विकल्प के संबंध में gcc में एक बग प्रतीत होता है। यदि आप ऑब्जेक्ट फ़ाइलनाम को obj / _file__c.o कहने के लिए सेट करते हैं तो जनरेट की गई फ़ाइल .d में अभी भी फ़ाइल .o होगी, obj / _file__c.o नहीं।


4
जब मैं इसे आज़माता हूँ तो यह मेरी सभी .o फ़ाइलों को रिक्त फ़ाइलों के रूप में निर्मित करता है। मैं एक बिल्ड सबफ़ोल्डर में अपनी वस्तुएं रखता हूं (इसलिए $ OBJECTS में बिल्ड / main.o build / smbus.o build / etc ... शामिल है) और यह निश्चित रूप से .d फाइलें बनाता है जैसा कि आपने स्पष्ट बग के साथ वर्णित किया है, लेकिन यह निश्चित रूप से है। .o फ़ाइलों का निर्माण नहीं कर रहा है, जबकि अगर मैं -MM और -MF को हटा देता हूं।
बोबापुल

1
प्रयोग -MT आपके उत्तर की अंतिम पंक्तियों में नोट को हल करेगा जो प्रत्येक निर्भरता सूची के लक्ष्य को अद्यतन करता है।
गोड्रिक सेर

3
@bobpaul का man gccकहना -MMहै कि इसका मतलब है -E, जो "प्रीप्रोसेसिंग के बाद बंद हो जाता है"। आपको -MMDइसके बजाय की जरूरत है : stackoverflow.com/a/30142139/895245
Ciro Santilli 病 over over over ''

23

कैसे कुछ के बारे में:

includes = $(wildcard include/*.h)

%.o: %.c ${includes}
    gcc -Wall -Iinclude ...

आप वाइल्डकार्ड का भी सीधे उपयोग कर सकते हैं, लेकिन मुझे लगता है कि मुझे एक से अधिक स्थानों पर उनकी आवश्यकता है।

ध्यान दें कि यह केवल छोटी परियोजनाओं पर अच्छी तरह से काम करता है, क्योंकि यह मानता है कि प्रत्येक ऑब्जेक्ट फ़ाइल प्रत्येक हेडर फ़ाइल पर निर्भर करती है।


धन्यवाद, मैं जरूरत से ज्यादा जटिल होने के लिए इसे बाहर कर रहा था
माइक

15
यह काम करता है, हालाँकि, इसके साथ समस्या यह है कि हर ऑब्जेक्ट फ़ाइल को पुनः प्राप्त किया जाता है, हर बार एक छोटा परिवर्तन किया जाता है, अर्थात, यदि आपके पास 100 स्रोत / हेडर फाइलें हैं, और आप केवल एक छोटे से परिवर्तन करते हैं, तो सभी 100 पुन: व्यवस्थित हो जाते हैं ।
निकोलस हैमिल्टन

1
आपको यह कहने के लिए वास्तव में अपना जवाब अपडेट करना चाहिए कि यह करने के लिए यह एक बहुत ही अक्षम तरीका है क्योंकि यह हर बार किसी भी हेडर फ़ाइल को बदलने पर सभी फ़ाइलों का पुनर्निर्माण करता है। अन्य उत्तर बहुत बेहतर हैं।
xaxxon

2
यह बहुत ही बुरा उपाय है। यकीन है कि यह एक छोटी परियोजना पर काम करेगा, लेकिन किसी भी उत्पादन आकार की टीम और निर्माण के लिए, इससे भयानक संकलन समय होगा और make clean allहर बार चलने के बराबर हो जाएगा ।
जुलिएन गुर्टॉल्ट

मेरे परीक्षण में, यह बिल्कुल काम नहीं करता है। gccलाइन सब पर क्रियान्वित नहीं है, लेकिन अंतर्निहित नियम ( %o: %.cनियम) के बजाय मार डाला जाता है।
पेंघे गेंग

4

ऊपर मार्टिन का समाधान बहुत अच्छा काम करता है, लेकिन उन फ़ाइलों को नहीं संभालता है जो उपनिर्देशिकाओं में रहती हैं। Godric बताता है कि -MT ध्वज उस समस्या का ध्यान रखता है, लेकिन यह साथ ही .o फ़ाइल को सही ढंग से लिखे जाने से रोकता है। निम्नलिखित उन दोनों समस्याओं का ध्यान रखेगा:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<

3

यह काम ठीक कर देगा, और यहां तक ​​कि निर्दिष्ट किए जाने वाले उपखंडों को भी संभालेंगे:

    $(CC) $(CFLAGS) -MD -o $@ $<

इसका परीक्षण gcc 4.8.3 के साथ किया गया


3

यहाँ एक दो-लाइनर है:

CPPFLAGS = -MMD
-include $(OBJS:.c=.d)

यह डिफॉल्ट मेक रेसिपी के साथ काम करता है, जब तक आपके पास आपकी सभी ऑब्जेक्ट फ़ाइलों की एक सूची है OBJS


1

मैं इस समाधान को पसंद करता हूं, माइकल विलियमसन द्वारा स्वीकार किए गए उत्तर पर, यह स्रोतों + इनलाइन फ़ाइलों, फिर स्रोतों + हेडर, और अंत में केवल स्रोतों में परिवर्तन को पकड़ता है। यहां फायदा यह है कि अगर पूरी तरह से कुछ बदलाव किए जाएं तो पूरी लाइब्रेरी को दोबारा नहीं बनाया जाएगा। फ़ाइलों की एक जोड़ी के साथ एक परियोजना के लिए एक बड़ा विचार नहीं है, अगर आपके पास 10 या 100 स्रोत हैं, तो आप अंतर को नोटिस करेंगे।

COMMAND= gcc -Wall -Iinclude ...

%.o: %.cpp %.inl
    $(COMMAND)

%.o: %.cpp %.hpp
    $(COMMAND)

%.o: %.cpp
    $(COMMAND)

2
यह केवल तभी काम करता है जब आपके पास अपनी हेडर फ़ाइलों में कुछ भी नहीं होता है जिसे किसी भी cpp-files को फिर से लागू करने की आवश्यकता होती है ताकि संबंधित कार्यान्वयन फ़ाइल।
matec


0

सोफी के उत्तर का थोड़ा संशोधित संस्करण जो एक अलग फ़ोल्डर में * .d फ़ाइलों को आउटपुट करने की अनुमति देता है (मैं केवल उस दिलचस्प हिस्से को पेस्ट करूंगा जो निर्भरता फ़ाइलों को उत्पन्न करता है):

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@

ध्यान दें कि पैरामीटर

-MT $@

इसका उपयोग यह सुनिश्चित करने के लिए किया जाता है कि उत्पन्न .d। फ़ाइलों में लक्ष्य (यानी ऑब्जेक्ट फ़ाइल नाम) * .o फ़ाइलों के लिए पूर्ण पथ होते हैं और न केवल फ़ाइल नाम।

मैं कारण है कि जब -MMD का उपयोग कर इस पैरामीटर की जरूरत नहीं है पता नहीं है संयोजन में -c साथ (सोफी के रूप में संस्करण )। इस संयोजन में यह * .o फ़ाइलों का पूरा पथ * .d फ़ाइलों में लिखने लगता है। इस संयोजन के बिना, -MMD भी * .d फ़ाइलों में किसी भी निर्देशिका घटकों के बिना केवल शुद्ध फ़ाइल नाम लिखता है। शायद किसी को पता है कि -MMD -c के साथ संयुक्त होने पर पूरा रास्ता क्यों लिखता है। मुझे g ++ मैन पेज में कोई संकेत नहीं मिला है।

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