विभिन्न निर्देशिकाओं में स्रोत फ़ाइलों के साथ Makefiles


135

मेरे पास एक परियोजना है जहां निर्देशिका संरचना इस प्रकार है:

                         $projectroot
                              |
              +---------------+----------------+
              |               |                |
            part1/          part2/           part3/
              |               |                |
       +------+-----+     +---+----+       +---+-----+
       |      |     |     |        |       |         |
     data/   src/  inc/  src/     inc/   src/       inc/

मुझे एक मेकफाइल कैसे लिखना चाहिए जो भाग / src (या जहां भी वास्तव में होगा) में भाग में c / c ++ स्रोत फ़ाइलों पर लिंक / संकलन कर सकता है? / Src

क्या मैं कुछ कर सकता हूँ -I $ प्रोजेक्टरो / part1 / src -I $ प्रोजेक्टरो / part1 / inc -I $ प्रॉजेक्ट / पार्ट 2 / src ...

अगर वह काम करेगा, तो क्या यह एक आसान तरीका है। मैंने उन परियोजनाओं को देखा है, जहां संबंधित भाग में प्रत्येक में एक मेकफिल होता है? फ़ोल्डरों। [इस पोस्ट में मैंने प्रश्न चिह्न का उपयोग किया जैसे bash syntax में]



1
फोनी टारगेट्स के तहत मूल गन्न मैनुअल ( gnu.org/software/make/manual/html_node/Phony-Targets.html ) में एक नमूना है recursive invocation, जो कि बहुत सुंदर होना चाहिए।
फ्रैंक नॉक

आपने उस टेक्स्ट ग्राफिक को बनाने के लिए किस टूल का उपयोग किया?
खालिद हुसैन

जवाबों:


114

पारंपरिक तरीके से एक है Makefileउपनिर्देशिका (में से प्रत्येक में part1, part2आदि) आप उन्हें स्वतंत्र रूप से निर्माण करने के लिए अनुमति देता है। इसके अलावा, Makefileपरियोजना की जड़ निर्देशिका में एक है जो सब कुछ बनाता है। "रूट" Makefileकुछ इस तरह दिखेगा:

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

चूंकि एक मेक टारगेट में प्रत्येक लाइन अपने स्वयं के शेल में चलाई जाती है, इसलिए डायरेक्टरी ट्री या अन्य डायरेक्टरी को पीछे करने की चिंता करने की कोई जरूरत नहीं है।

मेरा सुझाव है कि GNU मेनुअल सेक्शन 5.7 पर एक नज़र डालें ; यह बहुत ही उपयोगी है।


26
यह होना चाहिए +$(MAKE) -C part1आदि। यह मेक की नौकरी नियंत्रण को उपनिर्देशिकाओं में काम करने की अनुमति देता है।
युगांतरकारी

26
यह एक क्लासिक दृष्टिकोण है और व्यापक रूप से उपयोग किया जाता है, लेकिन यह कई मायनों में उप-इष्टतम है जो परियोजना के बढ़ने के साथ खराब हो जाते हैं। फॉलो करने के लिए डेव हिंटन के पास पॉइंटर है।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

89

यदि आपके पास एक उपनिर्देशिका में कोड है जो किसी अन्य उपनिर्देशिका में कोड पर निर्भर है, तो संभवतः आप शीर्ष स्तर पर एकल मेकफाइल के साथ बेहतर हैं।

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

ऊपर दिया गया लिंक नहीं पहुंच पा रहा है। एक ही दस्तावेज़ यहाँ उपलब्ध है:


3
धन्यवाद, इसकी जानकारी नहीं थी। चीजों को करने के "सही तरीके" को जानने के लिए यह बहुत उपयोगी है कि "सिर्फ काम" या मानक के रूप में स्वीकार किए जाते हैं।
tjklemz

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

36

VPATH विकल्प काम आ सकता है, जो बताता है कि स्रोत कोड के लिए कौन सी निर्देशिकाओं को देखना है। आपको अभी भी प्रत्येक के लिए एक -I विकल्प की आवश्यकता होगी, हालांकि इसमें पथ शामिल है। एक उदाहरण:

CXXFLAGS=-Ipart1/inc -Ipart2/inc -Ipart3/inc
VPATH=part1/src:part2/src:part3/src

OutputExecutable: part1api.o part2api.o part3api.o

