web-dev-qa-db-fra.com

Makefile, dépendances d'en-tête

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 

80
Mike

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

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

109
dmckee

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?

47
Sophie

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.

24
Martin Fido

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.

22

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 $@ $<
4
michael

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

3
g24l

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 ++.

0
MaximumFPS

Ce qui suit fonctionne pour moi:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<
0
Marcel Keller

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)
0
Nicholas Hamilton

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.

0
tbodt