Was bedeuten die Makefile-Symbole $ @ und $ <?

416
CC=g++
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
    $(CC) $(CFLAGS) $< -o $@

Was machen die $@und $<machen genau?

Mohit Deshpande
quelle
5
Der obige Link ist defekt, hier ist der andere: gnu.org/software/make/manual/html_node/Automatic-Variables.html
asciz
1
Hallo, was bedeutet dieses ".cpp.o:" als Ziel? (vor der letzten Zeile?).
Pseudonym_127
3
Die ".cpp.o:" bedeutet das Erstellen von ".o" (Objektdateien) aus ".cpp" (Quelldateien)
jaguzu
1
Ich denke, es sollte beachtet werden, dass es unter dem folgenden Link ein Make-Tutorial gibt, von dem ich glaube, dass Mohit das Makefile in seinem Beitrag erhalten hat. mrbook.org/blog/tutorials/make
DeepDeadpool
Microsoft nennt es Dateinamenmakros (für NMAKE), was klarer ist als automatische Variablen (für MAKE). Es ist nützlich, beide Seiten zu Bildungszwecken zu sehen.
Ivanzinho

Antworten:

502

$@ist der Name der zu generierenden Datei und $<die erste Voraussetzung (normalerweise die Quelldatei). Eine Liste all dieser speziellen Variablen finden Sie im Handbuch GNU Make .

Betrachten Sie beispielsweise die folgende Erklärung:

all: library.cpp main.cpp

In diesem Fall:

  • $@ bewertet zu all
  • $< bewertet zu library.cpp
  • $^ bewertet zu library.cpp main.cpp
bdonlan
quelle
16
Es ist erwähnenswert, dass $@dies nicht unbedingt eine Datei sein muss, sondern auch der Name eines .PHONYZiels.
Ephemera
Kann ich den $@sBefehlszeilenoptionen Folgendes hinzufügen: um Assembly-Ausgaben wie name.os zu generieren?
Huseyin Tugrul Buyukisik
4
Beachten Sie, dass $ <ausgewertet wird, wenn die erste Abhängigkeit eine Variable ist, die eine Liste darstellt. Wenn also LIST = lib1.cpp lib2.cpp und alle: $ {LIST} main.cpp, wird $ <nur als lib1.cpp ausgewertet. Vor ein paar Jahren hatte ich einige Zeit damit verbracht herauszufinden, was mit dem Ergebnis dieses Verhaltens passiert.
Chan Kim
Im Allgemeinen bezieht sich $ @ auf den Zielnamen, der sich auf der linken Seite von:
Deepak Kiran
78

Die $@und $<werden automatische Variablen genannt . Die Variable $@stellt den Namen der erstellten Datei dar (dh das Ziel) und $<stellt die erste Voraussetzung dar, die zum Erstellen der Ausgabedatei erforderlich ist.
Zum Beispiel:

hello.o: hello.c hello.h
         gcc -c $< -o $@

Hier hello.oist die Ausgabedatei. Darauf $@erweitert sich. Die erste Abhängigkeit ist hello.c. Darauf $<erweitert sich.

Das -cFlag generiert die .oDatei. siehe man gccfür eine detailliertere Erklärung. Das -ogibt die Ausgabedatei an, die erstellt werden soll.

Weitere Informationen finden Sie in diesem Artikel über Linux-Makefiles .

Sie können auch die GNU- makeHandbücher überprüfen . Dies erleichtert das Erstellen und Debuggen von Makefiles.

Wenn Sie diesen Befehl ausführen, wird die Makefile-Datenbank ausgegeben:

make -p 
geschickt
quelle
1
Ihre Antwort scheint $<sich auf hello.c hello.h(beide) auszudehnen . Bitte klären Sie.
Dr. Beco
Ja, es wird sowohl hello.c als auch hello.h enthalten
geschickt
19
$<ist nur der erste Punkt. Verwenden Sie, um alle einzuschließen $^.
Dr. Beco
1
Dr. Beco hat recht. Der Autor sollte seine Antwort ändern.
PT Huynh
67

Aus Verwalten von Projekten mit GNU Make, 3. Ausgabe, S. 22. 16 (unter der GNU Free Documentation License ):

Automatische Variablen werden festgelegt, makenachdem eine Regel abgeglichen wurde. Sie bieten Zugriff auf Elemente aus den Ziel- und Voraussetzungslisten, sodass Sie keine Dateinamen explizit angeben müssen. Sie sind sehr nützlich, um Codeduplikationen zu vermeiden, sind jedoch bei der Definition allgemeinerer Musterregeln von entscheidender Bedeutung.

Es gibt sieben automatische Kernvariablen:

  • $@: Der Dateiname, der das Ziel darstellt.

  • $%: Das Dateinamenelement einer Archivmitgliedsspezifikation.

  • $<: Der Dateiname der ersten Voraussetzung.

  • $?: Die Namen aller Voraussetzungen, die neuer als das Ziel sind, durch Leerzeichen getrennt.

  • $^: Die Dateinamen aller Voraussetzungen, durch Leerzeichen getrennt. In dieser Liste werden doppelte Dateinamen entfernt, da für die meisten Verwendungszwecke wie Kompilieren, Kopieren usw. keine doppelten Dateinamen gewünscht werden.

  • $+: Ähnlich wie bei $^sind dies die Namen aller durch Leerzeichen getrennten Voraussetzungen, außer dass $+Duplikate enthalten sind. Diese Variable wurde für bestimmte Situationen erstellt, z. B. für Argumente für Linker, bei denen doppelte Werte eine Bedeutung haben.

  • $*: Der Stamm des Zieldateinamens. Ein Stamm ist normalerweise ein Dateiname ohne Suffix. Von der Verwendung außerhalb von Musterregeln wird abgeraten.