यह स्वचालित रूप से किसी भी VPATH निर्दिष्ट निर्देशिकाओं में मिलान partXapi.cpp फ़ाइलों को ढूंढेगा और उन्हें संकलित करेगा। हालाँकि, यह तब अधिक उपयोगी होता है जब आपकी src डाइरेक्टरी उपनिर्देशिकाओं में टूट जाती है। आप जो वर्णन करते हैं, उसके लिए, जैसा कि दूसरों ने कहा है, आप शायद प्रत्येक भाग के लिए एक मेकफाइल के साथ बेहतर हैं, खासकर यदि प्रत्येक भाग अकेले खड़े हो सकते हैं।


2
मेरा मानना ​​है कि इस सरल सही उत्तर को अधिक वोट नहीं मिले हैं। इसका एक +1 मुझसे।
निकोलस हैमिल्टन

2
मेरे पास सबफ़ोल्डर में कई असमान परियोजनाओं के लिए एक उच्च निर्देशिका में कुछ सामान्य स्रोत फाइलें थीं, VPATH=..मेरे लिए काम किया!
एक्रीक्योर डे

23

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

सीसी = जी ++
लक्ष्य = cppTest
OTHERDIR = .. / .. / someotherpath / में / परियोजना / src

स्रोत = cppTest.cpp
SOURCE = $ (OTHERDIR) /file.cpp

## एंड सोर्स परिभाषा
INCLUDE = -I./ $ (AN_INCLUDE_DIR)  
INCLUDE = -I। $ (OTHERDIR) /../ inc
## अंत में अधिक शामिल हैं

VPATH = $ (OTHERDIR)
OBJ = $ (ज्वाइन $ (जोड़फिक्स ../obj/, $) (dir $ (SOURCE))), $ (notdir $ (SOURCE: .cpp = .o))। 

