2016-08-18 4 views
5

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

- myProject 
    | 
    |---+ source 
    | 
    '---+ build 

निर्माण फ़ोल्डर केवल एक makefile में शामिल है। आंकड़ा नीचे से पता चलता है कि क्या करना चाहिए जब मैं जीएनयू उपकरण बनाने चलाएँ:

enter image description here

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

जीएनयू बनाने के लिए प्रत्येक .c स्रोत फ़ाइल के लिए एक .d निर्भरता फ़ाइल (वास्तव में, निर्भरता फ़ाइल कुछ प्रकार का मेकफ़ाइल स्वयं) बनाना चाहिए। निर्भरता फ़ाइल जीएनयू में वर्णन किया गया मैनुअल अध्याय 4.14 "उत्पन्न किसी और चीज की स्वत:" बनाना:

प्रत्येक स्रोत फ़ाइल name.c एक makefile name.d जो क्या वस्तु फ़ाइल फ़ाइलों को सूचीबद्ध करता है के लिए name.o इस पर निर्भर करता है।

निम्नलिखित Stackoverflow सवाल About the GNU make dependency files *.d से, मुझे पता चला कि जीएनयू जीसीसी संकलक के CFLAGS करने के लिए विकल्पों -MMD और -MP कहा कि स्वचालित करने के लिए कर सकते हैं।

तो अब सवाल आता है। क्या कोई नमूना मेकफ़ाइल है जो इस तरह के स्रोत के निर्माण को निष्पादित करता है? या शुरू करने के तरीके पर कुछ अच्छी सलाह?

मुझे पूरा यकीन है कि ज्यादातर लोग जिन्होंने इस तरह के मेकफ़ाइल लिखे हैं, लिनक्स-लोग हैं। लेकिन माइक्रोकंट्रोलर प्रोजेक्ट को विंडोज मशीन पर भी बनाना चाहिए। वैसे भी, भले ही आपका मेकफ़ाइल केवल लिनक्स ही है, यह एक अच्छा प्रारंभिक बिंदु प्रदान करता है ;-)

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

मैं बहुत आभारी :-)


निर्भरता फ़ाइलों को अपडेट करना
कृपया इस सवाल पर एक नजर है होगा: What is the exact chain of events when GNU make updates the .d files?

+1

आप एक बहुत ही सरल मेकफ़ाइल मांग रहे हैं, इसलिए अगर कोई आपको यहां देता है तो मुझे आश्चर्य नहीं होगा। लेकिन आम तौर पर हम मुफ्त सहायता प्रदान करते हैं, मुफ्त कोड नहीं। – Beta

+0

हाँ आप सही हैं। –

+0

इस प्रकार का सरल मेकफ़ाइल वास्तव में नई प्रलेखन सुविधा के लिए एक अच्छा मैच होगा, लेकिन क्यू/ए के लिए यह थोड़ा सा व्यापक है। – Tim

उत्तर

10

संकलित करने के लिए चाहते हैं यहाँ Makefile मैं प्रलेखन के लिए जोड़ा गया है (फिलहाल समीक्षा में तो मैं इसे यहाँ पोस्ट करेंगे):

# Set project directory one level above the Makefile directory. $(CURDIR) is a GNU make variable containing the path to the current working directory 
PROJDIR := $(realpath $(CURDIR)/..) 
SOURCEDIR := $(PROJDIR)/Sources 
BUILDDIR := $(PROJDIR)/Build 

# Name of the final executable 
TARGET = myApp.exe 

# Decide whether the commands will be shown or not 
VERBOSE = TRUE 

# Create the list of directories 
DIRS = Folder0 Folder1 Folder2 
SOURCEDIRS = $(foreach dir, $(DIRS), $(addprefix $(SOURCEDIR)/, $(dir))) 
TARGETDIRS = $(foreach dir, $(DIRS), $(addprefix $(BUILDDIR)/, $(dir))) 

