Wie kann ich ein Makefile automatisch Quelldateien neu erstellen lassen, die eine geänderte Header-Datei enthalten? (In C / C ++)

91

Ich habe das folgende Makefile, mit dem ich ein Programm (eigentlich einen Kernel) erstelle, an dem ich arbeite. Es ist von Grund auf neu und ich lerne etwas über den Prozess, daher ist es nicht perfekt, aber ich denke, es ist zu diesem Zeitpunkt mächtig genug für meine Erfahrung beim Schreiben von 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

Mein Hauptproblem bei diesem Makefile ist, dass beim Ändern einer Header-Datei, die eine oder mehrere C-Dateien enthält, die C-Dateien nicht neu erstellt werden. Ich kann dies ganz einfach beheben, indem alle meine Header-Dateien Abhängigkeiten für alle meine C-Dateien darstellen. Dies würde jedoch jedes Mal, wenn ich eine Header-Datei ändere / hinzufüge, zu einer vollständigen Neuerstellung des Projekts führen, was nicht sehr elegant wäre.

Ich möchte, dass nur die C-Dateien, die die von mir geänderte Header-Datei enthalten, neu erstellt und das gesamte Projekt erneut verknüpft werden. Ich kann die Verknüpfung durchführen, indem alle Header-Dateien Abhängigkeiten vom Ziel darstellen, aber ich kann nicht herausfinden, wie die C-Dateien ungültig werden, wenn ihre enthaltenen Header-Dateien neuer sind.

Ich habe gehört, dass GCC einige Befehle hat, um dies zu ermöglichen (damit das Makefile irgendwie herausfinden kann, welche Dateien neu erstellt werden müssen), aber ich kann für mein ganzes Leben kein tatsächliches Implementierungsbeispiel finden, das ich mir ansehen kann. Kann jemand eine Lösung veröffentlichen, die dieses Verhalten in einem Makefile ermöglicht?

EDIT: Ich sollte klarstellen, ich bin mit dem Konzept vertraut, die einzelnen Ziele einzufügen und jedes Ziel zu haben. Um die Header-Dateien zu benötigen. Das erfordert, dass ich das Makefile jedes Mal bearbeite, wenn ich irgendwo eine Header-Datei einbinde, was ein bisschen mühsam ist. Ich suche nach einer Lösung, die die Abhängigkeiten der Header-Dateien selbst ableiten kann. Ich bin mir ziemlich sicher, dass ich sie in anderen Projekten gesehen habe.

Nicholas Flynt
quelle

Antworten:

30

Wie bereits an anderer Stelle auf dieser Site erwähnt, finden Sie auf dieser Seite: Automatische Abhängigkeitsgenerierung

Kurz gesagt, gcc kann automatisch .d-Abhängigkeitsdateien für Sie erstellen. Hierbei handelt es sich um Mini-Makefile-Fragmente, die die Abhängigkeiten der von Ihnen kompilierten .c-Datei enthalten. Jedes Mal, wenn Sie die .c-Datei ändern und kompilieren, wird die .d-Datei aktualisiert.

Neben dem Hinzufügen des -M-Flags zu gcc müssen Sie die .d-Dateien in das Makefile aufnehmen (wie Chris oben geschrieben hat). Es gibt einige kompliziertere Probleme auf der Seite, die mit sed behoben werden. Sie können sie jedoch ignorieren und "make clean" ausführen, um die .d-Dateien zu löschen, wenn make sich darüber beschwert, dass keine nicht mehr vorhandene Header-Datei erstellt werden kann .

Udi Meiri
quelle
2
Ich kann mich irren, aber ich denke, GCC hat tatsächlich eine Funktion hinzugefügt, um dieses Problem zu umgehen. Schauen Sie sich gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html speziell -MP an.
Eugene Marcotte
Ja, -MP existiert seit GCC 3, existiert in clang und icc und macht die Notwendigkeit von sed ungültig. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/…
hmijail trauert um den 27.
Die neueste Version des Links in der Antwort enthält Beispiele mit GCC-Flags.
MadScientist
20

Sie können einen 'make abhängigen' Befehl hinzufügen, wie andere angegeben haben, aber warum nicht gcc dazu bringen, Abhängigkeiten zu erstellen und gleichzeitig zu kompilieren:

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

-include $(DEPS)

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

Der Parameter '-MF' gibt eine Datei an, in der die Abhängigkeiten gespeichert werden sollen.

Der Bindestrich am Anfang von '-include' weist Make an, fortzufahren, wenn die .d-Datei nicht vorhanden ist (z. B. bei der ersten Kompilierung).

Beachten Sie, dass es in gcc einen Fehler bezüglich der Option -o zu geben scheint. Wenn Sie den Objektdateinamen auf "setzen" setzen, enthält obj/_file__c.oder generierte _file_.dweiterhin " _file_.oNicht" obj/_file_c.o.

Martin Fido
quelle
18
Das hat bei mir nicht funktioniert. Beispiel: Das Makefile g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp hat Folgendes generiert und ausgeführt: Ich habe die Datei main.d erhalten, aber main.o war 0 Byte. Das -MMD-Flag scheint jedoch genau das zu tun, was erforderlich war. So wurde meine Arbeits-Makefile-Regel:$(CC) -c $(CFLAGS) -MMD -o $@ $<
Darren Cook
17

