निर्देशिका बनाने के लिए फ़ाइल का उपयोग कर


101

मैं makefiles के लिए बहुत नया हूं और मैं makefile का उपयोग करके निर्देशिका बनाना चाहता हूं। मेरी परियोजना निर्देशिका इस प्रकार है

+--Project  
   +--output  
   +--source  
     +Testfile.cpp  
   +Makefile  

मैं सभी वस्तुओं और आउटपुट को संबंधित आउटपुट फ़ोल्डर में डालना चाहता हूं। मैं फ़ोल्डर संरचना बनाना चाहता हूं जो संकलन के बाद इस तरह होगा।

+--Project
   +--output
     +--debug (or release)
       +--objs
         +Testfile.o
       +Testfile (my executable file)
   +--source
     +Testfile.cpp
   +Makefile

मैंने कई विकल्पों के साथ कोशिश की, लेकिन सफल नहीं हो सका। कृपया फ़ाइल बनाने के लिए निर्देशिका बनाने में मेरी मदद करें। मैं आपके विचार के लिए अपना मेकफाइल पोस्ट कर रहा हूं।

#---------------------------------------------------------------------
# Input dirs, names, files
#---------------------------------------------------------------------
OUTPUT_ROOT := output/

TITLE_NAME := TestProj 

ifdef DEBUG 
    TITLE_NAME += _DEBUG
else
ifdef RELEASE
    TITLE_NAME += _RELEASE
endif
endif


# Include all the source files here with the directory tree
SOURCES := \
        source/TestFile.cpp \

#---------------------------------------------------------------------
# configs
#---------------------------------------------------------------------
ifdef DEBUG
OUT_DIR     := $(OUTPUT_ROOT)debug
CC_FLAGS    := -c -Wall
else
ifdef RELEASE
OUT_DIR     := $(OUTPUT_ROOT)release
CC_FLAGS    := -c -Wall
else
$(error no build type defined)
endif
endif

# Put objects in the output directory.
OUT_O_DIR   := $(OUT_DIR)/objs

#---------------------------------------------------------------------
# settings
#---------------------------------------------------------------------
OBJS = $(SOURCES:.cpp=.o)
DIRS = $(subst /,/,$(sort $(dir $(OBJS))))
DIR_TARGET = $(OUT_DIR)

OUTPUT_TARGET = $(OUT_DIR)/$(TITLE_NAME)

CC_FLAGS +=   

LCF_FLAGS := 

LD_FLAGS := 

#---------------------------------------------------------------------
# executables
#---------------------------------------------------------------------
MD := mkdir
RM := rm
CC := g++

#---------------------------------------------------------------------
# rules
#---------------------------------------------------------------------
.PHONY: all clean title 

all: title 

clean:
    $(RM) -rf $(OUT_DIR)

$(DIR_TARGET):
    $(MD) -p $(DIRS)

.cpp.o: 
    @$(CC) -c $< -o $@

$(OBJS): $(OUT_O_DIR)/%.o: %.cpp
    @$(CC) -c $< -o $@

title: $(DIR_TARGET) $(OBJS)

अग्रिम में धन्यवाद। अगर मैंने कोई गलती भी की हो तो कृपया मेरा मार्गदर्शन करें।

जवाबों:


88

यह ऐसा करेगा - एक यूनिक्स जैसा वातावरण।

MKDIR_P = mkdir -p

.PHONY: directories

all: directories program

directories: ${OUT_DIR}

${OUT_DIR}:
        ${MKDIR_P} ${OUT_DIR}

इसे शीर्ष-स्तरीय निर्देशिका में चलाया जाना चाहिए - या $ {OUT_DIR} की परिभाषा को इसे चलाने के सापेक्ष सही करना होगा। बेशक, यदि आप पीटर मिलर के " रिकर्सिव मेक कंसीडरेड हार्मफुल " पेपर के एडिट्स का पालन करते हैं , तो आप मेक-अप निर्देशिका में वैसे भी चल रहे होंगे।

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


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


