101 Stimmen

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

Ich habe das folgende Makefile, das ich verwende, um ein Programm (eigentlich einen Kernel) zu erstellen, an dem ich gerade arbeite. Es wurde von Grund auf neu erstellt und ich lerne gerade etwas über den Prozess, daher ist es nicht perfekt, aber ich denke, dass es zu diesem Zeitpunkt leistungsfähig genug ist 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 mit diesem Makefile ist, dass wenn ich eine Header-Datei ändere, 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 sind Abhängigkeiten für alle meine C-Dateien, aber das würde effektiv dazu führen, dass ein komplettes Rebuild des Projekts jedes Mal, wenn ich geändert/hinzugefügt eine Header-Datei, die nicht sehr anmutig sein würde.

Was ich möchte, ist, dass nur die C-Dateien, die einschließen. die von mir geänderte Header-Datei neu erstellt und das gesamte Projekt neu verlinkt werden muss. Ich kann die Verknüpfung tun, indem ich alle Header-Dateien zu Abhängigkeiten des Ziels, aber ich kann nicht herausfinden, wie die C-Dateien ungültig gemacht werden, wenn ihre enthaltenen Header-Dateien neuer sind.

Ich habe gehört, dass GCC einige Befehle hat, um dies möglich zu machen (so dass das Makefile irgendwie herausfinden kann, welche Dateien neu erstellt werden müssen), aber ich kann nicht für das Leben von mir ein tatsächliches Beispiel für die Implementierung finden, um zu sehen. Kann jemand eine Lösung posten, die dieses Verhalten in einem Makefile ermöglicht?

EDIT: Ich sollte klarstellen, ich bin vertraut mit dem Konzept der Platzierung der einzelnen Ziele in und mit jeder target.o erfordern die Header-Dateien. Das erfordert, dass ich die makefile jedes Mal, wenn ich eine Header-Datei irgendwo einschließen, die ein bisschen ein Schmerz ist zu bearbeiten. Ich bin auf der Suche nach einer Lösung, die die Header-Datei-Abhängigkeiten auf eigene Faust ableiten kann, die ich bin ziemlich sicher, ich habe in anderen Projekten gesehen.

38voto

Udi Meiri Punkte 1013

Wie bereits an anderer Stelle auf dieser Website dargelegt, siehe diese Seite: Automatische Abhängigkeitsgenerierung

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

Neben dem Hinzufügen der -M Flagge zu gcc, müssen Sie die .d Dateien in das Makefile einbinden (wie Chris oben schrieb). Es gibt einige kompliziertere Probleme auf der Seite, die mit sed gelöst werden, aber Sie können sie ignorieren und ein "make clean" durchführen, um die .d-Dateien zu entfernen, wenn make sich darüber beschwert, dass es nicht in der Lage ist, eine Header-Datei zu bauen, die nicht mehr existiert.

2 Stimmen

Ich kann mich irren, aber ich denke, GCC hat tatsächlich eine Funktion hinzugefügt, um zu versuchen, dieses sed-Problem zu umgehen. Schauen Sie sich gcc.gnu.org/onlinedocs/gcc-4.3.1/gcc/Preprocessor-Options.html speziell -MP.

0 Stimmen

Ja, -MP existiert seit GCC 3, existiert in clang und icc, und macht sed überflüssig. bruno.defraine.net/techtips/makefile-auto-dependencies-with-gcc/

0 Stimmen

Die neueste Version des Links in der Antwort enthält Beispiele mit GCC-Flags.

22voto

Martin Fido Punkte 1040

Sie könnten einen "make depend"-Befehl hinzufügen, wie andere gesagt 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.

Der Bindestrich am Anfang von '-include' weist Make an, fortzufahren, wenn die .d-Datei nicht existiert (z.B. beim ersten Kompilieren).

