Langue: 日本語 | Español | Français | Português | 中文 | English
Précédent | Suivant | Sommaire | Version originale anglaise (gnu.org)

4 Écrire des règles

Une règle (rule) apparaît dans un makefile et indique quand et comment refaire certains fichiers, appelés les cibles (target) de la règle (le plus souvent une seule par règle). Elle énumère les autres fichiers qui sont les prérequis (prerequisite) de la cible, ainsi que la recette (recipe) à utiliser pour créer ou mettre à jour la cible.

L'ordre dans lequel les règles sont écrites n'a en principe aucune importance, sauf pour déterminer le but par défaut : la cible que make s'efforce de traiter lorsque vous n'en spécifiez aucune autre. Le but par défaut est la première cible de la première règle du premier makefile. Il y a deux exceptions à cela : une cible commençant par un point n'est pas un but par défaut à moins de contenir aussi une ou plusieurs barres obliques « / » ; et une cible qui définit une règle de motif n'a aucun effet sur le but par défaut. (Voir Définir et redéfinir les règles de motif.)

C'est pourquoi on écrit généralement le makefile de telle sorte que la première règle soit celle qui compile l'ensemble du programme (ou tous les programmes décrits par le makefile) ; cette cible est souvent nommée « all ». Voir Arguments pour spécifier les buts.


4.1 Un exemple de règle

Voici d'abord un exemple de règle :

foo.o : foo.c defs.h       # module for twiddling the frobs
        cc -c -g foo.c

La cible de cette règle est foo.o, et ses prérequis sont foo.c et defs.h. La recette contient une seule commande : « cc -c -g foo.c ». La ligne de recette commence par une tabulation, ce qui permet de l'identifier comme une recette.

Cette règle énonce deux choses :


4.2 Syntaxe des règles

En général, une règle a la forme suivante :

targets : prerequisites
        recipe

ou bien la forme suivante :

targets : prerequisites ; recipe
        recipe

Les targets (cibles) sont des noms de fichiers séparés par des espaces. On peut y utiliser des caractères génériques (voir Utiliser des caractères génériques dans les noms de fichiers), et un nom de la forme « a(m) » désigne le membre m du fichier d'archive a (voir Les membres d'archive comme cibles). Généralement une règle n'a qu'une seule cible, mais il arrive qu'on ait des raisons d'en mettre plusieurs (voir Plusieurs cibles dans une règle).

Les lignes de recipe (recette) commencent par une tabulation (ou par le premier caractère de la valeur de la variable .RECIPEPREFIX ; voir Autres variables spéciales). La première ligne de la recette peut être placée sur la ligne suivant celle des prérequis, en la faisant précéder d'une tabulation, ou sur la même ligne, en la faisant précéder d'un point-virgule. Les deux écritures ont le même effet. La syntaxe des recettes comporte encore d'autres particularités ; voir Écrire les recettes d'une règle.

Comme le signe dollar sert à introduire une référence de variable dans make, lorsque vous voulez écrire un véritable signe dollar dans une cible ou un prérequis, vous devez l'écrire en double : « $$ » (voir Comment utiliser les variables). De plus, si vous avez activé l'expansion secondaire (voir Expansion secondaire) et que vous voulez écrire un véritable signe dollar dans une liste de prérequis, vous devez en réalité en écrire quatre (« $$$$ »).

Les lignes longues peuvent être coupées en plaçant une barre oblique inverse suivie d'un saut de ligne. Ce n'est pas indispensable, car make n'impose aucune limite à la longueur d'une ligne de makefile.