धन्यवाद जोनाथन जब मैंने कोशिश की कि मुझे एक त्रुटि मिली "लक्ष्य: output/debug', needed by निर्देशिका बनाने के लिए कोई नियम नहीं है "। स्टॉप। " लेकिन मैं अब इस बारे में चिंता करने वाला नहीं हूं। बुनियादी नियमों के साथ रहना होगा। :)। मार्गदर्शन के लिए धन्यवाद। और मैं केवल टॉपलेवल डायरेक्टरी से "मेक" चला रहा हूं।
जाबेज

बस निर्देशिकाओं के पीछे $ {OUT_DIR} हटाएं :, फिर यह काम करना चाहिए।
डॉक्टर ब्राउन

इसे लागू करने के लिए आवश्यक है कि आप हर संभव मामले को पकड़ें जहाँ आप कमांड-लाइन से निर्देशिकाओं का उपयोग करते हैं। इसके अलावा, आप किसी भी फ़ाइल को बनाने के नियम बनाने पर निर्भर नहीं कर सकते हैं, directoriesजो उन्हें हमेशा बनाए रखने के लिए निर्भर करता है ..
mtalexan

@mtalexan आपने इनमें से कुछ उत्तरों के साथ क्या गलत है यह बताते हुए टिप्पणियों की एक जोड़ी प्रदान की है लेकिन आपने वैकल्पिक उत्तर का प्रस्ताव नहीं किया है। इस समस्या के लिए अपना समाधान सुनने के लिए उत्सुक।
शमूएल 12

@ सैमुअल मैंने समस्याओं को हल किए बिना बताया क्योंकि मैं एक ही चीज की तलाश में था और कभी कोई समाधान नहीं मिला। मैंने आदर्श समाधान की तुलना में कम गिरावट से निपटने का काम किया।
mtalexan

135

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

यदि आप एक सामान्य या "पैटर्नयुक्त" फ़ाइल को लक्षित कर रहे हैं, तो बस makeआंतरिक चर का उपयोग करें $(@D), इसका मतलब है कि "निर्देशिका का वर्तमान लक्ष्य रहता है" (cmp। $@लक्ष्य के लिए)। उदाहरण के लिए,

$(OUT_O_DIR)/%.o: %.cpp
        @mkdir -p $(@D)
        @$(CC) -c $< -o $@

title: $(OBJS)

फिर, आप प्रभावी रूप से ऐसा ही कर रहे हैं: सभी के लिए निर्देशिका बनाएं $(OBJS), लेकिन आप इसे कम जटिल तरीके से करेंगे।

एक ही नीति (फाइलें लक्ष्य हैं, निर्देशिकाएं कभी नहीं होती हैं) का उपयोग विभिन्न अनुप्रयोगों में किया जाता है। उदाहरण के लिए, gitसंशोधन नियंत्रण प्रणाली निर्देशिकाओं को संग्रहीत नहीं करती है।


नोट: यदि आप इसका उपयोग करने जा रहे हैं, तो सुविधा चर को लागू करना और makeविस्तार नियमों का उपयोग करना उपयोगी हो सकता है।

dir_guard=@mkdir -p $(@D)

$(OUT_O_DIR)/%.o: %.cpp
        $(dir_guard)
        @$(CC) -c $< -o $@

$(OUT_O_DIR_DEBUG)/%.o: %.cpp
        $(dir_guard)
        @$(CC) -g -c $< -o $@

title: $(OBJS)

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

1
सीधे तौर पर mkdir को कॉल करने के बजाय, मैंने डायरेक्ट्री बनाने की कोशिश करने से बचने के लिए निम्न कार्य किया यदि यह पहले से मौजूद है: $ (शेल [-d $ (@ D)] && mkdir -p $ (@ D)
ब्रैडी

26

या, KISS।

DIRS=build build/bins

... 

$(shell mkdir -p $(DIRS))

यह मेकफाइल पार्स होने के बाद सभी निर्देशिकाओं का निर्माण करेगा।


3
मुझे यह दृष्टिकोण पसंद है क्योंकि मुझे निर्देशिकाओं को संभालने के लिए प्रत्येक लक्ष्य के साथ अव्यवस्था नहीं करनी है।
केन विलियम्स

1
मैं सिर्फ निर्देशिका बनाने की जरूरत है अगर यह मौजूद नहीं है। यह उत्तर मेरे मुद्दे के लिए एकदम सही है।
जेफ पाल

इतना ही नहीं, यह प्रत्येक निर्देशिका के संशोधित टाइमस्टैम्प को एक अनावश्यक निर्माण चरण को ट्रिगर करने से रोकता है। इसका उत्तर होना चाहिए
एसिमिलाटर

6
यह बेहतर है: $(info $(shell mkdir -p $(DIRS)))इसके बिना $(info ...), mkdirकमांड का आउटपुट मेकफाइल में चिपकाया जाएगा , जिससे सिंटैक्स त्रुटियां सबसे अच्छी होंगी। $(info ...)कॉल सुनिश्चित करता है कि) त्रुटियों (यदि हो तो) उपयोगकर्ता के लिए दिखाई दे रहे हैं, और ख) कि कुछ भी नहीं करने के लिए समारोह कॉल फैलता है।
cmaster - मोनिका

