Qual è lo scopo di .PHONY in un makefile?


1739

Cosa .PHONYsignifica in un Makefile? Ho passato con questo , ma è troppo complicato.

Qualcuno può spiegarmelo in termini semplici?

Risposte:


1947

Per impostazione predefinita, le destinazioni Makefile sono "destinazioni file": vengono utilizzate per creare file da altri file. Make presuppone che il suo target sia un file e questo rende la scrittura di Makefile relativamente semplice:

foo: bar
  create_one_from_the_other foo bar

Tuttavia, a volte si desidera che Makefile esegua comandi che non rappresentano file fisici nel file system. Buoni esempi per questo sono gli obiettivi comuni "clean" e "all". È probabile che non sia così, ma potresti avere potenzialmente un file chiamato cleannella tua directory principale. In tal caso, Make sarà confuso perché per impostazione predefinita la cleandestinazione sarebbe associata a questo file e Make lo eseguirà solo quando il file non sembra essere aggiornato in relazione alle sue dipendenze.

Questi target speciali sono chiamati fasulli e puoi dire esplicitamente a Make che non sono associati ai file, ad esempio:

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

Ora make cleanfunzionerà come previsto anche se hai un file chiamato clean.

In termini di Make, un target fasullo è semplicemente un target che è sempre obsoleto, quindi ogni volta che lo chiedi make <phony_target>, verrà eseguito, indipendentemente dallo stato del file system. Alcuni comuni makeobiettivi che sono spesso falsi sono: all, install, clean, distclean, TAGS, info, check.


49
@eSKay: 'perché si chiama' falso '?' - perché non è un vero obiettivo. Cioè, il nome della destinazione non è un file prodotto dai comandi di quella destinazione.
Bernard,

96
@Lazer: non so se sei un madrelingua inglese. Non sono. la parola falso non significa come suona. en.wiktionary.org/wiki/phony afferma: Frode; falso; avere un aspetto fuorviante.
Bahbar,

57
Questa risposta non è esattamente completa, sebbene possa essere affrontata nel tutorial collegato. .PHONY forza la creazione di un'etichetta / file in un Makefile se fa parte del tipo topologico di qualunque sia il tuo obiettivo. Vale a dire, se si dispone di un'etichetta 'cleanup:' impostata come fasulla e l'etichetta di installazione è definita con cleanup come prerequisito, ad esempio 'install: cleanup', la pulizia verrà sempre eseguita quando Makefile tenta di compilare 'installare'. Questo è utile per i passi che vuoi sempre fare indipendentemente dal loro successo: ignorerà i timestamp e lo forzerà.
synthesizerpatel

9
Si noti che non è necessario utilizzare .PHONY purché non si disponga di un file con lo stesso nome dell'attività. L'attività verrà sempre eseguita comunque e il Makefile sarà più leggibile.
Bernard,

15
"un obiettivo falso è semplicemente un obiettivo che è sempre obsoleto" - ottima spiegazione!
Danijel,

730

Supponiamo che tu abbia un installtarget, che è molto comune nei makefile. Se non si utilizza .PHONYed installesiste un file denominato nella stessa directory del Makefile, allora make installnon farà nulla . Questo perché Make interpreta la regola nel senso che "esegue tale ricetta per creare il file denominato install". Poiché il file è già lì e le sue dipendenze non sono cambiate, non verrà fatto nulla.

Tuttavia, se si crea il installtarget PHONY, questo indicherà allo strumento make che il target è fittizio e che make non dovrebbe aspettarsi che crei il file effettivo. Quindi non verificherà se il installfile esiste, ovvero: a) il suo comportamento non verrà modificato se il file esiste e b) extra stat()non verrà chiamato.

Generalmente tutte le destinazioni nel tuo Makefile che non producono un file di output con lo stesso nome del nome di destinazione dovrebbero essere PHONY. Questo include in genere all, install, clean, distclean, e così via.


6
@PineappleUndertheSea La risposta accettata è stata notevolmente migliorata dal suo livello iniziale di inutilità, ed è ora altrettanto buona di questa. Ho dovuto esaminare la cronologia delle revisioni per capire il tuo commento.
Mark Amery,

2
Questo sembra un po 'inutile poiché non avrò mai file denominati' install 'o simili nella mia base di codice. La maggior parte dei file avrà un'estensione di file, e i file senza estensione di solito sono in maiuscolo, come 'README'. Poi di nuovo, se hai uno script bash chiamato 'install' invece di 'install.sh', avrai dei brutti momenti.
nucleartide,

6
@JasonTu Questo non è necessariamente vero. Le convenzioni di script di Bash ti chiedono di omettere l' estensione .sho .bash"i programmi" che funzionano come se avessero una funzione principale e riservano l'aggiunta di un'estensione per le librerie che includi ( source mylib.sh). In effetti, sono arrivato a questa domanda SO perché avevo uno script nella stessa directory del mio Makefile chiamatoinstall
Kyle,

10
@Kyle Sì, non sono sicuro di cosa significasse il mio sé passato. In questi giorni lo uso .PHONYsempre ...
nucleartide,

2
@JasonTu La soluzione qui è semplice: costruisci una macchina del tempo e "sostituisci" il tuo io passato. Consiglio di portare una pala con te in modo che nessuno si renda conto che sei la .PHONYversione.
Mateen Ulhaq,

