2008-10-23 5 views
50

J'ai source dans un tas de sous-répertoires comme:Comment générer un Makefile avec la source dans les sous-répertoires en utilisant seulement un makefile

src/widgets/apple.cpp 
src/widgets/knob.cpp 
src/tests/blend.cpp 
src/ui/flash.cpp 

Dans la racine du projet, je veux générer un Makefile en utilisant un Règle comme:

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

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o 
    $(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe 

Lorsque j'essaie cela, il ne trouve pas de règle pour build/widgets/apple.o. Est-ce que je peux changer quelque chose pour que le% .o:% .cpp soit utilisé quand il doit faire build/widgets/apple.o?

Répondre

64

La raison en est que votre règle

%.o: %.cpp 
     ... 

attend le fichier .cpp de résider dans le même répertoire que le .o votre bâtiment. Puisque test.exe dans votre cas dépend de build/widgets/apple.o (etc), make s'attend à ce que apple.cpp soit build/widgets/apple.cpp.

Vous pouvez utiliser VPATH pour résoudre ce:

VPATH = src/widgets 

BUILDDIR = build/widgets 

$(BUILDDIR)/%.o: %.cpp 
     ... 

Lors d'une tentative de construire "construire/widgets/apple.o", make rechercher apple.cpp dans VPATH. Notez que la règle de construction doit utiliser des variables spéciales afin d'accéder à la marque de nom réel trouve:

$(BUILDDIR)/%.o: %.cpp 
     $(CC) $< -o [email protected] 

Où « < $ » est remplacé et le chemin où faire situé la première dépendance. Notez également que cela va générer tous les fichiers .o dans build/widgets. Si vous voulez construire les binaires dans des répertoires différents, vous pouvez faire quelque chose comme

build/widgets/%.o: %.cpp 
     .... 

build/ui/%.o: %.cpp 
     .... 

build/tests/%.o: %.cpp 
     .... 

Je vous recommande d'utiliser « canned command sequences » afin d'éviter de répéter la règle de construction réelle du compilateur:

define cc-command 
$(CC) $(CFLAGS) $< -o [email protected] 
endef 

vous pouvez avoir plusieurs règles comme celle-ci:

build1/foo.o build1/bar.o: %.o: %.cpp 
    $(cc-command) 

build2/frotz.o build2/fie.o: %.o: %.cpp 
    $(cc-command) 
+2

'VPATH' ne vous permet pas d'avoir différents fichiers source avec le même nom dans des répertoires différents, ce qui va à l'encontre de l'objectif des répertoires en premier lieu. –

+10

Eh, non, je ne le pense pas. Organiser votre code source dans différents répertoires a plus d'avantages que d'autoriser plusieurs fichiers source avec le même nom. – JesperE

+0

J'ai essayé de suivre votre solution et créé des cibles de la forme '.build/cwrapper /%. O:% .c% .cpp'. Cependant, lorsque je les utilise en tant que dépendance, par ex. 'cwrapper: .build/cwrapper /%. o',' make' se plaint: "Aucune règle pour rendre la cible' .build/cwrapper /%. o '". Qu'est-ce que je fais mal? – Raphael

0

Généralement, vous créez un fichier Makefile dans chaque sous-répertoire et vous écrivez dans le Makefile de niveau supérieur pour appeler make dans les sous-répertoires.

Cette page peut aider: http://www.gnu.org/software/make/

+1

Ceci est couramment fait, mais est plein de problèmes. Le principal est que personne ne rende le processus connu de toutes les dépendances, donc des choses comme -j2 sur les systèmes multicœurs ne fonctionneront pas. Voir http://aegis.sourceforge.net/auug97.pdf – KeithB

+4

La référence de Keith est à un excellent papier appelé «Recursive Make Considered Nocif». Ceci est une contribution à la série d'articles commençant par la lettre de Dijkstra "Go To Considered Harmful", et aboutissant à "Considéré Nocif" Considéré Nocif ". –

+4

C'est fait généralement parce que les gens ne comprennent pas comment écrire Makefiles. Un Makefile par répertoire est nul. – mxcl

4

chose est [email protected] comprendra le chemin complet (relatif) du fichier source qui est en t urne utilisée pour construire le nom de l'objet (et donc son chemin relatif)

Nous utilisons:

##################### 
# rules to build the object files 
$(OBJDIR_1)/%.o: %.c 
    @$(ECHO) "$< -> [email protected]" 
    @test -d $(OBJDIR_1) || mkdir -pm 775 $(OBJDIR_1) 
    @test -d $(@D) || mkdir -pm 775 $(@D) 
    @-$(RM) [email protected] 
    $(CC) $(CFLAGS) $(CFLAGS_1) $(ALL_FLAGS) $(ALL_DEFINES) $(ALL_INCLUDEDIRS:%=-I%) -c $< -o [email protected] 

Cela crée un répertoire d'objets avec le nom spécifié dans $(OBJDIR_1) et sous-répertoires selon les sous-répertoires dans la source.

Par exemple (en supposant objs comme répertoire d'objets de premier niveau), dans le Makefile:

widget/apple.cpp 
tests/blend.cpp 

résultats dans répertoire suivant de l'objet:

objs/widget/apple.o 
objs/tests/blend.o 
3

Cela fera sans manipulation douloureuse ou des séquences de commandes multiples:

 
build/%.o: src/%.cpp 
src/%.o: src/%.cpp 
%.o: 
    $(CC) -c $< -o [email protected] 

build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o 
    $(LD) $^ -o [email protected] 

JasperE a expliqué pourquoi "% .o:% .cpp" ne fonctionnera pas; cette version a une règle de modèle (% .o :) avec des commandes et pas de prereqs, et deux règles de modèle (build /% .o: et src /% .o :) avec prereqs et aucune commande. (Notez que j'ai mis dans la règle src /%. O pour traiter src/ui/flash.o, en supposant que ce n'était pas une faute de frappe pour build/ui/flash.o, donc si vous n'en avez pas besoin, vous pouvez laissez-le.)

build/besoins test.exe construire/widgets/apple.o,
build/widgets/apple.o ressemble build /%. o, donc il a besoin src /%. cpp (en build/widgets/apple.o ressemble aussi à% .o, donc il exécute la commande CC et utilise les prérequis qu'il vient de trouver (à savoir src/widgets/apple.cpp) pour construire la cible (construire/widgets/apple.o)

+0

Cela se casse, car votre règle '% .o:' n'a pas de prérequis, mais se réfère à eux via '$ <' (qui est donc vide). "Pas de fichiers d'entrée", désolé. – DevSolar

60

Cela fait l'affaire:

CC  := g++ 
LD  := g++ 

MODULES := widgets test ui 
SRC_DIR := $(addprefix src/,$(MODULES)) 
BUILD_DIR := $(addprefix build/,$(MODULES)) 

SRC  := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp)) 
OBJ  := $(patsubst src/%.cpp,build/%.o,$(SRC)) 
INCLUDES := $(addprefix -I,$(SRC_DIR)) 

vpath %.cpp $(SRC_DIR) 

define make-goal 
$1/%.o: %.cpp 
    $(CC) $(INCLUDES) -c $$< -o [email protected] 
endef 

.PHONY: all checkdirs clean 

all: checkdirs build/test.exe 

build/test.exe: $(OBJ) 
    $(LD) $^ -o [email protected] 


checkdirs: $(BUILD_DIR) 

$(BUILD_DIR): 
    @mkdir -p [email protected] 

clean: 
    @rm -rf $(BUILD_DIR) 

$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir)))) 

