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]
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.
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:
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.
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)
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)))
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)
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!
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
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.