Jaki jest cel pliku .PHONY w pliku makefile?

1735

Co .PHONYoznacza plik Makefile? Przeszedłem przez to , ale jest to zbyt skomplikowane.

Czy ktoś może mi to wyjaśnić w prosty sposób?

Lazer
źródło

Odpowiedzi:

1944

Domyślnie cele Makefile są „celami plików” - służą do budowania plików z innych plików. Make przyjmuje, że jego celem jest plik, a to sprawia, że ​​pisanie Makefiles jest stosunkowo łatwe:

foo: bar
  create_one_from_the_other foo bar

Czasami jednak chcesz, aby Twój Makefile uruchamiał polecenia, które nie reprezentują fizycznych plików w systemie plików. Dobrym przykładem tego są wspólne cele „czyste” i „wszystkie”. Możliwe, że tak nie jest, ale potencjalnie plik może mieć nazwę cleanw katalogu głównym. W takim przypadku Make zostanie zdezorientowany, ponieważ domyślnie cleancel będzie powiązany z tym plikiem, a Make uruchomi go tylko wtedy, gdy plik nie wydaje się być aktualny w odniesieniu do jego zależności.

Te specjalne cele nazywane są fałszywymi i możesz wyraźnie powiedzieć Make, że nie są powiązane z plikami, np .:

.PHONY: clean
clean:
  rm -rf *.o

Teraz make cleanbędzie działać zgodnie z oczekiwaniami, nawet jeśli masz plik o nazwie clean.

Pod względem marki fałszywy cel to po prostu cel, który jest zawsze nieaktualny, więc za każdym razem, gdy o to poprosisz make <phony_target>, będzie działał niezależnie od stanu systemu plików. Pewne wspólne makecele, które są często fałszywe są: all, install, clean, distclean, TAGS, info, check.

Eli Bendersky
źródło
49
@eSKay: „dlaczego nazywa się„ fałszywy ”?” - ponieważ to nie jest prawdziwy cel. Oznacza to, że nazwa celu nie jest plikiem generowanym przez polecenia tego celu.
Bernard
96
@Lazer: Nie wiem, czy mówisz po angielsku. Nie jestem. słowo fony nie znaczy, jak to brzmi. en.wiktionary.org/wiki/phony mówi: Oszustwo; imitacja; o wprowadzającym w błąd wyglądzie.
Bahbar
57
Ta odpowiedź nie jest dokładnie kompletna - chociaż można ją rozwiązać w połączonym samouczku. .PHONY wymusza budowanie etykiety / pliku w pliku Makefile, jeśli jest on częścią topologicznego rodzaju dowolnego celu. To znaczy, jeśli masz etykietę „cleanup:”, która jest fałszywa, a twoja etykieta instalacji jest zdefiniowana jako cleanup jako warunek wstępny - tj. „Install: cleanup”, czyszczenie będzie zawsze uruchamiane, gdy Makefile będzie próbował zbudować 'zainstalować'. Jest to przydatne w przypadku kroków, które zawsze chcesz podjąć, niezależnie od tego, czy są skuteczne - zignoruje znaczniki czasu i po prostu je wymusi.
synthesizerpatel
9
Pamiętaj, że nie musisz używać .PHONY, dopóki nie masz pliku o takiej samej nazwie jak zadanie. Zadanie i tak zawsze zostanie wykonane, a plik Makefile będzie bardziej czytelny.
Bernard,
15
„fałszywy cel to po prostu cel, który jest zawsze nieaktualny” - świetne wyjaśnienie!
Danijel,
729

Załóżmy, że masz installcel, który jest bardzo powszechny w plikach makefile. Jeśli nie używasz .PHONY, a plik o nazwie installistnieje w tym samym katalogu co plik Makefile, nic niemake install zrobi . Wynika to z faktu, że Make interpretuje regułę w ten sposób, że oznacza „wykonaj taką i inną receptę, aby utworzyć plik o nazwie ”. Ponieważ plik już tam jest, a jego zależności się nie zmieniły, nic nie zostanie zrobione.install

Jeśli jednak installustawisz docelowy obiekt PHONY, powie on narzędziu make, że cel jest fikcyjny, i marka nie powinna oczekiwać, że utworzy rzeczywisty plik. Dlatego nie sprawdzi, czy installplik istnieje, co oznacza: a) jego zachowanie nie zostanie zmienione, jeśli plik istnieje, oraz b) dodatkowe stat()nie zostaną wywołane.