Darüber hinaus verfügt jede der oben genannten Variablen über zwei Varianten zur Kompatibilität mit anderen Marken. Eine Variante gibt nur den Verzeichnisteil des Wertes zurück. Dies wird durch Anhängen eines „D“ zu dem Symbol gekennzeichnet, $(@D), $(<D)usw. Die andere Variante gibt nur die Datei Teil des Wertes. Dies ist durch ein „F“ auf das Symbol Anhängen $(@F), $(<F)usw. Beachten Sie, dass diese Variante Namen sind mehr als ein Zeichen lang und so müssen in Klammern gesetzt werden. GNU make bietet eine besser lesbare Alternative mit den Funktionen dir und notdir.

Alex
quelle
37

Die $@und $<sind spezielle Makros.

Wo:

$@ ist der Dateiname des Ziels.

$< ist der Name der ersten Abhängigkeit.

Eric
quelle
19

Das Makefile baut die helloausführbare Datei , wenn einer main.cpp, hello.cpp, factorial.cppgeändert. Das kleinstmögliche Makefile, um diese Spezifikation zu erreichen, könnte gewesen sein:

hello: main.cpp hello.cpp factorial.cpp
    g++ -o hello main.cpp hello.cpp factorial.cpp
  • Pro: sehr einfach zu lesen
  • con: Wartungsalptraum, Duplizierung der C ++ - Abhängigkeiten
  • con: Effizienzproblem, wir kompilieren alle C ++ neu, auch wenn nur eines geändert wurde

Um dies zu verbessern, kompilieren wir nur die C ++ - Dateien, die bearbeitet wurden. Dann verknüpfen wir einfach die resultierenden Objektdateien miteinander.

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

main.o: main.cpp
    g++ -c main.cpp

hello.o: hello.cpp
    g++ -c hello.cpp

factorial.o: factorial.cpp
    g++ -c factorial.cpp
  • pro: behebt Effizienzprobleme
  • con: neuer Wartungs-Albtraum, möglicher Tippfehler bei Objektdateiregeln

Um dies zu verbessern, können wir alle Objektdateiregeln durch eine einzige .cpp.oRegel ersetzen :

OBJECTS=main.o hello.o factorial.o

hello: $(OBJECTS)
    g++ -o hello $(OBJECTS)

.cpp.o:
    g++ -c $< -o $@
  • Pro: Zurück zu einem kurzen Makefile, etwas leicht zu lesen

Hier ist die .cpp.oRegel definiert , wie der Bau anyfile.ovon anyfile.cpp.

  • $< stimmt mit der ersten Abhängigkeit überein, in diesem Fall anyfile.cpp
  • $@entspricht dem Ziel in diesem Fall anyfile.o.

Die anderen Änderungen im Makefile sind:

  • Erleichtert das Ändern von Compilern von g ++ zu einem beliebigen C ++ - Compiler.
  • Erleichtert das Ändern der Compileroptionen.
  • Erleichtert das Ändern der Linkeroptionen.
  • Erleichtert das Ändern der C ++ - Quelldateien und der Ausgabe.
  • Es wurde eine Standardregel 'all' hinzugefügt, die als schnelle Überprüfung dient, um sicherzustellen, dass alle Ihre Quelldateien vorhanden sind, bevor versucht wird, Ihre Anwendung zu erstellen.
Stephen Quan
quelle
1

Zum Beispiel, wenn Sie Quellen kompilieren möchten, aber Objekte in einem anderen Verzeichnis haben:

Sie müssen tun:

gcc -c -o <obj/1.o> <srcs/1.c> <obj/2.o> <srcs/2.c> ...

Bei den meisten Makros sind das Ergebnis jedoch alle Objekte, denen alle Quellen folgen, wie z.

gcc -c -o <all OBJ path> <all SRC path>

Dies wird also nichts kompilieren ^^ und Sie werden nicht in der Lage sein, Ihre Objektdateien in ein anderes Verzeichnis zu stellen :(

Die Lösung besteht darin, diese speziellen Makros zu verwenden

$@ $<

Dadurch wird für jede .c-Datei in SRC (src / file.c) eine .o-Datei (obj / file.o) generiert.

$(OBJ):$(SRC)
   gcc -c -o $@ $< $(HEADERS) $(FLAGS)

es bedeutet :

    $@ = $(OBJ)
    $< = $(SRC)

aber Zeilen für Zeilen STATT aller Zeilen von OBJ, gefolgt von allen Zeilen von SRC

Aominé
quelle
Ich frage mich, was machen Sie mit all der Zeit, die Sie sparen, indem Sie "u" anstelle von "you" eingeben?
Ivanzinho