Ce Makefile suppose que vous avez vos fichiers include dans les répertoires source. Il vérifie également si les répertoires de construction existent et les crée s'ils n'existent pas.

La dernière ligne est la plus importante. Il crée les règles implicites pour chaque génération en utilisant la fonction make-goal, et il ne faut pas les écrire un par un

Vous pouvez également ajouter la génération automatique des dépendances, en utilisant Tromey's way

+0

Très bien, excatly ce que je cherchais;) Merci Manzill0 – Geoffroy

+0

Votre utilisation de foreach m'a vraiment aidé. Je crée une longue liste de fichiers C que je génère un fichier objet individuel. Merci! –

+0

@ Manzill0 la question suivante a le même ensemble de problèmes lorsqu'il s'agit de plusieurs cibles et de leurs dépendances? https://stackoverflow.com/questions/30043480/make-recipe-to-prevent-rebuilding-of-non-dependent-targets# –

3

C'est une autre astuce.

Dans 'Makefile' principal, définissez SRCDIR pour chaque répertoire source et incluez 'makef.mk' pour chaque valeur de SRCDIR. Dans chaque répertoire source, placez le fichier 'files.mk' avec la liste des fichiers source et compilez les options pour certains d'entre eux. Dans le Makefile principal, on peut définir des options de compilation et exclure des fichiers pour chaque valeur de SRCDIR.