Beachten Sie, dass es in gcc einen Fehler bezüglich der Option -o zu geben scheint. Wenn Sie den Dateinamen des Objekts auf obj/_file__c.o dann wird die erzeugte _file_.d enthält weiterhin _file_.o , nicht obj/_file_c.o .

19 Stimmen

Dies hat bei mir nicht funktioniert. Z.B. hat das Makefile dies erzeugt und ausgeführt: g++ -c -Wall -Werror -MM -MF main.d -o main.o main.cpp Ich habe die Datei main.d erhalten, aber main.o hatte 0 Bytes. Das -MMD-Flag scheint jedoch genau das zu tun, was erforderlich war. Also wurde meine funktionierende makefile-Regel: $(CC) -c $(CFLAGS) -MMD -o $@ $<

17voto

Dies ist gleichbedeutend mit Die Antwort von Chris Dodd , verwendet aber eine andere Namenskonvention (und benötigt zufälligerweise nicht die sed magisch. Kopiert aus ein späteres Duplikat .


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

depend: .depend

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

include .depend

Außerdem gibt es das Tool makedepend aber ich mochte es nie so sehr wie gcc -MM

4 Stimmen

Warum buchstabieren Sie SOURCES nicht aus? Das erfordert nicht besonders viele weitere Zeichen und ist nicht so verworren wie "SRCS", das wie ein Akronym aussehen kann.

0 Stimmen

@HelloGoodbye Ein bisschen Stil. Ich habe immer verwendet, und immer gesehen, SRCS y OBJS . In den meisten Fällen stimme ich zu, aber jeder sollte wissen, was das ist.

0 Stimmen

@sherrellbc Was Menschen wissen "sollten" und was sie tatsächlich wissen, sind oft zwei verschiedene Dinge :P

8voto

mipadi Punkte 377834

Sie müssen einzelne Ziele für jede C-Datei erstellen und dann die Header-Datei als Abhängigkeit auflisten. Sie können weiterhin Ihre generischen Ziele verwenden und einfach die .h Abhängigkeiten, etwa so:

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

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

0 Stimmen

Könnte das folgende nicht funktionieren? %.c : %.h

1 Stimmen

@cassepipe: Wenn die Header-Datei den gleichen Namen hat wie die C-Datei und die C-Datei nur von der zugehörigen Header-Datei abhängt, ja.

0 Stimmen

Es gibt ein Programm namens makeheaders von der Fossil Projekt (vcs-Alternative zu git). Es erzeugt und aktualisiert Kopfzeilen pro Datei aus Ihren .h's. Es kann also eine großartige Kombination sein mit %.c : %.h da die erzeugten .h 's den gleichen Namen haben wie die .c 's . Ein viel KISSer Weg zu gehen, als make's Auto-Abhängigkeit Generation Chaos. Sie können einfach kompilieren makeheaders aus [Quellcode][1]. Ich schlage auch vor, dass Sie das [doc][2] lesen. [1]: fossil-scm.org/fossil/file/src/makeheaders.c [2]: fossil-scm.org/fossil/doc/trunk/src/makeheaders.html#H0006

4voto

Chris Dodd Punkte 2886

Im Grunde 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 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 Ihrem Makefile.

2 Stimmen

Ich verstehe das irgendwie, außer dass ich (abgesehen davon, dass die Flags -MM und -MG neu sind) nicht verstehe, wofür die nach Regex aussehende Zeile mit kryptischem Text ist. Das wird mich als Teamkollege nicht glücklich machen... ^_^ Ich werde es aber versuchen und sehen, ob ich irgendwelche Ergebnisse habe.

0 Stimmen

Sed ist die Abkürzung für "stream editor", der einen Textstrom verändern kann, ohne eine Datei verwenden zu müssen. Es ist ein Standard-Unix-Werkzeug und kleiner und schneller, so dass es häufiger als awk oder perl verwendet wird.

0 Stimmen

Ah, da ist das Problem: Ich mache das unter Windows.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X