Quel est le but de .PHONY dans un makefile?


1739

Que .PHONYsignifie un Makefile? J'ai vécu ça , mais c'est trop compliqué.

Quelqu'un peut-il me l'expliquer en termes simples?

Réponses:


1947

Par défaut, les cibles Makefile sont des "cibles de fichiers" - elles sont utilisées pour créer des fichiers à partir d'autres fichiers. Make suppose que sa cible est un fichier, ce qui rend l'écriture de Makefiles relativement facile:

foo: bar
  create_one_from_the_other foo bar

Cependant, vous souhaitez parfois que votre Makefile exécute des commandes qui ne représentent pas des fichiers physiques dans le système de fichiers. De bons exemples en sont les cibles communes «propre» et «tout». Ce n'est probablement pas le cas, mais vous pouvez potentiellement avoir un fichier nommé cleandans votre répertoire principal. Dans un tel cas, Make sera confus parce que par défaut la cleancible serait associée à ce fichier et Make ne l'exécutera que lorsque le fichier ne semble pas à jour en ce qui concerne ses dépendances.

Ces cibles spéciales sont appelées fausses et vous pouvez dire explicitement à Make qu'elles ne sont pas associées à des fichiers, par exemple:

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

Exécute maintenant make cleancomme prévu même si vous avez un fichier nommé clean.

En termes de Make, une fausse cible est simplement une cible qui est toujours obsolète, donc chaque fois que vous le demandez make <phony_target>, elle s'exécutera, indépendamment de l'état du système de fichiers. Certaines communes makecibles qui sont souvent faux sont: all, install, clean, distclean, TAGS, info, check.


49
@eSKay: 'pourquoi est-il appelé' faux '?' - parce que ce n'est pas une vraie cible. Autrement dit, le nom de la cible n'est pas un fichier généré par les commandes de cette cible.
Bernard

97
@Lazer: Je ne sais pas si vous êtes de langue maternelle anglaise. Je ne suis pas. le mot faux ne signifie pas à quoi il ressemble. en.wiktionary.org/wiki/phony dit: Fraudulent; faux; ayant une apparence trompeuse.
Bahbar

57
Cette réponse n'est pas exactement complète - bien qu'elle puisse être traitée dans le didacticiel lié. .PHONY force la création d'un label / fichier dans un Makefile s'il fait partie du type topologique de votre cible. C'est-à-dire que si vous avez une étiquette 'cleanup:' qui est définie bidon, et que votre étiquette d'installation est définie avec le nettoyage comme condition préalable - c'est-à-dire 'install: cleanup', le nettoyage sera toujours exécuté lorsque le Makefile tentera de construire 'installer'. Ceci est utile pour les étapes que vous souhaitez toujours effectuer, qu'elles réussissent - cela ignorera les horodatages et les forcera simplement.
synthesizerpatel

9
Notez que vous n'avez pas besoin d'utiliser .PHONY tant que vous n'avez pas de fichier portant le même nom que la tâche. La tâche sera toujours exécutée de toute façon et le Makefile sera plus lisible.
Bernard

15
"une cible bidon est tout simplement une cible qui est toujours obsolète" - excellente explication!
Danijel

730

Supposons que vous ayez une installcible, ce qui est très courant dans les makefiles. Si vous ne pas utiliser .PHONY, et un fichier nommé installexiste dans le même répertoire que le Makefile, puis make installfera rien . En effet, Make interprète la règle comme signifiant "exécuter telle ou telle recette pour créer le fichier nommé install". Puisque le fichier est déjà là et que ses dépendances n'ont pas changé, rien ne sera fait.

Cependant, si vous créez la installPHONIE cible, il indiquera à l'outil de création que la cible est fictive, et que la marque ne doit pas s'attendre à ce qu'elle crée le fichier réel. Par conséquent, il ne vérifiera pas si le installfichier existe, ce qui signifie: a) son comportement ne sera pas modifié si le fichier existe et b) extra stat()ne sera pas appelé.

Généralement, toutes les cibles de votre Makefile qui ne produisent pas de fichier de sortie portant le même nom que le nom cible doivent être PHONY. Cela comprend généralement all, install, clean, distcleanet ainsi de suite.


6
@PineappleUndertheSea La réponse acceptée a été considérablement améliorée par rapport à son niveau initial de non-valeur, et est maintenant tout aussi bonne que celle-ci. J'ai dû parcourir son historique de révision pour comprendre votre commentaire.
Mark Amery du

2
Cela semble un peu inutile car je n'aurai jamais de fichiers nommés «installer» ou similaires dans ma base de code. La plupart des fichiers auront une extension de fichier, et les fichiers sans extension de fichier sont généralement en majuscules, comme «README». Là encore, si vous avez un script bash nommé 'install' au lieu de 'install.sh', vous allez avoir un mauvais moment.
Nucleartide

6
@JasonTu Ce n'est pas nécessairement vrai. Les conventions de script Bash vous demandent d'omettre l' extension .shor .bashpour les "programmes" qui s'exécutent comme s'ils avaient une fonction principale et de réserver l'ajout d'une extension pour les bibliothèques que vous incluez ( source mylib.sh). En fait, je suis arrivé à cette question SO parce que j'avais un script dans le même répertoire que mon Makefile appeléinstall
Kyle

10
@Kyle Oui, je ne sais pas ce que voulait dire mon passé. Ces jours-ci j'utilise .PHONYtout le temps ...
Nucleartide

2
@JasonTu La solution ici est simple: construire une machine à remonter le temps et "remplacer" votre passé. Je recommande de prendre une pelle avec vous pour que personne ne se rende compte que vous êtes la .PHONYversion.
Mateen Ulhaq

121