10

makeमें और खुद से, डायरेक्टरी टारगेट को फाइल टारगेट की तरह ही हैंडल करता है। इसलिए, इस तरह से नियम लिखना आसान है:

outDir/someTarget: Makefile outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

इसके साथ एकमात्र समस्या यह है कि निर्देशिका टाइमस्टैम्प इस बात पर निर्भर करती है कि अंदर की फाइलों का क्या किया जाता है। उपरोक्त नियमों के लिए, यह निम्नलिखित परिणाम की ओर जाता है:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget

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

हालाँकि, आप निर्देशिका के टाइमस्टैम्प को अनदेखा करने के लिए कहकर इस लूप को आसानी से तोड़ सकते हैं । यह केवल आदेश के रूप में निर्देशिका की घोषणा करके किया जाता है:

# The pipe symbol tells make that the following prerequisites are order-only
#                           |
#                           v
outDir/someTarget: Makefile | outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

यह सही ढंग से पैदावार देता है:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
make: 'outDir/someTarget' is up to date.

टी एल; डॉ:

निर्देशिका बनाने के लिए एक नियम लिखें:

$(OUT_DIR):
    mkdir -p $(OUT_DIR)

और सामान के लिए लक्ष्य निर्देशिका आदेश पर निर्भर हैं:

$(OUT_DIR)/someTarget: ... | $(OUT_DIR)

किस टूटे हुए OS / FS पर आपने touchमूल निर्देशिका पर कोई स्टेट डेटा संशोधित किया है ? इससे मुझे कोई मतलब नहीं है। एक डीआईआर का माइम केवल उस फ़ाइलनाम पर निर्भर करता है जिसमें यह शामिल है। मैं आपके मुद्दे को पुन: पेश नहीं कर सका।
जोहान बोले

@ जोहानबोल डेबियन।
सेमीस्टर - मोनिका

और क्या आपने ऐसे टूटे हुए व्यवहार के लिए एक बग भरा था?
जोहान बोले

6

स्वीकृत सभी सहित सभी समाधानों में कुछ मुद्दे हैं जैसा कि उनके संबंधित टिप्पणियों में कहा गया है। द्वारा @ जोनाथन-Leffler स्वीकार किए जाते हैं जवाब पहले से ही काफी अच्छा है, लेकिन प्रभाव है कि आवश्यक शर्तें जरूरी नहीं कि (के दौरान व्यवस्था में निर्मित किया जाना कर रहे हैं पर ध्यान नहीं देता make -jउदाहरण के लिए)। हालांकि बस हर रन AFAICT पर फिर से उत्तेजित करने के लिए directoriesशर्त से आगे बढ़ रहा है । निम्नलिखित समाधान में वह समस्या नहीं है और AFAICS इरादा के अनुसार काम करता है।allprogram

MKDIR_P := mkdir -p
OUT_DIR := build

.PHONY: directories all clean

all: $(OUT_DIR)/program

directories: $(OUT_DIR)

$(OUT_DIR):
    ${MKDIR_P} $(OUT_DIR)

$(OUT_DIR)/program: | directories
    touch $(OUT_DIR)/program

clean:
    rm -rf $(OUT_DIR)

4

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

define depend_on_dir
$(1): | $(dir $(1))

ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED
$(dir $(1)):
    mkdir -p $$@

$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1
endif
endef

$(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file))))

