Comment puis-je avoir un Makefile qui reconstruit automatiquement les fichiers source 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 sur le processus, donc ce n'est pas parfait, mais je pense que c'est assez puissant à ce stade pour mon niveau d'expérience de 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 arranger ça assez facilement en ayant tout de mes fichiers d'en-tête sont des dépendances pour tous mes fichiers C, mais cela provoquerait effectivement une reconstruction complète du projet chaque fois que j'ai changé/ajouté un fichier d'en-tête, qui ne serait pas très gracieux.

ce que je veux, c'est seulement pour les fichiers C que inclue le fichier d'en-tête que je change pour être reconstruit, et pour que l'ensemble du projet soit à nouveau lié. Je peux faire le lien En faisant en sorte que tous les fichiers d'en-tête soient des dépendances de la cible, mais je ne peux pas comprendre comment pour rendre les fichiers C invalides lorsque leurs fichiers d'en-tête inclus sont plus récents.

J'ai entendu que GCC a quelques commandes pour rendre cela possible (de sorte que le makefile peut d'une manière ou d'une autre comprendre quels fichiers doivent être reconstruits) mais je ne peux pas pour la vie de moi Trouver un exemple d'implémentation réelle à regarder. Quelqu'un peut-il poster une solution qui permettra ce comportement dans un makefile?

EDIT: je dois clarifier, je suis familier avec le concept de mettre le cibles individuelles et ayant chaque cible.o exiger de fichiers d'en-tête. Cela me demande d'éditer le makefile chaque fois que j'inclus un fichier d'en-tête quelque part, ce qui est un peu pénible. Je suis à la recherche d'une solution qui peut dériver les dépendances du fichier d'en-tête de ses propres, que je suis assez certain que j'ai vu dans d'autres projets.

83
demandé sur Nicholas Flynt 2008-11-18 03:55:54

9 réponses

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

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

en plus d'ajouter le drapeau-M à gcc, vous aurez besoin d'inclure .les fichiers d dans le makefile (comme Chris a écrit ci-dessus). Il y a des 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 .d files chaque fois que make se plaint de ne pas être en mesure de construire un fichier d'en-tête qui n'existe plus.

27
répondu Udi Meiri 2009-03-09 13:47:57

vous pouvez ajouter une commande 'make depend' comme d'autres l'ont indiqué, mais pourquoi ne pas obtenir de gcc pour créer des dépendances et 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 pour stocker les dépendances.

le tiret au début de '-include' indique à Make de continuer quand le .le fichier d n'existe pas (par exemple sur la première compilation).

Note il semble y avoir un bug dans gcc concernant l'option-O. Si vous définissez le nom de fichier de l'objet pour dire obj/_file__c.o alors le _file_.d généré contiendra toujours _file_.o , pas obj/_file_c.o .

20
répondu Martin Fido 2014-08-25 22:28:44

c'est l'équivalent de la réponse de Chris Dodd , mais utilise une convention de nommage différente (et par coïncidence ne nécessite pas la magie sed . Copié de une copie ultérieure de .


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 y a aussi l'outil makedepend , mais je n'ai jamais il aimait autant que gcc -MM

16
répondu dmckee 2017-05-23 12:18:19

vous devrez faire des cibles individuelles pour chaque fichier C, puis énumérer le fichier d'en-tête comme une dépendance. Vous pouvez toujours utiliser vos cibles génériques, et placer les dépendances .h après, comme ceci:

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

foo.c: bar.h
# And so on...
7
répondu mipadi 2008-11-18 00:59:40

au-delà de 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 pourriez même les générer dans un fichier séparé (peut-être " dépend.mk') que vous incluez ensuite dans le makefile. Ou vous pouvez trouver un " make depend règle " qui édite le fichier makefile avec les bonnes dépendances (Google termes: "ne pas supprimer cette ligne" et dépendent).

3
répondu Jonathan Leffler 2008-11-18 01:06:17

fondamentalement, vous devez dynamiquement créer les règles 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)/.o $(@D)/.d:,' >$@

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

dans votre makefile.

3
répondu Chris Dodd 2008-11-18 01:08:36

aucune des réponses n'a fonctionné pour moi. Par exemple: La réponse de Martin Fido suggère que gcc peut créer des fichiers de dépendances, mais quand j'ai essayé de générer des fichiers d'objets vides (zéro bytes) pour moi sans aucun avertissement ou erreur. Il peut ê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 cela n'a été mentionné par personne d'autre (par exemple "règle de remplacement du suffixe" spécifiée sous .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

avis que j'ai utilisé .cc .. Le Makefile ci-dessus est facile à régler .c fichiers.

également important de noter l'importance de ces deux lignes:

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

donc gcc est appelé une fois pour construire un fichier de dépendance d'abord, puis compile réellement un .fichier cc. Et ainsi de suite pour chaque fichier source.

0
répondu Tagar 2016-07-01 21:29:08

solution plus simple: il suffit d'utiliser le Makefile pour avoir le .c pour .o la règle de compilation dépend du(DES) fichier (s) d'en-tête et de tout ce qui est pertinent dans votre projet en tant que dépendance.

par exemple, dans le Makefile quelque part:

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
répondu Richard Elkins 2016-09-02 15:59:40

je crois que la commande mkdep est ce que vous voulez. Il fait des analyses .c fichiers pour les lignes #include et crée une arborescence de dépendances pour elles. Je crois que les projets Automake/Autoconf l'utilisent par défaut.

-1
répondu jdizzle 2010-03-07 00:45:15