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

StackOverflow https://stackoverflow.com/questions/231229

  •  04-07-2019
  •  | 
  •  

Question

J'ai la source dans un tas de sous-répertoires tels que:

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

À la racine du projet, je souhaite générer un seul fichier Makefile à l'aide d'une règle telle que:

%.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

Quand j'essaye ceci, 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 faut faire build / widgets / apple.o?

Était-ce utile?

La solution

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. Puisque test.exe dépend dans votre cas 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 problème:

VPATH = src/widgets

BUILDDIR = build/widgets

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

Lors de la tentative de création de " build / widgets / apple.o & "; make", make cherchera apple.cpp dans VPATH . Notez que la règle de construction doit utiliser des variables spéciales pour accéder au nom de fichier réel make find:

$(BUILDDIR)/%.o: %.cpp
        $(CC) $< -o $@

Où " $ < " se développe dans le 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 fichiers 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 recommanderais d'utiliser " en conserve séquences de commandes " afin d'éviter de répéter la règle de construction actuelle du compilateur:

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)

Autres conseils

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 vos fichiers d’inclusion se trouvent dans les répertoires sources. En outre, il vérifie 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 construction à l'aide de la fonction make-goal. Il n'est pas nécessaire de les écrire une par une

.

Vous pouvez également ajouter une génération automatique de dépendances en utilisant la façon de Tromey

Ce qui est $@ comprendra l'intégralité du chemin (relatif) du fichier source 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 $@

Ceci crée un répertoire d'objets avec le nom spécifié dans $(OBJDIR_1) et sous-répertoires en fonction des sous-répertoires de la source.

Par exemple (supposons que objs soit le répertoire de l'objet toplevel), dans le Makefile:

widget/apple.cpp
tests/blend.cpp

génère le répertoire d'objets suivant:

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

Cela se fera sans manipulation douloureuse ni plusieurs séquences de commandes:

build/%.o: src/%.cpp
src/%.o: src/%.cpp
%.o:
    $(CC) -c $< -o $@

build/test.exe: build/widgets/apple.o build/widgets/knob.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 aucun critère, et deux règles de modèle (build /%. o: et src /%. o :) avec des critères et des commandes. (Notez que je mets dans la règle src /% .o de traiter de 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 / test.exe nécessite build / widgets / apple.o,
build / widgets / apple.o ressemble à build /%. o, il faut donc src /%. cpp (dans ce cas src / widgets / apple.cpp),
build / widgets / apple.o ressemble également à% .o; il exécute donc la commande CC et utilise les conditions préalables qu'il vient de trouver (à savoir src / widgets / apple.cpp) pour générer la cible (build / widgets / apple.o).

C’est une autre astuce.

Dans le '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 sources 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 $@.p.d -c $(C_FLAGS) $(C_FLAGS_$(call clear_name,$<)) $< -o $@
    @sed -e 's,.*:,SRC_FILES += ,g' < $@.p.d > $@.d
    @sed -e "\$$s/$$/ $(subst /,\/,$(dir $<))files.mk\n/" < $@.p.d >> $@.d
    @sed -e 's,^[^:]*: *,,' -e 's,^[ \t]*,,' -e 's, \\$$,,' -e 's,$$, :,' < $@.p.d >> $@.d
    -@rm -f $@.p.d

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

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


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 à tout processus de compilation.

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

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top