REMARQUE : l'outil make lit le makefile et vérifie les horodatages de modification des fichiers à la fois du côté du symbole ':' dans une règle.

Exemple

Dans un répertoire 'test' les fichiers suivants sont présents:

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

Dans makefile, une règle est définie comme suit:

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

Supposons maintenant que le fichier «hello» est un fichier texte contenant des données, qui a été créé après le fichier «hello.c». Ainsi, l'horodatage de modification (ou de création) de «bonjour» sera plus récent que celui de «bonjour.c». Ainsi, lorsque nous invoquerons «faire bonjour» à partir de la ligne de commande, il s'affichera comme suit:

make: `hello' is up to date.

Accédez maintenant au fichier 'hello.c' et placez-y des espaces blancs, ce qui n'affecte pas la syntaxe ou la logique du code, puis enregistrez et quittez. Maintenant, l'horodatage de modification de hello.c est plus récent que celui de 'hello'. Maintenant, si vous appelez «make hello», il exécutera les commandes comme suit:

cc hello.c -o hello

Et le fichier 'hello' (fichier texte) sera remplacé par un nouveau fichier binaire 'hello' (résultat de la commande de compilation ci-dessus).

Si nous utilisons .PHONY dans le makefile comme suit:

.PHONY:hello

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

puis appelez «make hello», il ignorera tout fichier présent dans le test «pwd» et exécutera la commande à chaque fois.

Supposons maintenant que cette cible 'hello' n'ait aucune dépendance déclarée:

hello:
    cc hello.c -o hello

et le fichier 'hello' est déjà présent dans le test 'pwd', alors 'make hello' apparaîtra toujours comme:

make: `hello' is up to date.

3
Non seulement cela donne un sens aux commandes que j'exécute, mais cela donne finalement makeun sens à l'ensemble, c'est une question de fichiers! Merci pour cette réponse.
Kzqai

Voici à quoi ressemble une règle simple: `` `` cible: dépendances ... commandes ... '' `` Réf: gnu.org/software/make
VicX il y a

80
.PHONY: install
  • signifie que le mot "installer" ne représente pas un nom de fichier dans ce Makefile;
  • signifie que le Makefile n'a rien à voir avec un fichier appelé "install" dans le même répertoire.

45

Il s'agit d'une cible de génération qui n'est pas un nom de fichier.


33

La meilleure explication est le manuel GNU make lui-même: 4.6 Section Phony Targets .

.PHONYest l'un des noms de cible intégrés spéciaux de make . Il existe d'autres cibles qui pourraient vous intéresser, il vaut donc la peine de parcourir ces références.

Lorsqu'il est temps d'envisager une cible .PHONY, make exécutera sa recette sans condition, indépendamment de l'existence d'un fichier portant ce nom ou de l'heure de sa dernière modification.

Vous pouvez également être intéressé par les cibles standard de make telles que allet clean.


11

Il y a aussi un traitement délicat important de ".PHONY" - quand une cible physique dépend d'une cible bidon qui dépend d'une autre cible physique:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

Vous vous attendez simplement à ce que si vous mettez à jour TARGET2, TARGET1 doit être considéré comme périmé par rapport à TARGET1, donc TARGET1 doit être reconstruit. Et cela fonctionne vraiment de cette façon .

La partie délicate est lorsque TARGET2 n'est pas périmé par rapport à TARGET1 - auquel cas vous devez vous attendre à ce que TARGET1 ne soit pas reconstruit.

Cela ne fonctionne étonnamment pas parce que: la cible bidon a quand même été exécutée (comme le font normalement les cibles bidon) , ce qui signifie que la cible bidon a été considérée comme mise à jour . Et à cause de cela, TARGET1 est considéré comme périmé par rapport à la fausse cible .

Considérer:

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

Vous pouvez jouer avec ceci:

  • faites d'abord 'make prepare' pour préparer les "fichiers source"
  • jouer avec cela en touchant des fichiers particuliers pour les voir mis à jour

Vous pouvez voir que fileall dépend indirectement de file1 via une cible bidon - mais il est toujours reconstruit en raison de cette dépendance. Si vous modifiez la dépendance fileallde filefwdà file, maintenant fileallne sera pas reconstruit à chaque fois, mais uniquement lorsque l'une des cibles dépendantes est périmée en tant que fichier.


5

La cible spéciale .PHONY:permet de déclarer des cibles bidon, ce qui makene les vérifiera pas comme des noms de fichiers réels: cela fonctionnera tout le temps même si de tels fichiers existent toujours.

Vous pouvez en mettre plusieurs .PHONY:dans Makefile:

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

Il existe une autre façon de déclarer des cibles bidon: il suffit de mettre '::'

all :: prog1 prog2

...

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

Le '::' a une signification particulière: les cibles sont fausses et peuvent apparaître plusieurs fois:

clean ::
    rm file1

...


clean ::
    rm file2

Les blocs de commandes seront appelés les uns après les autres.


3

Je les utilise souvent pour dire à la cible par défaut de ne pas tirer.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Sans PHONY, make supercleantireraient clean, andsomethingelseet catcher superclean; mais avec PHONY, make supercleanne tirera pas le catcher superclean.

Nous n'avons pas à nous soucier de dire que la cleancible est PHONY, car elle n'est pas complètement bidon. Bien qu'il ne produise jamais le fichier propre, il a des commandes à tirer, donc make pensera que c'est une cible finale.

Cependant, la supercleancible est vraiment bidon, alors make essaiera de l'empiler avec tout ce qui fournit des deps pour la supercleancible - cela inclut d'autres supercleancibles et la %cible.

Notez que nous ne disons rien du tout sur andsomethingelseou blah, donc ils vont clairement au receveur.

La sortie ressemble à ceci:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.