Disons que j'ai un fichier makefile avec la règle
%.o: %.c
gcc -Wall -Iinclude ...
Je veux que * .o soit reconstruit chaque fois qu'un fichier d'en-tête change. Plutôt que d’établir une liste de dépendances, chaque fois que l’un des fichiers d’en-tête dans /include
change, tous les objets du répertoire doivent être reconstruits.
Je ne peux pas penser à un bon moyen de changer la règle pour accepter cela, je suis ouvert aux suggestions. Points bonus si la liste des en-têtes ne doit pas nécessairement être codée en dur
Si vous utilisez un compilateur GNU, le compilateur peut assembler une liste de dépendances. Fragment de Makefile:
depend: .depend
.depend: $(SRCS)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ -MF ./.depend;
include .depend
ou
depend: .depend
.depend: $(SRCS)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ > ./.depend;
include .depend
où SRCS
est une variable pointant vers votre liste complète de fichiers source.
Il y a aussi l'outil makedepend
, mais je ne l'ai jamais aimé autant que gcc -MM
La plupart des réponses sont étonnamment compliquées ou erronées. Cependant, des exemples simples et robustes ont été postés ailleurs [ codereview ]. Certes, les options fournies par le préprocesseur gnu sont un peu déroutantes. Cependant, la suppression de tous les répertoires de la cible de construction avec -MM
est documentée et non un bogue [ gpp ]:
Par défaut, CPP prend le nom du fichier d’entrée principal, supprime lescomposants du répertoire et tout suffixe de fichier tel que «.c», et ajoute le suffixe d'objet habituel de la plate-forme.
L'option (un peu plus récente) -MMD
est probablement ce que vous voulez. Pour être complet, voici un exemple de fichier qui prend en charge plusieurs répertoires src et répertoires de construction avec certains commentaires. Pour une version simple sans répertoire de construction, voir [ codereview ].
CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow
# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build
# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)
# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)
# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)
# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
# Create build directories - same structure as sources.
mkdir -p $(@D)
# Just link all the object files.
$(CXX) $(CXX_FLAGS) $^ -o $@
# Include all .d files
-include $(DEP)
# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
mkdir -p $(@D)
# The -MMD flags additionaly creates a .d file with
# the same name as the .o file.
$(CXX) $(CXX_FLAGS) -MMD -c $< -o $@
.PHONY : clean
clean :
# This should remove all generated files.
-rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)
Cette méthode fonctionne car s'il existe plusieurs lignes de dépendance pour une même cible, les dépendances sont simplement jointes, par exemple:
a.o: a.h
a.o: a.c
./cmd
est équivalent à:
a.o: a.c a.h
./cmd
comme mentionné à: Makefile plusieurs lignes de dépendance pour une même cible?
Comme j'ai posté ici gcc peut créer des dépendances et compiler en même temps:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
Le paramètre '-MF' spécifie un fichier dans lequel stocker les dépendances.
Le tiret au début de '-include' indique à Make de continuer lorsque le fichier .d n'existe pas (par exemple lors de la première compilation).
Notez qu'il semble y avoir un bogue dans gcc concernant l'option -o. Si vous définissez le nom de fichier de l'objet sur obj/_file__c.o, le fichier .d généré contiendra toujours fichier .o, et non obj/_file__c.o.
Que diriez-vous de quelque chose comme:
includes = $(wildcard include/*.h)
%.o: %.c ${includes}
gcc -Wall -Iinclude ...
Vous pouvez également utiliser les caractères génériques directement, mais j’ai tendance à penser que j’en ai besoin à plus d’un endroit.
Notez que cela ne fonctionne que sur les petits projets, car il suppose que chaque fichier objet dépend de chaque fichier d'en-tête.
La solution de Martin ci-dessus fonctionne très bien, mais ne gère pas les fichiers .o résidant dans des sous-répertoires. Godric fait remarquer que le drapeau -MT résout ce problème, mais empêche simultanément le fichier .o d'être écrit correctement. Ce qui suit va résoudre ces deux problèmes:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
$(CC) $(CFLAGS) -o $@ $<
Cela fera très bien l'affaire et traitera même les sous-répertoires spécifiés:
$(CC) $(CFLAGS) -MD -o $@ $<
testé avec gcc 4.8.3
Une version légèrement modifiée du réponse de Sophie qui permet de sortir les fichiers * .d dans un dossier différent (je ne collerai que la partie intéressante qui génère les fichiers de dépendance):
$(OBJDIR)/%.o: %.cpp
# Generate dependency file
mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
Notez que le paramètre
-MT $@
est utilisé pour garantir que les cibles (c'est-à-dire les noms de fichier objet) dans les fichiers * .d générés contiennent le chemin d'accès complet aux fichiers * .o et pas uniquement le nom de fichier.
Je ne sais pas pourquoi ce paramètre n'est PAS nécessaire lorsque vous utilisez -MMD en combinaison avec -c (comme dans le version } de Sophie _) de Sophie. Dans cette combinaison, il semble écrire le chemin complet des fichiers * .o dans les fichiers * .d. Sans cette combinaison, -MMD écrit également uniquement les noms de fichiers purs sans aucun composant de répertoire dans les fichiers * .d. Peut-être que quelqu'un sait pourquoi -MMD écrit le chemin complet lorsqu'il est combiné avec -c. Je n'ai trouvé aucun indice dans la page de manuel de g ++.
Ce qui suit fonctionne pour moi:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.cpp
$(CXX) $(CFLAGS) -MMD -c -o $@ $<
Je préfère cette solution, par rapport à la réponse acceptée par Michael Williamson, elle capture les modifications apportées aux sources + fichiers en ligne, puis aux sources + en-têtes et enfin aux sources uniquement. L'avantage ici est que toute la bibliothèque n'est pas recompilée si quelques modifications seulement sont apportées. Ce n’est pas très grave pour un projet avec quelques fichiers, mais si vous avez 10 ou 100 sources, vous remarquerez la différence.
COMMAND= gcc -Wall -Iinclude ...
%.o: %.cpp %.inl
$(COMMAND)
%.o: %.cpp %.hpp
$(COMMAND)
%.o: %.cpp
$(COMMAND)
Voici un deux lignes:
CPPFLAGS = -MMD
-include $(OBJS:.c=.d)
Cela fonctionne avec la recette de création par défaut, tant que vous avez une liste de tous vos fichiers objet dans OBJS
.