यहां देखिए यह कैसे काम करता है। मैं एक फ़ंक्शन को परिभाषित करता हूं depend_on_dirजो एक फ़ाइल नाम लेता है और एक नियम बनाता है जो फ़ाइल को उस निर्देशिका पर निर्भर करता है जिसमें यह होता है और फिर यदि आवश्यक हो तो उस निर्देशिका को बनाने के लिए एक नियम को परिभाषित करता है। तब मैं करने के foreachलिए उपयोग करेंcall प्रत्येक फ़ाइल नाम पर इस फ़ंक्शन का औरeval परिणाम ।

ध्यान दें कि आपको GNU के एक संस्करण की आवश्यकता होगी जो समर्थन करता है eval, जो मुझे लगता है कि संस्करण 3.81 और उच्चतर हैं।


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

3

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

उस ने कहा, स्रोत निर्देशिका से अलग निर्देशिका में बनाने का एक तरीका VPATH है ; मैं पैटर्न नियमों को प्राथमिकता देता हूं


3

ओएस स्वतंत्रता मेरे लिए महत्वपूर्ण है, इसलिए mkdir -pएक विकल्प नहीं है। मैंने उन कार्यों की यह श्रृंखला बनाई है evalजो मूल निर्देशिका पर पूर्वापेक्षा के साथ निर्देशिका लक्ष्य बनाने के लिए उपयोग करते हैं। यह लाभ है कि make -j 2निर्भरता सही ढंग से निर्धारित कर रहे हैं के बाद से इस मुद्दे के बिना काम करेंगे।

# convenience function for getting parent directory, will eventually return ./
#     $(call get_parent_dir,somewhere/on/earth/) -> somewhere/on/
get_parent_dir=$(dir $(patsubst %/,%,$1))

# function to create directory targets.
# All directories have order-only-prerequisites on their parent directories
# https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html#Prerequisite-Types
TARGET_DIRS:=
define make_dirs_recursively
TARGET_DIRS+=$1
$1: | $(if $(subst ./,,$(call get_parent_dir,$1)),$(call get_parent_dir,$1))
    mkdir $1
endef

# function to recursively get all directories 
#     $(call get_all_dirs,things/and/places/) -> things/ things/and/ things/and/places/
#     $(call get_all_dirs,things/and/places) -> things/ things/and/
get_all_dirs=$(if $(subst ./,,$(dir $1)),$(call get_all_dirs,$(call get_parent_dir,$1)) $1)

# function to turn all targets into directories
#     $(call get_all_target_dirs,obj/a.o obj/three/b.o) -> obj/ obj/three/
get_all_target_dirs=$(sort $(foreach target,$1,$(call get_all_dirs,$(dir $(target)))))

# create target dirs
create_dirs=$(foreach dirname,$(call get_all_target_dirs,$1),$(eval $(call make_dirs_recursively,$(dirname))))

TARGETS := w/h/a/t/e/v/e/r/things.dat w/h/a/t/things.dat

all: $(TARGETS)

# this must be placed after your .DEFAULT_GOAL, or you can manually state what it is
# https://www.gnu.org/software/make/manual/html_node/Special-Variables.html
$(call create_dirs,$(TARGETS))

# $(TARGET_DIRS) needs to be an order-only-prerequisite
w/h/a/t/e/v/e/r/things.dat: w/h/a/t/things.dat | $(TARGET_DIRS)
    echo whatever happens > $@

w/h/a/t/things.dat: | $(TARGET_DIRS)
    echo whatever happens > $@

उदाहरण के लिए, ऊपर चलने से निर्माण होगा:

$ make
mkdir w/
mkdir w/h/
mkdir w/h/a/
mkdir w/h/a/t/
mkdir w/h/a/t/e/
mkdir w/h/a/t/e/v/
mkdir w/h/a/t/e/v/e/
mkdir w/h/a/t/e/v/e/r/
echo whatever happens > w/h/a/t/things.dat
echo whatever happens > w/h/a/t/e/v/e/r/things.dat

1

Https://www.oreilly.com/library/view/managing-projects-with/0596006101/ch12.html देखें

REQUIRED_DIRS = ...
_MKDIRS := $(shell for d in $(REQUIRED_DIRS); \
             do                               \
               [[ -d $$d ]] || mkdir -p $$d;  \
             done)

$(objects) : $(sources)

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

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