Makefiles avec des fichiers source dans différents répertoires

J'ai un projet où la structure du répertoire est comme ceci:

                         $projectroot
                              |
              +---------------+----------------+
              |               |                |
            part1/          part2/           part3/
              |               |                |
       +------+-----+     +---+----+       +---+-----+
       |      |     |     |        |       |         |
     data/   src/  inc/  src/     inc/   src/       inc/

Comment devrais-je écrire un makefile qui serait en partie / src (ou où vraiment) qui pourrait compléter / lier sur les fichiers source C / C++ en partie?/ src ?

Puis-je faire quelque chose comme -I$projectroot/part1/src -I$projectroot/part1/inc -I$projectroot/part2/src ...

Si cela fonctionnait, y a-t-il un moyen plus facile de le faire. J'ai vu des projets où il y a un makefile dans chacune des parties correspondantes? dossier. [dans ce post, j'ai utilisé le point d'interrogation, comme dans la syntaxe bash]

112
demandé sur devin 2009-07-16 22:02:11

9 réponses

La méthode traditionnelle est d'avoir un Makefile dans chacun des sous-répertoires (part1, part2, etc.) vous permettant de les construire indépendamment. De plus, ayez un Makefile dans le répertoire racine du projet qui construit tout. La "racine" Makefile ressemblerait à quelque chose comme ceci:

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

Comme chaque ligne d'une cible make est exécutée dans son propre shell, il n'est pas nécessaire de s'inquiéter de la sauvegarde de l'arborescence des répertoires ou d'autres répertoires.

Je suggère de jeter un oeil à la GNU make manual Section 5.7; c'est très utile.

89
répondu akhan 2016-10-26 21:24:11

Si vous avez du code dans un sous-répertoire dépendant du code dans un autre sous-répertoire, vous êtes probablement mieux avec un seul makefile au niveau supérieur.

VoirRecursive Make considéré comme nuisible pour la justification complète, mais fondamentalement, vous voulez que make ait l'information complète dont il a besoin pour décider si un fichier doit être reconstruit ou non, et il ne l'aura pas si vous ne lui parlez que d'un tiers de votre projet.

Le lien ci-dessus semble ne pas être accessible. Le même document est accessible ici:

80
répondu dave4420 2015-08-16 18:55:13

L'option VPATH peut être utile, ce qui indique à make quels répertoires rechercher pour le code source. Vous auriez toujours besoin d'une option-I pour chaque chemin d'inclusion, cependant. Un exemple:

CXXFLAGS=-Ipart1/inc -Ipart2/inc -Ipart3/inc
VPATH=part1/src:part2/src:part3/src

OutputExecutable: part1api.o part2api.o part3api.o

Cela trouvera automatiquement la partXapi correspondante.fichiers cpp dans L'un des répertoires spécifiés par VPATH et les compiler. Cependant, cela est plus utile lorsque votre répertoire src est divisé en sous-répertoires. Pour ce que vous décrivez, comme d'autres l'ont dit, vous êtes probablement mieux avec un makefile pour chaque partie, surtout si chaque partie peut être autonome.

22
répondu CodeGoat 2009-07-16 18:46:22

Vous pouvez ajouter des règles à votre Makefile racine afin de compiler les fichiers cpp nécessaires dans d'autres répertoires. L'exemple de Makefile ci-dessous devrait être un bon début pour vous rendre là où vous voulez être.

CC=g++
TARGET=cppTest
OTHERDIR=../../someotherpath/in/project/src

SOURCE = cppTest.cpp
SOURCE = $(OTHERDIR)/file.cpp

## End sources definition
INCLUDE = -I./ $(AN_INCLUDE_DIR)  
INCLUDE = -I.$(OTHERDIR)/../inc
## end more includes

VPATH=$(OTHERDIR)
OBJ=$(join $(addsuffix ../obj/, $(dir $(SOURCE))), $(notdir $(SOURCE:.cpp=.o))) 

## Fix dependency destination to be ../.dep relative to the src dir
DEPENDS=$(join $(addsuffix ../.dep/, $(dir $(SOURCE))), $(notdir $(SOURCE:.cpp=.d)))

## Default rule executed
all: $(TARGET)
        @true

## Clean Rule
clean:
        @-rm -f $(TARGET) $(OBJ) $(DEPENDS)


## Rule for making the actual target
$(TARGET): $(OBJ)
        @echo "============="
        @echo "Linking the target $@"
        @echo "============="
        @$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
        @echo -- Link finished --

## Generic compilation rule
%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@


## Rules for object files from cpp files
## Object file for each file is put in obj directory
## one level up from the actual source directory.
../obj/%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

# Rule for "other directory"  You will need one per "other" dir
$(OTHERDIR)/../obj/%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