# Generate the GCC includes parameters by adding -I before each source folder 
INCLUDES = $(foreach dir, $(SOURCEDIRS), $(addprefix -I, $(dir))) 

# Add this list to VPATH, the place make will look for the source files 
VPATH = $(SOURCEDIRS) 

# Create a list of *.c sources in DIRS 
SOURCES = $(foreach dir,$(SOURCEDIRS),$(wildcard $(dir)/*.c)) 

# Define objects for all sources 
OBJS := $(subst $(SOURCEDIR),$(BUILDDIR),$(SOURCES:.c=.o)) 

# Define dependencies files for all objects 
DEPS = $(OBJS:.o=.d) 

# Name the compiler 
CC = gcc 

# OS specific part 
ifeq ($(OS),Windows_NT) 
    RM = del /F /Q 
    RMDIR = -RMDIR /S /Q 
    MKDIR = -mkdir 
    ERRIGNORE = 2>NUL || true 
    SEP=\\ 
else 
    RM = rm -rf 
    RMDIR = rm -rf 
    MKDIR = mkdir -p 
    ERRIGNORE = 2>/dev/null 
    SEP=/ 
endif 

# Remove space after separator 
PSEP = $(strip $(SEP)) 

# Hide or not the calls depending of VERBOSE 
ifeq ($(VERBOSE),TRUE) 
    HIDE = 
else 
    HIDE = @ 
endif 

# Define the function that will generate each rule 
define generateRules 
$(1)/%.o: %.c 
    @echo Building [email protected] 
    $(HIDE)$(CC) -c $$(INCLUDES) -o $$(subst /,$$(PSEP),[email protected]) $$(subst /,$$(PSEP),$$<) -MMD 
endef 

# Indicate to make which targets are not files 
.PHONY: all clean directories 

all: directories $(TARGET) 

$(TARGET): $(OBJS) 
    $(HIDE)echo Linking [email protected] 
    $(HIDE)$(CC) $(OBJS) -o $(TARGET) 

# Include dependencies 
-include $(DEPS) 

# Generate rules 
$(foreach targetdir, $(TARGETDIRS), $(eval $(call generateRules, $(targetdir)))) 

directories: 
    $(HIDE)$(MKDIR) $(subst /,$(PSEP),$(TARGETDIRS)) $(ERRIGNORE) 

# Remove all objects, dependencies and executable files generated during the build 
clean: 
    $(HIDE)$(RMDIR) $(subst /,$(PSEP),$(TARGETDIRS)) $(ERRIGNORE) 
    $(HIDE)$(RM) $(TARGET) $(ERRIGNORE) 
    @echo Cleaning done ! 

मुख्य विशेषताएं

    निर्दिष्ट फ़ोल्डरों
  • वस्तु और निर्भरता फ़ाइलों के लिए एकाधिक स्रोत फ़ोल्डर्स
  • एकाधिक इसी लक्ष्य फ़ोल्डर में C सूत्रों के
  • स्वत: पता लगाने
  • प्रत्येक लक्ष्य फ़ोल्डर के लिए स्वचालित नियम पीढ़ी
  • लक्ष्य फ़ोल्डर्स का निर्माण जब वे मौजूद नहीं हैं
  • gcc साथ निर्भरता प्रबंधन: निर्माण केवल क्या आवश्यक है Unix पर
  • काम करता है और DOS सिस्टम
  • GNU Make

के लिए लिखित इस Makefile का उपयोग कैसे करें

अनुकूलन करने के लिए यह मेकफ़ाइल आपकी प्रोजेक्ट में है:

  1. बदलें TARGET चर अपने लक्ष्य नाम
  2. बदलें SOURCEDIR में Sources और Build फ़ोल्डरों के नाम से मेल और BUILDDIR
  3. बदलें Makefile खुद या मेकअप कॉल में में Makefile के शब्दाडंबर स्तर (make all VERBOSE=FALSE को)
  4. बदलें DIRS में फोल्डर, अपने स्रोतों से मेल खाते हैं और फ़ोल्डरों
  5. निर्माण यदि आवश्यक करने के लिए संकलक बदल सकते हैं और झंडे
  6. के नाम

इस Makefile Folder0, Folder1 और Folder2 में अपने FolderA, FolderB और FolderC के बराबर हैं।

ध्यान दें कि मुझे इस समय यूनिक्स सिस्टम पर परीक्षण करने का अवसर नहीं मिला है लेकिन यह विंडोज पर सही तरीके से काम करता है।कुछ मुश्किल भागों में से


स्पष्टीकरण:

विंडोज mkdir त्रुटियों की उपेक्षा कर

ERRIGNORE = 2>NUL || true 

यह है दो प्रभाव: पहले एक , 2>NUL त्रुटि उत्पादन पुनर्निर्देशित करना होता है एनयूएल के लिए, क्योंकि यह कंसोल में नहीं आता है।

दूसरा, || true आदेश स्तर को बढ़ाने से कमांड को रोकता है। यह मेकफ़ाइल के साथ विंडोज़ सामग्री से संबंधित नहीं है, यह यहां है क्योंकि विंडोज़ 'mkdir कमांड त्रुटि स्तर को बढ़ाता है अगर हम पहले से मौजूद फ़ोल्डर बनाने का प्रयास करते हैं, जबकि हम वास्तव में परवाह नहीं करते हैं, अगर यह ठीक है तो यह ठीक है। सामान्य समाधान if not exist संरचना का उपयोग करना है, लेकिन यह यूनिक्स-संगत नहीं है, भले ही यह मुश्किल हो, मैं अपने समाधान को और अधिक स्पष्ट मानता हूं। OBJS की


निर्माण उनके सही पथ के साथ सभी वस्तु फ़ाइलें

OBJS := $(subst $(SOURCEDIR),$(BUILDDIR),$(SOURCES:.c=.o)) 

यहाँ हम OBJS उनके रास्ते के साथ सभी वस्तु फ़ाइलें हो चाहते हैं, और हम पहले से ही स्रोत है जिसमें सभी स्रोत पथ उनके पथ के साथ। $(SOURCES:.c=.o) सभी स्रोतों के लिए * .o में * .c बदलता है, लेकिन पथ अभी भी स्रोतों में से एक है। $(subst $(SOURCEDIR),$(BUILDDIR), ...) बस बिल्ड पथ के साथ पूरे स्रोत पथ को घटाएगा, इसलिए अंततः हमारे पास एक चर है जिसमें .o फाइलें उनके पथ हैं।


Windows और यूनिक्स शैली पथ विभाजक

SEP=\\ 
SEP =/
PSEP = $(strip $(SEP)) 

इस लेनदेन के साथ ही हर किसी स्लैश का उपयोग करता है, जबकि Makefile, यूनिक्स और विंडोज पर काम करने के बाद से विंडोज पथ के बैकस्लैश का उपयोग करता है अनुमति देने के लिए मौजूद हैं ।

SEP=\\ यहां बैकस्लैश चरित्र से बचने के लिए डबल बैकस्लैश का उपयोग किया जाता है, जो make आमतौर पर एकाधिक लाइनों पर लेखन की अनुमति देने के लिए "न्यूलाइन चरित्र को अनदेखा" के रूप में मानता है।

PSEP = $(strip $(SEP)) यह SEP चर के स्पेस char को हटा देगा, जो स्वचालित रूप से जोड़ा गया है। प्रत्येक लक्ष्य फ़ोल्डर

define generateRules 
$(1)/%.o: %.c 
    @echo Building [email protected] 
    $(HIDE)$(CC) -c $$(INCLUDES) -o $$(subst /,$$(PSEP),[email protected]) $$(subst /,$$(PSEP),$$<) -MMD 
endef 

शायद चाल है कि सबसे अपने USECASE के साथ संबंधित है कि के लिए नियमों की


स्वचालित पीढ़ी। यह एक नियम टेम्पलेट है जिसे $(eval $(call generateRules, param)) से उत्पन्न किया जा सकता है जहां param आप टेम्पलेट में $(1) के रूप में पा सकते हैं। यह मूलतः प्रत्येक लक्ष्य फ़ोल्डर के लिए इस तरह के नियमों के साथ Makefile भरना होगा:

path/to/target/%.o: %.c 
    @echo Building [email protected] 
    $(HIDE)$(CC) -c $(INCLUDES) -o $(subst /,$(PSEP),[email protected]) $(subst /,$(PSEP),$<) -MMD 
+0

आपको बहुत धन्यवाद टिम! आपका मेकफ़ाइल बहुत ही सुरुचिपूर्ण और अच्छी तरह से प्रलेखित है। आपका जवाब मेरे लिए बहुत बड़ी मदद है। मैं आज शाम का परीक्षण करूंगा। धन्यवाद, धन्यवाद, धन्यवाद :-) –

+0

मुझे खुशी है कि आपको यह पसंद है, वास्तव में मुझे यह करने में बहुत खुशी हुई है। यह थोड़ा चुनौतीपूर्ण था और यह मेकफ़ाइल के तीसरे नियम से संबंधित है: 'मौजूदा कामकाजी निर्देशिका में लक्ष्य बनाए जाने पर जीवन सबसे आसान है।' लेकिन कौन सा साधारण जीवन चाहता है? ;) – Tim

+0

यह बहुत अच्छा लग रहा है! मैंने इस मेकफ़ाइल की समझ को कम करने के उत्तर के अंत में स्पष्टीकरण जोड़े हैं। संदर्भ के लिए आप बस इस स्टैक ओवरफ्लो क्यू/ए से लिंक कर सकते हैं, यह मेरे लिए ठीक है। जिस तरह से मैं उपयोग करता हूं उनमें से अधिकांश चालें SO Q/A या अन्य साइटों पर पाई गई हैं। – Tim

2

यह काफी कम से कम makefile चाल करना चाहिए:

VPATH = ../source 
OBJS = FolderA/fileA1.o FolderA/fileA2.o FolderB/fileB1.o 
CPPFLAGS = -MMD -MP 

all: init myProgram 

myProgram: $(OBJS) 
     $(CC) $(LDFLAGS) -o [email protected] $(OBJS) $(LDLIBS) 

.PHONY: all init 

init: 
     mkdir -p FolderA 
     mkdir -p FolderB 

-include $(OBJS:%.o=%.d) 

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

एक वैकल्पिक तरीका है कि समानांतर लिए काम करना चाहिए बनाता सी फ़ाइलें संकलन के लिए मानक नियम को फिर से परिभाषित करने के लिए किया जाएगा:

VPATH = ../source 
OBJS = FolderA/fileA1.o FolderA/fileA2.o FolderB/fileB1.o 
CPPFLAGS = -MMD -MP 

myProgram: $(OBJS) 
     $(CC) $(LDFLAGS) -o [email protected] $(OBJS) $(LDLIBS) 

%.o: %.c 
     mkdir -p $(dir [email protected]) 
     $(CC) $(CFLAGS) $(CPPFLAGS) -c -o [email protected] $< 

-include $(OBJS:%.o=%.d) 

कौन सा नुकसान यह है कि आप भी किसी अन्य के लिए अंतर्निहित नियमों को फिर से परिभाषित करने की आवश्यकता होगी है एक तरह से sourcefile आप

+0

वाह, बहुत बहुत धन्यवाद! आप कहते हैं कि यह पहली बार चलने पर '-j2' के साथ काम नहीं करेगा। यह विकल्प निर्माण को समानांतर करना है, है ना? मुझे लगता है कि यह समानांतर निर्माण में काम नहीं करेगा क्योंकि ऑब्जेक्ट फ़ाइलों को बनाए जाने पर फ़ोल्डर्स FolderA और FolderB मौजूद नहीं हो सकता है। क्या मैं सही हूँ? –

+0

'VPATH' एक ही नाम के साथ एकाधिक स्रोत फ़ाइलों को कैसे संभालता है? –

+1

@MaximEgorushkin: आप एक ही निर्देशिका में एक ही नाम के साथ mulitple फ़ाइलें नहीं हो सकता है। उपनिर्देशिका (स्रोत के तहत) नाम का हिस्सा है, इसलिए विभिन्न उपनिर्देशिकाओं में समान आधार फ़ाइल नाम ठीक है। विस्तार और परिवर्तन के साथ ऑब्जेक्ट और निर्भरता फ़ाइलों का एक ही नाम (उपदिर नाम सहित) होगा। तो आप 'build' के तहत 'स्रोत' –

1

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

APP=app 

SRC_DIR=src 
INC_DIR=inc 
OBJ_DIR=obj 
BIN_DIR=bin 

CC=gcc 
LD=gcc 
CFLAGS=-O2 -c -Wall -pedantic -ansi 
LFLGAS= 
DFLAGS=-g3 -O0 -DDEBUG 
INCFLAGS=-I$(INC_DIR) 

SOURCES=$(wildcard $(SRC_DIR)/*.c) 
HEADERS=$(wildcard $(INC_DIR)/*.h) 
OBJECTS=$(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) 
DEPENDS=$(OBJ_DIR)/.depends 


.PHONY: all 
all: $(BIN_DIR)/$(APP) 

.PHONY: debug 
debug: CFLAGS+=$(DFLAGS) 
debug: all 


$(BIN_DIR)/$(APP): $(OBJECTS) | $(BIN_DIR) 
    $(LD) $(LFLGAS) -o [email protected] $^ 

$(OBJ_DIR)/%.o: | $(OBJ_DIR) 
    $(CC) $(CFLAGS) $(INCFLAGS) -o [email protected] $< 

$(DEPENDS): $(SOURCES) | $(OBJ_DIR) 
    $(CC) $(INCFLAGS) -MM $(SOURCES) | sed -e 's!^!$(OBJ_DIR)/!' >[email protected] 

ifneq ($(MAKECMDGOALS),clean) 
-include $(DEPENDS) 
endif 


$(BIN_DIR): 
    mkdir -p [email protected] 
$(OBJ_DIR): 
    mkdir -p [email protected] 

.PHONY: clean 
clean: 
    rm -rf $(BIN_DIR) $(OBJ_DIR) 
+0

वाह, इस फ़ाइल को साझा करने के लिए आपको बहुत बहुत धन्यवाद। –

-2

मैं सीधे मेकफ़ाइल में हेरफेर करने से बचूंगा, और इसके बजाय सीएमके का उपयोग करूंगा। बस अपनी स्रोत फ़ाइलों का वर्णन CMakeLists.txt में करें, नीचे दिए गए:

फ़ाइल MyProject/source/CMakeLists.txt फ़ाइल बनाएं;

project(myProject) 
add_executable(myExec FolderA/fileA1.c FolderA/fileA2.c FolderB/fileB1.c) 

MyProject/निर्माण के तहत,

cmake ../source/ 

चलाने आप एक Makefile अब मिलेगा। एक ही निर्माण/निर्देशिका के अंतर्गत निर्माण करने के लिए,

make 

तुम भी एक बिजली की तेजी निर्माण उपकरण के लिए स्विच कर सकते हैं, निंजा, बस निम्नलिखित के रूप में एक स्विच जोड़कर।

cmake -GNinja .. 
ninja 
+1

स्टैक ओवरफ़्लो में आपका स्वागत है! यह वास्तव में एक टिप्पणी है, जवाब नहीं। थोड़ा और प्रतिनिधि के साथ, [आप टिप्पणियां पोस्ट करने में सक्षम होंगे] (// stackoverflow.com/privileges/comment)। – manetsus

+0

@manetsus आपकी टिप्पणी के लिए धन्यवाद। मैंने अभी अपना जवाब संशोधित कर दिया है। – exavolt

संबंधित मुद्दे