Une règle indique deux choses à make : quand une cible devient périmée (a besoin d'être mise à jour), et comment la mettre à jour le cas échéant.

Le critère pour juger qu'une cible est périmée se spécifie au moyen des prerequisites (prérequis). Ceux-ci sont constitués de noms de fichiers séparés par des espaces. (Là encore, on peut utiliser des caractères génériques et des membres d'archive (voir Utiliser make pour mettre à jour les fichiers d'archive).) Une cible est considérée comme périmée si elle n'existe pas, ou si elle est plus ancienne (d'après la date de dernière modification) que l'un de ses prérequis. L'idée est que le contenu de la cible est calculé à partir de l'information contenue dans les prérequis, de sorte que, si l'un d'eux change, le contenu d'une cible déjà existante n'est plus nécessairement valide.

La manière de mettre à jour se spécifie au moyen de la recipe (recette). Il s'agit d'une ou plusieurs commandes exécutées par le shell (en général « sh »), assorties de quelques fonctionnalités supplémentaires (voir Écrire les recettes d'une règle).


4.3 Types de prérequis

GNU make comprend deux types de prérequis. Le premier est le prérequis ordinaire décrit dans la section précédente ; le second est le prérequis d'ordonnancement seul (order-only). Un prérequis ordinaire énonce deux choses. D'abord, il fixe l'ordre dans lequel les recettes sont invoquées : la recette de la cible ne commence qu'une fois achevées les recettes de tous ses prérequis. Ensuite, il établit une dépendance : si l'un des prérequis est plus récent que la cible, celle-ci est considérée comme périmée et doit être refaite.

C'est en général exactement le comportement souhaité : si un prérequis de la cible est mis à jour, la cible doit l'être aussi.

Mais il arrive parfois que l'on veuille garantir qu'un prérequis soit construit avant la cible, sans pour autant forcer la mise à jour de la cible quand le prérequis est mis à jour. Les prérequis d'ordonnancement seul servent à créer ce genre de relation. On les spécifie en plaçant une barre verticale (|) dans la liste des prérequis : les prérequis à gauche de la barre sont ordinaires, ceux à droite sont d'ordonnancement seul :

targets : normal-prerequisites | order-only-prerequisites

La partie des prérequis ordinaires peut bien sûr être vide. On peut aussi déclarer les prérequis d'une même cible sur plusieurs lignes, qui seront concaténées comme il convient (les prérequis ordinaires sont ajoutés à la liste des prérequis ordinaires, et les prérequis d'ordonnancement seul à la liste des prérequis d'ordonnancement seul). Notez que si vous déclarez un même fichier à la fois comme prérequis ordinaire et comme prérequis d'ordonnancement seul, le prérequis ordinaire l'emporte (car les prérequis ordinaires englobent entièrement le comportement des prérequis d'ordonnancement seul).

Les prérequis d'ordonnancement seul ne sont jamais examinés pour déterminer si la cible est périmée. Même si un prérequis d'ordonnancement seul est marqué comme cible factice (voir Cibles factices (Phony)), cela ne provoque pas la reconstruction de la cible.

Considérons par exemple le cas où l'on veut placer les cibles dans un répertoire distinct, qui pourrait ne pas exister avant l'exécution de make. Dans cette situation, on veut que le répertoire soit créé avant d'y placer la moindre cible, mais comme l'horodatage d'un répertoire change à chaque ajout, suppression ou renommage de fichier, on ne veut surtout pas refaire toutes les cibles chaque fois que l'horodatage du répertoire change. Une façon de gérer cela consiste à utiliser des prérequis d'ordonnancement seul : on fait du répertoire un prérequis d'ordonnancement seul de toutes les cibles :

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
        mkdir $(OBJDIR)

Ainsi, la règle qui crée le répertoire objdir est exécutée, si nécessaire, avant la construction de tout « .o » ; mais le seul fait que l'horodatage du répertoire objdir ait changé ne provoque la construction d'aucun « .o ».


4.4 Utiliser des caractères génériques dans les noms de fichiers

Un seul nom de fichier peut désigner de nombreux fichiers grâce aux caractères génériques (joker). Les caractères génériques de make sont « * », « ? » et « […] », les mêmes que ceux du shell Bourne. Par exemple, *.c désigne la liste de tous les fichiers (du répertoire de travail) dont le nom se termine par « .c ».

Lorsqu'une expression correspond à plusieurs fichiers, le résultat est trié.2 Toutefois, plusieurs expressions ne sont pas triées globalement. Par exemple, *.c *.h énumère tous les fichiers dont le nom se termine par « .c » (triés), suivis de tous les fichiers dont le nom se termine par « .h » (triés).

Le caractère « ~ » en début de nom de fichier a lui aussi une signification spéciale. Seul, ou suivi d'une barre oblique, il représente votre répertoire personnel. Par exemple, ~/bin se développe en /home/you/bin. Lorsque « ~ » est suivi d'un mot, la chaîne représente le répertoire personnel de l'utilisateur désigné par ce mot. Par exemple, ~john/bin se développe en /home/john/bin. Sur les systèmes qui n'ont pas de répertoire personnel par utilisateur (comme MS-DOS ou MS-Windows), cette fonctionnalité peut être simulée en définissant la variable d'environnement HOME.

L'expansion des caractères génériques est effectuée automatiquement par make dans les cibles et dans les prérequis. Dans les recettes, c'est le shell qui s'en charge. Dans les autres contextes, l'expansion des caractères génériques n'a lieu que si vous la demandez explicitement avec la fonction wildcard.

On peut annuler la signification spéciale d'un caractère générique en le faisant précéder d'une barre oblique inverse. Ainsi, foo\*bar désigne un fichier précis dont le nom est composé de « foo », d'un astérisque et de « bar ».


4.4.1 Exemples d'utilisation de caractères génériques

Les caractères génériques peuvent aussi être utilisés dans la recette d'une règle, où ils sont développés par le shell. Par exemple, voici une règle qui supprime tous les fichiers objets :

clean:
        rm -f *.o

Les caractères génériques sont aussi utiles dans les prérequis d'une règle. En écrivant la règle suivante dans un makefile, l'exécution de « make print » imprime uniquement les fichiers « .c » qui ont changé depuis la dernière impression :

print: *.c
        lpr -p $?
        touch print

Cette règle utilise print comme fichier cible vide. Voir Fichiers cibles vides pour enregistrer des événements. (La variable automatique « $? » est utilisée pour n'imprimer que les fichiers modifiés. Voir Variables automatiques.)

L'expansion des caractères génériques n'a pas lieu lors de la définition d'une variable. Ainsi, si vous écrivez :

objects = *.o

la valeur de la variable objects est la chaîne littérale « *.o ». En revanche, si vous utilisez la valeur de objects dans une cible ou un prérequis, l'expansion des caractères génériques y a lieu. Et si vous utilisez la valeur de objects dans une recette, le shell peut effectuer l'expansion lors de l'exécution de la recette. Pour affecter à objects le résultat déjà développé, utilisez plutôt :

objects := $(wildcard *.o)

Voir La fonction wildcard.


4.4.2 Les pièges de l'utilisation des caractères génériques

Voici un exemple où l'utilisation naïve de l'expansion des caractères génériques ne fonctionne pas comme on le voudrait. Supposons que vous vouliez créer le fichier exécutable foo à partir de tous les fichiers objets du répertoire et que vous écriviez :

objects = *.o

foo : $(objects)
        cc -o foo $(CFLAGS) $(objects)

La valeur de objects est la chaîne littérale « *.o ». L'expansion des caractères génériques se produit dans la règle de foo, de sorte que chaque fichier « .o » réellement existant devient un prérequis de foo et est recompilé au besoin.

Mais que se passe-t-il si vous supprimez tous les fichiers « .o » ? Lorsqu'un caractère générique ne correspond à aucun fichier, il est laissé tel quel. Alors foo dépend du fichier au nom étrange *.o. Comme un tel fichier n'existe vraisemblablement pas, make émet l'erreur indiquant qu'il ne sait pas comment fabriquer « *.o ». Ce n'est pas le comportement souhaité !

Il est en fait possible d'obtenir le résultat voulu avec l'expansion des caractères génériques, mais cela requiert des techniques un peu plus avancées, faisant appel à la fonction wildcard ou à la substitution de chaînes. Voir La fonction wildcard.

Sur les systèmes d'exploitation de Microsoft (MS-DOS et MS-Windows), les noms de chemin utilisent la barre oblique inverse comme séparateur de répertoires, comme dans :

  c:\foo\bar\baz.c

ce qui équivaut à la forme Unix c:/foo/bar/baz.c (la partie c: est ce qu'on appelle la lettre de lecteur). Lorsque make s'exécute sur ces systèmes, il prend en charge les barres obliques inverses aussi bien que les barres obliques de style Unix dans les noms de chemin. Cependant, cette prise en charge ne s'étend pas à l'expansion des caractères génériques, où la barre oblique inverse est un caractère de protection. Vous devez donc utiliser des barres obliques de style Unix dans ces cas.


4.4.3 La fonction wildcard

L'expansion des caractères génériques a lieu automatiquement dans les règles. En revanche, lors de la définition d'une variable ou dans les arguments d'une fonction, elle n'a normalement pas lieu. Pour effectuer l'expansion des caractères génériques à ces endroits, vous devez utiliser la fonction wildcard comme suit :

$(wildcard pattern…)

Partout où cette chaîne apparaît dans un makefile, elle est remplacée par une liste, séparée par des espaces, des noms des fichiers existants qui correspondent à l'un des motifs de noms de fichiers donnés. Si aucun fichier existant ne correspond à un motif, ce motif est retiré de la sortie de la fonction wildcard. Notez que cela diffère du comportement d'un caractère générique non satisfait dans une règle : là, un caractère générique non satisfait n'est pas ignoré mais utilisé tel quel (voir Les pièges de l'utilisation des caractères génériques).

Comme pour l'expansion des caractères génériques dans les règles, le résultat de la fonction wildcard est trié. Là encore, chaque expression est triée séparément. Ainsi, « $(wildcard *.c *.h) » se développe en tous les fichiers correspondant à « .c » (triés), suivis de tous les fichiers correspondant à « .h » (triés).

Un usage de la fonction wildcard consiste à obtenir la liste de tous les fichiers source C d'un répertoire, comme ceci :

$(wildcard *.c)

On peut transformer la liste des fichiers source C en liste de fichiers objets en remplaçant le suffixe « .c » par « .o » dans le résultat, comme suit :

$(patsubst %.c,%.o,$(wildcard *.c))

(Ici, nous avons utilisé une autre fonction, patsubst. Voir Fonctions de substitution et d'analyse de chaînes.)

Ainsi, un makefile qui compile et lie tous les fichiers source C d'un répertoire peut s'écrire ainsi :

objects := $(patsubst %.c,%.o,$(wildcard *.c))

foo : $(objects)
        cc -o foo $(objects)

(Cela tire parti de la règle implicite de compilation des programmes C, de sorte qu'il n'est pas nécessaire d'écrire des règles explicites pour compiler chaque fichier. « := » est une variante de « = » ; voir Les deux types de variables pour son explication.)


4.5 Recherche de répertoires pour les prérequis

Dans les grands systèmes, on souhaite souvent placer les sources dans un répertoire distinct des binaires. La fonctionnalité de recherche dans les répertoires (directory search) de make facilite cela en cherchant automatiquement les prérequis dans plusieurs répertoires. Lorsque vous redistribuez les fichiers entre répertoires, vous n'avez pas à réécrire chaque règle : il suffit de modifier le chemin de recherche.


4.5.1 VPATH : chemin de recherche pour tous les prérequis

La valeur de la variable make VPATH spécifie la liste des répertoires dans lesquels make doit chercher. Le plus souvent, on suppose qu'ils contiennent des fichiers prérequis absents du répertoire courant, mais make utilise VPATH comme liste de recherche aussi bien pour les prérequis que pour les cibles des règles.

Ainsi, si un fichier mentionné comme cible ou comme prérequis n'existe pas dans le répertoire courant, make cherche un fichier portant ce nom parmi les répertoires énumérés dans VPATH. Si un tel fichier y est trouvé, il peut devenir le prérequis (voir ci-dessous). On peut alors écrire dans les règles des noms de fichiers de la liste des prérequis comme s'ils existaient tous dans le répertoire courant. Voir Écrire des recettes adaptées à la recherche dans les répertoires.

Dans la variable VPATH, les noms de répertoires sont séparés par des deux-points ou des espaces. L'ordre dans lequel les répertoires sont énumérés est celui dans lequel make les parcourt. (Sous MS-DOS et MS-Windows, comme les deux-points peuvent figurer dans le nom de chemin lui-même après une lettre de lecteur, on utilise des points-virgules pour séparer les noms de répertoires de VPATH.)

Par exemple,

VPATH = src:../headers

spécifie un chemin contenant deux répertoires, src et ../headers, que make parcourt dans cet ordre.

Avec cette valeur de VPATH, la règle suivante,

foo.o : foo.c

est interprétée comme si elle avait été écrite ainsi :

foo.o : src/foo.c

à condition que foo.c n'existe pas dans le répertoire courant mais soit trouvé dans le répertoire src.


4.5.2 La directive vpath

Semblable à la variable VPATH mais plus sélective, la directive vpath (en minuscules, notez-le) permet de spécifier un chemin de recherche pour une classe particulière de noms de fichiers, ceux qui correspondent à un motif donné. On peut ainsi attribuer certains répertoires de recherche à une classe de noms de fichiers, et d'autres répertoires (ou aucun) à d'autres noms de fichiers.

La directive vpath existe sous trois formes :

vpath pattern directories

Spécifie directories comme chemin de recherche pour les noms de fichiers qui correspondent à pattern.

Le chemin de recherche directories est une liste de répertoires à parcourir, séparés par des deux-points (des points-virgules sous MS-DOS et MS-Windows) ou des espaces, exactement comme le chemin de recherche utilisé dans la variable VPATH.

vpath pattern

Efface le chemin de recherche associé à pattern.

vpath

Efface tous les chemins de recherche précédemment spécifiés par des directives vpath.

Le motif d'une directive vpath est une chaîne contenant le caractère « % ». Cette chaîne doit correspondre au nom de fichier d'un prérequis recherché ; le caractère « % » correspond à une chaîne de zéro caractère ou plus (comme dans les règles de motif ; voir Définir et redéfinir les règles de motif). Par exemple, %.h correspond aux fichiers se terminant par .h. (En l'absence de « % », le motif doit correspondre exactement au prérequis, ce qui est rarement utile.)

Le caractère « % » dans le motif d'une directive vpath peut être protégé en le faisant précéder d'une barre oblique inverse (« \ »). Les barres obliques inverses qui protégeraient sinon des caractères « % » peuvent elles-mêmes être protégées par des barres obliques inverses supplémentaires. Les barres obliques inverses qui protègent un caractère « % » ou une autre barre oblique inverse sont retirées avant que le motif ne soit comparé à un nom de fichier. En revanche, les barres obliques inverses qui ne risquent pas de protéger un caractère « % » sont laissées intactes.

Lorsqu'un prérequis n'existe pas dans le répertoire courant, si le pattern d'une directive vpath correspond au nom de ce fichier prérequis, les directories de cette directive sont parcourus comme (et avant) les répertoires de la variable VPATH.

Par exemple,

vpath %.h ../headers

indique à make de chercher dans le répertoire ../headers tout prérequis dont le nom se termine par .h et qui n'est pas trouvé dans le répertoire courant.

Si plusieurs motifs vpath correspondent au nom du fichier prérequis, make traite une à une les directives vpath correspondantes et parcourt tous les répertoires énumérés dans chacune d'elles. make traite les directives vpath multiples dans l'ordre où elles apparaissent dans le makefile ; les directives portant le même motif sont indépendantes les unes des autres.

Ainsi,

vpath %.c foo
vpath %   blish
vpath %.c bar

cherche les fichiers se terminant par « .c » dans foo, puis blish, puis bar, tandis que

vpath %.c foo:bar
vpath %   blish

cherche les fichiers se terminant par « .c » dans foo, puis bar, puis blish.


4.5.3 Comment la recherche dans les répertoires est effectuée

Lorsqu'un prérequis est trouvé par recherche dans les répertoires (générale ou sélective), le nom de chemin trouvé n'est pas forcément celui que make vous fournira effectivement dans la liste des prérequis. Il arrive en effet que le chemin découvert par la recherche dans les répertoires soit abandonné.

L'algorithme qu'utilise make pour décider de conserver ou d'abandonner un chemin trouvé par recherche dans les répertoires est le suivant :

  1. Si le fichier cible n'existe pas au chemin spécifié dans le makefile, une recherche dans les répertoires est effectuée.
  2. Si la recherche dans les répertoires réussit, ce chemin est conservé et ce fichier est provisoirement retenu comme cible.
  3. Tous les prérequis de cette cible sont examinés de la même manière.
  4. Après le traitement des prérequis, la cible peut avoir ou non besoin d'être refaite :
    1. Si la cible n'a pas besoin d'être refaite, le chemin du fichier trouvé par recherche dans les répertoires est utilisé dans toute liste de prérequis contenant cette cible. En somme, si make n'a pas besoin de refaire la cible, il utilise le chemin trouvé par recherche dans les répertoires.
    2. Si la cible a besoin d'être refaite (elle est périmée), le nom de chemin trouvé par recherche dans les répertoires est abandonné, et la cible est refaite en utilisant le nom de fichier spécifié dans le makefile. En somme, lorsque make doit refaire une cible, celle-ci est refaite localement, et non dans le répertoire trouvé par recherche dans les répertoires.

Cet algorithme peut sembler compliqué, mais en pratique c'est très souvent exactement ce que l'on souhaite.

D'autres versions de make utilisent un algorithme plus simple : si le fichier n'existe pas et qu'il est trouvé par recherche dans les répertoires, alors ce nom de chemin est toujours utilisé, que la cible ait ou non besoin d'être refaite. Ainsi, si la cible est refaite, elle est créée à l'emplacement du nom de chemin découvert par recherche dans les répertoires.

En réalité, si vous souhaitez ce comportement pour certains répertoires ou pour tous, vous pouvez l'indiquer à make au moyen de la variable GPATH.

GPATH a la même syntaxe et le même format que VPATH (c'est-à-dire une liste de noms de chemin séparés par des espaces ou des deux-points). Si une cible périmée est trouvée par recherche dans les répertoires dans un répertoire qui figure aussi dans GPATH, ce nom de chemin n'est pas abandonné : la cible est refaite en utilisant le chemin développé.


4.5.4 Écrire des recettes adaptées à la recherche dans les répertoires

Le fait qu'un prérequis soit trouvé par recherche dans les répertoires dans un autre répertoire ne change rien à la recette de la règle : celle-ci est exécutée telle qu'elle est écrite. Il faut donc écrire la recette avec soin pour qu'elle cherche le prérequis dans le répertoire où make l'a trouvé.

Cela se fait au moyen des variables automatiques telles que « $^ » (voir Variables automatiques). Par exemple, la valeur de « $^ » est la liste de tous les prérequis de la règle, y compris le nom des répertoires où ils ont été trouvés, et la valeur de « $@ » est la cible. Ainsi :

foo.o : foo.c
        cc -c $(CFLAGS) $^ -o $@

(La variable CFLAGS existe pour vous permettre de spécifier les drapeaux à passer à la compilation C par les règles implicites ; nous l'utilisons ici de façon cohérente pour qu'elle agisse uniformément sur toutes les compilations C. Voir Variables utilisées par les règles implicites.)

Les prérequis comprennent souvent des fichiers d'en-tête que l'on ne veut pas mentionner dans la recette. La variable automatique « $< » représente uniquement le premier prérequis :

VPATH = src:../headers
foo.o : foo.c defs.h hack.h
        cc -c $(CFLAGS) $< -o $@

4.5.5 Recherche dans les répertoires et règles implicites

La recherche dans les répertoires spécifiés par VPATH ou vpath a aussi lieu lors de l'examen des règles implicites (voir Utilisation des règles implicites).

Par exemple, si le fichier foo.o n'a pas de règle explicite, make examine les règles implicites, comme la règle intégrée qui le compile si foo.c existe. Si ce fichier est introuvable dans le répertoire courant, les répertoires appropriés sont parcourus. Si foo.c existe dans l'un de ces répertoires (ou est mentionné dans le makefile), la règle implicite de compilation C s'applique.

Les recettes des règles implicites utilisent normalement des variables automatiques, par nécessité. En conséquence, elles utilisent tels quels les noms de fichiers trouvés par recherche dans les répertoires, sans effort particulier.


4.5.6 Recherche dans les répertoires pour les bibliothèques à lier

La recherche dans les répertoires s'applique d'une manière particulière aux bibliothèques utilisées par l'éditeur de liens. Cette fonctionnalité spéciale entre en jeu lorsque vous écrivez un prérequis dont le nom est de la forme « -lname ». (Le fait qu'il se passe ici quelque chose d'inhabituel se reconnaît à ceci : un prérequis est d'ordinaire le nom d'un fichier, alors que le nom de fichier d'une bibliothèque a généralement la forme libname.a, et non « -lname ».)

Lorsqu'un prérequis a la forme « -lname », make le traite de manière spéciale : il cherche d'abord un fichier libname.so et, à défaut, un fichier libname.a, dans le répertoire courant, dans les répertoires spécifiés par les chemins de recherche vpath correspondants et le chemin de recherche VPATH, puis dans les répertoires /lib, /usr/lib et prefix/lib (normalement /usr/local/lib, mais la version MS-DOS/MS-Windows de make se comporte comme si prefix était défini comme la racine de l'arborescence d'installation de DJGPP), dans cet ordre.

Par exemple, si la bibliothèque /usr/lib/libcurses.a existe sur votre système (et qu'il n'y a pas de fichier /usr/lib/libcurses.so), alors

foo : foo.c -lcurses
        cc $^ -o $@

exécute la commande « cc foo.c /usr/lib/libcurses.a -o foo » lorsque foo est plus ancien que foo.c ou que /usr/lib/libcurses.a.

L'ensemble par défaut des fichiers à rechercher est libname.so et libname.a, mais il peut être personnalisé au moyen de la variable .LIBPATTERNS. Chaque mot de la valeur de cette variable est une chaîne de motif. Lorsqu'un prérequis tel que « -lname » apparaît, make remplace le signe pourcentage de chaque motif de la liste par name, et effectue la recherche dans les répertoires décrite ci-dessus pour chacun des noms de fichiers de bibliothèque ainsi obtenus.

La valeur par défaut de .LIBPATTERNS est « lib%.so lib%.a », ce qui donne le comportement par défaut décrit plus haut.

En affectant à cette variable une valeur vide, vous désactivez complètement l'expansion des bibliothèques à lier.


4.6 Cibles factices (Phony)

Une cible factice (phony) est une cible qui n'est pas en réalité le nom d'un fichier. C'est plutôt un simple nom donné à une recette à exécuter lorsque vous le demandez explicitement. Il y a deux raisons d'utiliser une cible factice : éviter un conflit avec un fichier de même nom, et améliorer les performances.

Si vous écrivez une règle dont la recette ne crée pas le fichier cible, la recette est exécutée chaque fois que cette cible est envisagée pour reconstruction. En voici un exemple :

clean:
        rm *.o temp

Comme la commande rm ne crée pas de fichier nommé clean, un tel fichier n'existera vraisemblablement jamais. C'est pourquoi la commande rm est exécutée chaque fois que vous tapez « make clean ».

Dans cet exemple, si un fichier nommé clean venait à être créé dans ce répertoire, la cible clean cesserait de fonctionner correctement. N'ayant pas de prérequis, clean serait toujours considérée comme à jour, et sa recette ne serait plus jamais exécutée. Pour éviter ce problème, vous pouvez déclarer explicitement une cible comme factice en en faisant un prérequis de la cible spéciale .PHONY (voir Noms de cibles intégrées spéciales), comme suit :

.PHONY: clean
clean:
        rm *.o temp

Avec cela, « make clean » exécute la recette qu'un fichier nommé clean existe ou non.

Un prérequis de .PHONY est toujours interprété comme un nom de cible littéral, jamais comme un motif (même s'il contient le caractère « % »). Pour qu'une règle de motif soit toujours refaite, envisagez d'utiliser une « cible de forçage » (force target) (voir Règles sans recette ni prérequis).

Les cibles factices sont aussi utiles en conjonction avec les invocations récursives de make (voir L'utilisation récursive de make). Dans cette situation, le makefile contient souvent une variable qui énumère un certain nombre de sous-répertoires à construire. Une façon naïve de gérer cela consiste à définir une seule règle dont la recette parcourt en boucle les sous-répertoires, comme suit :

SUBDIRS = foo bar baz

subdirs:
        for dir in $(SUBDIRS); do \
          $(MAKE) -C $$dir; \
        done

Cette méthode présente toutefois plusieurs problèmes. D'abord, toute erreur détectée par un sous-make est ignorée par cette règle, de sorte que la construction des autres répertoires se poursuit même si l'un échoue. On peut y remédier en ajoutant une commande shell qui note l'erreur et sort, mais make sortirait alors même s'il avait été invoqué avec l'option -k, ce qui est gênant. Ensuite, et c'est sans doute plus important, comme il n'y a qu'une seule règle, on ne tire pas pleinement parti de la capacité de make à construire les cibles en parallèle (voir L'exécution parallèle). Les cibles d'un même makefile sont construites en parallèle, mais les sous-répertoires sont construits un à la fois.

En déclarant les sous-répertoires comme cibles .PHONY (ce qui est indispensable, car le fichier de sous-répertoire existe manifestement toujours ; sinon ils ne seraient pas construits), on élimine ces problèmes :

SUBDIRS = foo bar baz

.PHONY: subdirs $(SUBDIRS)

subdirs: $(SUBDIRS)

$(SUBDIRS):
        $(MAKE) -C $@

foo: baz

Ici, nous déclarons en outre que le sous-répertoire foo ne peut être construit avant que le sous-répertoire baz soit achevé ; ce type de déclaration de relation est particulièrement important lorsqu'on tente des constructions parallèles.

La recherche de règles implicites (voir Utilisation des règles implicites) est omise pour les cibles .PHONY. C'est pourquoi déclarer une cible comme .PHONY est bénéfique pour les performances, même si vous ne vous souciez pas de l'existence d'un fichier réel.

Une cible factice ne devrait pas être un prérequis d'un véritable fichier cible ; sinon, la recette de la cible factice serait exécutée chaque fois que make examine ce fichier. Tant qu'une cible factice n'est pas le prérequis d'une cible réelle, sa recette n'est exécutée que lorsque la cible factice est un but spécifié (voir Arguments pour spécifier les buts).

Un makefile inclus ne doit pas être déclaré comme cible factice. Les cibles factices ne sont pas censées représenter des fichiers réels, et comme la cible est toujours considérée comme périmée, make la refait toujours puis se relance lui-même (voir Comment les makefiles sont refaits). Pour éviter cela, make ne se relance pas lorsqu'un fichier inclus marqué comme factice est refait.

Les cibles factices peuvent avoir des prérequis. Lorsqu'un répertoire contient plusieurs programmes, il est plus pratique de tous les décrire dans un seul makefile ./Makefile. Comme la cible refaite par défaut est la première du makefile, on en fait souvent une cible factice nommée « all » dont les prérequis énumèrent tous les programmes. Par exemple :

all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
        cc -o prog1 prog1.o utils.o

prog2 : prog2.o
        cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
        cc -o prog3 prog3.o sort.o utils.o

Ainsi, il suffit de taper « make » pour refaire les trois programmes, ou bien de spécifier en argument celui que l'on veut refaire (comme « make prog1 prog3 »). Le caractère factice n'est pas héréditaire : les prérequis d'une cible factice ne deviennent pas eux-mêmes factices à moins d'être explicitement déclarés comme tels.

Lorsqu'une cible factice est le prérequis d'une autre cible factice, elle joue le rôle d'un sous-programme de cette dernière. Par exemple, dans l'exemple suivant, « make cleanall » supprime les fichiers objets, les fichiers de différences et le fichier program :

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
        rm program

cleanobj :
        rm *.o

cleandiff :
        rm *.diff

4.7 Règles sans recette ni prérequis

Si une règle n'a ni prérequis ni recette, et que sa cible est un fichier inexistant, make considère cette cible comme mise à jour chaque fois que la règle est exécutée. Cela signifie que toutes les cibles qui dépendent de cette cible exécuteront toujours leur recette.

Un exemple le montrera clairement :

clean: FORCE
        rm $(objects)
FORCE:

Ici, la cible « FORCE » satisfait la condition spéciale décrite plus haut, de sorte que la cible clean qui en dépend est forcée d'exécuter sa recette. Le nom « FORCE » n'a en lui-même aucune signification spéciale, mais c'est l'un des noms couramment employés à cet usage.

Comme on le voit, utiliser « FORCE » de cette manière donne le même résultat qu'utiliser « .PHONY: clean ».

Utiliser « .PHONY » est plus explicite et plus efficace. Cependant, d'autres versions de make ne prennent pas en charge « .PHONY » ; c'est pourquoi « FORCE » apparaît dans de nombreux makefiles. Voir Cibles factices (Phony).


4.8 Fichiers cibles vides pour enregistrer des événements

La cible vide (empty target) est une variante de la cible factice ; elle sert à conserver la recette d'une action que l'on demande de temps en temps explicitement. Contrairement à une cible factice, le fichier cible peut réellement exister ; mais le contenu du fichier n'a pas d'importance, et il est généralement vide.

Le but d'un fichier cible vide est d'enregistrer, au moyen de sa date de dernière modification, le moment où la recette de la règle a été exécutée pour la dernière fois. C'est possible parce que l'une des commandes de la recette est une commande touch qui met à jour le fichier cible.

Un fichier cible vide devrait avoir des prérequis (sinon il n'aurait pas de sens). Lorsque vous demandez à refaire une cible vide, sa recette est exécutée si l'un des prérequis est plus récent que la cible, c'est-à-dire si l'un des prérequis a changé depuis la dernière fois que la cible a été refaite. En voici un exemple :

print: foo.c bar.c
        lpr -p $?
        touch print

Avec cette règle, « make print » exécute la commande lpr si l'un des fichiers source a changé depuis le dernier « make print ». La variable automatique « $? » est utilisée pour n'imprimer que les fichiers modifiés (voir Variables automatiques).


4.9 Noms de cibles intégrées spéciales

Certains noms ont une signification spéciale lorsqu'ils apparaissent comme cibles.

.PHONY

Les prérequis de la cible spéciale .PHONY sont considérés comme des cibles factices. Lorsqu'une telle cible est envisagée, make exécute sa recette inconditionnellement, qu'un fichier de ce nom existe ou non, et quelle que soit sa date de dernière modification. Voir Cibles factices (Phony).

.SUFFIXES

Les prérequis de la cible spéciale .SUFFIXES sont la liste des suffixes à utiliser pour rechercher des règles de suffixe. Voir Les anciennes règles de suffixe.

.DEFAULT

La recette spécifiée pour .DEFAULT est utilisée pour toute cible pour laquelle aucune règle (explicite ou implicite) n'a été trouvée. Voir Définir une règle par défaut de dernier recours. Si une recette .DEFAULT est spécifiée, elle est exécutée à la place pour tous les fichiers mentionnés comme prérequis, mais non comme cibles, dans une règle. Voir Algorithme de recherche des règles implicites.

.PRECIOUS

Les cibles mentionnées comme prérequis de .PRECIOUS reçoivent le traitement spécial suivant : si make est tué ou interrompu pendant l'exécution de leur recette, la cible n'est pas supprimée. Voir Interrompre ou tuer make. De plus, si la cible est un fichier intermédiaire, normalement supprimé lorsqu'il n'est plus nécessaire, elle ne l'est pas non plus. Voir Chaînes de règles implicites. Sur ce dernier point, le fonctionnement recoupe celui de la cible spéciale .SECONDARY.

On peut aussi mentionner un motif de cible de règle implicite (comme « %.o ») comme fichier prérequis de la cible spéciale .PRECIOUS, afin de préserver les fichiers intermédiaires créés par les règles dont la cible porte un nom correspondant à ce motif.

.INTERMEDIATE

Les cibles dont dépend .INTERMEDIATE sont traitées comme des fichiers intermédiaires. Voir Chaînes de règles implicites. .INTERMEDIATE sans prérequis n'a aucun effet.

.NOTINTERMEDIATE

Les prérequis de la cible spéciale .NOTINTERMEDIATE ne sont jamais considérés comme des fichiers intermédiaires. Voir Chaînes de règles implicites. .NOTINTERMEDIATE sans prérequis fait traiter toutes les cibles comme non intermédiaires.

Si le prérequis est un motif de cible, les cibles construites au moyen de cette règle de motif ne sont plus considérées comme des fichiers intermédiaires.

.SECONDARY

Les cibles dont dépend .SECONDARY sont traitées comme des fichiers intermédiaires, mais ne sont jamais supprimées automatiquement. Voir Chaînes de règles implicites.

.SECONDARY peut servir à éviter des reconstructions redondantes dans certaines situations inhabituelles. Par exemple :

hello.bin: hello.o bye.o
        $(CC) -o $@ $^

%.o: %.c
        $(CC) -c -o $@ $<

.SECONDARY: hello.o bye.o

Considérons la situation où hello.bin est à jour par rapport aux fichiers source, mais où le fichier objet hello.o est introuvable. Sans .SECONDARY, make refait hello.o bien que les fichiers source n'aient pas changé, puis refait hello.bin à son tour. En déclarant hello.o comme .SECONDARY, make n'a plus besoin de le refaire, et hello.bin non plus. Bien entendu, si l'un des fichiers source est mis à jour, tous les fichiers objets sont refaits afin que la génération de hello.bin réussisse.

.SECONDARY sans prérequis fait traiter toutes les cibles comme secondaires (c'est-à-dire qu'aucune cible n'est supprimée au motif qu'elle a été considérée comme fichier intermédiaire).

.SECONDEXPANSION

Si .SECONDEXPANSION est mentionnée comme cible quelque part dans le makefile, alors toutes les listes de prérequis définies après son apparition sont développées une seconde fois après la lecture de tous les makefiles. Voir Expansion secondaire.

.DELETE_ON_ERROR

Si .DELETE_ON_ERROR est mentionnée comme cible quelque part dans le makefile, alors make supprime la cible d'une règle si celle-ci a changé et que sa recette se termine avec un code de retour non nul, exactement comme s'il avait reçu un signal. Voir Les erreurs dans les recettes.

.IGNORE

Si vous spécifiez des prérequis pour .IGNORE, make ignore les erreurs survenant lors de l'exécution de la recette de ces fichiers particuliers. La recette de .IGNORE (s'il y en a une) est ignorée.

Si elle est mentionnée comme cible sans prérequis, .IGNORE indique d'ignorer les erreurs lors de l'exécution de la recette de tous les fichiers. Cet usage de « .IGNORE » n'est pris en charge que pour des raisons de compatibilité historique. Comme il affecte toutes les recettes du makefile, il n'est guère utile ; il est recommandé d'utiliser des méthodes plus sélectives pour ignorer les erreurs d'une recette particulière. Voir Les erreurs dans les recettes.

.LOW_RESOLUTION_TIME

Si vous spécifiez des prérequis pour .LOW_RESOLUTION_TIME, make suppose que ces fichiers sont créés par des commandes qui produisent des horodatages de faible résolution. La recette de la cible .LOW_RESOLUTION_TIME est ignorée.

Les horodatages de fichiers à haute résolution dont disposent de nombreux systèmes de fichiers modernes réduisent le risque que make juge à tort un fichier à jour. Malheureusement, certains hôtes n'offrent aucun moyen de définir un horodatage de fichier à haute résolution, de sorte que les commandes qui fixent explicitement l'horodatage d'un fichier, comme « cp -p », doivent en abandonner la partie inférieure à la seconde. Un fichier créé par une telle commande devrait être mentionné comme prérequis de .LOW_RESOLUTION_TIME, afin que make ne le juge pas périmé à tort. Par exemple :

.LOW_RESOLUTION_TIME: dst
dst: src
        cp -p src dst

Comme « cp -p » abandonne la partie inférieure à la seconde de l'horodatage de src, dst est généralement légèrement plus ancien que src même lorsqu'il est à jour. La ligne .LOW_RESOLUTION_TIME fait que make considère dst comme à jour lorsque son horodatage se situe au début de la même seconde que celui de src.

En raison des limitations du format d'archive, les horodatages des membres d'archive sont toujours de faible résolution. Il n'est pas nécessaire de mentionner les membres d'archive comme prérequis de .LOW_RESOLUTION_TIME, car make le fait automatiquement.

.SILENT

Si vous spécifiez des prérequis pour .SILENT, make n'affiche pas, avant de l'exécuter, la recette utilisée pour refaire ces fichiers particuliers. La recette de .SILENT est ignorée.

Si elle est mentionnée comme cible sans prérequis, .SILENT indique de ne rien afficher avant d'exécuter les recettes. On peut aussi utiliser des méthodes plus sélectives pour rendre silencieuses seulement certaines lignes de commande d'une recette. Voir L'écho des recettes. Si vous voulez rendre silencieuses toutes les recettes d'une exécution particulière de make, utilisez l'option « -s » ou « --silent » (voir Résumé des options).

.EXPORT_ALL_VARIABLES

Mentionnée simplement comme cible, elle indique à make d'exporter par défaut toutes les variables vers les processus enfants. C'est une alternative à l'emploi de export sans argument. Voir Communiquer des variables à un sous-make.

.NOTPARALLEL

Si .NOTPARALLEL est mentionnée comme cible sans prérequis, toutes les cibles de cette invocation de make sont exécutées en série, même si l'option « -j » a été donnée. Les commandes make invoquées récursivement continuent d'exécuter leurs recettes en parallèle (à moins que leur makefile ne contienne lui aussi cette cible).

Si .NOTPARALLEL a des cibles comme prérequis, tous les prérequis de ces cibles sont exécutés en série. Cela revient à ajouter implicitement un .WAIT entre chacun des prérequis des cibles énumérées. Voir Désactiver l'exécution parallèle.

.ONESHELL

Si .ONESHELL est mentionnée comme cible, alors lors de la construction des cibles, toutes les lignes de la recette sont passées à une seule invocation du shell, au lieu d'être invoquées séparément. Voir L'exécution des recettes.

.POSIX

Si .POSIX est mentionnée comme cible, le makefile est analysé et exécuté en mode conforme à POSIX. Cela ne signifie pas que seuls les makefiles conformes à POSIX seront acceptés : toutes les fonctionnalités avancées de GNU make restent disponibles. Cette cible fait que make se comporte comme POSIX l'exige là où le comportement par défaut de make en diffère.

En particulier, lorsque cette cible est mentionnée, les recettes sont invoquées comme si le drapeau -e avait été passé au shell : la première commande qui échoue dans une recette fait échouer la recette immédiatement.

Les suffixes des règles implicites prédéfinies sont eux aussi considérés comme des cibles spéciales lorsqu'ils apparaissent comme cibles, de même que la concaténation de deux suffixes telle que « .c.o ». Ces cibles sont des règles de suffixe, l'ancienne manière de définir des règles implicites (encore largement utilisée). En principe, n'importe quel nom de cible peut recevoir une signification spéciale de cette manière, en le coupant en deux et en ajoutant les deux fragments à la liste des suffixes. En pratique, les suffixes commencent habituellement par « . », de sorte que ces noms de cibles spéciales commencent aussi par « . ». Voir Les anciennes règles de suffixe.


4.10 Plusieurs cibles dans une règle

Lorsqu'une règle explicite a plusieurs cibles, elles sont traitées de l'une des deux façons suivantes : soit comme des cibles indépendantes, soit comme des cibles groupées. Le traitement appliqué dépend du séparateur qui apparaît après la liste des cibles.

Règles à cibles indépendantes

Une règle qui utilise le séparateur de cibles standard « : » définit des cibles indépendantes. Cela équivaut à écrire la même règle une fois pour chaque cible, en répétant les prérequis et la recette. En général, la recette utilise des variables automatiques telles que « $@ » pour préciser quelle cible est en cours de construction.

Les règles à cibles indépendantes sont utiles dans deux cas :

Par ailleurs, de même que la variable « $@ » permet de faire varier la recette, on peut souhaiter faire varier les prérequis selon la cible. On ne peut pas le faire avec plusieurs cibles dans une règle ordinaire, mais on le peut avec une règle de motif statique. Voir Règles de motif statiques.

Règles à cibles groupées

Si vous disposez d'une recette qui produit plusieurs fichiers en une seule invocation, plutôt que des cibles indépendantes, vous pouvez exprimer cette relation en déclarant que la règle utilise des cibles groupées (grouped targets). Une règle à cibles groupées utilise le séparateur « &: » (où « & » suggère « toutes »).

Lorsque make construit l'une quelconque des cibles groupées, il comprend que toutes les autres cibles du groupe sont mises à jour par la même invocation de la recette. De plus, si seule une partie des cibles groupées est périmée ou manquante, make reconnaît que l'exécution de la recette mettra à jour toutes les cibles. Enfin, si l'une quelconque des cibles groupées est périmée, toutes les cibles groupées sont considérées comme périmées.

Par exemple, la règle suivante définit des cibles groupées :

foo bar biz &: baz boz
        echo $^ > foo
        echo $^ > bar
        echo $^ > biz

Pendant l'exécution de la recette d'une règle à cibles groupées, la variable automatique « $@ » est définie au nom de la cible particulière du groupe qui a déclenché la règle. Soyez prudent si vous vous reposez sur cette variable dans la recette d'une règle à cibles groupées.

Contrairement aux cibles indépendantes, une règle à cibles groupées doit comporter une recette. Cependant, une cible membre d'un groupe de cibles peut aussi apparaître dans la définition d'une règle à cibles indépendantes dépourvue de recette.

Une seule recette peut être associée à chaque cible. Si une cible groupée apparaît dans une règle à cibles indépendantes ou dans une autre règle à cibles groupées dotée d'une recette, un avertissement est émis et la recette de la seconde remplace celle de la première. De plus, cette cible est retirée du groupe précédent et n'apparaît plus que dans le nouveau groupe.

Si vous voulez qu'une cible figure dans plusieurs groupes, vous devez utiliser le séparateur de cibles groupées à double deux-points « &:: » lors de la déclaration de tous les groupes qui contiennent cette cible. Les cibles groupées à double deux-points sont traitées chacune indépendamment, et la recette de chaque règle à cibles groupées à double deux-points est exécutée au plus une fois, si au moins l'une de ses multiples cibles a besoin d'être mise à jour.


4.11 Plusieurs règles pour une cible

Un même fichier peut être la cible de plusieurs règles. Tous les prérequis mentionnés dans l'ensemble des règles sont réunis en une seule liste de prérequis de la cible. Si la cible est plus ancienne que l'un quelconque des prérequis de l'une quelconque des règles, la recette est exécutée.

Un seul fichier ne peut avoir qu'une seule recette à exécuter. Si plusieurs règles donnent une recette pour un même fichier, make utilise la dernière donnée et affiche un message d'erreur. (Dans le cas particulier où le nom du fichier commence par un point, le message d'erreur n'est pas affiché. Ce comportement étrange n'existe que par compatibilité avec d'autres implémentations de make ; vous devriez donc l'éviter.) Il arrive que l'on veuille déclencher plusieurs recettes définies à des endroits différents du makefile pour une même cible. Pour cela, on peut utiliser les règles à double deux-points (voir Règles à double deux-points).

Une règle supplémentaire ne comportant que des prérequis permet de donner d'un coup quelques prérequis supplémentaires à de nombreux fichiers. Par exemple, un makefile possède souvent une variable telle que objects qui contient la liste de tous les fichiers de sortie du compilateur dans le système en cours de construction. Une manière commode d'indiquer que tous ces fichiers doivent être recompilés si config.h change consiste à écrire :

objects = foo.o bar.o
foo.o : defs.h
bar.o : defs.h test.h
$(objects) : config.h

On peut insérer ou retirer une telle règle sans modifier celles qui spécifient réellement comment fabriquer les fichiers objets. C'est une forme commode lorsqu'on veut ajouter par intermittence des prérequis supplémentaires.

Autre astuce : on peut spécifier les prérequis supplémentaires au moyen d'une variable définie par un argument de la ligne de commande de make (voir Redéfinir des variables). Par exemple,

extradeps=
$(objects) : $(extradeps)

fait que la commande « make extradeps=foo.h » considère foo.h comme un prérequis de chaque fichier objet, tandis qu'un simple « make » ne le fait pas.

Si aucune des règles explicites d'une cible n'a de recette, make cherche à appliquer une règle implicite (voir Utilisation des règles implicites).


4.12 Règles de motif statiques

Les règles de motif statiques (static pattern rules) sont des règles qui spécifient plusieurs cibles et construisent le nom des prérequis de chaque cible à partir de son nom. Elles sont plus générales que les règles ordinaires à plusieurs cibles, car les cibles n'ont pas besoin d'avoir des prérequis identiques. Leurs prérequis doivent être analogues, mais pas nécessairement identiques.


4.12.1 Syntaxe des règles de motif statiques

La syntaxe d'une règle de motif statique est la suivante :

targets …: target-pattern: prereq-patternsrecipe

La liste de targets spécifie les cibles auxquelles la règle s'applique. Les cibles peuvent contenir des caractères génériques, comme les cibles des règles ordinaires (voir Utiliser des caractères génériques dans les noms de fichiers).

Le target-pattern et les prereq-patterns décrivent comment calculer les prérequis de chaque cible. Chaque cible est comparée au target-pattern, ce qui extrait une partie du nom de la cible, appelée le radical (stem). Ce radical est ensuite substitué dans chaque prereq-pattern pour produire un nom de prérequis (un par prereq-pattern).

Chaque motif contient normalement exactement un caractère « % ». Lorsque le target-pattern correspond à une cible, le « % » peut correspondre à n'importe quelle partie du nom de la cible ; cette partie est appelée le radical. Le reste du motif doit correspondre exactement. Par exemple, la cible foo.o correspond au motif « %.o », avec « foo » comme radical. Les cibles foo.c et foo.out ne correspondent pas à ce motif.

Le nom de prérequis de chaque cible est obtenu en remplaçant le « % » de chaque motif de prérequis par le radical. Par exemple, si un motif de prérequis est %.c, la substitution du radical « foo » donne le nom de prérequis foo.c. Il est permis d'écrire un motif de prérequis ne contenant pas de « % » ; ce prérequis est alors le même pour toutes les cibles.

Le caractère « % » d'une règle de motif peut être protégé en le faisant précéder d'une barre oblique inverse (« \ »). Les barres obliques inverses qui protégeraient sinon des caractères « % » peuvent elles-mêmes être protégées par des barres obliques inverses supplémentaires. Les barres obliques inverses qui protègent un caractère « % » ou une autre barre oblique inverse sont retirées avant que le motif ne soit comparé à un nom de fichier ou qu'un radical n'y soit substitué. En revanche, les barres obliques inverses qui ne risquent pas de protéger un caractère « % » sont laissées intactes. Par exemple, le motif the\%weird\\%pattern\\ a « the%weird\ » avant le caractère « % » effectif, puis « pattern\\ » à sa suite. Les deux dernières barres obliques inverses sont laissées telles quelles, car elles n'affectent aucun caractère « % ».

Voici un exemple qui compile chacun des fichiers foo.o et bar.o à partir du fichier .c correspondant :

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

Ici, « $< » est la variable automatique qui contient le nom du prérequis, et « $@ » la variable automatique qui contient le nom de la cible. Voir Variables automatiques.

Chaque cible spécifiée doit correspondre au motif de cible ; un avertissement est émis pour chaque cible qui n'y correspond pas. Si vous disposez d'une liste de fichiers dont seuls certains correspondent au motif, vous pouvez utiliser la fonction filter pour retirer les noms de fichiers qui ne correspondent pas (voir Fonctions de substitution et d'analyse de chaînes) :

files = foo.elc bar.o lose.o

$(filter %.o,$(files)): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
        emacs -f batch-byte-compile $<

Dans cet exemple, le résultat de « $(filter %.o,$(files)) » est bar.o lose.o, et la première règle de motif statique met à jour chacun de ces fichiers objets en compilant le fichier source C correspondant. Le résultat de « $(filter %.elc,$(files)) » est foo.elc, de sorte que ce fichier est fabriqué à partir de foo.el.

Voici un autre exemple, qui montre comment utiliser $* dans une règle de motif statique :

bigoutput littleoutput : %output : text.g
        generate text.g -$* > $@

Lorsque la commande generate est exécutée, $* se développe en le radical, soit « big », soit « little ».


4.12.2 Règles de motif statiques et règles implicites

Les règles de motif statiques ont beaucoup en commun avec les règles implicites définies sous forme de règles de motif (voir Définir et redéfinir les règles de motif). Les unes et les autres ont un motif pour la cible et des motifs pour construire les noms des prérequis. La différence tient à la manière dont make décide quand la règle s'applique.

Une règle implicite peut s'appliquer à toute cible correspondant à son motif, mais elle ne s'applique que si la cible n'a aucune recette spécifiée par ailleurs et si l'on peut trouver ses prérequis. Lorsque plusieurs règles implicites semblent applicables, une seule s'applique ; le choix dépend de l'ordre des règles.

À l'inverse, une règle de motif statique s'applique à la liste précise des cibles spécifiées dans la règle. Elle ne peut s'appliquer à aucune autre cible, et elle s'applique invariablement à chacune des cibles spécifiées. Si deux règles en conflit s'appliquent et que toutes deux comportent une recette, c'est une erreur.

La règle de motif statique peut être supérieure à la règle implicite pour les raisons suivantes :


4.13 Règles à double deux-points

Les règles à double deux-points (double-colon) sont des règles explicites écrites avec « :: » au lieu de « : » après le nom de la cible. Elles sont traitées différemment des règles ordinaires lorsqu'une même cible apparaît dans plusieurs règles. Les règles de motif à double deux-points ont une tout autre signification (voir Règles de motif correspondant à tout).

Lorsqu'une cible apparaît dans plusieurs règles, toutes ces règles doivent être du même type : toutes ordinaires, ou toutes à double deux-points. Dans le cas du double deux-points, chacune est indépendante des autres. La recette de chaque règle à double deux-points est exécutée si la cible est plus ancienne que l'un quelconque des prérequis de cette règle. Si la règle n'a pas de prérequis, sa recette est toujours exécutée (même si la cible existe déjà). Il peut donc en résulter qu'aucune, une partie ou la totalité des règles à double deux-points soient exécutées.

Les règles à double deux-points portant la même cible sont en fait totalement distinctes les unes des autres. Chaque règle à double deux-points est traitée séparément, exactement comme le seraient des règles portant sur des cibles différentes.

Les règles à double deux-points d'une cible sont exécutées dans l'ordre où elles apparaissent dans le makefile. Cela dit, les règles à double deux-points n'ont vraiment de sens que dans les cas où l'ordre d'exécution des recettes est sans importance.

Les règles à double deux-points sont assez obscures et rarement utiles. Elles fournissent un mécanisme pour les cas où la façon de mettre à jour une cible diffère selon le fichier prérequis qui a provoqué la mise à jour, mais de tels cas sont rares.

Chaque règle à double deux-points devrait spécifier une recette ; à défaut, une règle implicite applicable est utilisée le cas échéant. Voir Utilisation des règles implicites.


4.14 Générer les prérequis automatiquement

Dans le makefile d'un programme, beaucoup des règles à écrire ne font souvent qu'énoncer qu'un fichier objet dépend d'un fichier d'en-tête. Par exemple, si main.c utilise defs.h via #include, on écrira :

main.o: defs.h

Cette règle est nécessaire pour indiquer à make qu'il doit refaire main.o chaque fois que defs.h change. On voit que, pour un grand programme, il faudrait écrire des dizaines de telles règles dans le makefile. Et il faudrait veiller scrupuleusement à mettre à jour le makefile chaque fois qu'on ajoute ou supprime un #include.

Pour éviter cette corvée, la plupart des compilateurs C modernes écrivent ces règles à votre place, en examinant les lignes #include des fichiers source. Ils le font généralement au moyen de l'option « -M » du compilateur. Par exemple, la commande

cc -M main.c

produit la sortie :

main.o : main.c defs.h

Ainsi, vous n'avez plus à écrire vous-même toutes ces règles : le compilateur le fait pour vous.

Notez qu'une telle règle mentionne main.o dans le makefile, de sorte qu'il ne sera jamais considéré comme un fichier intermédiaire par la recherche de règles implicites. Cela signifie que make ne le supprimera jamais après l'avoir utilisé. Voir Chaînes de règles implicites.

Avec les anciens programmes make, l'usage traditionnel consistait à employer cette fonctionnalité du compilateur pour générer les prérequis à la demande, au moyen d'une commande comme « make depend ». Cette commande créait un fichier depend contenant tous les prérequis générés automatiquement ; le makefile n'avait plus qu'à le lire avec include (voir Inclure d'autres makefiles).

Avec GNU make, la possibilité de refaire les makefiles a rendu cette pratique obsolète : comme make régénère toujours un makefile périmé, il n'est plus nécessaire de lui demander explicitement de régénérer les prérequis. Voir Comment les makefiles sont refaits.

La pratique que nous recommandons pour la génération automatique des prérequis consiste à avoir un makefile par fichier source. Pour chaque fichier source name.c, on dispose d'un makefile name.d qui énumère les fichiers dont dépend le fichier objet name.o. Ainsi, on n'a besoin de réexaminer que les fichiers source qui ont changé pour refaire les nouveaux prérequis.

Voici une règle de motif qui génère un fichier de prérequis (c'est-à-dire un makefile) nommé name.d à partir d'un fichier source C name.c :

%.d: %.c
        @set -e; rm -f $@; \
         $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
         sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
         rm -f $@.$$$$

Pour la définition des règles de motif, voir Définir et redéfinir les règles de motif. Le drapeau « -e » passé au shell fait sortir celui-ci immédiatement si la commande $(CC) (ou toute autre commande) échoue (se termine avec un code de retour non nul).

Avec le compilateur C de GNU, vous préférerez peut-être utiliser le drapeau « -MM » plutôt que « -M » ; il omet les prérequis relatifs aux fichiers d'en-tête système. Pour plus de détails, voir Options de contrôle du préprocesseur dans Using GNU CC.

La commande sed a pour but de transformer (par exemple)

main.o : main.c defs.h

en

main.o main.d : main.c defs.h

Cela fait que chaque fichier « .d » dépend de tous les fichiers source et d'en-tête dont dépend le fichier « .o » correspondant. make sait alors qu'il doit régénérer les prérequis dès que l'un quelconque des fichiers source ou d'en-tête change.

Une fois définie la règle qui refait les fichiers « .d », on utilise la directive include pour les lire tous. Voir Inclure d'autres makefiles. Par exemple :

sources = foo.c bar.c

include $(sources:.c=.d)

(Cet exemple utilise une référence de substitution pour transformer la liste des fichiers source « foo.c bar.c » en la liste des makefiles de prérequis « foo.d bar.d ». Pour plus de détails sur les références de substitution, voir Références de substitution.) Comme les fichiers « .d » sont des makefiles comme les autres, make les refait au besoin sans que vous ayez à intervenir. Voir Comment les makefiles sont refaits.

Notez que, comme les fichiers « .d » contiennent des définitions de cibles, vous devez veiller à placer la directive include après le premier but par défaut du makefile ; sinon, un fichier objet quelconque risquerait de devenir le but par défaut. Voir Comment make traite un Makefile.


[N.d.T.] À propos de la note de bas de page (2) du texte. La note de l'original indique que « certaines anciennes versions de GNU make ne triaient pas le résultat de l'expansion des caractères génériques ». Dans les versions actuelles de GNU make, le résultat est trié pour chaque expression générique.


Précédent | Suivant | Sommaire | Version originale anglaise (gnu.org)