Dies entspricht der Antwort von Chris Dodd , verwendet jedoch eine andere Namenskonvention (und erfordert zufällig keine sedMagie. Von einem späteren Duplikat kopiert .


Wenn Sie einen GNU-Compiler verwenden, kann der Compiler eine Liste von Abhängigkeiten für Sie zusammenstellen. Makefile-Fragment:

depend: .depend

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

include .depend

Es gibt auch das Tool makedepend, aber es hat mir nie so gut gefallen wiegcc -MM

dmckee --- Ex-Moderator Kätzchen
quelle
4
Warum buchstabieren Sie nicht QUELLEN? Benötigt nicht besonders viele weitere Zeichen und ist nicht so verschleiert wie "SRCS", was wie ein Akronym aussehen kann.
HelloGoodbye
@HelloGoodbye Ein bisschen Stil. Ich habe immer benutzt und immer gesehen, SRCSund OBJS. Ich würde die meiste Zeit zustimmen, aber jeder sollte wissen, was das sind.
Sherellbc
@sherrellbc Was die Leute "wissen" sollten und was die Leute tatsächlich wissen, sind oft zwei verschiedene Dinge: P
HelloGoodbye
7

Sie müssen für jede C-Datei individuelle Ziele festlegen und dann die Header-Datei als Abhängigkeit auflisten. Sie können weiterhin Ihre generischen Ziele verwenden und anschließend die .hAbhängigkeiten wie folgt platzieren:

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

foo.c: bar.h
# And so on...
Mipadi
quelle
4

Grundsätzlich müssen Sie die Makefile-Regeln dynamisch erstellen, um die Objektdateien neu zu erstellen, wenn sich die Header-Dateien ändern. Wenn Sie gcc und gnumake verwenden, ist dies ziemlich einfach. Setzen Sie einfach so etwas wie:

$(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

in deinem Makefile.

Chris Dodd
quelle
2
Ich verstehe das irgendwie, außer dass ich (abgesehen davon, dass die -MM- und -MG-Flags neu sind) nicht verstehe, wofür die Regex-Lookin-Zeile des kryptischen Textes gedacht ist. Das wird mich nicht glücklich machen ... ^ _ ^ Ich werde es trotzdem versuchen und sehen, ob ich irgendwelche Ergebnisse habe.
Nicholas Flynt
sed ist die Abkürzung für "Stream Editor", mit dem ein Textstrom geändert werden kann, ohne dass eine Datei verwendet werden muss. Es ist ein Standard-Unix-Tool und kleiner und schneller, sodass es häufiger als awk oder perl verwendet wird.
Zan Lynx
Ah, da ist das Problem: Ich mache das unter Windows.
Nicholas Flynt
3

Über das hinaus, was @mipadi gesagt hat, können Sie auch die Verwendung der -MOption ' ' untersuchen, um einen Datensatz der Abhängigkeiten zu generieren. Sie können diese sogar in einer separaten Datei (möglicherweise 'abhängige.mk') generieren, die Sie dann in das Makefile aufnehmen. Oder Sie finden eine ' make depend' Regel, die das Makefile mit den richtigen Abhängigkeiten bearbeitet (Google-Begriffe: "Diese Zeile nicht entfernen" und abhängig).

Jonathan Leffler
quelle
0

Keine der Antworten hat bei mir funktioniert. Zum Beispiel schlägt die Antwort von Martin Fido vor, dass gcc eine Abhängigkeitsdatei erstellen kann, aber als ich versuchte, leere Objektdateien (null Bytes) für mich ohne Warnungen oder Fehler zu generieren. Es könnte ein gcc-Fehler sein. ich bin on

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

Hier ist mein komplettes Makefile, das für mich funktioniert. Es ist eine Kombination aus Lösungen + etwas, das von niemand anderem erwähnt wurde (z. B. "Suffix-Ersetzungsregel", angegeben als .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

Beachten Sie, dass ich .cc verwendet habe. Das obige Makefile kann einfach für .c-Dateien angepasst werden.

Es ist auch wichtig, die Bedeutung dieser beiden Zeilen zu beachten:

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

Daher wird gcc einmal aufgerufen, um zuerst eine Abhängigkeitsdatei zu erstellen und dann tatsächlich eine .cc-Datei zu kompilieren. Und so weiter für jede Quelldatei.

Tagar
quelle
0

Einfachere Lösung: Verwenden Sie einfach das Makefile, damit die Kompilierungsregel .c to .o von den Header-Dateien und allen anderen in Ihrem Projekt relevanten Abhängigkeiten abhängig ist.

ZB irgendwo im 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 $@ $<
Richard Elkins
quelle
-1

Ich glaube, der mkdepBefehl ist das, was Sie wollen. Es durchsucht tatsächlich .c-Dateien nach #includeZeilen und erstellt einen Abhängigkeitsbaum für diese. Ich glaube, dass Automake / Autoconf-Projekte dies standardmäßig verwenden.

jdizzle
quelle