Qual è lo scopo di .PHONY in un makefile?

Cosa significa .PHONY in un Makefile? L’ho passato, ma è troppo complicato.

Qualcuno può spiegarmelo in termini semplici?

Per impostazione predefinita, i target Makefile sono “target di file”: vengono utilizzati per creare file da altri file. Make presuppone che il suo objective sia un file, e questo rende la scrittura di Makefile relativamente facile:

 foo: bar create_one_from_the_other foo bar 

Tuttavia, a volte si desidera che il Makefile esegua comandi che non rappresentano file fisici nel file system. Buoni esempi per questo sono gli obiettivi comuni “puliti” e “tutti”. È probabile che questo non sia il caso, ma potresti avere un file chiamato clean nella tua directory principale. In tal caso, Make verrà confuso poiché, per impostazione predefinita, il target clean verrà associato a questo file e Make lo eseguirà solo quando il file non sembra aggiornato per quanto riguarda le sue dipendenze.

Questi obiettivi 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 clean verrà eseguito come previsto anche se si ha un file chiamato clean .

In termini di Make, un target fasullo è semplicemente un target che è sempre scaduto, quindi ogni volta che chiedi make , verrà eseguito, indipendentemente dallo stato del file system. Alcuni obiettivi comuni che spesso sono fasulli sono: all , install , clean , distclean , TAGS , info , check .

Supponiamo che tu abbia install target, che è molto comune nei makefile. Se non si utilizza .PHONY e un file denominato install esiste nella stessa directory del Makefile, quindi make install non farà nulla . Questo perché Make interpreta la regola per significare “eseguire tale e quella 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 effettua l’ install dell’objective PHONY, verrà indicato allo strumento di creazione che il target è fittizio e che non dovrebbe aspettarsi che crei il file effettivo. Quindi non controllerà se il file di install esiste, ovvero: a) il suo comportamento non sarà alterato se il file esiste e b) extra stat() non verrà chiamato.

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

NOTA : lo strumento di creazione legge il makefile e controlla la data / ora 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 

Supponiamo ora che il file “ciao” sia un file di testo contenente alcuni dati, 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 “ciao.c” e inserisci alcuni spazi bianchi, che non influiscono sulla syntax del codice o sulla logica, quindi salva e chiudi. Ora il timestamp di modifica di ciao.c è più recente di quello del “ciao”. Ora se invochi “make hello”, eseguirà i comandi come:

 cc hello.c -o hello 

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

Se usiamo .PHONY nel makefile come segue:

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

e quindi invoca ‘make hello’, ignorerà se qualsiasi file presente nel pwd chiama ‘hello’ ed esegue il comando ogni volta.

Supponiamo ora che non ci siano dipendenze di target nel makefile:

 hello: cc hello.c -o hello 

e il file ‘ciao’ è già presente nel pwd ‘test’, quindi ‘make hello’ sarà sempre mostrato come:

 make: `hello' is up to date. 
 .PHONY: install 
  • significa che la parola “installa” non rappresenta un nome file in questo Makefile;
  • significa che il Makefile non ha nulla a che fare con un file chiamato “install” nella stessa directory.

È un objective di build che non è un nome di file.

La spiegazione migliore è il manuale di make di GNU stesso: 4.6 Sezione Phony Target .

.PHONY è uno dei nomi di target incorporati speciali di make. Ci sono altri obiettivi a cui potresti essere interessato, quindi vale la pena sfogliare questi riferimenti.

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

Potresti anche essere interessato a obiettivi standard di make come all e clean .

C’è anche un importante trucco di “.PHONY” – quando un bersaglio fisico dipende da un bersaglio fasullo 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 in questo modo .

La parte più difficile è quando TARGET2 non è obsoleto rispetto a TARGET1 – nel qual caso dovresti aspettarti che TARGET1 non debba essere ricostruito.

Questo sorprendentemente non funziona perché: il fasullo target è stato comunque eseguito (come normalmente fanno gli obiettivi fasulli) , il che significa che il fasullo target è stato considerato aggiornato . E a causa di ciò TARGET1 è considerato obsoleto contro il bersaglio fasullo .

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 “preparare” per preparare i “file sorgente”
  • giocare con questo toccando determinati file per vederli aggiornati

Puoi vedere che la fileall dipende indirettamente da file1 attraverso una destinazione fasulla, ma viene sempre ricostruita a causa di questa dipendenza. Se si modifica la dipendenza in fileall da filefwd a file , ora fileall non viene ricostruita ogni volta, ma solo quando uno qualsiasi dei target dipendenti è inattivo su di esso come un file.

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

 superclean: clean andsomethingelse blah: superclean clean: @echo clean %: @echo catcher $@ .PHONY: superclean 

Senza PHONY, make superclean fuoco clean , e andsomethingelse , e il catcher superclean ; ma con PHONY, make superclean non catcher superclean il catcher superclean del catcher superclean .

Non dobbiamo preoccuparci di dire che rendere il bersaglio clean è PHONY, perché non è completamente fasullo. Anche se non produce mai il file pulito, ha i comandi per sparare, così farà pensare che sia un objective finale.

Tuttavia, il bersaglio superclean è davvero fasullo, quindi make proverà a superclean con qualsiasi altra cosa che fornisca deps per il target superclean – questo include altri obiettivi superclean e il target % .

Nota che non diciamo nulla su andsomethingelse o blah , quindi vanno chiaramente al catcher.

L’output è simile a questo:

 $ make clean clean $ make superclean clean catcher andsomethingelse $ make blah clean catcher andsomethingelse catcher blah