web-dev-qa-db-fra.com

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

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

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

Comment devrais-je écrire un fichier make qui serait en partie/src (ou ailleurs) et qui pourrait compléter/lier en partie les fichiers sources c/c ++?/Src?

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

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

120
devin

La méthode traditionnelle consiste à avoir un Makefile dans chacun des sous-répertoires (part1, part2, etc.), ce qui vous permet de les créer indépendamment. De plus, placez un Makefile dans le répertoire racine du projet qui construit tout. La "racine" Makefile ressemblerait à ceci:

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

Étant donné que chaque ligne d'une cible est exécutée dans son propre shell, vous n'avez pas à vous soucier de la sauvegarde de l'arborescence de répertoires ni des autres répertoires.

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

99
anon

Si vous avez du code dans un sous-répertoire dépendant du code dans un autre sous-répertoire, il vaut probablement mieux utiliser un seul fichier Make au niveau supérieur.

Voir Recursive Make Considered Nocif pour connaître le principe complet, mais vous voulez en principe que vous disposiez de toutes les informations dont vous avez besoin pour décider si un fichier doit être reconstruit ou non. ne lui racontez qu'un tiers de votre projet.

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

83
dave4420

L'option VPATH peut s'avérer utile, car elle indique les répertoires dans lesquels rechercher le code source. Vous auriez quand même besoin d'une option -I pour chaque chemin d'inclusion. 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 les fichiers partXapi.cpp correspondants dans l'un des répertoires spécifiés par VPATH et les compilera. 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 déjà dit, il est probablement préférable d’utiliser un fichier makefile pour chaque partie, surtout si chaque partie peut être autonome.

30
CodeGoat

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 amener là où vous voulez être.

 CC = g ++ 
 TARGET = cppTest 
 OTHERDIR = ../../une autre route/dans/projet/src 
 
 SOURCE = cppTest. cpp 
 SOURCE = $ (OTHERDIR) /file.cpp 
 
 ## Définition des sources d'extrémité 
 INCLUDE = -I./ $ (AN_INCLUDE_DIR) 
 INCLUDE = -I. $ (OTHERDIR) /../ inc 
 ## end more inclut 
 
 VPATH = $ (OTHERDIR) 
 OBJ = $ ( rejoindre $ (adduffix ../obj/, $ (dir $ (SOURCE))), $ (notdir $ (SOURCE: .cpp = .o))) 
 
 ## Fixer la destination des dépendances être ../.dep relatif au src dir 
 DEPENDS = $ (rejoindre $ (adduffix ../.dep/, $ (dir $ (SOURCE))), $ (notdir $ (SOURCE :. cpp = .d))) 
 
 ## Règle par défaut exécutée 
 tout: $ (CIBLE) 
 @ vrai 
 
 ## Clean Rule 
 Clean: 
 @ -Rm -f $ (CIBLE) $ (OBJ) $ (DÉPENDANT) 
 
 
 ## Règle permettant de définir la cible réelle 
 $ (CIBLE): $ (OBJ) 
 @Echo "============" 
 @Echo " Lier la cible $ @ "
 @Echo" ============= "
 @ $ (CC) $ (CFLAGS) -o $ @ $ ^ $ (LIBS) 
 @Echo - Lien terminé - 
 
 ## Règle de compilation générique 
%. o:% .cpp 
 @mkdir -p $ (dir $ @) 
 @echo "============" 
 @echo "Compiler $ <" 
 @ $ (CC) $ (CFLAGS) -c $ <-o $ @ 
 
 
 ## Règles pour objet fichiers à partir de fichiers cpp 
 ## Le fichier objet de chaque fichier est placé dans le répertoire obj 
 ## à un niveau supérieur du répertoire source actuel. 
 ../ obj /%. o :% .cpp 
 @mkdir -p $ (dir $ @) 
 @echo "============" 
 @echo "Compilation $ <"
 @ $ (CC) $ (CFLAGS) -c $ <-o $ @ 
 
 # Règle pour" autre répertoire "Vous aurez besoin d'un par" autre " dir 
 $ (OTHERDIR) /../ obj /%. o:% .cpp 
 @mkdir -p $ (dir $ @) 
 @echo "==== ========= "
 @echo" Compiler $ <"
 @ $ (CC) $ (CFLAGS) -c $ <-o $ @ 
 
 ## Définir des règles de dépendance 
 ./. Dep /%. D:% .cpp 
 @mkdir -p $ (dir $ @) 
 @echo "=============" 
 @echo Fichier de dépendances de construction pour $ *. o 
 @ $ (Shell) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ ../obj/$ *. o ^"> $ @ '
 
 ## Règle de dépendance pour le répertoire "other" 
 $ ( OTHERDIR) /../. Dep /%. D:% .cpp 
 @Mkdir -p $ (dir $ @) 
 @Echo "=========== == "
 @echo Fichier de dépendances de construction pour $ *. o 
 @ $ (Shell) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ $ (OTHERDIR) /../ obj/$ *. o ^"> $ @ '
 
 ## Inclure les fichiers de dépendance 
 -include $ (DÉPENDU) 
 
