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?
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
.
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.
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.
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.
.PHONY: install
C'est une cible de construction qui n'est pas un nom de fichier.
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
.
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:
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.
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