Une variable est un nom défini dans un makefile pour représenter une chaîne de texte, appelée la valeur de la variable. Ces valeurs sont insérées, sur demande explicite, dans les cibles, les prérequis (prerequisite), les recettes (recipe) et d'autres parties du makefile. (Dans certaines autres versions de make, les variables sont appelées macros.)
Les variables et les fonctions, dans toutes les parties d'un makefile, sont développées au moment de la lecture, sauf dans les recettes, dans les membres de droite des définitions de variables utilisant ‘=’, et dans le corps des définitions de variables utilisant la directive define. La valeur produite par le développement d'une variable est celle de sa définition la plus récente au moment du développement. Autrement dit, les variables ont une portée dynamique.
Les variables peuvent représenter des listes de noms de fichiers, des options à passer aux compilateurs, des programmes à exécuter, des répertoires où chercher les fichiers source, des répertoires où écrire la sortie, ou tout ce que vous pouvez imaginer.
Un nom de variable peut être n'importe quelle suite de caractères ne contenant ni ‘:’, ni ‘#’, ni ‘=’, ni espace blanc. Toutefois, les noms de variables contenant des caractères autres que des lettres, des chiffres et des tirets bas doivent être employés avec prudence, car dans certains shells ils ne peuvent pas être transmis via l'environnement à un sous-make (voir Transmettre des variables à un sous-make). Les noms de variables commençant par ‘.’ suivi d'une lettre majuscule pourront recevoir une signification particulière dans les futures versions de make.
Les noms de variables sont sensibles à la casse. Les noms ‘foo’, ‘FOO’ et ‘Foo’ désignent tous des variables différentes.
Par tradition, on utilise des lettres majuscules dans les noms de variables, mais nous recommandons d'utiliser des minuscules pour les variables servant à des usages internes au makefile, et de réserver les majuscules aux paramètres qui contrôlent les règles implicites ou que l'utilisateur peut redéfinir (override) au moyen d'options de commande (voir Redéfinir des variables).
Quelques variables ont un nom constitué d'un seul caractère de ponctuation ou de quelques caractères seulement. Ce sont les variables automatiques (automatic variables), et elles ont des usages spécialisés particuliers. Voir Variables automatiques.
override
Pour insérer la valeur d'une variable, écrivez un signe dollar suivi du nom de la variable entre parenthèses ou entre accolades : ‘$(foo)’ comme ‘${foo}’ sont des références valides à la variable foo. C'est à cause de cette signification spéciale de ‘$’ que vous devez écrire ‘$$’ pour obtenir l'effet d'un seul signe dollar dans un nom de fichier ou une recette.
Les références de variables peuvent s'utiliser dans n'importe quel contexte : cibles, prérequis, recettes, la plupart des directives et nouvelles valeurs de variables. Voici un exemple d'un cas courant, où une variable contient les noms de tous les fichiers objets d'un programme :
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
Les références de variables fonctionnent par substitution textuelle stricte. Ainsi, la règle
foo = c
prog.o : prog.$(foo)
$(foo)$(foo) -$(foo) prog.$(foo)
pourrait servir à compiler un programme C prog.c. Comme les espaces précédant la valeur sont ignorés dans les affectations de variables, la valeur de foo est exactement ‘c’. (N'écrivez pas réellement vos makefiles de cette façon !)
Un signe dollar suivi d'un caractère autre qu'un signe dollar, une parenthèse ouvrante ou une accolade ouvrante traite ce caractère unique comme le nom de la variable. Ainsi, vous pourriez référencer la variable x par ‘$x’. Toutefois, cette pratique peut prêter à confusion (par exemple, ‘$foo’ désigne la variable f suivie de la chaîne oo) ; c'est pourquoi nous recommandons d'entourer toutes les variables de parenthèses ou d'accolades, même les variables d'une seule lettre, sauf si leur omission améliore nettement la lisibilité. Les variables automatiques sont un cas où la lisibilité est souvent améliorée ainsi (voir Variables automatiques).
Il existe différentes manières pour une variable de GNU make d'obtenir une valeur ; nous les appelons les types (flavor) de variables. Les types se distinguent par la façon dont ils traitent les valeurs qui leur sont affectées dans le makefile, et par la façon dont ces valeurs sont gérées lorsque la variable est ensuite utilisée et développée.
Le premier type de variable est la variable à expansion récursive (recursively expanded variable). Les variables de cette sorte sont définies par des lignes utilisant ‘=’ (voir Définir des variables) ou par la directive define (voir Définir des variables multilignes). La valeur que vous indiquez est installée telle quelle ; si elle contient des références à d'autres variables, ces références sont développées chaque fois que cette variable est insérée (au cours de l'expansion d'une autre chaîne). Lorsque cela se produit, on parle d'expansion récursive (recursive expansion).
Par exemple,
foo = $(bar) bar = $(ugh) ugh = Huh? all:;echo $(foo)
affiche ‘Huh?’ : ‘$(foo)’ se développe en ‘$(bar)’, qui se développe en ‘$(ugh)’, qui finalement se développe en ‘Huh?’.
Ce type de variable est le seul que prennent en charge la plupart des autres versions de make. Il a ses avantages et ses inconvénients. Un avantage (selon la plupart des gens) est le suivant :
CFLAGS = $(include_dirs) -O include_dirs = -Ifoo -Ibar
fait ce qui était voulu : lorsque ‘CFLAGS’ est développée dans une recette, elle se développe en ‘-Ifoo -Ibar -O’. Un inconvénient majeur est que vous ne pouvez pas ajouter quelque chose à la fin d'une variable, comme dans
CFLAGS = $(CFLAGS) -O
car cela provoquerait une boucle infinie dans l'expansion de la variable. (En réalité, make détecte la boucle infinie et signale une erreur.)
Un autre inconvénient est que toute fonction (voir Fonctions de transformation de texte) référencée dans la définition sera exécutée chaque fois que la variable est développée. Cela ralentit make ; pire, cela rend les résultats des fonctions wildcard et shell imprévisibles, car vous ne pouvez pas facilement contrôler quand elles sont appelées, ni même combien de fois.
Pour éviter les problèmes et inconvénients des variables à expansion récursive, il existe un autre type : les variables à expansion simple.
Les variables à expansion simple (simply expanded variable) sont définies par des lignes utilisant ‘:=’ ou ‘::=’ (voir Définir des variables). Les deux formes sont équivalentes dans GNU make ; toutefois, seule la forme ‘::=’ est décrite par la norme POSIX (la prise en charge de ‘::=’ a été ajoutée à la norme POSIX dans POSIX Issue 8).
La valeur d'une variable à expansion simple est parcourue une seule fois, au moment où la variable est définie, en développant alors toute référence à d'autres variables et fonctions. Une fois cette expansion achevée, la valeur de la variable n'est plus jamais développée : lorsque la variable est utilisée, sa valeur est copiée telle quelle comme résultat de l'expansion. Si la valeur contenait des références à des variables, le résultat de l'expansion contiendra leurs valeurs telles qu'elles étaient au moment où cette variable a été définie. Par conséquent,
x := foo y := $(x) bar x := later
est équivalent à
y := foo bar x := later
Voici un exemple un peu plus compliqué, illustrant l'emploi de ‘:=’ conjointement avec la fonction shell. (Voir La fonction shell.) Cet exemple montre aussi l'emploi de la variable MAKELEVEL, qui change lorsqu'elle est transmise d'un niveau à l'autre. (Voir Transmettre des variables à un sous-make pour des informations sur MAKELEVEL.)
ifeq (0,${MAKELEVEL})
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif
Un avantage de cet emploi de ‘:=’ est qu'une recette typique de « descente dans un répertoire » s'écrit alors ainsi :
${subdirs}:
${MAKE} -C $@ all
Les variables à expansion simple rendent généralement la programmation complexe de makefiles plus prévisible, car elles fonctionnent comme les variables de la plupart des langages de programmation. Elles vous permettent de redéfinir une variable à partir de sa propre valeur (ou de sa valeur traitée d'une manière ou d'une autre par l'une des fonctions d'expansion) et d'utiliser les fonctions d'expansion bien plus efficacement (voir Fonctions de transformation de texte).
Vous pouvez aussi les utiliser pour introduire de façon contrôlée des espaces blancs en tête des valeurs de variables. Les caractères d'espace blanc en tête sont supprimés de votre entrée avant la substitution des références de variables et des appels de fonctions ; cela signifie que vous pouvez inclure des espaces en tête d'une valeur de variable en les protégeant par des références de variables, comme ceci :
nullstring := space := $(nullstring) # end of the line
Ici, la valeur de la variable space est exactement un espace. Le commentaire ‘# end of the line’ n'est inclus ici que par souci de clarté. Comme les caractères d'espace en fin de valeur ne sont pas supprimés des valeurs de variables, un simple espace en fin de ligne aurait le même effet (mais serait plutôt difficile à lire). Si vous placez un espace blanc à la fin d'une valeur de variable, il est conseillé de mettre un commentaire de ce genre en fin de ligne pour rendre votre intention claire. À l'inverse, si vous ne voulez aucun caractère d'espace blanc à la fin de votre valeur de variable, vous devez veiller à ne pas placer un commentaire quelconque en fin de ligne après un espace, comme ceci :
dir := /foo/bar # directory to put the frobs in
Ici, la valeur de la variable dir est ‘/foo/bar ’ (avec quatre espaces en fin), ce qui n'était probablement pas l'intention. (Imaginez quelque chose comme ‘$(dir)/file’ avec cette définition !)
Une autre forme d'affectation permet une expansion immédiate, mais, contrairement à l'affectation simple, la variable résultante est récursive : elle sera de nouveau redéveloppée à chaque utilisation. Afin d'éviter des résultats inattendus, une fois la valeur immédiatement développée, elle est automatiquement protégée : toutes les occurrences de $ dans la valeur après expansion sont converties en $$. Ce type d'affectation utilise l'opérateur ‘:::=’. Par exemple,
var = first OUT :::= $(var) var = second
donne pour résultat que la variable OUT contient le texte ‘first’, tandis qu'ici :
var = one$$two OUT :::= $(var) var = three$$four
la variable OUT contient le texte ‘one$$two’. La valeur est développée au moment où la variable est affectée ; le résultat est donc l'expansion de la première valeur de var, ‘one$two’ ; puis la valeur est de nouveau échappée avant que l'affectation ne soit terminée, ce qui donne le résultat final ‘one$$two’.
La variable OUT est ensuite considérée comme une variable récursive ; elle sera donc redéveloppée lorsqu'elle est utilisée.
Cela semble fonctionnellement équivalent aux opérateurs ‘:=’ / ‘::=’, mais il y a quelques différences :
Premièrement, après l'affectation, la variable est une variable récursive ordinaire ; lorsque vous lui ajoutez du texte avec ‘+=’, la valeur du membre de droite n'est pas développée immédiatement. Si vous préférez que l'opérateur ‘+=’ développe immédiatement le membre de droite, vous devriez utiliser plutôt l'affectation ‘:=’ / ‘::=’.
Deuxièmement, ces variables sont légèrement moins efficaces que les variables à expansion simple, car elles doivent être redéveloppées à l'utilisation plutôt que simplement copiées. Cependant, comme toutes les références de variables sont échappées, cette expansion se contente de déséchapper la valeur : elle ne développe aucune variable et n'exécute aucune fonction.
Voici un autre exemple :
var = one$$two OUT :::= $(var) OUT += $(var) var = three$$four
Après cela, la valeur de OUT est le texte ‘one$$two $(var)’. Lorsque cette variable est utilisée, elle est développée et le résultat est ‘one$two three$four’.
Ce style d'affectation est équivalent à l'opérateur ‘:=’ du make BSD traditionnel ; comme vous pouvez le voir, il fonctionne un peu différemment de l'opérateur ‘:=’ de GNU make. L'opérateur :::= a été ajouté à la spécification POSIX dans Issue 8 pour assurer la portabilité.
Il existe un autre opérateur d'affectation pour les variables, ‘?=’. On l'appelle opérateur d'affectation conditionnelle de variable, car il n'a d'effet que si la variable n'est pas encore définie. L'instruction :
FOO ?= bar
est exactement équivalente à ceci (voir La fonction origin) :
ifeq ($(origin FOO), undefined) FOO = bar endif
Notez qu'une variable affectée à une valeur vide est tout de même définie ; ‘?=’ ne définira donc pas cette variable.
Cette section décrit quelques fonctionnalités avancées qui permettent de référencer les variables de manières plus souples.
Une référence de substitution (substitution reference) insère la valeur d'une variable en y appliquant les modifications que vous indiquez. Elle a la forme ‘$(var:a=b)’ (ou ‘${var:a=b}’) et sa signification est de prendre la valeur de la variable var, d'y remplacer chaque a situé en fin de mot par b, et d'insérer la chaîne résultante.
Lorsque nous disons « en fin de mot », nous voulons dire que a doit, pour être remplacé, être soit suivi d'un espace blanc, soit situé à la fin de la valeur ; les autres occurrences de a dans la valeur restent inchangées. Par exemple :
foo := a.o b.o l.a c.o bar := $(foo:.o=.c)
affecte à ‘bar’ la valeur ‘a.c b.c l.a c.c’. Voir Définir des variables.
Une référence de substitution est une notation abrégée de la fonction d'expansion patsubst (voir Fonctions de substitution et d'analyse de chaînes) : ‘$(var:a=b)’ est équivalent à ‘$(patsubst %a,%b,var)’. Nous fournissons les références de substitution en plus de patsubst pour la compatibilité avec d'autres implémentations de make.
Un autre type de référence de substitution vous permet d'exploiter toute la puissance de la fonction patsubst. Elle a la même forme ‘$(var:a=b)’ décrite ci-dessus, sauf que maintenant a doit contenir un unique caractère ‘%’. Ce cas est équivalent à ‘$(patsubst a,b,$(var))’. Voir Fonctions de substitution et d'analyse de chaînes pour une description de la fonction patsubst. Par exemple :
foo := a.o b.o l.a c.o bar := $(foo:%.o=%.c)
affecte à ‘bar’ la valeur ‘a.c b.c l.a c.c’.
Les noms de variables calculés sont un concept avancé, très utile dans la programmation de makefiles plus sophistiquée. Dans les situations simples, vous n'avez pas à vous en préoccuper, mais ils peuvent se révéler extrêmement utiles.
Une variable peut être référencée à l'intérieur du nom d'une autre variable. On parle alors de nom de variable calculé (computed variable name) ou de référence de variable imbriquée (nested variable reference). Par exemple,
x = y y = z a := $($(x))
définit a comme ‘z’ : le ‘$(x)’ à l'intérieur de ‘$($(x))’ se développe en ‘y’, de sorte que ‘$($(x))’ se développe en ‘$(y)’, qui à son tour se développe en ‘z’. Ici, le nom de la variable à référencer n'est pas indiqué explicitement ; il est calculé par l'expansion de ‘$(x)’. La référence ‘$(x)’ est ici imbriquée dans la référence de variable extérieure.
L'exemple précédent montre deux niveaux d'imbrication, mais n'importe quel nombre de niveaux est possible. Par exemple, en voici trois :
x = y y = z z = u a := $($($(x)))
Ici, le ‘$(x)’ le plus interne se développe en ‘y’, de sorte que ‘$($(x))’ se développe en ‘$(y)’, qui à son tour se développe en ‘z’ ; il reste alors ‘$(z)’, qui devient ‘u’.
Les références à des variables à expansion récursive à l'intérieur d'un nom de variable sont redéveloppées de la manière habituelle. Par exemple :
x = $(y) y = z z = Hello a := $($(x))
définit a comme ‘Hello’ : ‘$($(x))’ devient ‘$($(y))’, qui devient ‘$(z)’, qui devient ‘Hello’.
Les références de variables imbriquées peuvent aussi contenir des références modifiées et des appels de fonctions (voir Fonctions de transformation de texte), tout comme n'importe quelle autre référence. Par exemple, en utilisant la fonction subst (voir Fonctions de substitution et d'analyse de chaînes) :
x = variable1 variable2 := Hello y = $(subst 1,2,$(x)) z = y a := $($($(z)))
définit finalement a comme ‘Hello’. Il est douteux que quiconque veuille un jour écrire une référence imbriquée aussi alambiquée que celle-ci, mais elle fonctionne : ‘$($($(z)))’ se développe en ‘$($(y))’, qui devient ‘$($(subst 1,2,$(x)))’. Ceci récupère la valeur ‘variable1’ depuis x et la transforme par substitution en ‘variable2’, de sorte que la chaîne entière devient ‘$(variable2)’, une simple référence de variable dont la valeur est ‘Hello’.
Un nom de variable calculé n'a pas à être constitué entièrement d'une seule référence de variable. Il peut contenir plusieurs références de variables, ainsi que du texte invariable. Par exemple,
a_dirs := dira dirb 1_dirs := dir1 dir2
a_files := filea fileb 1_files := file1 file2
ifeq "$(use_a)" "yes" a1 := a else a1 := 1 endif
ifeq "$(use_dirs)" "yes" df := dirs else df := files endif dirs := $($(a1)_$(df))
donnera à dirs la même valeur que a_dirs, 1_dirs, a_files ou 1_files selon les réglages de use_a et use_dirs.
Les noms de variables calculés peuvent aussi être utilisés dans des références de substitution :
a_objects := a.o b.o c.o 1_objects := 1.o 2.o 3.o sources := $($(a1)_objects:.o=.c)
définit sources soit comme ‘a.c b.c c.c’, soit comme ‘1.c 2.c 3.c’, selon la valeur de a1.
La seule restriction à ce genre d'utilisation des références de variables imbriquées est qu'elles ne peuvent pas spécifier une partie du nom d'une fonction à appeler. Cela tient au fait que le test d'un nom de fonction reconnu est effectué avant l'expansion des références imbriquées. Par exemple,
ifdef do_sort func := sort else func := strip endif
bar := a d b g q c
foo := $($(func) $(bar))
tente de donner à ‘foo’ la valeur de la variable ‘sort a d b g q c’ ou ‘strip a d b g q c’, au lieu de fournir ‘a d b g q c’ comme argument à la fonction sort ou strip. Cette restriction pourrait être levée à l'avenir si ce changement s'avérait être une bonne idée.
Vous pouvez aussi utiliser des noms de variables calculés dans le membre de gauche d'une affectation de variable, ou dans une directive define, comme dans :
dir = foo $(dir)_sources := $(wildcard $(dir)/*.c) define $(dir)_print = lpr $($(dir)_sources) endef
Cet exemple définit les variables ‘dir’, ‘foo_sources’ et ‘foo_print’.
Notez que les références de variables imbriquées sont bien différentes des variables à expansion récursive (voir Les deux types de variables), bien que les deux soient employées ensemble de façon complexe lors de la programmation de makefiles.
Les variables peuvent obtenir des valeurs de plusieurs manières différentes :
make. Voir Redéfinir des variables.
let (voir La fonction let) ou avec la fonction foreach (voir La fonction foreach).
make. Voir Variables issues de l'environnement.
Pour définir une variable depuis le makefile, écrivez une ligne commençant par le nom de la variable, suivi de l'un des opérateurs d'affectation ‘=’, ‘:=’, ‘::=’ ou ‘:::=’. Tout ce qui suit l'opérateur, déduction faite de l'espace blanc initial de la ligne, devient la valeur. Par exemple,
objects = main.o foo.o bar.o utils.o
définit une variable nommée objects contenant la valeur ‘main.o foo.o bar.o utils.o’. L'espace blanc autour du nom de la variable et immédiatement après le ‘=’ est ignoré.
Les variables définies avec ‘=’ sont des variables à expansion récursive. Les variables définies avec ‘:=’ ou ‘::=’ sont des variables à expansion simple ; ces définitions peuvent contenir des références de variables qui seront développées avant que la définition ne soit faite. Les variables définies avec ‘:::=’ sont des variables à expansion immédiate. Les différents opérateurs d'affectation sont décrits dans Les deux types de variables.
Le nom de la variable peut contenir des références de fonctions et de variables, qui sont développées au moment de la lecture de la ligne afin de déterminer le nom réel de la variable à utiliser.
La longueur de la valeur d'une variable n'est limitée que par la quantité de mémoire de l'ordinateur. Vous pouvez répartir la valeur d'une variable sur plusieurs lignes physiques pour la lisibilité (voir Découper les longues lignes).
La plupart des noms de variables sont considérés comme ayant la chaîne vide pour valeur si vous ne les avez jamais définis. Plusieurs variables possèdent des valeurs initiales intégrées qui ne sont pas vides, mais vous pouvez les définir de la manière habituelle (voir Variables utilisées par les règles implicites). Plusieurs variables spéciales reçoivent automatiquement une nouvelle valeur pour chaque règle ; on les appelle les variables automatiques (voir Variables automatiques).
Si vous souhaitez qu'une variable ne soit affectée à une valeur que si elle ne l'est pas déjà, vous pouvez utiliser l'opérateur abrégé ‘?=’ au lieu de ‘=’. Les deux définitions suivantes de la variable ‘FOO’ sont identiques (voir La fonction origin) :
FOO ?= bar
et
ifeq ($(origin FOO), undefined) FOO = bar endif
L'opérateur d'affectation shell ‘!=’ permet d'exécuter un script shell et d'affecter sa sortie à une variable. Cet opérateur évalue d'abord le membre de droite, puis passe ce résultat au shell pour exécution. Si le résultat de l'exécution se termine par un saut de ligne, ce saut de ligne unique est supprimé ; tous les autres sauts de ligne sont remplacés par des espaces. La chaîne obtenue est alors placée dans la variable à expansion récursive désignée. Par exemple :
hash != printf '\043' file_list != find . -name '*.c'
Si le résultat de l'exécution peut produire un $, et que vous ne voulez pas que ce qui le suit soit interprété comme une référence de variable ou de fonction de make, vous devez remplacer chaque $ par $$ dans le cadre de l'exécution. Vous pouvez aussi, à la place, affecter le résultat de l'exécution d'un programme à une variable à expansion simple au moyen d'un appel à la fonction shell. Voir La fonction shell. Par exemple :
hash := $(shell printf '\043') var := $(shell find . -name "*.c")
Comme pour la fonction shell, le code de retour du script shell qui vient d'être invoqué est stocké dans la variable .SHELLSTATUS.
Il est souvent utile d'ajouter (append) du texte à la valeur d'une variable déjà définie. Vous le faites avec une ligne contenant ‘+=’, comme ceci :
objects += another.o
Ceci prend la valeur de la variable objects et y ajoute le texte ‘another.o’ (précédé d'un seul espace, si elle a déjà une valeur). Ainsi :
objects = main.o foo.o bar.o utils.o objects += another.o
affecte à objects la valeur ‘main.o foo.o bar.o utils.o another.o’.
Utiliser ‘+=’ revient à peu près à :
objects = main.o foo.o bar.o utils.o objects := $(objects) another.o
mais en diffère par des aspects qui deviennent importants lorsque vous utilisez des valeurs plus complexes.
Lorsque la variable en question n'a pas été définie auparavant, ‘+=’ se comporte exactement comme un ‘=’ ordinaire : il définit une variable à expansion récursive. Cependant, lorsqu'il existe une définition antérieure, ce que fait exactement ‘+=’ dépend du type de variable que vous aviez défini au départ. Voir Les deux types de variables pour une explication des deux types de variables.
Lorsque vous ajoutez à la valeur d'une variable avec ‘+=’, make se comporte essentiellement comme si vous aviez inclus le texte supplémentaire dans la définition initiale de la variable. Si vous l'avez d'abord définie avec ‘:=’ ou ‘::=’, en en faisant une variable à expansion simple, ‘+=’ ajoute à cette définition à expansion simple et développe le nouveau texte avant de l'ajouter à l'ancienne valeur, exactement comme le fait ‘:=’ (voir Définir des variables pour une explication complète de ‘:=’ ou ‘::=’). En fait,
variable := value variable += more
est exactement équivalent à :
variable := value variable := $(variable) more
En revanche, lorsque vous utilisez ‘+=’ avec une variable que vous avez d'abord définie comme étant à expansion récursive au moyen d'un simple ‘=’ ou de ‘:::=’, make ajoute le texte non développé à la valeur existante, quelle qu'elle soit. Cela signifie que
variable = value variable += more
est à peu près équivalent à :
temp = value variable = $(temp) more
à ceci près, bien sûr, qu'il ne définit jamais de variable appelée temp. L'importance de cela apparaît lorsque l'ancienne valeur de la variable contient des références de variables. Prenons cet exemple courant :
CFLAGS = $(includes) -O … CFLAGS += -pg # enable profiling
La première ligne définit la variable CFLAGS avec une référence à une autre variable, includes. (CFLAGS est utilisée par les règles de compilation C ; voir Catalogue des règles intégrées.) Le fait d'utiliser ‘=’ pour la définition fait de CFLAGS une variable à expansion récursive, ce qui signifie que ‘$(includes) -O’ n'est pas développé lorsque make traite la définition de CFLAGS. Ainsi, includes n'a pas besoin d'être déjà définie pour que sa valeur prenne effet. Elle doit seulement être définie avant toute référence à CFLAGS. Si nous tentions d'ajouter à la valeur de CFLAGS sans utiliser ‘+=’, nous pourrions le faire ainsi :
CFLAGS := $(CFLAGS) -pg # enable profiling
C'est assez proche, mais pas tout à fait ce que nous voulons. Utiliser ‘:=’ redéfinit CFLAGS comme une variable à expansion simple ; cela signifie que make développe le texte ‘$(CFLAGS) -pg’ avant de définir la variable. Si includes n'est pas encore définie, nous obtenons ‘ -O -pg’, et une définition ultérieure de includes n'aura aucun effet. À l'inverse, en utilisant ‘+=’, nous affectons à CFLAGS la valeur non développée ‘$(includes) -O -pg’. Nous préservons ainsi la référence à includes, de sorte que si cette variable est définie à un moment ultérieur, une référence comme ‘$(CFLAGS)’ utilise toujours sa valeur.
override
Si une variable a été définie au moyen d'un argument de commande (voir Redéfinir des variables), alors les affectations ordinaires dans le makefile sont ignorées. Si vous voulez définir la variable dans le makefile bien qu'elle ait été définie par un argument de commande, vous pouvez utiliser une directive override, qui est une ligne ayant cette allure :
override variable = value
ou
override variable := value
Pour ajouter du texte à une variable définie sur la ligne de commande, utilisez :
override variable += more text
Voir Ajouter du texte à des variables.
Les affectations de variables marquées du drapeau override ont une priorité plus élevée que toutes les autres affectations, à l'exception d'un autre override. Les affectations ou ajouts ultérieurs à cette variable qui ne sont pas marqués override seront ignorés.
La directive override n'a pas été inventée pour faire monter d'un cran la guerre entre les makefiles et les arguments de commande. Elle a été inventée pour que vous puissiez modifier et compléter les valeurs que l'utilisateur indique au moyen d'arguments de commande.
Par exemple, supposons que vous vouliez toujours l'option ‘-g’ lorsque vous exécutez le compilateur C, mais que vous souhaitiez permettre à l'utilisateur de spécifier les autres options au moyen d'un argument de commande, comme d'habitude. Vous pourriez utiliser cette directive override :
override CFLAGS += -g
Vous pouvez aussi utiliser des directives override avec des directives define. Cela se fait comme on pourrait s'y attendre :
override define foo = bar endef
Voir Définir des variables multilignes.
Une autre manière de définir la valeur d'une variable est d'utiliser la directive define. Cette directive a une syntaxe inhabituelle qui permet d'inclure des caractères de saut de ligne dans la valeur, ce qui est commode à la fois pour définir des séquences de commandes toutes prêtes (voir Définir des recettes toutes prêtes) et des fragments de syntaxe de makefile à utiliser avec eval (voir La fonction eval).
La directive define est suivie, sur la même ligne, du nom de la variable à définir et d'un opérateur d'affectation (facultatif), et de rien d'autre. La valeur à donner à la variable apparaît sur les lignes suivantes. La fin de la valeur est marquée par une ligne contenant uniquement le mot endef.
À cette différence de syntaxe près, define fonctionne exactement comme n'importe quelle autre définition de variable. Le nom de la variable peut contenir des références de fonctions et de variables, qui sont développées au moment de la lecture de la directive afin de déterminer le nom réel de la variable à utiliser.
Le dernier saut de ligne avant le endef n'est pas inclus dans la valeur ; si vous voulez que votre valeur contienne un saut de ligne final, vous devez inclure une ligne vide. Par exemple, pour définir une variable contenant un caractère de saut de ligne, vous devez utiliser deux lignes vides, et non une :
define newline endef
Vous pouvez omettre l'opérateur d'affectation de variable si vous le préférez. S'il est omis, make suppose qu'il s'agit de ‘=’ et crée une variable à expansion récursive (voir Les deux types de variables). Lorsque vous utilisez un opérateur ‘+=’, la valeur est ajoutée à la valeur précédente comme pour toute autre opération d'ajout : avec un seul espace séparant l'ancienne et la nouvelle valeur.
Vous pouvez imbriquer les directives define : make suit la trace des directives imbriquées et signale une erreur si elles ne sont pas toutes correctement fermées par endef. Notez que les lignes commençant par le caractère de préfixe de recette sont considérées comme faisant partie d'une recette ; ainsi, toute chaîne define ou endef apparaissant sur une telle ligne n'est pas considérée comme une directive de make.
define two-lines echo foo echo $(bar) endef
Lorsqu'il est utilisé dans une recette, l'exemple précédent est fonctionnellement équivalent à ceci :
two-lines = echo foo; echo $(bar)
car deux commandes séparées par un point-virgule se comportent à peu près comme deux commandes shell distinctes. Notez toutefois que l'utilisation de deux lignes distinctes amène make à invoquer le shell deux fois, exécutant un sous-shell indépendant pour chaque ligne. Voir Exécution des recettes.
Si vous voulez que les définitions de variables faites avec define aient priorité sur les définitions de variables de la ligne de commande, vous pouvez utiliser la directive override conjointement avec define :
override define two-lines = foo $(bar) endef
Voir La directive override.
Si vous voulez vider une variable, lui affecter une valeur vide suffit généralement. Le développement d'une telle variable donnera le même résultat (la chaîne vide), qu'elle ait été définie ou non. Cependant, si vous utilisez les fonctions flavor (voir La fonction flavor) et origin (voir La fonction origin), il existe une différence entre une variable qui n'a jamais été définie et une variable ayant une valeur vide. Dans de telles situations, vous pourriez vouloir utiliser la directive undefine pour faire en sorte qu'une variable apparaisse comme si elle n'avait jamais été définie. Par exemple,
foo := foo bar = bar undefine foo undefine bar $(info $(origin foo)) $(info $(flavor bar))
Cet exemple affiche « undefined » pour les deux variables.
Si vous voulez annuler une définition de variable faite sur la ligne de commande, vous pouvez utiliser la directive override conjointement avec undefine, comme pour les définitions de variables :
override undefine CFLAGS
Les variables de make peuvent provenir de l'environnement dans lequel make est exécuté. Toute variable d'environnement que make voit à son démarrage est transformée en une variable de make de même nom et de même valeur. Cependant, une affectation explicite dans le makefile, ou au moyen d'un argument de commande, l'emporte sur l'environnement. (Si le drapeau ‘-e’ est spécifié, les valeurs de l'environnement l'emportent sur les affectations dans le makefile. Voir Résumé des options. Mais cette pratique n'est pas recommandée.)
Ainsi, en définissant la variable CFLAGS dans votre environnement, vous pouvez faire en sorte que toutes les compilations C de la plupart des makefiles utilisent les options de compilation que vous préférez. C'est sans danger pour les variables ayant une signification standard ou conventionnelle, car vous savez qu'aucun makefile ne les utilisera à d'autres fins. (Notez que ce n'est pas totalement fiable ; certains makefiles définissent CFLAGS explicitement et ne sont donc pas affectés par la valeur de l'environnement.)
Lorsque make exécute une recette, certaines variables définies dans le makefile sont placées dans l'environnement de chaque commande que make invoque. Par défaut, seules les variables provenant de l'environnement de make ou définies sur la ligne de commande sont placées dans l'environnement des commandes. Vous pouvez utiliser la directive export pour transmettre d'autres variables. Voir Transmettre des variables à un sous-make pour tous les détails.
Les autres usages des variables issues de l'environnement ne sont pas recommandés. Il n'est pas judicieux que les makefiles dépendent, pour leur fonctionnement, de variables d'environnement définies en dehors de leur contrôle, car cela amènerait différents utilisateurs à obtenir des résultats différents à partir du même makefile. Cela va à l'encontre de la raison d'être même de la plupart des makefiles.
De tels problèmes seraient particulièrement probables avec la variable SHELL, qui est normalement présente dans l'environnement pour indiquer le shell interactif choisi par l'utilisateur. Il serait très indésirable que ce choix influe sur make ; aussi make traite-t-il la variable d'environnement SHELL d'une manière particulière. Voir Choisir le shell.
Les valeurs des variables de make sont normalement globales ; autrement dit, elles sont les mêmes quel que soit l'endroit où elles sont évaluées (à moins, bien sûr, d'être redéfinies). Font exception les variables définies par les fonctions let (voir La fonction let) ou foreach (voir La fonction foreach), ainsi que les variables automatiques (voir Variables automatiques).
Une autre exception sont les valeurs de variables propres à une cible (target-specific variable value). Cette fonctionnalité vous permet de définir des valeurs différentes pour la même variable selon la cible que make est en train de construire. Comme pour les variables automatiques, ces valeurs ne sont disponibles que dans le contexte de la recette d'une cible (et dans d'autres affectations propres à une cible).
On définit une valeur de variable propre à une cible ainsi :
target … : variable-assignment
Les affectations de variables propres à une cible peuvent être préfixées par l'un quelconque, ou par plusieurs, des mots-clés spéciaux export, unexport, override ou private. Ceux-ci appliquent leur comportement habituel à cette seule instance de la variable.
Lorsque plusieurs target sont indiqués, une valeur de variable propre à la cible est créée individuellement pour chaque membre de la liste de cibles.
variable-assignment peut être n'importe quelle forme d'affectation valide : récursive (‘=’), simple (‘:=’ ou ‘::=’), immédiate (‘:::=’), par ajout (‘+=’) ou conditionnelle (‘?=’). Toutes les variables qui apparaissent dans variable-assignment sont évaluées dans le contexte de la cible ; ainsi, toute valeur de variable propre à la cible définie auparavant prend effet. Notez que cette variable est en réalité distincte de toute valeur « globale » : les deux variables n'ont pas besoin d'être du même type (récursive ou simple).
Les variables propres à une cible ont la même priorité que n'importe quelle autre variable de makefile. Les variables fournies sur la ligne de commande (et celles de l'environnement si l'option ‘-e’ est active) sont prioritaires. La spécification de la directive override permet de donner la préférence à la valeur de variable propre à la cible.
Les variables propres à une cible ont encore une particularité : lorsque vous définissez une variable propre à une cible, cette valeur de variable prend aussi effet pour tous les prérequis de cette cible, ainsi que tous leurs prérequis, et ainsi de suite (à moins que ces prérequis ne redéfinissent cette variable par leur propre valeur de variable propre à la cible). Ainsi, par exemple, une instruction telle que celle-ci :
prog : CFLAGS = -g prog : prog.o foo.o bar.o
affectera ‘-g’ à CFLAGS dans la recette de prog, mais elle affectera aussi ‘-g’ à CFLAGS dans les recettes qui créent prog.o, foo.o et bar.o, ainsi que dans toutes les recettes qui créent leurs prérequis.
Sachez qu'un prérequis donné ne sera construit qu'une seule fois au plus par invocation de make. Si le même fichier est un prérequis de plusieurs cibles, et que chacune de ces cibles a une valeur différente pour la même variable propre à la cible, alors la première cible à être construite provoquera la construction de ce prérequis, et le prérequis héritera de la valeur propre à la cible de la première cible. Il ignorera les valeurs propres à la cible de toutes les autres cibles.
En plus des valeurs de variables propres à une cible (voir Valeurs de variables propres à une cible), GNU make prend en charge les valeurs de variables propres à un motif. Sous cette forme, la variable est définie pour toute cible qui correspond au motif spécifié.
On définit une valeur de variable propre à un motif ainsi :
pattern … : variable-assignment
où pattern est un motif en ‘%’. Comme pour les valeurs de variables propres à une cible, lorsque plusieurs pattern sont indiqués, une valeur de variable propre au motif est créée individuellement pour chaque motif. variable-assignment peut être n'importe quelle forme d'affectation valide. Tout réglage de variable sur la ligne de commande sera prioritaire, à moins que override ne soit spécifié.
Par exemple,
%.o : CFLAGS = -O
attribue à CFLAGS la valeur ‘-O’ pour toutes les cibles correspondant au motif %.o.
Si une cible correspond à plusieurs motifs, les variables propres à un motif correspondantes ayant les radicaux (stem) les plus longs sont interprétées en premier. Il en résulte que les variables plus spécifiques l'emportent sur les plus génériques, par exemple :
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
lib/%.o: CFLAGS := -fPIC -g
%.o: CFLAGS := -g
all: foo.o lib/bar.o
Dans cet exemple, bien que la deuxième définition s'applique elle aussi à la cible lib/bar.o, c'est la première définition de la variable CFLAGS qui est utilisée pour mettre à jour lib/bar.o. Les variables propres à un motif qui donnent un radical de même longueur sont considérées dans l'ordre où elles ont été définies dans le makefile.
Les variables propres à un motif sont recherchées après les variables propres à une cible définies explicitement pour cette cible, et avant les variables propres à une cible définies pour la cible parente.
Comme décrit dans les sections précédentes, les variables de make sont héritées par les prérequis. Cette possibilité vous permet de modifier le comportement d'un prérequis selon les cibles qui ont provoqué sa reconstruction. Par exemple, vous pourriez définir une variable propre à une cible sur une cible debug ; l'exécution de ‘make debug’ ferait alors hériter cette variable à tous les prérequis de debug, tandis que la simple exécution de ‘make all’ (par exemple) n'effectuerait pas cette affectation.
Cependant, il arrive que vous ne vouliez pas qu'une variable soit héritée. Pour ces situations, make fournit le modificateur private. Bien que ce modificateur puisse s'utiliser avec n'importe quelle affectation de variable, il a le plus de sens avec les variables propres à une cible et propres à un motif. Toute variable marquée private est visible pour sa cible locale, mais n'est pas héritée par les prérequis de cette cible. Une variable globale marquée private est visible dans la portée globale, mais n'est héritée par aucune cible, et n'est donc visible dans aucune recette.
À titre d'exemple, considérez le makefile suivant :
EXTRA_CFLAGS = prog: private EXTRA_CFLAGS = -L/usr/local/lib prog: a.o b.o
Grâce au modificateur private, a.o et b.o n'héritent pas de l'affectation de la variable EXTRA_CFLAGS provenant de la cible prog.
GNU make prend en charge certaines variables ayant des propriétés spéciales.
MAKEFILE_LIST
Contient le nom de chaque makefile analysé par make, dans l'ordre où il a été analysé. Le nom est ajouté juste avant que make ne commence à analyser le makefile. Ainsi, si la première chose que fait un makefile est d'examiner le dernier mot de cette variable, ce sera le nom du makefile courant. Toutefois, une fois que le makefile courant a utilisé include, le dernier mot sera le makefile qui vient d'être inclus.
Si un makefile nommé Makefile a le contenu suivant :
name1 := $(lastword $(MAKEFILE_LIST))
include inc.mk
name2 := $(lastword $(MAKEFILE_LIST))
all:
@echo name1 = $(name1)
@echo name2 = $(name2)
alors vous devez vous attendre à voir la sortie suivante :
name1 = Makefile name2 = inc.mk
.DEFAULT_GOAL
Définit le but (goal) par défaut à utiliser si aucune cible n'a été spécifiée sur la ligne de commande (voir Arguments pour spécifier les buts). La variable .DEFAULT_GOAL vous permet de découvrir le but par défaut courant, de relancer l'algorithme de sélection du but par défaut en effaçant sa valeur, ou de définir explicitement le but par défaut. L'exemple suivant illustre ces cas :
# Query the default goal. ifeq ($(.DEFAULT_GOAL),) $(warning no default goal is set) endif .PHONY: foo foo: ; @echo $@ $(warning default goal is $(.DEFAULT_GOAL)) # Reset the default goal. .DEFAULT_GOAL := .PHONY: bar bar: ; @echo $@ $(warning default goal is $(.DEFAULT_GOAL)) # Set our own. .DEFAULT_GOAL := foo
Ce makefile affiche :
no default goal is set default goal is foo default goal is bar foo
Notez que l'affectation de plus d'un nom de cible à .DEFAULT_GOAL est invalide et provoquera une erreur.
MAKE_RESTARTS
Cette variable n'est définie que si cette instance de make a redémarré (voir Comment les Makefiles sont régénérés) : elle contient le nombre de fois où cette instance a redémarré. Notez que cela n'est pas la même chose que la récursion (comptée par la variable MAKELEVEL). Vous ne devez pas définir, modifier ni exporter cette variable.
MAKE_TERMOUTMAKE_TERMERR
Au démarrage, make vérifie si la sortie standard et la sortie d'erreur standard afficheront leur sortie sur un terminal. Si c'est le cas, il affecte respectivement à MAKE_TERMOUT et MAKE_TERMERR le nom du périphérique de terminal (ou true si cela ne peut pas être déterminé). Si elles sont définies, ces variables sont marquées pour l'exportation. Ces variables ne sont pas modifiées par make et elles ne le sont pas si elles sont déjà définies.
Ces valeurs peuvent être utilisées (en particulier en combinaison avec la synchronisation de la sortie (voir La sortie lors de l'exécution parallèle) pour déterminer si make lui-même écrit sur un terminal ; on peut par exemple les tester pour décider de forcer ou non les commandes de recette à produire une sortie colorisée.
Si vous invoquez un sous-make et redirigez sa sortie standard ou sa sortie d'erreur standard, il vous incombe de réinitialiser ou de désexporter ces variables également, si vos makefiles s'appuient sur elles.
.RECIPEPREFIXLe premier caractère de la valeur de cette variable est utilisé comme le caractère que make considère comme introduisant une ligne de recette. Si la variable est vide (ce qui est le cas par défaut), ce caractère est le caractère de tabulation standard. Par exemple, voici un makefile valide :
.RECIPEPREFIX = > all: > @echo Hello, world
La valeur de .RECIPEPREFIX peut être modifiée autant de fois que vous voulez ; une fois définie, elle reste en vigueur pour toutes les règles analysées par la suite, jusqu'à ce qu'elle soit modifiée.
.VARIABLESSe développe en une liste des noms de toutes les variables globales définies jusqu'à présent. Cela inclut les variables ayant des valeurs vides, ainsi que les variables intégrées (voir Variables utilisées par les règles implicites), mais n'inclut aucune variable définie uniquement dans un contexte propre à une cible. Notez que toute valeur que vous affectez à cette variable est ignorée ; elle renvoie toujours sa valeur spéciale.
.FEATURES
Se développe en une liste des fonctionnalités spéciales prises en charge par cette version de make. Les valeurs possibles incluent, sans s'y limiter :
Prend en charge les fichiers ar (archive) au moyen d'une syntaxe de nom de fichier particulière. Voir Utiliser make pour mettre à jour des fichiers d'archive.
Prend en charge le drapeau -L (--check-symlink-times). Voir Résumé des options.
Prend en charge les conditionnelles « else if » non imbriquées. Voir Syntaxe des conditionnelles.
Prend en charge la cible spéciale .EXTRA_PREREQS.
Prend en charge la syntaxe de cibles groupées pour les règles explicites. Voir Plusieurs cibles dans une règle.
Dispose de GNU Guile comme langage d'extension intégré. Voir Intégration de GNU Guile.
Prend en charge les builds parallèles améliorés par « jobserver ». Voir Exécution parallèle.
Prend en charge les builds parallèles améliorés par « jobserver » au moyen de tubes nommés. Voir Intégrer GNU make.
Prend en charge les objets chargeables dynamiquement pour créer des extensions personnalisées. Voir Charger des objets dynamiques.
Prend en charge la cible spéciale .NOTINTERMEDIATE. Voir Intégrer GNU make.
Prend en charge la cible spéciale .ONESHELL. Voir Utiliser un seul shell.
Prend en charge les prérequis d'ordonnancement seul (order-only). Voir Types de prérequis.
Prend en charge l'option de ligne de commande --output-sync. Voir Résumé des options.
Prend en charge l'expansion secondaire des listes de prérequis.
Prend en charge l'exportation des variables de make vers les fonctions shell.
Utilise la méthode du « radical le plus court » pour choisir le motif à utiliser parmi plusieurs possibilités. Voir Comment les motifs correspondent.
Prend en charge les affectations de variables propres à une cible et propres à un motif. Voir Valeurs de variables propres à une cible.
Prend en charge la directive undefine. Voir Annuler la définition de variables.
.INCLUDE_DIRS
Se développe en une liste des répertoires dans lesquels make recherche les makefiles inclus (voir Inclure d'autres Makefiles). Notez que modifier la valeur de cette variable ne change pas la liste des répertoires qui sont parcourus.
.EXTRA_PREREQSChaque mot de cette variable est un nouveau prérequis qui est ajouté aux cibles pour lesquelles elle est définie. Ces prérequis diffèrent des prérequis ordinaires en ce qu'ils n'apparaissent dans aucune des variables automatiques (voir Variables automatiques). Cela vous permet de définir des prérequis qui n'ont pas d'incidence sur la recette.
Considérez une règle pour lier un programme :
myprog: myprog.o file1.o file2.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
Supposons maintenant que nous voulions améliorer ce makefile pour qu'il relie aussi le programme si le compilateur est mis à jour. Nous pourrions ajouter le compilateur comme prérequis, mais nous devons nous assurer qu'il n'est pas passé comme argument à la commande d'édition de liens. Il nous faudrait quelque chose comme ceci :
myprog: myprog.o file1.o file2.o $(CC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
$(filter-out $(CC),$^) $(LDLIBS)
Considérons maintenant que nous avons plusieurs prérequis supplémentaires. Nous devons tous les exclure. En utilisant .EXTRA_PREREQS et une variable propre à une cible, nous obtenons une solution plus simple :
myprog: myprog.o file1.o file2.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
myprog: .EXTRA_PREREQS = $(CC)
Cette fonctionnalité est aussi utile si vous voulez ajouter des prérequis à un makefile que vous ne pouvez pas facilement modifier. Créez un nouveau fichier, par exemple extra.mk :
myprog: .EXTRA_PREREQS = $(CC)
puis invoquez make -f extra.mk -f Makefile.
Définir .EXTRA_PREREQS globalement ajoutera ces prérequis à chaque cible (qui ne l'a pas redéfini par sa propre valeur propre à la cible). Notez que make est assez intelligent pour ne pas ajouter un prérequis listé dans .EXTRA_PREREQS comme prérequis de lui-même.