Antworten:
Standardmäßig sind Makefile-Ziele "Dateiziele" - sie werden zum Erstellen von Dateien aus anderen Dateien verwendet. Make geht davon aus, dass das Ziel eine Datei ist, und dies macht das Schreiben von Makefiles relativ einfach:
foo: bar
create_one_from_the_other foo bar
Manchmal möchten Sie jedoch, dass Ihr Makefile Befehle ausführt, die keine physischen Dateien im Dateisystem darstellen. Gute Beispiele dafür sind die gemeinsamen Ziele "sauber" und "alle". Möglicherweise ist dies nicht der Fall, aber möglicherweise befindet sich eine Datei mit dem Namen clean
in Ihrem Hauptverzeichnis. In einem solchen Fall wird Make verwirrt, da das clean
Ziel standardmäßig dieser Datei zugeordnet ist und Make es nur ausführt, wenn die Datei hinsichtlich ihrer Abhängigkeiten nicht auf dem neuesten Stand zu sein scheint.
Diese speziellen Ziele werden als falsch bezeichnet und Sie können explizit festlegen, dass Make nicht mit Dateien verknüpft ist, z.
.PHONY: clean
clean:
rm -rf *.o
Jetzt make clean
wird es wie erwartet ausgeführt, auch wenn Sie eine Datei mit dem Namen haben clean
.
In Bezug auf Make ist ein falsches Ziel einfach ein Ziel, das immer veraltet ist. Wenn Sie also danach fragen make <phony_target>
, wird es unabhängig vom Status des Dateisystems ausgeführt. Einige gemeinsame make
Ziele , die oft unecht sind: all
, install
, clean
, distclean
, TAGS
, info
, check
.
Nehmen wir an, Sie haben ein install
Ziel, was in Makefiles sehr häufig vorkommt. Wenn Sie nicht verwenden .PHONY
und eine Datei mit dem Namen install
im selben Verzeichnis wie das Makefile vorhanden ist, make install
wird nichts unternommen . Dies liegt daran, dass Make die Regel so interpretiert, dass sie "das eine oder andere Rezept ausführen, um die Datei mit dem Namen zu erstellen install
" bedeutet. Da die Datei bereits vorhanden ist und sich ihre Abhängigkeiten nicht geändert haben, wird nichts unternommen.
Wenn Sie jedoch das install
Ziel PHONY erstellen, teilt es dem make-Tool mit, dass das Ziel fiktiv ist und dass make nicht erwarten sollte, dass es die eigentliche Datei erstellt. Daher wird nicht geprüft, ob die install
Datei vorhanden ist, was bedeutet: a) ihr Verhalten wird nicht geändert, wenn die Datei vorhanden ist, und b) extra stat()
wird nicht aufgerufen.
Im Allgemeinen sollten alle Ziele in Ihrem Makefile, die keine Ausgabedatei mit demselben Namen wie der Zielname erzeugen, PHONY sein. Dies umfasst in der Regel all
, install
, clean
, distclean
, und so weiter.
.sh
oder .bash
für "Programme" wegzulassen, die so ausgeführt werden, als hätten sie eine Hauptfunktion, und das Hinzufügen einer Erweiterung für Bibliotheken, die Sie einschließen ( source mylib.sh
). Tatsächlich kam ich zu dieser SO-Frage, weil ich ein Skript im selben Verzeichnis wie mein Makefile hatteinstall
.PHONY
ganze Zeit ...
.PHONY
Version sind.
HINWEIS : Das Make-Tool liest das Makefile und überprüft in einer Regel die Änderungszeitstempel der Dateien auf beiden Seiten des Symbols ':'.
In einem Verzeichnis 'test' sind folgende Dateien vorhanden:
prerit@vvdn105:~/test$ ls
hello hello.c makefile
In Makefile wird eine Regel wie folgt definiert:
hello:hello.c
cc hello.c -o hello
Nehmen wir nun an, dass die Datei 'Hallo' eine Textdatei ist, die einige Daten enthält, die nach der Datei 'Hallo.c' erstellt wurden. Der Zeitstempel für die Änderung (oder Erstellung) von "Hallo" ist also neuer als der von "Hallo.c". Wenn wir also 'Hallo machen' über die Befehlszeile aufrufen, wird Folgendes gedruckt:
make: `hello' is up to date.
Greifen Sie jetzt auf die Datei 'hello.c' zu und fügen Sie einige Leerzeichen ein, die die Codesyntax oder -logik nicht beeinflussen. Speichern und beenden Sie sie dann. Jetzt ist der Änderungszeitstempel von hello.c neuer als der von 'hallo'. Wenn Sie nun 'Hallo machen' aufrufen, werden die Befehle wie folgt ausgeführt:
cc hello.c -o hello
Und die Datei 'Hallo' (Textdatei) wird mit einer neuen Binärdatei 'Hallo' (Ergebnis des obigen Kompilierungsbefehls) überschrieben.
Wenn wir .PHONY im Makefile wie folgt verwenden:
.PHONY:hello
hello:hello.c
cc hello.c -o hello
Wenn Sie dann 'make hello' aufrufen, werden alle im pwd 'test' vorhandenen Dateien ignoriert und der Befehl jedes Mal ausgeführt.
Angenommen, für das Hallo-Ziel sind keine Abhängigkeiten deklariert:
hello:
cc hello.c -o hello
und 'Hallo' Datei ist bereits im pwd 'Test' vorhanden, dann wird 'Hallo machen' immer angezeigt als:
make: `hello' is up to date.
make
, sinnvoll, sondern macht letztendlich auch insgesamt Sinn, es geht nur um die Dateien! Vielen Dank für diese Antwort.
.PHONY: install
Die beste Erklärung ist das GNU-Handbuch selbst: 4.6 Abschnitt "Falsche Ziele" .
.PHONY
ist einer der speziellen integrierten Zielnamen von make . Es gibt andere Ziele, an denen Sie interessiert sein könnten. Es lohnt sich daher, diese Referenzen durchzublättern.
Wenn es an der Zeit ist, ein .PHONY-Ziel zu berücksichtigen, führt make sein Rezept bedingungslos aus, unabhängig davon, ob eine Datei mit diesem Namen vorhanden ist oder wie lange sie zuletzt geändert wurde.
Möglicherweise interessieren Sie sich auch für die Standardziele von make wie all
und clean
.
Es gibt auch einen wichtigen kniffligen Leckerbissen von ".PHONY" - wenn ein physisches Ziel von einem falschen Ziel abhängt, das von einem anderen physischen Ziel abhängt:
TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2
Sie würden einfach erwarten, dass TARGET1, wenn Sie TARGET2 aktualisiert haben, als veraltet gegenüber TARGET1 angesehen werden sollte, sodass TARGET1 neu erstellt werden sollte. Und so funktioniert es wirklich .
Der schwierige Teil ist, wenn TARGET2 gegen TARGET1 nicht veraltet ist. In diesem Fall sollten Sie damit rechnen, dass TARGET1 nicht neu erstellt werden sollte.
Dies funktioniert überraschenderweise nicht, weil: das falsche Ziel trotzdem ausgeführt wurde (wie es normalerweise bei falschen Zielen der Fall ist ) , was bedeutet, dass das falsche Ziel als aktualisiert angesehen wurde . Und aus diesem Grund gilt TARGET1 als abgestanden gegen das falsche Ziel .
Erwägen:
all: fileall
fileall: file2 filefwd
echo file2 file1 >fileall
file2: file2.src
echo file2.src >file2
file1: file1.src
echo file1.src >file1
echo file1.src >>file1
.PHONY: filefwd
.PHONY: filefwd2
filefwd: filefwd2
filefwd2: file1
@echo "Produced target file1"
prepare:
echo "Some text 1" >> file1.src
echo "Some text 2" >> file2.src
Sie können damit herumspielen:
Sie können sehen, dass fileall indirekt über ein falsches Ziel von file1 abhängt. Aufgrund dieser Abhängigkeit wird es jedoch immer neu erstellt . Wenn Sie die Abhängigkeit ändern in fileall
von filefwd
zu file
, jetzt fileall
wird nicht jedes Mal wieder aufgebaut, aber nur dann , wenn alle abhängigen Ziele ist abgestanden dagegen als Datei.
Das spezielle Ziel .PHONY:
ermöglicht es, falsche Ziele zu deklarieren, so dassmake
nicht als tatsächliche Dateinamen überprüft werden. Es funktioniert immer, auch wenn solche Dateien noch vorhanden sind.
Sie können mehrere .PHONY:
in Ihre Makefile
:
.PHONY: all
all : prog1 prog2
...
.PHONY: clean distclean
clean :
...
distclean :
...
Es gibt eine andere Möglichkeit, falsche Ziele zu deklarieren: Setzen Sie einfach '::'
all :: prog1 prog2
...
clean ::
...
distclean ::
...
Das '::' hat eine besondere Bedeutung: Ziele sind falsch und können mehrmals vorkommen:
clean ::
rm file1
...
clean ::
rm file2
Die Befehlsblöcke werden nacheinander aufgerufen.
Ich benutze sie oft, um dem Standardziel mitzuteilen, dass es nicht feuern soll.
superclean: clean andsomethingelse
blah: superclean
clean:
@echo clean
%:
@echo catcher $@
.PHONY: superclean
Ohne PHONY, make superclean
würde Feuer clean
, andsomethingelse
und catcher superclean
; aber mit PHONY make superclean
wird das nicht feuern catcher superclean
.
Wir müssen uns keine Sorgen machen, dass wir das machen clean
Ziel PHONY ist, weil es nicht ganz falsch ist. Obwohl die saubere Datei nie erstellt wird, müssen Befehle ausgelöst werden, sodass make den Eindruck erweckt, dass es sich um ein endgültiges Ziel handelt.
Das superclean
Ziel ist jedoch wirklich falsch, daher wird make versuchen, es mit allem anderen zu stapeln, das Deps für das superclean
Ziel bereitstellt - dies schließt andere superclean
Ziele und das %
Ziel ein.
Beachten Sie, dass wir überhaupt nichts über andsomethingelse
oder sagen blah
, also gehen sie eindeutig zum Fänger.
Die Ausgabe sieht ungefähr so aus:
$ make clean
clean
$ make superclean
clean
catcher andsomethingelse
$ make blah
clean
catcher andsomethingelse
catcher blah