J'ai la source dans un tas de sous-répertoires comme:
src/widgets/Apple.cpp
src/widgets/knob.cpp
src/tests/blend.cpp
src/ui/flash.cpp
À la racine du projet, je veux générer un seul Makefile en utilisant une 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, il ne trouve pas de règle pour build/widgets/Apple.o. Puis-je changer quelque chose pour que le% .o:% .cpp soit utilisé quand il doit créer build/widgets/Apple.o?
La raison en est que votre règle
%.o: %.cpp
...
s'attend à ce que le fichier .cpp réside dans le même répertoire que le .o de votre bâtiment. Étant donné que test.exe dans votre cas dépend de build/widgets/Apple.o (etc.), make s'attend à ce qu'Apple.cpp soit build/widgets/Apple.cpp.
Vous pouvez utiliser VPATH pour résoudre ce problème:
VPATH = src/widgets
BUILDDIR = build/widgets
$(BUILDDIR)/%.o: %.cpp
...
Lorsque vous essayez de construire "build/widgets/Apple.o", make recherchera Apple.cpp dans VPATH . Notez que la règle de construction doit utiliser des variables spéciales pour accéder à la recherche de nom de fichier réelle:
$(BUILDDIR)/%.o: %.cpp
$(CC) $< -o $@
Où "$ <" se développe jusqu'au chemin où make a localisé la première dépendance.
Notez également que cela générera tous les fichiers .o dans build/widgets. Si vous voulez construire les binaires dans différents répertoires, vous pouvez faire quelque chose comme
build/widgets/%.o: %.cpp
....
build/ui/%.o: %.cpp
....
build/tests/%.o: %.cpp
....
Je vous recommande d'utiliser " séquences de commandes prédéfinies " afin d'éviter de répéter la règle de construction du compilateur réelle:
define cc-command
$(CC) $(CFLAGS) $< -o $@
endef
Vous pouvez alors 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)
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 $$@
endef
.PHONY: all checkdirs clean
all: checkdirs build/test.exe
build/test.exe: $(OBJ)
$(LD) $^ -o $@
checkdirs: $(BUILD_DIR)
$(BUILD_DIR):
@mkdir -p $@
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 génération 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 build en utilisant la fonction make-goal
, et il n'est pas nécessaire de les écrire un par un
Vous pouvez également ajouter une génération automatique de dépendances, en utilisant à la manière de Tromey
La chose est que $@
Inclura le chemin complet (relatif) du fichier source qui est à son tour utilisé 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) "$< -> $@"
@test -d $(OBJDIR_1) || mkdir -pm 775 $(OBJDIR_1)
@test -d $(@D) || mkdir -pm 775 $(@D)
@-$(RM) $@
$(CC) $(CFLAGS) $(CFLAGS_1) $(ALL_FLAGS) $(ALL_DEFINES) $(ALL_INCLUDEDIRS:%=-I%) -c $< -o $@
Cela crée un répertoire d'objets avec un nom spécifié dans $(OBJDIR_1)
et des sous-répertoires en fonction des sous-répertoires dans la source.
Par exemple (supposez objs comme répertoire d'objet de niveau supérieur), dans Makefile:
widget/Apple.cpp
tests/blend.cpp
donne le répertoire d'objets suivant:
objs/widget/Apple.o
objs/tests/blend.o
Cela le fera sans manipulation douloureuse ni séquences de commandes multiples:
build /%. o: src /%. cpp src /%. o: src /%. cpp %. o: $ (CC) -c $ <-o $ @ build/test.exe: build/widgets/Apple.o build/widgets/button.o build/tests/blend.o src/ui/flash .o $ (LD) $ ^ -o $ @
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 prérequis, et deux règles de modèle (build /%. o: et src /%. o :) avec des prérequis et aucune commande. (Notez que j'ai mis la règle src /%. O pour gérer 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 laisser de côté.)
build/test.exe a besoin de build/widgets/Apple.o,
build/widgets/Apple.o ressemble à build /%. o, il a donc besoin de src /%. cpp (dans ce cas src/widgets/Apple.cpp),
build/widgets/Apple.o ressemble également à% .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 (build/widgets /Apple.o)
Ceci 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 les options de compilation 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 $@
%.lst: $(OBJDIR)/%.elf
-@rm $@ 2> /dev/nul
$(OBJDUMP) -h -s -S $< > $@
%.hex: $(OBJDIR)/%.elf
-@rm $@ 2> /dev/nul
$(OBJCOPY) -j .text -j .data -O ihex $< $@
$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile
$(CC) -MMD -MF [email protected] -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o $@
@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]
-@rm -f [email protected]
$(CPP_OBJS) : $(OBJDIR)/%.o : %.cpp Makefile
$(CC) -MMD -MF [email protected] -c $(CPP_FLAGS) $(CPP_FLAGS_$(call clear_name,$<)) $< -o $@
@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]
-@rm -f [email protected]
$(AS_OBJS) : $(OBJDIR)/%.o : %.S Makefile
$(CC) -MMD -MF [email protected] -c $(AS_FLAGS) $(AS_FLAGS_$(call clear_name,$<)) $< -o $@
@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]
-@rm -f [email protected]
clean:
-@rm -rf $(OBJDIR)/$(PRG).elf
-@rm -rf $(PRG).lst $(OBJDIR)/$(PRG).map
-@rm -rf $(PRG).hex $(PRG).bin $(PRG).srec
-@rm -rf $(PRG)_eeprom.hex $(PRG)_eeprom.bin $(PRG)_eeprom.srec
-@rm -rf $(MK_DIRS:%=%/*.o) $(MK_DIRS:%=%/*.o.d)
-@rm -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
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 src/
répertoire):
# 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 $@
.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 bien pour une bibliothèque partagée, et il devrait être très facile de l'adapter à n'importe quel processus de compilation.
Habituellement, vous créez un Makefile dans chaque sous-répertoire et é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/