Zasadniczo wszystkie cele w twoim Makefile, które nie tworzą pliku wyjściowego o takiej samej nazwie jak nazwa celu, powinny mieć wartość PHONY. To zazwyczaj obejmuje all, install, clean, distclean, i tak dalej.

George Y.
źródło
6
@PineappleUndertheSea Zaakceptowana odpowiedź została znacznie poprawiona od początkowego poziomu bezwartościowości i jest teraz tak samo dobra jak ta. Musiałem przejrzeć historię zmian, aby zrozumieć twój komentarz.
Mark Amery
2
Wydaje się to trochę bezcelowe, ponieważ nigdy nie będę mieć plików o nazwie „install” lub podobnych w mojej bazie kodu. Większość plików będzie miała rozszerzenie, a pliki bez rozszerzenia są zwykle pisane wielkimi literami, np. „README”. Z drugiej strony, jeśli masz skrypt bash o nazwie „install” zamiast „install.sh”, będziesz miał zły czas.
nukleartyd
6
@JasonTu To niekoniecznie jest prawda. Konwencje skryptów Bash proszą Cię o pominięcie rozszerzenia „ .shlub .bash” dla programów, które działają tak, jakby miały główną funkcję, i rezerwowanie dodawania rozszerzenia dla bibliotek, które dołączasz ( source mylib.sh). W rzeczywistości dotarłem do tego pytania SO, ponieważ miałem skrypt w tym samym katalogu, co mój Makefile o nazwieinstall
Kyle
10
@Kyle Tak, nie jestem pewien, co znaczyło moje poprzednie „ja”. Obecnie używam .PHONYcały czas ...
nucleartide
2
@JasonTu Rozwiązanie tutaj jest proste: zbuduj maszynę czasu i „zastąp” swoje poprzednie ja. Zalecam zabranie ze sobą łopaty, aby nikt nie zorientował się, że jesteś jej .PHONYwersją.
Mateen Ulhaq,
120

UWAGA : Narzędzie make odczytuje plik makefile i sprawdza znaczniki czasu modyfikacji plików po obu stronach symbolu „:” w regule.

Przykład

W katalogu „test” obecne są następujące pliki:

prerit@vvdn105:~/test$ ls
hello  hello.c  makefile

W makefile reguła jest zdefiniowana następująco:

hello:hello.c
    cc hello.c -o hello

Załóżmy teraz, że plik „hello” to plik tekstowy zawierający pewne dane, który został utworzony po pliku „hello.c”. Tak więc znacznik czasu modyfikacji (lub tworzenia) „hello” będzie nowszy niż znacznik „hello.c”. Kiedy więc wywołamy „make hello” z wiersza poleceń, zostanie wydrukowany jako:

make: `hello' is up to date.

Teraz uzyskaj dostęp do pliku „hello.c” i umieść w nim trochę białych znaków, co nie wpływa na składnię ani logikę kodu, a następnie zapisz i wyjdź. Teraz znacznik czasu modyfikacji hello.c jest nowszy niż znacznik „hello”. Teraz, jeśli wywołasz „make hello”, wykona polecenia w następujący sposób:

cc hello.c -o hello

Plik „hello” (plik tekstowy) zostanie zastąpiony nowym plikiem binarnym „hello” (wynik powyższego polecenia kompilacji).

Jeśli użyjemy .PHONY w pliku makefile w następujący sposób:

.PHONY:hello

hello:hello.c
    cc hello.c -o hello

a następnie wywołać „make hello”, zignoruje każdy plik obecny w „teście pwd” i wykona polecenie za każdym razem.

Załóżmy teraz, że cel „cześć” nie ma zadeklarowanych zależności:

hello:
    cc hello.c -o hello

a plik „hello” jest już obecny w „teście” pwd, wtedy „make hello” będzie zawsze wyświetlane jako:

make: `hello' is up to date.
prerit jain
źródło
3
To nie tylko sprawia, że ​​polecenia, które uruchamiam, mają sens, ale w końcu sprawia, że makejako całość mają sens, to wszystko dotyczy plików! Dziękuję za tę odpowiedź.
Kzqai
80
.PHONY: install
  • oznacza, że ​​słowo „zainstaluj” nie reprezentuje nazwy pliku w tym Makefile;
  • oznacza, że ​​Makefile nie ma nic wspólnego z plikiem o nazwie „install” w tym samym katalogu.
