web-dev-qa-db-fra.com

Comment générer un Makefile avec la source dans des sous-répertoires en utilisant un seul makefile

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?

54
Jeroen Dirks

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)
67
JesperE

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

65
Manzill0

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
4
Tim Ruijs

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)

3
Beta

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
3
iLya

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.

3
Amaury Bouchard

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/

0
stephanea