## Make dependancy rules
../.dep/%.d: %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo Building dependencies file for $*.o
        @$(SHELL) -ec '$(CC) -M $(CFLAGS) $< | sed "s^$*.o^../obj/$*.o^" > $@'

## Dependency rule for "other" directory
$(OTHERDIR)/../.dep/%.d: %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo Building dependencies file for $*.o
        @$(SHELL) -ec '$(CC) -M $(CFLAGS) $< | sed "s^$*.o^$(OTHERDIR)/../obj/$*.o^" > $@'

## Include the dependency files
-include $(DEPENDS)

22
répondu RC. 2009-07-16 19:23:01

Si les sources sont réparties dans de nombreux dossiers, et qu'il est logique d'avoir des Makefiles individuels, comme suggéré précédemment, recursive make est une bonne approche, mais pour les petits projets, il est plus facile de lister tous les fichiers source dans le Makefile avec leur chemin relatif vers le Makefile comme ceci:

# common sources
COMMON_SRC := ./main.cpp \
              ../src1/somefile.cpp \
              ../src1/somefile2.cpp \
              ../src2/somefile3.cpp \

Je peux alors définir VPATH de cette façon:

VPATH := ../src1:../src2

Ensuite, je construis les objets:

COMMON_OBJS := $(patsubst %.cpp, $(ObjDir)/%$(ARCH)$(DEBUG).o, $(notdir $(COMMON_SRC)))

Maintenant, la règle est simple:

# the "common" object files
$(ObjDir)/%$(ARCH)$(DEBUG).o : %.cpp Makefile
    @echo creating $@ ...
    $(CXX) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<

Et bâtiment la sortie est encore plus facile:

# This will make the cbsdk shared library
$(BinDir)/$(OUTPUTBIN): $(COMMON_OBJS)
    @echo building output ...
    $(CXX) -o $(BinDir)/$(OUTPUTBIN) $(COMMON_OBJS) $(LFLAGS)

On peut même rendre la génération VPATH automatisée par:

VPATH := $(dir $(COMMON_SRC))

Ou en utilisant le fait que sort supprime les doublons (bien que cela ne devrait pas avoir d'importance):

VPATH := $(sort  $(dir $(COMMON_SRC)))
16
répondu dashesy 2012-05-16 04:32:06

Je pense qu'il est préférable de souligner que L'utilisation de Make (récursif ou non) est quelque chose que vous pouvez généralement éviter, car par rapport aux outils d'aujourd'hui, il est difficile d'apprendre, de maintenir et d'évoluer.

C'est un outil merveilleux mais son utilisation directe devrait être considérée comme obsolète en 2010+.

Sauf si, bien sûr, vous travaillez dans un environnement spécial, c'est-à-dire avec un projet hérité, etc.

Utiliser un IDE, CMake ou, si vous êtes integrés, la Autotools.

(édité en raison de downvotes, ty Honza pour souligner)

10
répondu IlDan 2012-09-30 11:42:19

Le message de RC était SUPER utile. Je n'ai jamais pensé à utiliser la fonction $(dir$@), mais elle a fait exactement ce que j'avais besoin de faire.

Dans parentDir, avoir un tas de répertoires avec des fichiers source: dirA, dirB, dirC. Divers fichiers dépendent des fichiers objet dans d'autres répertoires, donc je voulais pouvoir créer un fichier à partir d'un répertoire, et le faire créer cette dépendance en appelant le makefile associé à cette dépendance.

Essentiellement, j'ai fait un Makefile dans parentDir qui avait (entre autres choses) une règle générique similaire aux RC:

%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

Chaque sous-répertoire incluait ce makefile de niveau supérieur afin d'hériter de cette règle Générique. Dans le Makefile de chaque sous-répertoire, j'ai écrit une règle personnalisée pour chaque fichier afin que je puisse garder une trace de tout ce dont chaque fichier individuel dépendait.

Chaque fois que j'avais besoin de créer un fichier, j'ai utilisé (essentiellement) cette règle pour créer récursivement toutes les dépendances. Parfait!

NOTE: il y a un utilitaire appelé "makepp" qui semble faire cette tâche même encore plus intuitivement, mais pour des raisons de portabilité et ne pas dépendre d'un autre outil, j'ai choisi de le faire de cette façon.

Espérons que cela aide!

2
répondu jvriesem 2012-02-15 19:15:24

Récursive recours

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

Cela permet à make de se diviser en tâches et d'utiliser plusieurs cœurs

2
répondu EhevuTov 2012-04-05 00:16:56

Je suggère d'utiliser autotools:

//## Placer les fichiers d'objets générés (.o) dans le même répertoire que leurs fichiers sources, afin d'éviter les collisions lorsque make non récursif est utilisé.

AUTOMAKE_OPTIONS = subdir-objects

Juste l'inclure dans Makefile.am avec les autres choses assez simples.

Voici le tutoriel.

0
répondu user2763812 2013-09-10 07:00:28