web-dev-qa-db-fra.com

Quel est le but de .PHONY dans un fichier makefile?

Que signifie .PHONY dans un Makefile? J'ai traversé this , mais c'est trop compliqué.

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

1464
Lazer

Par défaut, les cibles Makefile sont des "cibles de fichiers" - elles sont utilisées pour construire 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 voulez 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 à cet égard sont les objectifs communs "nettoyer" et "tous". Il est fort probable que ce ne soit pas le cas, mais vous pouvez éventuellement avoir un fichier nommé clean dans votre répertoire principal. Dans un tel cas, Make sera confondu car par défaut, la cible clean serait associée à ce fichier et Make ne l'exécutera que lorsque le fichier ne semble pas être à jour en ce qui concerne ses dépendances.

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

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

Maintenant, make clean fonctionnera comme prévu même si vous avez un fichier nommé clean.

En termes de création, une cible fictive est simplement une cible toujours obsolète. Ainsi, chaque fois que vous demanderez make <phony_target>, elle s'exécutera indépendamment de l'état du système de fichiers. Les cibles make courantes qui sont souvent fausses sont les suivantes: all, install, clean, distclean, TAGS, info, check.

1669
Eli Bendersky

Supposons que vous avez install cible, ce qui est très courant dans les makefiles. Si vous utilisez pas utilisez .PHONY, et si un fichier nommé install existe dans le même répertoire que le Makefile, alors make install fera l'affaire rien. En effet, Make interprète la règle comme signifiant "exécuter telle ou telle recette pour créer le fichier nommé install". Comme le fichier existe déjà et que ses dépendances n'ont pas changé, rien ne sera fait.

Toutefois, si vous créez la cible install target PHONY, elle indiquera à l'outil de création que la cible est fictive et que cette commande ne devrait pas s'attendre à ce qu'elle crée le fichier réel. Par conséquent, il ne vérifiera pas si le fichier install existe, ce qui signifie: a) son comportement ne sera pas modifié si le fichier existe déjà et b) extra stat() ne sera pas appelé.

En règle générale, toutes les cibles de votre Makefile qui ne produisent pas un fichier de sortie portant le même nom que le nom de la cible doivent être PHONY. Cela inclut généralement all, install, clean, distclean, etc.

691
George Y.

NOTE: L'outil make lit le makefile et vérifie les horodatages de modification des fichiers des deux côtés 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 le 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, 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 "make hello" à partir de la ligne de commande, le résultat sera le suivant:

make: `hello' is up to date.

Accédez maintenant au fichier 'hello.c' et mettez-y des espaces, ce qui n’affectera 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 en tant que:

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 fichier Make comme suit:

.PHONY:hello

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

puis appelez 'make hello', il ignorera tous les fichiers présents dans le test 'pwd' et exécutera la commande à chaque fois.

Supposons maintenant que la cible 'bonjour' n'ait pas de dépendances déclarées:

hello:
    cc hello.c -o hello

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

make: `hello' is up to date.
99
prerit jain
.PHONY: install
  • signifie que le mot "install" 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.
71
YourBestBet

C'est une cible de construction qui n'est pas un nom de fichier.

39
JohnMcG

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

.PHONY est l'un des noms de make noms de cible intégrés spéciaux . Il y a d'autres cibles qui pourraient vous intéresser, aussi vaut-il la peine de parcourir ces références.

Lorsqu'il est temps d'envisager une cible .PHONY, make exécutera sa recette de manière inconditionnelle, qu'un fichier portant ce nom existe ou qu'il soit l'heure de sa dernière modification.

Vous pouvez également être intéressé par les marques Standard Targets telles que all et clean.

27
James Wald

Il existe également un traitement délicat important de ". PHONY" - lorsqu'une cible physique dépend d'une cible fictive qui dépend d'une autre cible physique:

TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2

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

La difficulté réside dans le fait que TARGET2 n'est pas obsolète contre TARGET1 - auquel cas vous devriez vous attendre à ce que TARGET1 ne soit pas reconstruit.

Cela ne fonctionne étonnamment pas parce que: la cible fictive a de toute façon été exécutée (comme le font normalement les cibles fictives), ce qui signifie que la cible factice a été considérée comme mise à jour. Et à cause de cela TARGET1 est considéré comme périmé par rapport à la cible factice.

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 ça:

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

Vous pouvez voir que fileall dépend de file1 indirectement via une cible factice - mais il toujours est reconstruit en raison de cette dépendance. Si vous changez la dépendance dans fileall de filefwd à file, _ et maintenant fileall ne sera pas reconstruit à chaque fois, mais uniquement lorsque l'une des cibles dépendantes sera obsolète, fichier.

11
Ethouris

Je les utilise souvent pour indiquer à la cible par défaut de ne pas se déclencher.

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

Sans PHONY, make superclean déclencherait clean, andsomethingelse et catcher superclean; mais avec PHONY, make superclean ne déclenchera pas le catcher superclean.

Nous n'avons pas à nous soucier de dire que la cible clean est PHONY, car ce n'est pas complètement bidon. Bien qu'il ne produise jamais le fichier en mode minimal, il a des commandes à déclencher, ce qui signifie que Make pense que c'est une cible finale.

Cependant, la cible superclean est vraiment fausse, alors make essaiera de l'empiler avec tout ce qui fournit des indications pour la cible superclean - cela inclut d'autres cibles superclean et le % cible.

Notez que nous ne disons rien du tout sur andsomethingelse ou blah, alors ils vont clairement au récupérateur.

La sortie ressemble à ceci:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah
2
jettero