YourBestBet
źródło
45

Jest to cel kompilacji, który nie jest nazwą pliku.

JohnMcG
źródło
33

Najlepszym wyjaśnieniem jest sam GNU make manual: sekcja 4.6 Fikcyjne cele .

.PHONYjest jedną z wbudowanych specjalnych nazw marek . Istnieją inne cele, które mogą Cię zainteresować, więc warto przejrzeć te odniesienia.

Kiedy przyjdzie czas na rozważenie celu .PHONY, make uruchomi swój przepis bezwarunkowo, niezależnie od tego, czy istnieje plik o tej nazwie lub jaki jest czas ostatniej modyfikacji.

Możesz być także zainteresowany standardowymi celami marki, takimi jak alli clean.

James Wald
źródło
11

Jest też jedna ważna podstępna metoda „.PHONY” - kiedy cel fizyczny zależy od fałszywego celu, który zależy od innego celu fizycznego:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

Po prostu można oczekiwać, że jeśli zaktualizujesz TARGET2, to TARGET1 należy uznać za nieaktualny w stosunku do TARGET1, więc TARGET1 powinien zostać odbudowany. I to naprawdę działa w ten sposób .

Trudna część polega na tym, że TARGET2 nie jest przestarzały w stosunku do TARGET1 - w takim przypadku należy spodziewać się, że TARGET1 nie powinien zostać odbudowany.

To zaskakująco nie działa, ponieważ: fałszywy cel i tak został uruchomiony (jak zwykle fałszywe cele) , co oznacza, że fałszywy cel został uznany za zaktualizowany . Z tego powodu TARGET1 jest uważany za przestarzały w stosunku do fałszywego celu .

Rozważać:

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

Możesz się tym pobawić:

  • najpierw wykonaj polecenie „przygotuj”, aby przygotować „pliki źródłowe”
  • baw się tym, dotykając określonych plików, aby je zaktualizować

Możesz zobaczyć, że fileall zależy od file1 pośrednio poprzez fałszywy cel - ale zawsze jest odbudowywany z powodu tej zależności. Jeśli zmienisz zależność fileallz filefwdna file, teraz fileallnie będzie ona przebudowywana za każdym razem, ale tylko wtedy, gdy któryś z zależnych celów jest nieaktualny w stosunku do niego jako plik.

Ethouris
źródło
5

Specjalny cel .PHONY:pozwala zadeklarować fałszywe cele, aby makenie sprawdzały ich jako rzeczywistych nazw plików: będzie działał przez cały czas, nawet jeśli takie pliki nadal istnieją.

Możesz umieścić kilka .PHONY:w swoim Makefile:

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

Istnieje inny sposób deklarowania fałszywych celów: wystarczy wpisać „::”

all :: prog1 prog2

...

clean ::
    ...
distclean ::
    ...

„::” ma specjalne znaczenie: cele są fałszywe, a ponadto mogą pojawiać się kilka razy:

clean ::
    rm file1

...


clean ::
    rm file2

Bloki poleceń będą wywoływane jeden po drugim.

Edouard Thiel
źródło
3

Często używam ich, aby powiedzieć domyślnemu celowi, aby nie strzelał.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Bez fałszywej, make supercleanby ogień clean, andsomethingelsei catcher superclean; ale dzięki PHONY make supercleannie zwolni catcher superclean.

Nie musimy się martwić, że cleancelem będzie PHONY, ponieważ nie jest on całkowicie fałszywy. Chociaż nigdy nie tworzy czystego pliku, ma polecenia do uruchomienia, więc make pomyśli, że jest to ostateczny cel.

Jednak supercleancel jest naprawdę fałszywy, więc make spróbuje go ułożyć w stos z czymkolwiek innym, co zapewnia dep dla supercleancelu - w tym inne supercleancele i %cel.

Zauważ, że nie mówimy nic o andsomethingelselub blah, więc najwyraźniej idą do łapacza.

Dane wyjściowe wyglądają mniej więcej tak:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah
jettero
źródło