120

NOTA : lo strumento make legge il makefile e controlla i timestamp di modifica dei file su entrambi i lati del simbolo ':' in una regola.

Esempio

In una directory 'test' sono presenti i seguenti file:

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

Nel makefile una regola è definita come segue:

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

Ora supponiamo che il file 'ciao' sia un file di testo contenente alcuni dati, che è stato creato dopo il file 'ciao.c'. Quindi il timestamp di modifica (o creazione) di 'hello' sarà più recente di quello di 'hello.c'. Quindi quando invocheremo 'make hello' dalla riga di comando, verrà stampato come:

make: `hello' is up to date.

Ora accedi al file 'hello.c' e inserisci degli spazi bianchi, che non influiscono sulla sintassi o sulla logica del codice, quindi salva ed esci. Ora il timestamp di modifica di hello.c è più recente di quello di 'hello'. Ora se invochi 'make hello', eseguirà i comandi come:

cc hello.c -o hello

E il file 'ciao' (file di testo) verrà sovrascritto con un nuovo file binario 'ciao' (risultato del comando di compilazione sopra).

Se usiamo .PHONY nel makefile come segue:

.PHONY:hello

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

e quindi invoca 'make hello', ignorerà qualsiasi file presente nel pwd 'test' ed eseguirà il comando ogni volta.

Supponiamo ora che quel bersaglio "ciao" non abbia dipendenze dichiarate:

hello:
    cc hello.c -o hello

e il file 'hello' è già presente nel pwd 'test', quindi 'make hello' mostrerà sempre come:

make: `hello' is up to date.

3
Non solo ha senso i comandi che eseguo, ma alla fine fa in modo makeche abbia un senso nel suo insieme, dipende solo dai file! Grazie per questa risposta
Kzqai,

80
.PHONY: install
  • significa che la parola "installa" non rappresenta un nome di file in questo Makefile;
  • significa che il Makefile non ha nulla a che fare con un file chiamato "install" nella stessa directory.


33

La migliore spiegazione è lo stesso manuale di GNU make: 4.6 Sezione Targeting per falso .

.PHONYè uno dei nomi target incorporati speciali di make . Ci sono altri obiettivi che potrebbero interessarti, quindi vale la pena scorrere questi riferimenti.

Quando è il momento di prendere in considerazione un obiettivo .PHONY, make eseguirà la sua ricetta incondizionatamente, indipendentemente dal fatto che esista un file con quel nome o quale sia il suo ultimo tempo di modifica.

Potresti anche essere interessato a target standard di make come alle clean.


11

C'è anche un'importante sorpresa di ".PHONY" - quando un bersaglio fisico dipende da un bersaglio falso che dipende da un altro bersaglio fisico:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

Ti aspetteresti semplicemente che se hai aggiornato TARGET2, TARGET1 dovrebbe essere considerato obsoleto rispetto a TARGET1, quindi TARGET1 dovrebbe essere ricostruito. E funziona davvero così .

La parte difficile è quando TARGET2 non è stantio rispetto a TARGET1, nel qual caso dovresti aspettarti che TARGET1 non debba essere ricostruito.

Ciò sorprendentemente non funziona perché: il target falso è stato eseguito comunque (come normalmente fanno i target falsi) , il che significa che il target falso è stato considerato aggiornato . E a causa di ciò TARGET1 è considerato stantio contro il bersaglio falso .

Prendere in considerazione:

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

Puoi giocare con questo:

  • prima fai 'make prepar' per preparare i "file sorgente"
  • giocaci toccando determinati file per vederli aggiornati

Puoi vedere che fileall dipende indirettamente da file1 attraverso un obiettivo falso, ma viene sempre ricostruito a causa di questa dipendenza. Se modifichi la dipendenza fileallda da filefwda file, ora fileallnon viene ricostruita ogni volta, ma solo quando uno dei target dipendenti è stantio contro di esso come file.


5

Il target speciale .PHONY:consente di dichiarare target falsi, in modo che makenon li controlli come nomi di file effettivi: funzionerà continuamente anche se tali file esistono ancora.

Puoi metterne diversi .PHONY:nel tuo Makefile:

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

Esiste un altro modo per dichiarare bersagli falsi: basta inserire '::'

all :: prog1 prog2

...

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

Il '::' ha un significato speciale: gli obiettivi sono falsi e possono apparire più volte:

clean ::
    rm file1

...


clean ::
    rm file2

I blocchi di comandi verranno chiamati uno dopo l'altro.


3

Li uso spesso per dire al bersaglio predefinito di non sparare.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Senza falsi, make supercleanavrebbe sparato clean, andsomethingelsee catcher superclean; ma con PHONY, make supercleannon sparerà catcher superclean.

Non dobbiamo preoccuparci di dire che l' cleanobiettivo è PHONY, perché non è completamente falso. Sebbene non produca mai il file pulito, ha i comandi da sparare, quindi make penserà che sia un obiettivo finale.

Tuttavia, il supercleantarget è davvero falso, quindi make cercherà di impilarlo con qualsiasi altra cosa che fornisca deps al supercleantarget - questo include altri supercleantarget e il %target.

Nota che non diciamo assolutamente nulla andsomethingelseo blah, quindi vanno chiaramente al ricevitore.

L'output è simile al seguente:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.