23
RC.

Si les sources sont réparties dans de nombreux dossiers et qu’il est logique d’avoir des Makefiles individuels, comme indiqué précédemment, make récursif est une bonne approche, mais pour les projets plus petits, il est plus facile de répertorier tous les fichiers source dans 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 régler 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 la construction de 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 automatiser la génération VPATH en:

VPATH := $(dir $(COMMON_SRC))

Ou en utilisant le fait que sort supprime les doublons (bien que cela ne soit pas grave):

VPATH := $(sort  $(dir $(COMMON_SRC)))
18
dashesy

Je pense qu'il est préférable de souligner que l'utilisation de Make (récursive ou non) est une chose que vous voudrez peut-être éviter, car comparée aux outils actuels, il est difficile d'apprendre, de maintenir et d'évoluer.

C'est un outil formidable, 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.

Utilisez un IDE, CMake ou, si vous êtes en dur, le Autotools .

(édité à cause de votes négatifs, ty Honza pour avoir souligné)

9
IlDan

Le poste 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, vous avez plusieurs répertoires contenant des fichiers sources: dirA, dirB, dirC. Divers fichiers dépendent des fichiers objets dans d'autres répertoires. Je souhaitais donc pouvoir créer un fichier à partir d'un répertoire et lui demander de créer cette dépendance en appelant le fichier Make associé à cette dépendance.

En gros, j'ai créé un fichier Makefile dans parentDir qui contenait (entre autres choses) une règle générique semblable à celle des RC:

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

Chaque sous-répertoire incluait ce fichier makefile de niveau supérieur afin d’hériter de cette règle générique. Dans chaque Makefile de chaque sous-répertoire, j'ai écrit une règle personnalisée pour chaque fichier afin de pouvoir garder une trace de tout ce dont dépend chaque fichier.

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

REMARQUE: il existe un utilitaire appelé "makepp" qui semble effectuer cette tâche encore plus intuitivement, mais pour des raisons de portabilité et ne dépendant pas d'un autre outil, j'ai choisi de le faire de cette façon.

J'espère que cela t'aides!

2
jvriesem

tilisation récursive de Make

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

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

2
EhevuTov

Je cherchais quelque chose comme cela et après quelques tentatives infructueuses, je crée mon propre fichier Make, je sais que ce n'est pas "la manière idiomatique" mais c'est un début pour comprendre make et cela fonctionne pour moi, peut-être que vous pourriez essayer dans votre projet.

PROJ_NAME=mono

CPP_FILES=$(Shell find . -name "*.cpp")

S_OBJ=$(patsubst %.cpp, %.o, $(CPP_FILES))

CXXFLAGS=-c \
         -g \
        -Wall

all: $(PROJ_NAME)
    @echo Running application
    @echo
    @./$(PROJ_NAME)

$(PROJ_NAME): $(S_OBJ)
    @echo Linking objects...
    @g++ -o $@ $^

%.o: %.cpp %.h
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS) -o $@

main.o: main.cpp
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS)

clean:
    @echo Removing secondary things
    @rm -r -f objects $(S_OBJ) $(PROJ_NAME)
    @echo Done!

Je sais que c'est simple et pour certaines personnes, mes drapeaux sont faux, mais comme je l'ai dit, c'est mon premier Makefile à compiler mon projet dans plusieurs répertoires et à le lier ensuite pour créer mon bac.

J'accepte les suggestions: D

Je suggère d'utiliser autotools:

//## Placez les fichiers d'objet générés (.o) dans le même répertoire que leurs fichiers source, afin d'éviter les collisions lors de l'utilisation d'une création non récursive.

AUTOMAKE_OPTIONS = subdir-objects

il suffit de l'inclure dans Makefile.am avec d'autres trucs assez simples.

Voici le tutoriel .

0
user2763812