99 votes

Comment puis-je faire en sorte qu'un Makefile reconstruise automatiquement les fichiers sources qui incluent un fichier d'en-tête modifié ? (En C/C++)

J'ai le makefile suivant que j'utilise pour construire un programme (un noyau, en fait) sur lequel je travaille. C'est à partir de zéro et j'apprends le processus, donc ce n'est pas parfait, mais je pense que c'est assez puissant à ce stade pour mon niveau d'expérience dans l'écriture de makefiles.

AS  =   nasm
CC  =   gcc
LD  =   ld

TARGET      =   core
BUILD       =   build
SOURCES     =   source
INCLUDE     =   include
ASM         =   assembly

VPATH = $(SOURCES)

CFLAGS  =   -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
            -nostdinc -fno-builtin -I $(INCLUDE)
ASFLAGS =   -f elf

#CFILES     =   core.c consoleio.c system.c
CFILES      =   $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
SFILES      =   assembly/start.asm

SOBJS   =   $(SFILES:.asm=.o)
COBJS   =   $(CFILES:.c=.o)
OBJS    =   $(SOBJS) $(COBJS)

build : $(TARGET).img

$(TARGET).img : $(TARGET).elf
    c:/python26/python.exe concat.py stage1 stage2 pad.bin core.elf floppy.img

$(TARGET).elf : $(OBJS)
    $(LD) -T link.ld -o $@ $^

$(SOBJS) : $(SFILES)
    $(AS) $(ASFLAGS) $< -o $@

%.o: %.c
    @echo Compiling $<...
    $(CC) $(CFLAGS) -c -o $@ $<

#Clean Script - Should clear out all .o files everywhere and all that.
clean:
    -del *.img
    -del *.o
    -del assembly\*.o
    -del core.elf

Mon principal problème avec ce makefile est que lorsque je modifie un fichier d'en-tête qu'un ou plusieurs fichiers C incluent, les fichiers C ne sont pas reconstruits. Je peux résoudre ce problème assez facilement en faisant en sorte que tous mes fichiers d'en-tête soient des dépendances pour tous mes fichiers C, mais cela entraînerait effectivement une reconstruction complète du projet à chaque fois que je modifie/ajoute un fichier d'en-tête, ce qui ne serait pas très gracieux.

Ce que je veux, c'est que seulement les fichiers C que inclure le fichier d'en-tête que je modifie doit être reconstruit, et le projet entier doit être lié à nouveau. Je peux effectuer la liaison en faisant en sorte que tous les fichiers d'en-tête soient des dépendances de la cible, mais je n'arrive pas à trouver comment faire en sorte que les fichiers C soient invalidés lorsque les fichiers d'en-tête inclus sont plus récents.

J'ai entendu dire que GCC avait des commandes pour rendre cela possible (de sorte que le makefile puisse d'une manière ou d'une autre déterminer quels fichiers doivent être reconstruits) mais je n'arrive pas à trouver un exemple d'implémentation réelle à regarder. Quelqu'un peut-il poster une solution permettant d'activer ce comportement dans un fichier makefile ?

EDIT : Je devrais clarifier, je suis familier avec le concept de mettre les cibles individuelles et d'avoir chaque cible.o nécessitant les fichiers d'en-tête. Cela m'oblige à modifier le makefile à chaque fois que j'inclus un fichier d'en-tête quelque part, ce qui est un peu pénible. Je cherche une solution qui puisse dériver les dépendances des fichiers d'en-tête par elle-même, ce que je suis pratiquement certain d'avoir vu dans d'autres projets.

34voto

UdiM Points 393

Comme déjà signalé ailleurs sur ce site, voir cette page : Génération automatique de dépendances

En bref, gcc peut créer automatiquement des fichiers de dépendance .d pour vous, qui sont des mini-fragments de makefile contenant les dépendances du fichier .c que vous avez compilé. Chaque fois que vous modifiez le fichier .c et le compilez, le fichier .d sera mis à jour.

En plus d'ajouter le drapeau -M à gcc, vous devrez inclure les fichiers .d dans le makefile (comme Chris l'a écrit ci-dessus). Il y a quelques problèmes plus compliqués dans la page qui sont résolus en utilisant sed, mais vous pouvez les ignorer et faire un "make clean" pour effacer les fichiers .d chaque fois que make se plaint de ne pas pouvoir construire un fichier d'en-tête qui n'existe plus.