Makefile:

PRG    := prog-name 

OPTIMIZE  := -O2 -fomit-frame-pointer 

CFLAGS += -finline-functions-called-once 
LDFLAGS += -Wl,--gc-section,--reduce-memory-overheads,--relax 


.DEFAULT_GOAL := hex 

OBJDIR   := obj 

MK_DIRS   := $(OBJDIR) 


SRCDIR   := . 
include   makef.mk 

SRCDIR := crc 
CFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE 
ASFLAGS_crc := -DCRC8_BY_TABLE -DMODBUS_CRC_BY_TABLE 
include makef.mk 

################################################################ 

CC    := avr-gcc -mmcu=$(MCU_TARGET) -I. 
OBJCOPY   := avr-objcopy 
OBJDUMP   := avr-objdump 

C_FLAGS   := $(CFLAGS) $(REGS) $(OPTIMIZE) 
CPP_FLAGS  := $(CPPFLAGS) $(REGS) $(OPTIMIZE) 
AS_FLAGS  := $(ASFLAGS) 
LD_FLAGS  := $(LDFLAGS) -Wl,-Map,$(OBJDIR)/$(PRG).map 


C_OBJS   := $(C_SRC:%.c=$(OBJDIR)/%.o) 
CPP_OBJS  := $(CPP_SRC:%.cpp=$(OBJDIR)/%.o) 
AS_OBJS   := $(AS_SRC:%.S=$(OBJDIR)/%.o) 

C_DEPS   := $(C_OBJS:%=%.d) 
CPP_DEPS  := $(CPP_OBJS:%=%.d) 
AS_DEPS   := $(AS_OBJS:%=%.d) 

OBJS   := $(C_OBJS) $(CPP_OBJS) $(AS_OBJS) 
DEPS   := $(C_DEPS) $(CPP_DEPS) $(AS_DEPS) 


hex: $(PRG).hex 
lst: $(PRG).lst 


$(OBJDIR)/$(PRG).elf : $(OBJS) 
    $(CC) $(C_FLAGS) $(LD_FLAGS) $^ -o [email protected] 

%.lst: $(OBJDIR)/%.elf 
    [email protected] [email protected] 2> /dev/nul 
    $(OBJDUMP) -h -s -S $< > [email protected] 

%.hex: $(OBJDIR)/%.elf 
    [email protected] [email protected] 2> /dev/nul 
    $(OBJCOPY) -j .text -j .data -O ihex $< [email protected] 


$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile 
    $(CC) -MMD -MF [email protected] -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o [email protected] 
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected] 
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected] 
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected] 
    [email protected] -f [email protected] 

$(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile 
    $(CC) -MMD -MF [email protected] -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o [email protected] 
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected] 
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected] 
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected] 
    [email protected] -f [email protected] 

