161 votes

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

J'ai un projet dont la structure de répertoire est la suivante :

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

Comment puis-je écrire un makefile qui serait dans part/src (ou n'importe où en fait) qui pourrait compléter/lien sur les fichiers sources c/c++ dans part?/src ?

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

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

0 votes

2 votes

Dans le manuel original de gnu ( gnu.org/software/make/manuel/html_node/Phony-Targets.html ) sous Phony Targets il y a un échantillon sur recursive invocation cela pourrait être très élégant.

0 votes

Quel outil avez-vous utilisé pour créer ce graphique de texte ?

134voto

La méthode traditionnelle consiste à avoir un Makefile dans chacun des sous-répertoires ( part1 , part2 ) vous permettant de les construire indépendamment. En outre, disposez d'un Makefile dans le répertoire racine du projet qui construit tout. Le répertoire "Root" Makefile ressemblerait à ce qui suit :

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

Puisque chaque ligne d'une cible make est exécutée dans son propre shell, il n'y a pas besoin de se soucier de remonter dans l'arborescence des répertoires ou dans d'autres répertoires.

Je suggère de jeter un coup d'œil à la Manuel GNU make section 5.7 ; c'est très utile.

27 votes

Cela devrait être +$(MAKE) -C part1 etc. Cela permet au contrôle des tâches de Make de travailler dans les sous-répertoires.

29 votes

Il s'agit d'une approche classique et largement utilisée, mais elle est sous-optimale à plusieurs égards et s'aggrave à mesure que le projet se développe. Dave Hinton a le pointeur à suivre.

98voto

dave4420 Points 31298

Si vous avez du code dans un sous-répertoire qui dépend du code dans un autre sous-répertoire, il est probablement préférable d'avoir un seul makefile au niveau supérieur.

Voir La fabrication récursive est considérée comme nuisible pour le raisonnement complet, mais en gros vous voulez que make ait toutes les informations dont il a besoin pour décider si oui ou non un fichier doit être reconstruit, et il ne les 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 :

4 votes

Merci, je ne le savais pas. Il est très utile de connaître la "bonne façon" de faire les choses plutôt que les façons qui "fonctionnent" ou qui sont acceptées comme standard.

4 votes

Le make récursif était considéré comme nuisible, à l'époque où il ne fonctionnait vraiment pas bien. Il n'est pas considéré comme nuisible de nos jours, en fait, c'est la façon dont les autotools / automake gèrent les grands projets.

1 votes

Depuis septembre 2020, tous ces liens semblent inaccessibles. Vous pouvez cependant le trouver en googlant

43voto

CodeGoat Points 552

L'option VPATH, qui indique à make dans quels répertoires chercher le code source, peut s'avérer utile. Vous aurez 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

Ceci trouvera automatiquement les fichiers partXapi.cpp correspondants dans n'importe lequel des répertoires spécifiés dans le VPATH et les compilera. Cependant, ceci 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.

4 votes

Je ne peux pas croire que cette réponse simple et parfaite n'ait pas reçu plus de votes. C'est un +1 pour moi.

3 votes

J'avais quelques fichiers sources communs dans un répertoire supérieur pour plusieurs projets disparates dans des sous-dossiers, VPATH=.. a fonctionné pour moi !

28voto

RC. Points 15804

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=../../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)

7 votes

Je sais que cette question n'est plus d'actualité, mais est-ce que SOURCE et INCLUDE ne sont pas écrasés par une autre affectation une ligne plus loin ?

0 votes

@skelliam oui, c'est vrai.

1 votes

Cette approche viole le principe DRY. C'est une mauvaise idée de dupliquer le code pour chaque "autre répertoire".

26voto

dashesy Points 376

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

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

Je peux alors mettre VPATH de cette façon :

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

Ensuite, je construis les objets :

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

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 faire le VPATH génération 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)))

0 votes

Cela fonctionne très bien pour les petits projets où vous voulez juste ajouter quelques bibliothèques personnalisées et les avoir dans un dossier séparé. Merci beaucoup

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X