2 votes

Je peux me tromper, mais je pense que GCC a ajouté une fonctionnalité pour essayer de contourner ce problème de sed. Regardez gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html spécifiquement -MP.

0 votes

Oui, -MP existe depuis GCC 3, existe dans clang et icc, et annule le besoin de sed. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/

0 votes

La dernière version du lien dans la réponse contient des exemples utilisant les drapeaux GCC.

21voto

Martin Fido Points 636

Vous pourriez ajouter une commande 'make depend' comme d'autres l'ont dit, mais pourquoi ne pas demander à gcc de créer les dépendances et de compiler en même temps ?

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

-include $(DEPS)

%.o: %.c
    $(CC) -c $(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 comme suit obj/_file__c.o alors le produit _file_.d contiendra toujours _file_.o pas obj/_file_c.o .

19 votes

Cela n'a pas fonctionné pour moi. Par exemple, le makefile a généré et exécuté ceci : g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp J'ai obtenu le fichier main.d, mais main.o avait 0 octet. Cependant, le drapeau -MMD semble faire exactement ce qui était requis. Donc ma règle de travail du makefile est devenue : $(CC) -c $(CFLAGS) -MMD -o $@ $<

18voto

dmckee Points 50318

Ceci est équivalent à Réponse de Chris Dodd mais utilise une convention d'appellation différente (et, par coïncidence, ne nécessite pas l'ajout de la balise sed magique. Copié de un duplicata ultérieur .


Si vous utilisez un compilateur GNU, le compilateur peut assembler une liste de dépendances pour vous. Fragment de Makefile :

depend: .depend

.depend: $(SOURCES)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^>>./.depend;

include .depend

Il existe également l'outil makedepend mais je ne l'ai jamais autant aimé que gcc -MM

4 votes

Pourquoi n'écrivez-vous pas SOURCES ? Cela ne nécessite pas beaucoup de caractères supplémentaires et n'est pas aussi obscur que "SRCS" qui peut ressembler à un acronyme.

0 votes

@HelloGoodbye Un peu de style. J'ai toujours utilisé, et toujours vu, SRCS y OBJS . Je suis d'accord la plupart du temps, mais tout le monde devrait savoir ce qu'ils sont.

0 votes

@sherrellbc Ce que les gens "devraient" savoir et ce qu'ils savent réellement sont souvent deux choses différentes :P

8voto

mipadi Points 135410

Vous devrez créer des cibles individuelles pour chaque fichier C, puis lister le fichier d'en-tête comme une dépendance. Vous pouvez toujours utiliser vos cibles génériques, en plaçant simplement la balise .h dépendances après, comme ceci :

%.o: %.c
        @echo Compiling $<...
        $(CC) $(CFLAGS) -c -o $@ $<

foo.c: bar.h
# And so on...

0 votes

La formule suivante ne pourrait-elle pas fonctionner ? %.c : %.h

1 votes

@cassepipe : Si le fichier d'en-tête a le même nom que le fichier C et que le fichier C ne dépend que du fichier d'en-tête qui l'accompagne, oui.

0 votes

Il existe un programme appelé makeheaders de la Fossil (alternative vcs à git). Il génère et met à jour en-têtes par fichier de vos .h. Cela peut donc être un excellent combo avec %.c : %.h puisque les .h générés ont le même nom que les .c . Une manière bien plus KISS de procéder que la génération de dépendances automatiques de make. Vous pouvez facilement compiler makeheaders à partir du [code source][1]. Je vous suggère également de lire la [doc][2]. [1] : fossil-scm.org/fossil/file/src/makeheaders.c [2] : fossil-scm.org/fossil/doc/trunk/src/makeheaders.html#H0006

3voto

Jonathan Leffler Points 299946

En plus de ce que @mipadi a dit, vous pouvez également explorer l'utilisation de l'élément ' -M L'option ' pour générer un enregistrement des dépendances. Vous pouvez même les générer dans un fichier séparé (peut-être 'depend.mk') que vous incluez ensuite dans le makefile. Ou vous pouvez trouver une option ' make depend qui édite le makefile avec les dépendances correctes (termes Google : "ne pas supprimer cette ligne" et dépendance).

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