$(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile 
    $(CC) -MMD -MF [email protected] -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o [email protected] 
    @sed -e 's,.*:,SRC_FILES += ,g' < [email protected] > [email protected] 
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < [email protected] >> [email protected] 
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < [email protected] >> [email protected] 
    [email protected] -f [email protected] 


clean: 
    [email protected] -rf $(OBJDIR)/$(PRG).elf 
    [email protected] -rf $(PRG).lst $(OBJDIR)/$(PRG).map 
    [email protected] -rf $(PRG).hex $(PRG).bin $(PRG).srec 
    [email protected] -rf $(PRG)_eeprom.hex $(PRG)_eeprom.bin $(PRG)_eeprom.srec 
    [email protected] -rf $(MK_DIRS:%=%/*.o) $(MK_DIRS:%=%/*.o.d) 
    [email protected] -f tags cscope.out 

# -rm -rf $(OBJDIR)/* 
# -rm -rf $(OBJDIR) 
# -rm $(PRG) 


tag: tags 
tags: $(SRC_FILES) 
    if [ -e tags ] ; then ctags -u $? ; else ctags $^ ; fi 
    cscope -U -b $^ 


# include dep. files 
ifneq "$(MAKECMDGOALS)" "clean" 
-include $(DEPS) 
endif 


# Create directory 
$(shell mkdir $(MK_DIRS) 2>/dev/null) 

makef.mk

SAVE_C_SRC := $(C_SRC) 
SAVE_CPP_SRC := $(CPP_SRC) 
SAVE_AS_SRC := $(AS_SRC) 

C_SRC := 
CPP_SRC := 
AS_SRC := 


include $(SRCDIR)/files.mk 
MK_DIRS += $(OBJDIR)/$(SRCDIR) 


clear_name = $(subst /,_,$(1)) 


define rename_var 
$(2)_$(call clear_name,$(SRCDIR))_$(call clear_name,$(1)) := \ 
    $($(subst _,,$(2))_$(call clear_name,$(SRCDIR))) $($(call clear_name,$(1))) 
$(call clear_name,$(1)) := 
endef 


define proc_lang 

ORIGIN_SRC_FILES := $($(1)_SRC) 

ifneq ($(strip $($(1)_ONLY_FILES)),) 
$(1)_SRC := $(filter $($(1)_ONLY_FILES),$($(1)_SRC)) 
else 

ifneq ($(strip $(ONLY_FILES)),) 
$(1)_SRC := $(filter $(ONLY_FILES),$($(1)_SRC)) 
else 
$(1)_SRC := $(filter-out $(EXCLUDE_FILES),$($(1)_SRC)) 
endif 

endif 

$(1)_ONLY_FILES := 
$(foreach name,$($(1)_SRC),$(eval $(call rename_var,$(name),$(1)_FLAGS))) 
$(foreach name,$(ORIGIN_SRC_FILES),$(eval $(call clear_name,$(name)) :=)) 

endef 


$(foreach lang,C CPP AS, $(eval $(call proc_lang,$(lang)))) 


EXCLUDE_FILES := 
ONLY_FILES := 


SAVE_C_SRC += $(C_SRC:%=$(SRCDIR)/%) 
SAVE_CPP_SRC += $(CPP_SRC:%=$(SRCDIR)/%) 
SAVE_AS_SRC += $(AS_SRC:%=$(SRCDIR)/%) 

C_SRC := $(SAVE_C_SRC) 
CPP_SRC := $(SAVE_CPP_SRC) 
AS_SRC := $(SAVE_AS_SRC) 

./files.mk

C_SRC := main.c 
CPP_SRC := 
AS_SRC := timer.S 

main.c += -DDEBUG 

./crc/files.mk

C_SRC := byte-modbus-crc.c byte-crc8.c 
AS_SRC := modbus-crc.S crc8.S modbus-crc-table.S crc8-table.S 

byte-modbus-crc.c += --std=gnu99 
byte-crc8.c  += --std=gnu99 
2

Voici ma solution, inspirée de la réponse de Beta. C'est plus simple que les autres solutions proposées

J'ai un projet avec plusieurs fichiers C, stockés dans de nombreux sous-répertoires. Par exemple:

src/lib.c 
src/aa/a1.c 
src/aa/a2.c 
src/bb/b1.c 
src/cc/c1.c 

Voici mon Makefile (dans le répertoire src/):

# make  -> compile the shared library "libfoo.so" 
# make clean -> remove the library file and all object files (.o) 
# make all -> clean and compile 
SONAME = libfoo.so 
SRC  = lib.c \ 
      aa/a1.c \ 
      aa/a2.c \ 
      bb/b1.c \ 
      cc/c1.c 
# compilation options 
CFLAGS = -O2 -g -W -Wall -Wno-unused-parameter -Wbad-function-cast -fPIC 
# linking options 
LDFLAGS = -shared -Wl,-soname,$(SONAME) 

# how to compile individual object files 
OBJS = $(SRC:.c=.o) 
.c.o: 
    $(CC) $(CFLAGS) -c $< -o [email protected] 

.PHONY: all clean 

# library compilation 
$(SONAME): $(OBJS) $(SRC) 
    $(CC) $(OBJS) $(LDFLAGS) -o $(SONAME) 

# cleaning rule 
clean: 
    rm -f $(OBJS) $(SONAME) *~ 

# additional rule 
all: clean lib 

Cet exemple fonctionne très bien pour une bibliothèque partagée, et il devrait être très facile d'adapter à tout processus de compilation .

Questions connexes