web-dev-qa-db-fra.com

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

J'ai le makefile suivant que je 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 fichier make 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 que tous mes fichiers d'en-tête deviennent des dépendances pour tous mes fichiers C, mais cela provoquerait une reconstruction complète du projet chaque fois que je modifierais/ajouterais un fichier en-tête, ce qui ne serait pas très gracieux.

Ce que je veux, c’est que seuls les fichiers C qui incluent le fichier d’en-tête que je modifie soient reconstruits et que le projet entier soit lié à nouveau. Je peux faire la liaison en faisant en sorte que tous les fichiers d’en-tête soient des dépendances de la cible, mais je ne vois pas comment rendre les fichiers C invalides, quand leurs fichiers d’en-tête inclus sont plus récents.

J'ai entendu dire que GCC avait quelques commandes pour rendre cela possible (afin que le fichier make puisse en quelque sorte déterminer quels fichiers doivent être reconstruits), mais je ne peux pas pour toute la vie me trouver un exemple d'implémentation réel à regarder. Quelqu'un peut-il publier une solution qui permettra ce comportement dans un fichier makefile?

EDIT: Je dois clarifier, je connais bien le concept de mettre les cibles individuelles dans et avoir chaque cible.o besoin des fichiers en-tête. Cela nécessite que je modifie 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 capable de dériver seule les dépendances de fichiers d'en-tête, ce que je suis assez certain d'avoir déjà vu dans d'autres projets.

83
Nicholas Flynt

Comme déjà indiqué ailleurs sur ce site, voir cette page: http://make.paulandlesley.org/autodep.html

En bref, gcc peut créer automatiquement pour vous des fichiers de dépendance .d, qui sont des fragments de mini-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 être mis à jour.

Outre l’ajout de l’indicateur -M à gcc, vous devez inclure les fichiers .d dans le fichier makefile (comme Chris l’a écrit plus haut) . Il existe quelques problèmes plus complexes dans la page qui sont résolus à l’aide de sed, mais vous pouvez ignorez-les et faites un "nettoyage" pour effacer les fichiers .d chaque fois que se plaindre de ne pas être en mesure de créer un fichier d'en-tête qui n'existe plus.

28
Udi Meiri

Vous pouvez ajouter une commande 'make depend' comme d'autres l'ont dit, mais pourquoi ne pas demander à gcc de créer des 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 sur obj/_file__c.o, le _file_.d généré contiendra toujours _file_.o et non obj/_file_c.o.

20
Martin Fido

Cela équivaut à la réponse de Chris Dodd , mais utilise une convention de dénomination différente (et coïncide, elle n'exige pas la magie sed. Copiée à partir de une copie ultérieure .


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

depend: .depend

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

include .depend

Il y a aussi l'outil makedepend, mais je ne l'ai jamais aimé autant que gcc -MM

16
dmckee

Vous devrez créer des cibles individuelles pour chaque fichier C, puis lister le fichier d’en-tête comme dépendance. Vous pouvez toujours utiliser vos cibles génériques, et placez les dépendances .h ensuite, comme suit:

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

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

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

3
Jonathan Leffler

Fondamentalement, vous devez créer dynamiquement les règles de makefile pour reconstruire les fichiers objet lorsque les fichiers d’en-tête changent. Si vous utilisez gcc et gnumake, c'est assez facile. il suffit de mettre quelque chose comme:

$(OBJDIR)/%.d: %.c
        $(CC) -MM -MG $(CPPFLAGS) $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >$@

ifneq ($(MAKECMDGOALS),clean)
include $(SRCS:%.c=$(OBJDIR)/%.d)
endif

dans votre makefile.

3
Chris Dodd

Solution plus simple: utilisez simplement le Makefile pour que la règle de compilation .c to .o soit dépendante du ou des fichiers d’en-tête et de tout autre élément pertinent dans votre projet en tant que dépendance.

Par exemple, quelque part dans le Makefile:

DEPENDENCIES=mydefs.h yourdefs.h Makefile GameOfThrones.S07E01.mkv

::: (your other Makefile statements like rules 
:::  for constructing executables or libraries)

# Compile any .c to the corresponding .o file:
%.o: %.c $(DEPENDENCIES)
        $(CC) $(CFLAGS) -c -o $@ $<
0
Richard Elkins

Aucune des réponses n'a fonctionné pour moi. Par exemple. La réponse de Martin Fido suggère que gcc puisse créer un fichier de dépendance, mais lorsque j'ai essayé, il générait des fichiers objets vides (zéro octet) pour moi sans aucun avertissement ni erreur. Ce pourrait être un bug de gcc. Je suis sur 

$ gcc --version gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

Alors voici mon Makefile complet qui fonctionne pour moi; c'est une combinaison de solutions + quelque chose qui n'a été mentionné par personne d'autre (par exemple, "règle de remplacement de suffixe" spécifiée comme .cc.o :):

CC = g++
CFLAGS = -Wall -g -std=c++0x
INCLUDES = -I./includes/

# LFLAGS = -L../lib
# LIBS = -lmylib -lm

# List of all source files
SRCS = main.cc cache.cc

# Object files defined from source files
OBJS = $(SRCS:.cc=.o)

# # define the executable file 
MAIN = cache_test

#List of non-file based targets:
.PHONY: depend clean all

##  .DEFAULT_GOAL := all

# List of dependencies defined from list of object files
DEPS := $(OBJS:.o=.d)

all: $(MAIN)

-include $(DEPS)

$(MAIN): $(OBJS)
    $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)

#suffix replacement rule for building .o's from .cc's
#build dependency files first, second line actually compiles into .o
.cc.o:
    $(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<

clean:
    $(RM) *.o *~ $(MAIN) *.d

Notez que j'ai utilisé le fichier .cc .. Le Makefile ci-dessus est facile à ajuster pour les fichiers .c.

Il est également important de noter l’importance de ces deux lignes:

$(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<

ainsi, gcc est appelé une fois pour créer un fichier de dépendance, puis compile un fichier .cc. Et ainsi de suite pour chaque fichier source.

0
Tagar