La recette (recipe) d'une règle (le contenu exécuté par la règle) se compose d'une ou de plusieurs lignes de commandes shell à exécuter. Ces commandes sont exécutées une par une, dans l'ordre où elles apparaissent. Normalement, le résultat de l'exécution de ces commandes est que la cible de la règle est mise à jour.
Les utilisateurs emploient des programmes shell très divers, mais les recettes des makefiles sont toujours interprétées par /bin/sh, sauf si le makefile en décide autrement. Voir L'exécution des recettes.
Les makefiles ont une propriété un peu inhabituelle : un même fichier contient en réalité deux syntaxes distinctes. La majeure partie du makefile utilise la syntaxe de make (voir Écrire des makefiles). Les recettes, en revanche, sont destinées à être interprétées par le shell et s'écrivent donc avec la syntaxe du shell. Le programme make ne cherche pas à comprendre la syntaxe du shell : il effectue seulement quelques transformations très précises sur le contenu de la recette avant de le transmettre au shell.
Chaque ligne de la recette doit commencer par une tabulation (ou par le caractère défini dans la variable .RECIPEPREFIX ; voir Autres variables spéciales). Seule exception : la première ligne de la recette peut être accolée à la ligne de la cible et des prérequis (prerequisite), séparée par un point-virgule. Toute ligne du makefile qui commence par une tabulation et apparaît dans un « contexte de règle » (c'est-à-dire après le début d'une règle et jusqu'à ce qu'une autre règle ou une définition de variable apparaisse) est considérée comme faisant partie de la recette de cette règle. Des lignes vides ou ne contenant que des commentaires peuvent apparaître entre les lignes de recette ; elles sont ignorées.
Ces règles ont notamment les conséquences suivantes :
make : il est transmis tel quel au shell. Que le shell le traite comme un commentaire ou non dépend de votre shell.
make, et elle est transmise au shell.
ifdef, ifeq, etc. ; voir La syntaxe des conditionnelles) dans un « contexte de règle » dont le premier caractère de la ligne est une tabulation est considérée comme faisant partie d'une recette et transmise au shell.
L'une des rares situations où make interprète réellement les recettes est la vérification de la présence d'une barre oblique inverse (backslash) juste avant le saut de ligne. Comme dans la syntaxe habituelle des makefiles, une ligne de recette logique peut être découpée en plusieurs lignes physiques dans le makefile en plaçant une barre oblique inverse avant chaque saut de ligne. Une telle suite de lignes est considérée comme une seule ligne de recette, et une seule instance du shell est lancée pour l'exécuter.
Cependant, contrairement à ce qui se passe ailleurs dans un makefile (voir Découper les longues lignes), les paires barre oblique inverse/saut de ligne ne sont pas supprimées de la recette. La barre oblique inverse comme le saut de ligne sont tous deux conservés et transmis au shell. La façon dont la paire barre oblique inverse/saut de ligne est interprétée dépend de votre shell. Si le premier caractère de la ligne suivant la paire barre oblique inverse/saut de ligne est le caractère de préfixe de recette (une tabulation par défaut ; voir Autres variables spéciales), alors ce caractère (et seulement ce caractère) est supprimé. Aucun espace n'est jamais ajouté à la recette.
Par exemple, considérons la recette de la cible all dans ce makefile :
all :
@echo no\
space
@echo no\
space
@echo one \
space
@echo one\
space
Elle se compose de quatre commandes shell distinctes, dont la sortie est la suivante :
nospace nospace one space one space
Comme exemple un peu plus complexe, voyons ce makefile :
all : ; @echo 'hello \
world' ; echo "hello \
world"
Il lance une seule fois le shell avec la commande suivante :
echo 'hello \
world' ; echo "hello \
world"
Puis, selon les règles de citation (quoting) du shell, on obtient la sortie suivante :
hello \ world hello world
Remarquez que la paire barre oblique inverse/saut de ligne a été supprimée à l'intérieur de la chaîne entre guillemets doubles ("…"), mais pas à l'intérieur de la chaîne entre guillemets simples ('…'). C'est ainsi que le shell par défaut (/bin/sh) traite les paires barre oblique inverse/saut de ligne. Si vous indiquez un autre shell dans vos makefiles, le traitement peut être différent.
Il arrive que vous souhaitiez découper une longue ligne à l'intérieur de guillemets simples, sans pour autant que la paire barre oblique inverse/saut de ligne apparaisse dans le contenu cité. C'est souvent le cas lorsqu'on transmet des scripts à des langages comme Perl, où des barres obliques inverses superflues dans le script peuvent en changer le sens, voire provoquer une erreur de syntaxe. Une manière simple de gérer cela consiste à placer la chaîne citée, ou même la commande entière, dans une variable make, puis à utiliser cette variable dans la recette. Dans ce cas, ce sont les règles de citation des sauts de ligne des makefiles qui s'appliquent, et la paire barre oblique inverse/saut de ligne est supprimée. Si nous réécrivons l'exemple ci-dessus de cette manière :
HELLO = 'hello \ world' all : ; @echo $(HELLO)
nous obtenons une sortie comme celle-ci :
hello world
Si vous le préférez, vous pouvez aussi utiliser des variables propres à une cible (voir Valeurs des variables propres à une cible) pour obtenir une correspondance plus étroite entre la variable et la recette qui l'utilise.
L'autre traitement que make applique aux recettes est l'expansion des références de variables qu'elles contiennent (voir Les bases des références de variables). Cela se produit après que make a fini de lire tous les makefiles et a déterminé que la cible est périmée (qu'elle doit être mise à jour) ; ainsi, les recettes des cibles qui ne sont pas reconstruites ne sont jamais développées.
Les références de variables et de fonctions dans les recettes ont exactement la même syntaxe et la même signification que les références ailleurs dans le makefile. Elles obéissent aussi aux mêmes règles de citation : si vous voulez qu'un signe dollar apparaisse dans votre recette, vous devez le doubler (« $$ »). Pour les shells comme le shell par défaut, qui utilisent le signe dollar pour introduire des variables, il importe de bien distinguer dans votre esprit si la variable que vous voulez référencer est une variable make (utilisez un seul signe dollar) ou une variable du shell (utilisez deux signes dollar). Par exemple :
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
Cela aboutit à ce que la commande suivante soit transmise au shell :
for i in one two three; do \
echo $i; \
done
ce qui produit le résultat attendu :
one two three
Normalement, make affiche chaque ligne de la recette avant de l'exécuter. Nous appelons cela l'écho (echo), car cela donne l'impression que vous tapez vous-même ces lignes.
Lorsqu'une ligne commence par « @ », l'écho de cette ligne est supprimé. Le « @ » est retiré avant que la ligne soit transmise au shell. On l'utilise typiquement pour une commande dont le seul effet est d'afficher quelque chose, comme une commande echo qui signale la progression dans le makefile :
@echo About to make distribution files
Quand on donne à make le drapeau « -n » ou « --just-print », il se contente d'afficher l'écho de la plupart des recettes, sans les exécuter (voir Résumé des options). Dans ce cas, même les lignes de recette commençant par « @ » sont affichées. Ce drapeau est utile pour découvrir quelles recettes make juge nécessaires sans réellement les exécuter.
Le drapeau « -s » ou « --silent » de make empêche tout écho, comme si toutes les recettes commençaient par « @ ». Une règle dans le makefile pour la cible spéciale .SILENT sans prérequis a le même effet (voir Noms de cibles intégrées spéciales).
Lorsque vient le moment d'exécuter les recettes pour mettre à jour une cible, chaque ligne de la recette est exécutée en lançant un nouveau sous-shell, sauf si la cible spéciale .ONESHELL est active (voir Utiliser un seul shell). (En pratique, make peut prendre des raccourcis qui n'affectent pas les résultats.)
Attention : cela implique que définir des variables du shell ou lancer des commandes shell comme cd, qui établissent un contexte local à chaque processus, n'a aucun effet sur les lignes suivantes de la recette. (N.d.T. : comme cd agit dans chaque sous-shell de façon indépendante, son effet n'est pas transmis à la ligne suivante.) Si vous voulez que cd affecte l'instruction suivante, placez les deux instructions sur une même ligne de recette. make lancera alors un seul shell pour exécuter toute la ligne, et le shell exécutera les instructions l'une après l'autre. Par exemple :
foo : bar/lose
cd $(<D) && gobble $(<F) > ../$@
Ici nous utilisons l'opérateur ET (AND) du shell (&&) afin que, si la commande cd échoue, le script échoue sans tenter de lancer la commande gobble dans le mauvais répertoire, ce qui pourrait causer des problèmes (dans ce cas, cela tronquerait à coup sûr ../foo, au minimum).
Il arrive que vous préfériez que toutes les lignes de la recette soient transmises à une seule invocation du shell. Deux situations le justifient généralement : d'une part, cela peut améliorer les performances dans les makefiles où les recettes comportent de nombreuses lignes de commandes, en évitant des processus superflus. D'autre part, vous pourriez vouloir inclure des sauts de ligne dans la commande de votre recette (par exemple si vous utilisez comme SHELL un interpréteur très différent). Si la cible spéciale .ONESHELL apparaît quelque part dans le makefile, alors toutes les lignes de recette de chaque cible sont fournies à une seule invocation du shell. Les sauts de ligne entre les lignes de recette sont conservés. Par exemple :
.ONESHELL:
foo : bar/lose
cd $(<D)
gobble $(<F) > ../$@
Cela fonctionnerait désormais comme prévu, même si les commandes sont sur des lignes de recette différentes.
Si .ONESHELL est indiqué, seule la première ligne de la recette est examinée à la recherche des caractères de préfixe spéciaux (« @ », « - » et « + »). Les lignes suivantes conserveront ces caractères spéciaux dans la ligne de recette lorsque le SHELL est invoqué. Si vous voulez que votre recette commence par l'un de ces caractères spéciaux, vous devrez faire en sorte qu'ils ne soient pas les premiers caractères de la première ligne, par exemple en ajoutant un commentaire ou quelque chose de similaire. Ainsi, l'exemple suivant serait une erreur de syntaxe en Perl, car le premier « @ » est retiré par make :
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
@f = qw(a b c);
print "@f\n";
En revanche, chacune de ces deux variantes fonctionnerait correctement :
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
# Make sure "@" is not the first character on the first line
@f = qw(a b c);
print "@f\n";
ou
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
my @f = qw(a b c);
print "@f\n";
Comme fonctionnalité spéciale, si SHELL est reconnu comme un shell de style POSIX, les caractères de préfixe spéciaux des lignes de recette « internes » (à partir de la deuxième) sont retirés avant que la recette ne soit traitée. Cette fonctionnalité vise à permettre aux makefiles existants d'ajouter la cible spéciale .ONESHELL tout en continuant à fonctionner correctement sans modifications importantes. Comme ces caractères de préfixe spéciaux ne sont pas autorisés en début de ligne dans un script shell POSIX, il n'y a là aucune perte de fonctionnalité. Par exemple, ceci fonctionne comme prévu :
.ONESHELL:
foo : bar/lose
@cd $(@D)
@gobble $(@F) > ../$@
Néanmoins, même avec cette fonctionnalité spéciale, les makefiles utilisant .ONESHELL se comporteront différemment, parfois de manière notable. Par exemple, normalement, si une ligne quelconque de la recette échoue, la règle échoue et aucune autre ligne de recette n'est traitée. Sous .ONESHELL, un échec sur toute ligne autre que la dernière ne sera pas remarqué par make. Vous pouvez modifier .SHELLFLAGS pour ajouter l'option -e au shell, ce qui fera échouer le shell à tout échec survenant n'importe où dans la ligne de commande, mais cela peut en soi modifier le comportement de votre recette. En fin de compte, vous pourriez devoir renforcer vos lignes de recette pour qu'elles fonctionnent avec .ONESHELL.
Le programme utilisé comme shell est tiré de la variable SHELL. Si cette variable n'est pas définie dans votre makefile, c'est le programme /bin/sh qui est utilisé comme shell. Le ou les arguments passés au shell sont tirés de la variable .SHELLFLAGS. La valeur par défaut de .SHELLFLAGS est -c en temps normal, ou -ec en mode conforme POSIX.
Contrairement à la plupart des variables, la variable SHELL n'est jamais définie à partir de l'environnement. Cela tient au fait que la variable d'environnement SHELL sert à indiquer votre choix personnel de programme shell pour un usage interactif. Il serait très fâcheux que des choix personnels de ce genre influent sur le fonctionnement des makefiles. Voir Variables issues de l'environnement.
De plus, lorsque vous définissez SHELL dans votre makefile, cette valeur n'est pas exportée dans l'environnement vers les lignes de recette que make invoque. C'est plutôt la valeur héritée de l'environnement de l'utilisateur, s'il y en a une, qui est exportée. Vous pouvez modifier ce comportement en exportant explicitement SHELL (voir Communiquer des variables à un sous-make), ce qui le force à être transmis dans l'environnement aux lignes de recette.
Toutefois, sous MS-DOS et MS-Windows, la valeur de SHELL présente dans l'environnement est utilisée, car sur ces systèmes la plupart des utilisateurs ne définissent pas cette variable, et il est donc très probable qu'elle ait été définie spécifiquement pour être utilisée par make. Sous MS-DOS, si le réglage de SHELL ne convient pas à make, vous pouvez définir la variable MAKESHELL sur le shell que make doit utiliser ; si elle est définie, c'est elle qui sera utilisée comme shell à la place de la valeur de SHELL.
Choisir un shell sous MS-DOS et MS-Windows est bien plus complexe que sur les autres systèmes.
Sous MS-DOS, si SHELL n'est pas défini, c'est la valeur de la variable COMSPEC (qui est toujours définie) qui est utilisée à la place.
Le traitement des lignes qui définissent la variable SHELL dans les makefiles diffère sous MS-DOS. Le shell standard, command.com, est d'une pauvreté fonctionnelle risible, et de nombreux utilisateurs de make ont tendance à installer un shell de remplacement. C'est pourquoi, sous MS-DOS, make examine la valeur de SHELL et adapte son comportement selon qu'elle désigne un shell de style Unix ou un shell de style DOS. Cela permet de disposer de fonctionnalités raisonnables même si SHELL désigne command.com.
Si SHELL désigne un shell de style Unix, make sous MS-DOS vérifie en outre que ce shell peut effectivement être trouvé ; sinon, il ignore la ligne qui définit SHELL. Sous MS-DOS, GNU make recherche le shell aux endroits suivants :
SHELL. Par exemple, si le makefile précise « SHELL = /bin/sh », make cherchera dans le répertoire /bin du lecteur courant.
PATH, dans l'ordre.
Dans chaque répertoire qu'il examine, make cherche d'abord le fichier exact (sh dans l'exemple ci-dessus). S'il ne le trouve pas, il cherche aussi dans ce répertoire ce fichier muni de l'une des extensions connues qui identifient les fichiers exécutables. Par exemple .exe, .com, .bat, .btm, .sh, et quelques autres.
Si l'une de ces tentatives réussit, la valeur de SHELL est fixée au chemin d'accès complet du shell ainsi trouvé. En revanche, si aucun n'est trouvé, la valeur de SHELL n'est pas modifiée, et la ligne qui la définit est donc de fait ignorée. Il en est ainsi pour que make ne prenne en charge les fonctionnalités propres à un shell de style Unix que si un tel shell est réellement installé sur le système où make s'exécute.
Notez que cette recherche étendue du shell se limite aux cas où SHELL est défini depuis le makefile ; s'il est défini dans l'environnement ou sur la ligne de commande, on attend de vous que vous lui donniez le chemin d'accès complet du shell, exactement comme sous Unix.
Conséquence du traitement spécifique à DOS décrit ci-dessus : un makefile contenant « SHELL = /bin/sh » (comme le font de nombreux makefiles Unix) fonctionnera sous MS-DOS sans modification si, par exemple, vous avez installé sh.exe dans un répertoire de votre PATH.
GNU make sait exécuter plusieurs recettes à la fois. Normalement, make n'exécute qu'une seule recette à la fois et attend qu'elle se termine avant d'exécuter la suivante. Cependant, l'option « -j » ou « --jobs » indique à make d'exécuter de nombreuses recettes simultanément. Vous pouvez aussi empêcher le parallélisme pour certaines cibles, ou pour toutes, depuis le makefile (voir Désactiver l'exécution parallèle).
Sous MS-DOS, l'option « -j » est sans effet, car ce système ne prend pas en charge le multi-traitement.
Si l'option « -j » est suivie d'un entier, c'est le nombre de recettes à exécuter en même temps ; on l'appelle le nombre d'emplacements de tâches (job slots). Si rien ne ressemble à un entier après l'option « -j », le nombre d'emplacements de tâches est illimité. Le nombre par défaut d'emplacements de tâches est de un, ce qui signifie une exécution séquentielle (une chose à la fois).
La gestion des invocations récursives de make soulève des questions pour l'exécution parallèle. Pour en savoir plus à ce sujet, voir Communiquer des options à un sous-make.
Si une recette échoue (tuée par un signal ou terminée avec un code de retour non nul) et que les erreurs ne sont pas ignorées pour cette recette (voir Les erreurs dans les recettes), les lignes de recette restantes pour régénérer la même cible ne sont pas exécutées. Si une recette échoue et que l'option « -k » ou « --keep-going » n'a pas été donnée (voir Résumé des options), make interrompt l'exécution. Si make se termine pour une raison quelconque (y compris un signal) alors que des processus enfants sont encore en cours, il attend qu'ils se terminent avant de quitter réellement.
Lorsque le système est très chargé, vous voudrez probablement exécuter moins de tâches que lorsqu'il l'est peu. Vous pouvez utiliser l'option « -l » pour indiquer à make de limiter le nombre de tâches exécutées à la fois, en fonction de la charge moyenne (load average). L'option « -l » ou « --max-load » est suivie d'un nombre à virgule flottante. Par exemple,
-l 2.5
empêche make de démarrer plus d'une tâche tant que la charge moyenne dépasse 2,5. L'option « -l » sans nombre à sa suite supprime la limite de charge, si une limite avait été donnée par une option « -l » précédente.
Plus précisément, lorsque make s'apprête à démarrer une tâche et qu'au moins une tâche est déjà en cours, il vérifie la charge moyenne actuelle ; si elle n'est pas inférieure à la limite donnée par « -l », make attend que la charge moyenne passe sous cette limite, ou que toutes les autres tâches se terminent.
Par défaut, il n'y a aucune limite de charge.
Si un makefile définit de façon complète et exacte les relations de dépendance entre toutes ses cibles, alors make construira correctement les buts, que l'exécution parallèle soit activée ou non. C'est la manière idéale d'écrire les makefiles.
Cependant, il arrive que certaines cibles d'un makefile, ou toutes, ne puissent pas s'exécuter en parallèle et qu'il ne soit pas réaliste d'ajouter les prérequis nécessaires pour en informer make. Dans ce cas, le makefile peut recourir à diverses méthodes pour désactiver l'exécution parallèle.
Si la cible spéciale .NOTPARALLEL sans prérequis est indiquée quelque part, alors l'instance entière de make s'exécute de façon séquentielle, quel que soit le réglage du parallélisme. Par exemple :
all: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL:
Dans ce cas, quelle que soit la façon dont make est invoqué, les cibles one, two et three s'exécutent de façon séquentielle.
Si la cible spéciale .NOTPARALLEL possède des prérequis, alors chacun de ces prérequis est considéré comme une cible et tous les prérequis de ces cibles s'exécutent de façon séquentielle. Notez que les prérequis ne s'exécutent séquentiellement que lors de la construction de cette cible : si une autre cible énumère les mêmes prérequis sans être visée par .NOTPARALLEL, ces prérequis peuvent s'exécuter en parallèle. Par exemple :
all: base notparallel base: one two three notparallel: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL: notparallel
Ici, « make -j base » exécute les cibles one, two et three en parallèle, tandis que « make -j notparallel » les exécute de façon séquentielle. Si vous lancez « make -j all », elles seront exécutées en parallèle, car base les énumère comme prérequis sans être sérialisée.
La cible .NOTPARALLEL ne devrait pas avoir de commandes.
Enfin, vous pouvez contrôler finement la sérialisation de prérequis précis à l'aide de la cible spéciale .WAIT. Lorsque cette cible apparaît dans une liste de prérequis et que l'exécution parallèle est activée, make ne construit aucun des prérequis situés à droite de .WAIT tant que tous les prérequis situés à gauche de .WAIT ne sont pas terminés. Par exemple :
all: one two .WAIT three one two three: ; @sleep 1; echo $@
Si l'exécution parallèle est activée, make essaie de construire one et two en parallèle, mais n'entreprend pas la construction de three avant que les deux soient terminés.
Comme pour les cibles données à .NOTPARALLEL, .WAIT ne prend effet que lors de la construction de la cible dans la liste de prérequis de laquelle il figure. Si les mêmes prérequis sont présents dans d'autres cibles, sans .WAIT, ils peuvent malgré tout s'exécuter en parallèle. De ce fait, ni .NOTPARALLEL avec des cibles ni .WAIT ne sont aussi fiables pour contrôler l'exécution parallèle que la définition d'une relation de prérequis. Ils sont néanmoins simples à utiliser et peuvent suffire dans les situations peu complexes.
Le prérequis .WAIT n'apparaît dans aucune des variables automatiques de la règle.
Pour des raisons de portabilité, vous pouvez créer une véritable cible .WAIT dans votre makefile, mais ce n'est pas obligatoire pour utiliser cette fonctionnalité. Si une cible .WAIT est créée, elle ne devrait avoir ni prérequis ni commandes.
La fonctionnalité .WAIT est également implémentée dans d'autres versions de make et elle est spécifiée dans la norme POSIX de make.
Lorsque plusieurs recettes s'exécutent en parallèle, la sortie de chaque recette apparaît dès qu'elle est produite, si bien que les messages provenant de recettes différentes peuvent s'entremêler, parfois même apparaître sur la même ligne. Cela peut rendre la lecture de la sortie très difficile.
Pour l'éviter, vous pouvez utiliser l'option « --output-sync » (« -O »). Cette option demande à make de mettre de côté la sortie des commandes qu'il invoque et de tout afficher une fois les commandes terminées. De plus, s'il y a plusieurs invocations récursives de make qui s'exécutent en parallèle, elles communiquent pour qu'une seule d'entre elles produise de la sortie à la fois.
Si l'affichage du répertoire de travail est activé (voir L'option « --print-directory »), les messages d'entrée/sortie sont affichés autour de chaque groupe de sortie. Si vous préférez ne pas voir ces messages, ajoutez l'option « --no-print-directory » à MAKEFLAGS.
La synchronisation de la sortie comporte quatre niveaux de granularité, que l'on indique en donnant un argument à l'option (par exemple « -Oline » ou « --output-sync=recurse »).
noneC'est la valeur par défaut : toute la sortie est envoyée directement telle qu'elle est produite, sans aucune synchronisation.
lineLa sortie de chaque ligne de la recette est regroupée et affichée dès que cette ligne est terminée. Si une recette comporte plusieurs lignes, elles peuvent s'entremêler avec les lignes d'autres recettes.
targetLa sortie de la recette entière de chaque cible est regroupée et affichée une fois la cible terminée. C'est la valeur par défaut si l'option --output-sync ou -O est donnée sans argument.
recurseLa sortie de chaque invocation récursive de make est regroupée et affichée une fois l'invocation récursive terminée.
Quel que soit le mode choisi, la durée totale de la construction reste la même. Seule diffère la façon dont la sortie apparaît.
Les modes « target » et « recurse » recueillent tous deux la sortie de la recette entière d'une cible et l'affichent sans interruption lorsque la recette se termine. La différence entre eux réside dans la façon dont sont traitées les recettes contenant des invocations récursives de make (voir L'utilisation récursive de make). Pour toutes les recettes qui n'ont pas de ligne récursive, les modes « target » et « recurse » se comportent de manière identique.
Si le mode « recurse » est choisi, les recettes contenant des invocations récursives de make sont traitées comme les autres cibles : la sortie de la recette, y compris celle du make récursif, est mise de côté et affichée une fois la recette entière terminée. Cela garantit que la sortie de toutes les cibles construites par une instance récursive donnée de make est regroupée, ce qui peut faciliter la compréhension de la sortie. Mais cela conduit aussi à de longues périodes, pendant la construction, où l'on ne voit aucune sortie, suivies de grandes rafales de sortie. Si vous ne suivez pas la construction au fil de son déroulement, mais consultez plutôt un journal de la construction après coup, c'est peut-être la meilleure option pour vous.
Si vous suivez la sortie en direct, les longs silences pendant la construction peuvent être frustrants. Le mode de synchronisation de sortie « target » détecte quand make est sur le point d'être invoqué récursivement, par les méthodes standard, et ne synchronise pas la sortie de ces lignes. Le make récursif effectue la synchronisation pour ses propres cibles, et la sortie de chacune est affichée immédiatement à son achèvement. Attention : la sortie des lignes récursives de la recette n'est pas synchronisée (par exemple, si la ligne récursive affiche un message avant de lancer make, ce message ne sera pas synchronisé).
Le mode « line » peut être utile aux interfaces (front-ends) qui surveillent la sortie de make pour suivre le début et la fin des recettes.
Certains programmes invoqués par make se comportent différemment selon qu'ils déterminent écrire leur sortie vers un terminal ou vers un fichier (ce que l'on décrit souvent comme les modes « interactif » et « non interactif »). Par exemple, de nombreux programmes capables de colorer leur sortie ne le font pas s'ils déterminent qu'ils n'écrivent pas vers un terminal. Si votre makefile invoque un tel programme, l'utilisation des options de synchronisation de sortie amènera le programme à croire qu'il s'exécute en mode « non interactif », alors même que la sortie finira par parvenir au terminal.
Deux processus ne peuvent pas prendre leur entrée du même périphérique en même temps. Pour s'assurer qu'une seule recette à la fois tente de prendre son entrée depuis le terminal, make invalide les flux d'entrée standard de toutes les recettes en cours sauf une. Si une autre recette tente de lire depuis l'entrée standard, elle subira généralement une erreur fatale (un signal « Broken pipe »).
Il est impossible de prévoir quelle recette disposera d'un flux d'entrée standard valide (qui proviendra du terminal, ou de l'endroit vers lequel vous avez redirigé l'entrée standard de make). La première recette exécutée l'obtient toujours en premier, puis la première recette démarrée après l'achèvement de celle-ci l'obtient ensuite, et ainsi de suite.
Nous modifierons le fonctionnement de cet aspect de make si nous trouvons une meilleure solution. En attendant, si vous utilisez la fonctionnalité d'exécution parallèle, vous ne devriez compter sur aucune recette utilisant l'entrée standard ; en revanche, si vous n'utilisez pas cette fonctionnalité, l'entrée standard fonctionne normalement dans toutes les recettes.
Chaque fois qu'une invocation du shell retourne, make examine son code de retour. Si le shell s'est terminé avec succès (code de retour nul), la ligne suivante de la recette est exécutée dans un nouveau shell ; une fois la dernière ligne terminée, la règle est achevée.
S'il y a eu une erreur (code de retour non nul), make abandonne la règle en cours, et éventuellement toutes les règles.
L'échec d'une certaine ligne de recette n'indique pas toujours un problème. Par exemple, vous pouvez utiliser la commande mkdir pour vous assurer qu'un répertoire existe. Si le répertoire existe déjà, mkdir signalera une erreur, mais vous voulez probablement que make continue malgré tout.
Pour ignorer les erreurs d'une ligne de recette, écrivez un « - » au début du texte de la ligne (après la tabulation initiale). Le « - » est retiré avant que la ligne soit transmise au shell pour exécution.
Par exemple,
clean:
-rm -f *.o
Cela permet à make de continuer même si rm ne parvient pas à supprimer un fichier.
Lorsque vous exécutez make avec le drapeau « -i » ou « --ignore-errors », les erreurs sont ignorées dans toutes les recettes de toutes les règles. Une règle dans le makefile pour la cible spéciale .IGNORE a le même effet, si elle n'a pas de prérequis. C'est moins souple, mais parfois utile.
Lorsque les erreurs doivent être ignorées, du fait d'un « - » ou du drapeau « -i », make traite un retour en erreur exactement comme un succès, à ceci près qu'il affiche un message indiquant le code de retour avec lequel le shell s'est terminé et signale que l'erreur a été ignorée.
Lorsqu'une erreur que l'on n'a pas demandé à make d'ignorer se produit, cela signifie que la cible en cours ne peut pas être correctement régénérée, et qu'aucune autre cible qui en dépend, directement ou indirectement, ne le peut non plus. Aucune autre recette ne sera exécutée pour ces cibles, puisque leurs préconditions n'ont pas été remplies.
Normalement, dans cette situation, make abandonne aussitôt en renvoyant un code non nul. Cependant, si le drapeau « -k » ou « --keep-going » est indiqué, make continue d'examiner les autres prérequis des cibles en attente, et les régénère si nécessaire, avant d'abandonner et de renvoyer un code non nul. Par exemple, après une erreur lors de la compilation d'un fichier objet, « make -k » poursuivra la compilation des autres fichiers objets, même s'il sait déjà qu'il sera impossible de les lier. Voir Résumé des options.
Le comportement habituel suppose que votre objectif est de mettre à jour les cibles indiquées ; dès que make apprend que c'est impossible, autant signaler l'échec immédiatement. L'option « -k » indique que le but réel est de tester autant que possible les modifications apportées au programme (sans doute pour repérer plusieurs problèmes indépendants et les corriger tous avant la prochaine tentative de compilation). C'est la raison pour laquelle la commande compile d'Emacs passe le drapeau « -k » par défaut.
Habituellement, lorsqu'une ligne de recette échoue, si elle a si peu que ce soit modifié le fichier cible, ce fichier est corrompu et inutilisable (ou du moins il n'est pas complètement mis à jour). Pourtant, l'horodatage du fichier indique qu'il est désormais à jour, de sorte que, la prochaine fois que make s'exécutera, il n'essaiera pas de le mettre à jour. La situation est exactement la même que lorsque le shell est tué par un signal ; voir Interrompre ou tuer make. Aussi, la bonne chose à faire en général est de supprimer le fichier cible si la recette échoue après avoir commencé à modifier le fichier. make le fera si .DELETE_ON_ERROR apparaît comme cible. C'est presque toujours ce que vous attendez de make, mais ce n'est pas l'usage historique ; aussi, pour des raisons de compatibilité, vous devez le demander explicitement.
Si make reçoit un signal fatal pendant qu'un shell s'exécute, il peut supprimer le fichier cible que la recette était censée mettre à jour. Cela se produit si la date de dernière modification du fichier cible a changé depuis que make l'a vérifiée pour la première fois.
Le but de la suppression de la cible est de garantir qu'elle sera reconstruite de zéro lors de la prochaine exécution de make. Pourquoi cela ? Supposons que vous tapiez Ctrl-c pendant qu'un compilateur s'exécute, juste au moment où il commence à écrire un fichier objet foo.o. Le Ctrl-c tue le compilateur, ce qui laisse un fichier incomplet dont la date de dernière modification est plus récente que celle du fichier source foo.c. Mais make reçoit lui aussi le signal Ctrl-c et supprime ce fichier incomplet. Si make ne le faisait pas, la prochaine invocation de make jugerait que foo.o n'a pas besoin d'être mis à jour, ce qui conduirait l'éditeur de liens à essayer de lier un fichier objet à moitié manquant et à produire un étrange message d'erreur.
Pour empêcher ce genre de suppression d'un fichier cible, faites en sorte que la cible spéciale .PRECIOUS dépende de ce fichier. Avant de régénérer une cible, make vérifie si elle figure parmi les prérequis de .PRECIOUS et décide ainsi s'il doit la supprimer en cas de signal. Vous pourriez vouloir cela parce que la cible est mise à jour d'une manière atomique (indivisible), ou parce qu'elle n'existe que pour enregistrer une date de modification (son contenu importe peu), ou encore parce qu'elle doit toujours exister afin d'éviter d'autres types de problèmes.
make fait de son mieux pour nettoyer, mais il existe des situations où le nettoyage est impossible. Par exemple, make peut être tué par un signal qu'il ne peut pas intercepter. Ou bien l'un des programmes que make a lancés peut être tué ou planter, laissant un fichier cible corrompu mais à jour du point de vue de l'horodatage ; dans ce cas, make ne s'aperçoit pas que cet échec nécessite un nettoyage de la cible. Ou encore, make lui-même peut rencontrer un bogue et planter.
Pour ces raisons, le mieux est d'écrire des recettes défensives (defensive recipes). Une recette défensive ne laisse pas de cible corrompue, même en cas d'échec. L'approche la plus courante consiste à ne pas mettre à jour la cible directement, mais à créer un fichier temporaire, puis à renommer ce fichier temporaire en nom de cible final. Certains compilateurs fonctionnent déjà ainsi ; dans ce cas, il n'est pas nécessaire d'écrire une recette défensive.
L'utilisation récursive de make consiste à employer make comme commande à l'intérieur d'un makefile. Cette technique est utile lorsque vous voulez disposer de makefiles distincts pour les différents sous-systèmes qui composent un système plus vaste. Par exemple, supposons que vous ayez un sous-répertoire subdir doté de son propre makefile, et que vous vouliez exécuter make sur ce sous-répertoire depuis le makefile du répertoire qui le contient. On peut le faire en écrivant ceci :
subsystem:
cd subdir && $(MAKE)
Ou bien l'écriture équivalente suivante conviendrait aussi (voir Résumé des options) :
subsystem:
$(MAKE) -C subdir
Vous pouvez écrire une commande make récursive en recopiant simplement cet exemple, mais il y a beaucoup à savoir sur la façon dont ces commandes fonctionnent et pourquoi, ainsi que sur la relation entre le sous-make et le make de premier niveau. Il peut aussi être utile de déclarer « .PHONY » la cible qui lance une commande make récursive (pour plus de détails sur les cas où c'est utile, voir Cibles factices).
Par commodité, lorsqu'il démarre (après avoir traité l'option -C), GNU make place dans la variable CURDIR le chemin du répertoire de travail courant. Cette valeur n'est plus jamais touchée par make ensuite. Notez en particulier que le fait d'include des fichiers d'autres répertoires ne change pas la valeur de CURDIR. Cette valeur a la même priorité que si elle avait été définie dans le makefile (par défaut, la variable d'environnement CURDIR ne redéfinit pas cette valeur). Notez que définir cette variable n'a aucun effet sur le fonctionnement de make (par exemple, make ne change pas de répertoire de travail).
Dans une commande make récursive, vous devriez toujours utiliser la variable MAKE plutôt que le nom de commande explicite « make ». On écrit ceci :
subsystem:
cd subdir && $(MAKE)
La valeur de cette variable est le nom de fichier sous lequel make a été invoqué. Si ce nom de fichier était /bin/make, la recette exécutée serait « cd subdir && /bin/make ». Si vous utilisez une version particulière de make pour exécuter le makefile de premier niveau, la même version particulière sera exécutée lors des invocations récursives.
Comme fonctionnalité spéciale, l'utilisation de la variable MAKE dans la recette d'une règle modifie les effets des options « -t » (« --touch »), « -n » (« --just-print ») et « -q » (« --question »). Utiliser la variable MAKE a le même effet que placer un caractère « + » au début de la ligne de recette. Voir Au lieu d'exécuter les recettes. Cette fonctionnalité spéciale n'est active que si la variable MAKE apparaît directement dans la recette. Elle ne s'applique pas si la variable MAKE est référencée à travers l'expansion d'une autre variable ; dans ce cas, vous devez utiliser le jeton « + » pour obtenir ces effets spéciaux.
Considérons la commande « make -t » dans l'exemple ci-dessus. (L'option « -t » marque en fait les cibles comme à jour sans exécuter aucune recette ; voir Au lieu d'exécuter les recettes.) Selon la définition habituelle de « -t », la commande « make -t » de cet exemple ne ferait que créer un fichier nommé subsystem et rien d'autre. Or ce que vous voulez réellement, c'est exécuter « cd subdir && make -t », ce qui exige d'exécuter la recette, alors que « -t » demande de ne pas l'exécuter.
Cette fonctionnalité spéciale produit le comportement souhaité. Tant que la ligne de recette d'une règle contient la variable MAKE, les drapeaux « -t », « -n » et « -q » ne s'appliquent pas à cette ligne. La ligne de recette contenant MAKE est exécutée normalement, même en présence d'un drapeau qui empêche l'exécution de la plupart des recettes. Et comme le mécanisme habituel de MAKEFLAGS transmet les drapeaux au sous-make (voir Communiquer des options à un sous-make), votre demande de toucher les fichiers ou d'afficher les recettes se propage au sous-système.
Les valeurs des variables du make de premier niveau peuvent être transmises au sous-make par l'intermédiaire de l'environnement, à condition de le demander explicitement. Ces variables sont définies comme valeurs par défaut dans le sous-make, mais elles ne redéfinissent pas les variables définies dans le makefile utilisé par le sous-make, sauf si vous employez le commutateur « -e » (voir Résumé des options).
Pour transmettre une variable vers le bas, c'est-à-dire pour l'exporter (export), make ajoute la variable et sa valeur à l'environnement dans lequel il exécute chaque ligne de recette. Le sous-make, à son tour, utilise cet environnement pour initialiser sa table des valeurs de variables. Voir Variables issues de l'environnement.
Sauf demande explicite, make n'exporte une variable que si elle était définie au départ dans l'environnement, ou si elle a été définie sur la ligne de commande et que son nom ne contient que des lettres, des chiffres et des traits de soulignement.
La valeur de la variable make SHELL n'est pas exportée. C'est plutôt la valeur de la variable SHELL présente dans l'environnement de départ qui est transmise au sous-make. En employant la directive export décrite plus loin, vous pouvez faire en sorte que make exporte la valeur de SHELL. Voir Choisir le shell.
La variable spéciale MAKEFLAGS est toujours exportée (sauf si vous faites unexport). MAKEFILES est exportée dès lors qu'on lui donne une valeur.
make transmet automatiquement vers le bas les valeurs des variables définies sur la ligne de commande. Il le fait en les plaçant dans la variable MAKEFLAGS. Voir Communiquer des options à un sous-make.
Si une variable a été créée par défaut par make (voir Variables utilisées par les règles implicites), elle n'est normalement pas transmise vers le bas. Le sous-make les définit lui-même.
Si vous voulez exporter des variables particulières vers le sous-make, utilisez la directive export ainsi :
export variable …
À l'inverse, si vous voulez empêcher qu'une variable soit exportée, utilisez la directive unexport ainsi :
unexport variable …
Dans les deux formes, les arguments de export et unexport sont développés. Vous pouvez donc indiquer des variables ou des fonctions qui se développent en (une liste de) noms de variables à (un)exporter.
Pratique : vous pouvez aussi définir et exporter une variable en même temps :
export variable = value
Cela produit le même résultat que :
variable = value export variable
De même,
export variable := value
produit le même résultat que :
variable := value export variable
De la même façon,
export variable += value
est exactement équivalent à :
variable += value export variable
Voir Ajouter du texte aux variables.
Vous l'avez peut-être remarqué : les directives export et unexport fonctionnent dans make exactement comme elles le font dans le shell sh.
Si vous voulez exporter toutes les variables par défaut, vous pouvez utiliser export seul :
export
Cela indique à make qu'il doit aussi exporter les variables qui ne sont pas explicitement mentionnées dans une directive export ou unexport. Les variables indiquées dans une directive unexport ne sont malgré tout pas exportées.
Le comportement provoqué par export seul était le comportement par défaut dans les anciennes versions de GNU make. Si votre makefile dépend de ce comportement et que vous voulez rester compatible avec les anciennes versions de make, vous pouvez ajouter au makefile la cible spéciale .EXPORT_ALL_VARIABLES au lieu d'utiliser la directive export. Elle est ignorée par les anciens make, tandis que la directive export, elle, provoque une erreur de syntaxe avec les anciens make.
Lorsque vous exportez des variables par défaut au moyen de export seul ou de .EXPORT_ALL_VARIABLES, seules les variables dont le nom ne contient que des caractères alphanumériques et des traits de soulignement sont exportées. Pour exporter les autres variables, vous devez les nommer une par une dans une directive export.
Pour ajouter la valeur d'une variable à l'environnement, il faut la développer. Si l'expansion de la variable a des effets de bord (comme les fonctions info ou eval), ces effets se manifestent chaque fois qu'une commande est invoquée. Vous pouvez l'éviter en donnant à ces variables un nom qui n'est pas exporté par défaut. Cela dit, une meilleure solution consiste à ne pas utiliser du tout cette fonctionnalité d'« exportation par défaut » et à exporter explicitement, en les nommant, les variables concernées.
Utiliser unexport seul indique à make de ne pas exporter les variables par défaut. Comme c'est le comportement par défaut, vous n'en avez besoin que si export a été précédemment utilisé seul (sans doute dans un makefile inclus, par exemple). Vous ne pouvez pas utiliser export et unexport seuls pour exporter des variables dans certaines recettes et pas dans d'autres. La dernière directive export ou unexport apparue seule détermine le comportement de toute l'exécution de make.
Comme fonctionnalité spéciale, la variable MAKELEVEL change lorsqu'elle est transmise d'un niveau au niveau inférieur. La valeur de cette variable est une chaîne représentant en notation décimale la profondeur du niveau. La valeur est « 0 » pour le make de premier niveau, « 1 » pour un sous-make, « 2 » pour un sous-sous-make, et ainsi de suite. Cette incrémentation a lieu lorsque make prépare l'environnement destiné à une recette.
Le principal usage de MAKELEVEL est de le tester dans des directives conditionnelles (voir Parties conditionnelles d'un makefile). Vous pouvez ainsi écrire un makefile qui se comporte différemment selon qu'il est exécuté récursivement ou que vous l'exécutez directement.
La variable MAKEFILES permet de faire utiliser des makefiles supplémentaires par toutes les commandes sous-make. La valeur de MAKEFILES est une liste de noms de fichiers séparés par des espaces. Si cette variable est définie dans un makefile d'un niveau extérieur, elle est transmise vers le bas par l'intermédiaire de l'environnement ; elle sert alors de liste de makefiles supplémentaires que le sous-make lit avant de lire ses makefiles habituels ou indiqués. Voir La variable MAKEFILES.
Les drapeaux comme « -s » ou « -k » sont transmis automatiquement au sous-make par l'intermédiaire de la variable MAKEFLAGS. Cette variable est préparée automatiquement par make pour contenir les lettres des drapeaux qu'il a reçus. Ainsi, si vous exécutez « make -ks », MAKEFLAGS prend la valeur « ks ».
Par conséquent, chaque sous-make reçoit la valeur de MAKEFLAGS dans son environnement. En retour, le sous-make extrait les drapeaux de cette valeur et les traite comme s'ils avaient été donnés en argument. Voir Résumé des options. Cela signifie que, contrairement aux autres variables d'environnement, le MAKEFLAGS indiqué dans l'environnement l'emporte sur le MAKEFLAGS indiqué dans le makefile.
La valeur de MAKEFLAGS est un ensemble (éventuellement vide) de caractères représentant les options à une lettre qui ne prennent pas d'argument, suivi d'une espace, puis des options qui prennent un argument et des options à nom long. Lorsqu'une option possède à la fois une version à une lettre et une version à nom long, c'est toujours la version à une lettre qui est utilisée. Si la ligne de commande ne comporte aucune option à une lettre, la valeur de MAKEFLAGS commence par une espace.
De même, les variables définies sur la ligne de commande sont elles aussi transmises au sous-make par l'intermédiaire de MAKEFLAGS. Tout mot de la valeur de MAKEFLAGS qui contient un « = » est traité par make comme une définition de variable apparue sur la ligne de commande. Voir Redéfinir des variables.
Les options « -C », « -f », « -o » et « -W » ne sont pas placées dans MAKEFLAGS ; ces options ne sont pas transmises vers le bas.
L'option « -j » est un cas particulier (voir L'exécution parallèle). Si vous lui donnez une valeur numérique « N » et que votre système d'exploitation le prend en charge (la plupart des systèmes UNIX le font, mais les autres rarement), le make parent et tous les sous-make se coordonnent pour qu'au total seules « N » tâches s'exécutent simultanément. Notez que les tâches marquées comme récursives (voir Au lieu d'exécuter les recettes) ne sont pas comptées dans le total des tâches (sinon « N » sous-make tourneraient, ne laissant aucun emplacement pour le travail réel !).
Si votre système d'exploitation ne prend pas en charge la coordination décrite ci-dessus, « -j » n'est pas ajouté à MAKEFLAGS, de sorte que les sous-make s'exécutent en mode non parallèle. Si l'option « -j » était transmise aux sous-make, on se retrouverait avec bien plus de tâches en parallèle que le nombre demandé. Donner « -j » sans argument numérique signifie exécuter autant de tâches que possible en parallèle, et cela est transmis vers le bas, car peu importe le nombre d'infinis, ils ne valent jamais qu'un seul infini.
Si vous ne voulez pas transmettre les autres drapeaux vers le bas, vous devez modifier la valeur de MAKEFLAGS ainsi :
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
Les définitions de variables de la ligne de commande apparaissent en réalité dans la variable MAKEOVERRIDES, et MAKEFLAGS contient une référence à cette variable. Si vous voulez transmettre les drapeaux vers le bas comme d'habitude, mais sans transmettre les définitions de variables de la ligne de commande, vous pouvez réinitialiser MAKEOVERRIDES à vide ainsi :
MAKEOVERRIDES =
Ce n'est pas quelque chose que l'on fait d'habitude. Mais certains systèmes imposent une petite limite fixe à la taille de l'environnement, et placer autant d'informations dans la valeur de MAKEFLAGS peut la dépasser. Si vous obtenez le message d'erreur « Arg list too long », c'est peut-être la cause. (Pour une stricte conformité à POSIX.2, si la cible spéciale « .POSIX » apparaît dans le makefile, modifier MAKEOVERRIDES n'a aucun effet sur MAKEFLAGS. Vous n'avez probablement pas à vous en soucier.)
Pour des raisons de compatibilité historique, il existe aussi une variable très semblable, MFLAGS. Elle a la même valeur que MAKEFLAGS, à ceci près qu'elle ne contient pas les définitions de variables de la ligne de commande et que, tant qu'elle n'est pas vide, elle commence toujours par un tiret (MAKEFLAGS ne commence par un tiret que lorsqu'il débute par une option sans version à une lettre, comme « --warn-undefined-variables »). MFLAGS était traditionnellement utilisé explicitement dans une commande make récursive, ainsi :
subsystem:
cd subdir && $(MAKE) $(MFLAGS)
Mais comme MAKEFLAGS existe désormais, cet usage n'est plus nécessaire. Si vous voulez que votre makefile reste compatible avec les anciens programmes make, utilisez cette technique ; elle fonctionne sans problème avec les versions plus récentes de make.
La variable MAKEFLAGS est également utile si vous voulez régler certaines options, comme « -k » (voir Résumé des options), à chaque exécution de make. Il vous suffit de placer une valeur de MAKEFLAGS dans l'environnement. Vous pouvez aussi définir MAKEFLAGS dans un makefile pour indiquer des drapeaux supplémentaires à activer pour ce makefile. (Notez que MFLAGS ne peut pas servir à cet usage : cette variable n'est définie que pour des raisons de compatibilité, et make n'interprète en aucune façon la valeur que vous lui donnez.)
Lorsque make interprète la valeur de MAKEFLAGS (qu'elle vienne de l'environnement ou du makefile), il commence par ajouter un tiret en tête si la valeur n'en a pas déjà un. Il découpe ensuite la valeur en mots séparés par des espaces et analyse ces mots comme s'il s'agissait d'options données sur la ligne de commande (sauf que « -C », « -f », « -h », « -o », « -W » et leurs versions à nom long sont ignorées, et qu'une option invalide ne provoque pas d'erreur).
Si vous placez MAKEFLAGS dans l'environnement, veillez à n'y inclure aucune option qui bouleverserait le fonctionnement de make et ruinerait l'objet du makefile ou de make lui-même. Par exemple, placer les options « -t », « -n » ou « -q » dans l'une de ces variables pourrait avoir des conséquences désastreuses, et aurait à coup sûr des effets pour le moins surprenants, et probablement agaçants.
Si vous voulez aussi utiliser d'autres implémentations de make que GNU make et que, pour cette raison, vous ne voulez pas ajouter de drapeaux propres à GNU make dans la variable MAKEFLAGS, vous pouvez les ajouter à la place dans la variable GNUMAKEFLAGS. Cette variable est analysée juste avant MAKEFLAGS, de la même manière que MAKEFLAGS. Lorsqu'il compose le MAKEFLAGS à transmettre à un make récursif, make inclut tous les drapeaux, y compris ceux issus de GNUMAKEFLAGS. C'est pourquoi, après avoir analysé GNUMAKEFLAGS, GNU make fixe cette variable à la chaîne vide afin d'éviter que les drapeaux ne soient dupliqués pendant la récursion.
Le mieux est de n'employer dans GNUMAKEFLAGS que des drapeaux qui ne modifient pas vraiment le comportement du makefile. Si votre makefile requiert de toute façon GNU Make, contentez-vous d'utiliser MAKEFLAGS. Des drapeaux comme « --no-print-directory » ou « --output-sync » conviennent peut-être à GNUMAKEFLAGS.
Lorsque vous utilisez plusieurs niveaux d'invocations récursives de make, l'option « -w » ou « --print-directory » rend la sortie bien plus facile à comprendre, car elle fait afficher par make chaque répertoire au moment où il commence et termine son traitement. Par exemple, si vous exécutez « make -w » dans le répertoire /u/gnu/make, make affiche, avant toute autre chose, une ligne de la forme :
make: Entering directory `/u/gnu/make'.
et affiche, une fois le traitement terminé, une ligne de la forme :
make: Leaving directory `/u/gnu/make'.
En général, vous n'avez pas besoin d'indiquer cette option, car « make » le fait automatiquement pour vous. « -w » s'active automatiquement lorsque vous utilisez l'option « -C » et dans les sous-make. Toutefois, make n'active pas automatiquement « -w » si vous utilisez aussi « -s », qui demande le silence, ou « --no-print-directory », qui le désactive explicitement.
Lorsqu'une même suite de commandes sert à produire diverses cibles, vous pouvez la définir comme séquence préfabriquée (canned sequence) au moyen de la directive define, puis référencer cette séquence préfabriquée depuis les recettes de ces cibles. Une séquence préfabriquée est en réalité une variable ; son nom ne doit donc pas entrer en conflit avec d'autres noms de variables.
Voici un exemple de définition d'une recette préfabriquée :
define run-yacc = yacc $(firstword $^) mv y.tab.c $@ endef
Ici, run-yacc est le nom de la variable définie. endef marque la fin de la définition, et les lignes situées entre les deux sont les commandes. La directive define ne développe pas les références de variables ni les appels de fonctions à l'intérieur de la séquence préfabriquée : le caractère « $ », les parenthèses, les noms de variables, etc. font tous partie de la valeur de la variable en cours de définition. Pour une description complète de define, voir Définir des variables multilignes.
La première commande de cet exemple exécute Yacc sur le premier prérequis de la règle qui utilise cette séquence préfabriquée. Le fichier de sortie de Yacc s'appelle toujours y.tab.c. La deuxième commande déplace cette sortie vers le nom du fichier cible de la règle.
Pour utiliser une séquence préfabriquée, on substitue sa variable dans la recette d'une règle. On peut la substituer comme n'importe quelle autre variable (voir Les bases des références de variables). Comme une variable définie par define est une variable à expansion récursive, toutes les références de variables écrites à l'intérieur du define sont développées ici même. Par exemple :
foo.c : foo.y
$(run-yacc)
Lorsque « $^ » apparaît dans la valeur de run-yacc, « foo.y » y est substitué, et « foo.c » est substitué à « $@ ».
C'est un exemple réaliste, mais celui-ci en particulier n'est pas réellement nécessaire, car make possède une règle implicite qui détermine ces commandes à partir des noms de fichiers concernés (voir Utiliser les règles implicites).
Lors de l'exécution de la recette, chaque ligne de la séquence préfabriquée est traitée comme si cette ligne apparaissait seule dans la règle, précédée d'une tabulation. En particulier, make lance un sous-shell distinct pour chaque ligne. Vous pouvez utiliser les caractères de préfixe spéciaux qui agissent sur les lignes de commande (« @ », « - », « + ») sur chaque ligne de la séquence préfabriquée. Voir Écrire les recettes d'une règle. Par exemple, avec la séquence préfabriquée suivante :
define frobnicate = @echo "frobnicating target $@" frob-step-1 $< -o $@-step-1 frob-step-2 $@-step-1 -o $@ endef
make n'affiche pas l'écho de la première ligne, à savoir la commande echo. En revanche, il affiche bien l'écho des deux lignes de recette suivantes.
En revanche, le caractère de préfixe placé sur la ligne de recette qui référence la séquence préfabriquée s'applique à toutes les lignes de la séquence. Ainsi, la règle suivante :
frob.out: frob.in
@$(frobnicate)
n'affiche l'écho d'aucune ligne de recette. (Pour une description complète de « @ », voir L'écho des recettes.)
Il est parfois utile de définir une recette qui ne fait rien. Il suffit pour cela de donner une recette ne contenant que des espaces. Par exemple :
target: ;
Ceci définit une recette vide pour target. Vous pourriez aussi définir une recette vide sur une ligne commençant par le caractère de préfixe de recette, mais une telle ligne aurait l'air vide, ce qui prêterait à confusion.
Vous vous demandez peut-être pourquoi définir une recette qui ne fait rien. L'une des raisons pour lesquelles c'est utile est d'empêcher qu'une cible reçoive une recette implicite (provenant d'une règle implicite ou de la cible spéciale .DEFAULT ; voir Utiliser les règles implicites et Définir une règle par défaut de dernier recours).
Les recettes vides peuvent aussi servir à éviter des erreurs pour des cibles créées comme effet de bord d'une autre recette. Si la cible n'existe pas, une recette vide fait que make ne se plaint pas de ne pas savoir comment fabriquer cette cible et la considère comme périmée.
Vous pourriez être tenté de définir une recette vide pour une cible qui n'est pas un fichier réel, mais qui n'existe que pour faire régénérer ses prérequis. Ce n'est cependant pas la meilleure façon de procéder, car si le fichier cible existe réellement, les prérequis risquent de ne pas être régénérés correctement. Pour une meilleure manière de faire cela, voir Cibles factices.