## निर्भरता गंतव्य को ठीक करें .. / src dir के सापेक्ष जोड़ें
DEPENDS = $ (ज्वाइन $ (जोड़ेफिक्स ../.dep/, $ (dir $ (SOURCE))), $ (notdir $ (SOURCE: .cpp = .d))।

## डिफ़ॉल्ट नियम निष्पादित
सभी: $ (TARGET)
        @सच

## स्वच्छ नियम
स्वच्छ:
        @ -rm -f $ (TARGET) $ (OBJ) $ (DEPENDS)


## वास्तविक लक्ष्य बनाने के लिए नियम
$ (TARGET): $ (OBJ)
        @ जियो "============="
        @echo "लक्ष्य $ @ को जोड़ना"
        @ जियो "============="
        @ $ (CC) $ (CFLAGS) -o $ @ $ ^ $ (LIBS)
        @ जियो - लिंक समाप्त -

## सामान्य संकलन नियम
% .o:% .cpp
        @mkdir -p $ (dir $ @)
        @ जियो "============="
        @echo "संकलन $ <"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @


Cpp फ़ाइलों से ऑब्जेक्ट फ़ाइलों के लिए ## नियम
## प्रत्येक फ़ाइल के लिए ऑब्जेक्ट फ़ाइल obj डायरेक्टरी में डाली जाती है
वास्तविक स्रोत निर्देशिका से ## एक स्तर ऊपर।
../obj/%.o:% .cpp
        @mkdir -p $ (dir $ @)
        @ जियो "============="
        @echo "संकलन $ <"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

# "अन्य निर्देशिका" के लिए नियम आपको प्रति "एक" अन्य की आवश्यकता होगी
$ (OTHERDIR) /../ obj /%। O:% .cpp
        @mkdir -p $ (dir $ @)
        @ जियो "============="
        @echo "संकलन $ <"
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

## निर्भरता नियम बनाएं
../.dep/%.d:% .cpp
        @mkdir -p $ (dir $ @)
        @ जियो "============="
        @echo भवन निर्भरता फ़ाइल $ *। o के लिए
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *। o ^ .. / obj / $ *। o ^"> $ @ '

## "अन्य" निर्देशिका के लिए निर्भरता नियम
$ (OTHERDIR) /../। Dep /%। D:% .cpp
        @mkdir -p $ (dir $ @)
        @ जियो "============="
        @echo भवन निर्भरता फ़ाइल $ *। o के लिए
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *। o ^ $ (OTHERDIR) /../ obj / $ *। o ^"> $ '

## निर्भरता फ़ाइलों को शामिल करें
-किन्नर $ (DEPENDS)


7
मुझे पता है कि यह अब तक बहुत पुराना है, लेकिन SOURCE और INCLUDE दोनों ही एक लाइन के बाद किसी अन्य असाइनमेंट द्वारा ओवरराइट नहीं किए गए हैं?
स्कैलियम

@ एस्सेलियम हाँ, यह करता है।
नबरॉयन

1
यह दृष्टिकोण DRY सिद्धांत का उल्लंघन करता है। प्रत्येक "अन्य निर्देशिका" के लिए कोड की नकल करने के लिए एक बुरा विचार है
यीशु

20

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

# common sources
COMMON_SRC := ./main.cpp \
              ../src1/somefile.cpp \
              ../src1/somefile2.cpp \
              ../src2/somefile3.cpp \

मैं तो VPATHइस तरह सेट कर सकते हैं :

VPATH := ../src1:../src2

तब मैं वस्तुओं का निर्माण करता हूं:

COMMON_OBJS := $(patsubst %.cpp, $(ObjDir)/%$(ARCH)$(DEBUG).o, $(notdir $(COMMON_SRC)))

अब नियम सरल है:

# the "common" object files
$(ObjDir)/%$(ARCH)$(DEBUG).o : %.cpp Makefile
    @echo creating $@ ...
    $(CXX) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<

और आउटपुट का निर्माण और भी आसान है:

# This will make the cbsdk shared library
$(BinDir)/$(OUTPUTBIN): $(COMMON_OBJS)
    @echo building output ...
    $(CXX) -o $(BinDir)/$(OUTPUTBIN) $(COMMON_OBJS) $(LFLAGS)

यहां तक ​​कि एक VPATHपीढ़ी को इसके द्वारा स्वचालित भी बनाया जा सकता है:

VPATH := $(dir $(COMMON_SRC))

या sortडुप्लिकेट को हटाने वाले तथ्य का उपयोग करते हुए (हालांकि इससे कोई फर्क नहीं पड़ता):

VPATH := $(sort  $(dir $(COMMON_SRC)))

यह उन छोटी परियोजनाओं के लिए बहुत अच्छा काम करता है जहाँ आप सिर्फ कुछ कस्टम लाइब्रेरी जोड़ना चाहते हैं और उन्हें एक अलग फ़ोल्डर में रखना चाहते हैं। बहुत बहुत धन्यवाद
zitroneneis

6

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

यह एक अद्भुत उपकरण है, लेकिन इसका प्रत्यक्ष उपयोग 2010+ में अप्रचलित माना जाना चाहिए।

जब तक, निश्चित रूप से, आप एक विशेष वातावरण में काम कर रहे हैं अर्थात एक विरासत परियोजना आदि के साथ।

एक IDE, CMake का उपयोग करें या, यदि आप हार्ड कोरड हैं, तो ऑटोटूलस

(डाउनवोट्स के कारण संपादित, बाहर निकलने के संकेत के लिए ty Honza)


1
यह समझाया जाना अच्छा होगा। मैं अपना मेकफाइल्स खुद भी बनाता था।
IlDan

2
मैं "के लिए ऐसा नहीं करते" सुझाव के लिए upvoting हूँ - केडेलव्यूड में मेक और अन्य बिल्ड सिस्टम को कॉन्फ़िगर करने के लिए एक बहुत ही ग्राफिकल इंटरफ़ेस है, और जब मैं मेकफाइल्स को खुद लिखता हूं, तो केडेवलप वह है जो मैं अपने काम करने वालों को देता हूं - लेकिन मैं नहीं लगता कि ऑटोटूलस लिंक मदद करता है। ऑटोटूलस को समझने के लिए, आपको m4, libtool, automake, autoconf, shell, make, और मूल रूप से पूरे स्टैक को समझने की आवश्यकता है।
युगांतरकारी

7
Downvotes शायद इसलिए हैं क्योंकि आप अपने खुद के सवाल का जवाब दे रहे हैं। सवाल यह नहीं था कि किसी को मेकफाइल्स का उपयोग करना चाहिए या नहीं। यह उन्हें लिखने के तरीके के बारे में था।
होन्जा

8
यह अच्छा होगा यदि आप कुछ वाक्यों में यह बता सकें कि कैसे आधुनिक उपकरण जीवन को आसान बनाते हैं जो मेकफाइल लिखते हैं। क्या वे उच्च स्तर की अमूर्तता प्रदान करते हैं? और क्या?
अभिषेक आनंद

1
xterm, vi, bash, makefile == दुनिया पर राज करते हैं, cmake उन लोगों के लिए है जो कोड हैक नहीं कर सकते।
--ολ16ν.λαβέ

2

आरसी का पद सुपर उपयोगी था। मैंने कभी भी $ (dir $ @) फ़ंक्शन का उपयोग करने के बारे में नहीं सोचा था, लेकिन यह वही करता था जो मुझे करने की आवश्यकता थी।

ParentDir में, उन में स्रोत फ़ाइलों के साथ निर्देशिकाओं का एक गुच्छा है: dirA, dirB, dirC। विभिन्न फाइलें अन्य निर्देशिकाओं में ऑब्जेक्ट फ़ाइलों पर निर्भर करती हैं, इसलिए मैं एक निर्देशिका में से एक फ़ाइल बनाने में सक्षम होना चाहता था, और क्या यह उस निर्भरता से जुड़े मेकफाइल को कॉल करके निर्भरता बनाता है।

मूल रूप से, मैंने parentDir में एक मेकफाइल बनाया था जिसमें (कई अन्य बातों के अलावा) RC के समान एक सामान्य नियम था:

%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

प्रत्येक उपनिर्देशिका में इस सामान्य नियम को प्राप्त करने के लिए ऊपरी स्तर का मेकफाइल शामिल था। प्रत्येक उपनिर्देशिका के मेकफाइल में, मैंने प्रत्येक फ़ाइल के लिए एक कस्टम नियम लिखा है ताकि मैं हर उस चीज़ पर नज़र रख सकूं जिस पर प्रत्येक व्यक्तिगत फ़ाइल निर्भर थी।

जब भी मुझे एक फ़ाइल बनाने की आवश्यकता होती है, मैं इस (अनिवार्य रूप से) किसी भी / सभी निर्भरता को पुन: बनाने के लिए इस नियम का उपयोग करता था। उत्तम!

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

उम्मीद है की यह मदद करेगा!


2

बनाने का पुनरावर्ती उपयोग

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

यह makeनौकरियों में विभाजित करने और कई कोर का उपयोग करने की अनुमति देता है


2
यह कुछ करने से बेहतर कैसे है make -j4?
देवी

1
@ देविन जैसा कि मैं देख रहा हूं, यह बेहतर नहीं है , यह सिर्फ नौकरी पर नियंत्रण का उपयोग करने में सक्षम बनाता make है। जब मेक की अलग प्रक्रिया चलती है, तो यह नौकरी पर नियंत्रण के अधीन नहीं है।
माइकल पैंकोव

1

मैं कुछ इस तरह की तलाश कर रहा था और कुछ कोशिशों के बाद मैं अपना खुद का मेकफाइल बनाता हूं, मुझे पता है कि यह "मुहावरेदार तरीका" नहीं है, लेकिन यह समझने के लिए एक भयावह है और यह मेरे लिए काम करता है, शायद आप अपने प्रोजेक्ट में कोशिश कर सकते हैं।

PROJ_NAME=mono

CPP_FILES=$(shell find . -name "*.cpp")

S_OBJ=$(patsubst %.cpp, %.o, $(CPP_FILES))

CXXFLAGS=-c \
         -g \
        -Wall

all: $(PROJ_NAME)
    @echo Running application
    @echo
    @./$(PROJ_NAME)

$(PROJ_NAME): $(S_OBJ)
    @echo Linking objects...
    @g++ -o $@ $^

%.o: %.cpp %.h
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS) -o $@

main.o: main.cpp
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS)

clean:
    @echo Removing secondary things
    @rm -r -f objects $(S_OBJ) $(PROJ_NAME)
    @echo Done!

मुझे पता है कि यह आसान है और कुछ लोगों के लिए मेरे झंडे गलत हैं, लेकिन जैसा कि मैंने कहा कि यह मेरा पहला मेकफाइल है जो कई डायरियों में मेरे प्रोजेक्ट को संकलित करता है और सभी को एक साथ जोड़कर मेरा बिन बनाता है।

मैं स्वीकार कर रहा हूँ: डी


-1

मैं उपयोग करने का सुझाव देता हूं autotools:

//## गैर-पुनरावर्ती उपयोग किए जाने पर टकराव से बचने के लिए, उनके स्रोत फ़ाइलों के रूप में एक ही निर्देशिका में उत्पन्न ऑब्जेक्ट फ़ाइलें (.o) रखें।

AUTOMAKE_OPTIONS = subdir-objects

बस इसे Makefile.amअन्य काफी सरल सामान के साथ शामिल करें।

यहाँ ट्यूटोरियल है

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