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

7 Parties conditionnelles des makefiles

Une directive conditionnelle permet d'exécuter ou d'ignorer une partie d'un makefile selon la valeur des variables. Une conditionnelle peut comparer la valeur d'une variable à celle d'une autre, ou la valeur d'une variable à une chaîne constante. Les conditionnelles contrôlent ce que make « voit » réellement dans le makefile ; elles ne peuvent pas être utilisées pour contrôler les recettes (recipes) au moment de leur exécution.

7.1 Exemple de conditionnelle

L'exemple de conditionnelle suivant indique à make d'utiliser un certain ensemble de bibliothèques si la variable CC vaut « gcc », et un autre ensemble de bibliothèques sinon. Cela fonctionne en contrôlant laquelle de deux lignes de recette (recipe) sera utilisée pour la règle. Le résultat est que « CC=gcc » passé comme argument à make change non seulement le compilateur utilisé, mais aussi les bibliothèques liées.

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
else
        $(CC) -o foo $(objects) $(normal_libs)
endif

Cette conditionnelle utilise trois directives : un ifeq, un else et un endif.

La directive ifeq commence la conditionnelle et spécifie la condition. Elle prend deux arguments, séparés par une virgule et entourés de parenthèses. La substitution de variables est effectuée sur les deux arguments, puis ceux-ci sont comparés. Les lignes du makefile qui suivent le ifeq sont exécutées si les deux arguments concordent ; sinon, elles sont ignorées.

La directive else fait exécuter les lignes qui la suivent lorsque la condition précédente a échoué. Dans l'exemple ci-dessus, cela signifie que la seconde commande de liaison de remplacement est utilisée chaque fois que la première n'est pas utilisée. La présence d'un else dans une conditionnelle est facultative.

La directive endif termine la conditionnelle. Toute conditionnelle doit se terminer par un endif. Du texte de makefile inconditionnel suit ensuite.

Comme l'illustre cet exemple, les conditionnelles agissent au niveau textuel : les lignes de la conditionnelle sont traitées comme faisant partie du makefile, ou ignorées, selon la condition. C'est pourquoi les unités syntaxiques plus larges du makefile, comme les règles, peuvent franchir le début ou la fin de la conditionnelle.

Lorsque la variable CC a la valeur « gcc », l'exemple ci-dessus a cet effet :

foo: $(objects)
        $(CC) -o foo $(objects) $(libs_for_gcc)

Lorsque la variable CC a toute autre valeur, l'effet est le suivant :

foo: $(objects)
        $(CC) -o foo $(objects) $(normal_libs)

On peut obtenir des résultats équivalents d'une autre manière, en rendant conditionnelle une affectation de variable, puis en utilisant la variable de façon inconditionnelle :

libs_for_gcc = -lgnu
normal_libs =

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif

foo: $(objects)
        $(CC) -o foo $(objects) $(libs)

7.2 Syntaxe des conditionnelles

La syntaxe d'une conditionnelle simple sans else est la suivante :

conditional-directive
text-if-true
endif

text-if-true peut être n'importe quelles lignes de texte, qui seront considérées comme faisant partie du makefile si la condition est vraie. Si la condition est fausse, aucun texte n'est utilisé à la place.

La syntaxe d'une conditionnelle complexe est la suivante :

conditional-directive
text-if-true
else
text-if-false
endif

ou :

conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif

Il peut y avoir autant de clauses « else conditional-directive » que nécessaire. Dès qu'une condition donnée est vraie, son text-if-true est utilisé et aucune autre clause ne l'est ; si aucune condition n'est vraie, c'est text-if-false qui est utilisé. text-if-true et text-if-false peuvent comporter un nombre quelconque de lignes de texte.

La syntaxe de conditional-directive est la même, que la conditionnelle soit simple ou complexe, et qu'elle suive un else ou non. Il existe quatre directives différentes qui testent des conditions différentes. En voici un tableau :

ifeq (arg1, arg2)
ifeq 'arg1' 'arg2'
ifeq "arg1" "arg2"
ifeq "arg1" 'arg2'
ifeq 'arg1' "arg2"

Développe toutes les références de variables dans arg1 et arg2, puis les compare. Si elles sont identiques, text-if-true est actif ; sinon, c'est text-if-false, s'il existe, qui est actif.

On veut souvent vérifier si une variable a une valeur non vide. Lorsque la valeur résulte de développements complexes de variables et de fonctions, des développements que vous considéreriez comme vides peuvent en réalité contenir des caractères d'espacement et ne sont donc pas vus comme vides. Cependant, vous pouvez utiliser la fonction strip (voir Fonctions de substitution et d'analyse de chaînes) pour éviter d'interpréter un espace blanc comme une valeur non vide. Par exemple :

ifeq ($(strip $(foo)),)
text-if-empty
endif

évaluera text-if-empty même si le développement de $(foo) contient des caractères d'espacement.

ifneq (arg1, arg2)
ifneq 'arg1' 'arg2'
ifneq "arg1" "arg2"
ifneq "arg1" 'arg2'
ifneq 'arg1' "arg2"

Développe toutes les références de variables dans arg1 et arg2, puis les compare. Si elles sont différentes, text-if-true est actif ; sinon, c'est text-if-false, s'il existe, qui est actif.

ifdef variable-name

La forme ifdef prend en argument le nom d'une variable, et non une référence à une variable. Si la valeur de cette variable est non vide, text-if-true est actif ; sinon, c'est text-if-false, s'il existe, qui est actif. Les variables qui n'ont jamais été définies ont une valeur vide. Le texte variable-name est développé, de sorte qu'il peut s'agir d'une variable ou d'une fonction qui se développe en le nom d'une variable. Par exemple :

bar = true
foo = bar
ifdef $(foo)
frobozz = yes
endif

La référence de variable $(foo) est développée, donnant bar, qui est considéré comme le nom d'une variable. La variable bar elle-même n'est pas développée, mais sa valeur est examinée pour déterminer si elle est non vide.

Notez que ifdef teste seulement si une variable a une valeur. Il ne développe pas la variable pour voir si cette valeur est non vide. Par conséquent, les tests utilisant ifdef renvoient vrai pour toutes les définitions, sauf celles du type foo =. Pour tester une valeur vide, utilisez ifeq ($(foo),). Par exemple, si l'on écrit :

bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif

« frobozz » prend la valeur « yes », tandis que :

foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif

« frobozz » prend la valeur « no ».

ifndef variable-name

Si la variable variable-name a une valeur vide, text-if-true est actif ; sinon, c'est text-if-false, s'il existe, qui est actif. Les règles de développement et de test de variable-name sont identiques à celles de la directive ifdef.

Des espaces supplémentaires sont autorisés et ignorés en début de la ligne de directive conditionnelle, mais une tabulation n'est pas autorisée. (Si la ligne commence par une tabulation, elle sera considérée comme faisant partie d'une recette pour une règle.) À part cela, des espaces ou des tabulations supplémentaires peuvent être insérés sans effet n'importe où, sauf à l'intérieur du nom de la directive ou à l'intérieur d'un argument. Un commentaire commençant par « # » peut apparaître en fin de ligne.

Les deux autres directives qui jouent un rôle dans une conditionnelle sont else et endif. Chacune de ces directives s'écrit en un seul mot, sans arguments. Des espaces supplémentaires sont autorisés et ignorés en début de ligne, de même que des espaces ou des tabulations en fin de ligne. Un commentaire commençant par « # » peut apparaître en fin de ligne.

Les conditionnelles déterminent quelles lignes du makefile make utilise. Si la condition est vraie, make lit les lignes de text-if-true comme faisant partie du makefile ; si la condition est fausse, make ignore complètement ces lignes. Il en découle que les unités syntaxiques du makefile, comme les règles, peuvent être scindées sans danger au début ou à la fin de la conditionnelle.

make évalue les conditionnelles au moment où il lit un makefile. Par conséquent, vous ne pouvez pas utiliser de variables automatiques dans les tests des conditionnelles, car elles ne sont pas définies tant que les recettes ne sont pas exécutées (voir Variables automatiques).

Pour éviter une confusion intolérable, il n'est pas permis de commencer une conditionnelle dans un makefile et de la terminer dans un autre. Vous pouvez toutefois écrire une directive include à l'intérieur d'une conditionnelle, à condition de ne pas tenter de terminer la conditionnelle dans le fichier inclus.

7.3 Conditionnelles qui testent les drapeaux

Vous pouvez écrire une conditionnelle qui teste des drapeaux de commande de make tels que « -t » en utilisant la variable MAKEFLAGS conjointement avec la fonction findstring (voir Fonctions de substitution et d'analyse de chaînes). Cela est utile lorsque touch ne suffit pas à faire paraître un fichier à jour.

Rappelez-vous que MAKEFLAGS regroupe toutes les options à une lettre (comme « -t ») dans le premier mot, et que ce mot sera vide si aucune option à une lettre n'a été donnée. Pour composer avec cela, il est utile d'ajouter une valeur au début afin de garantir la présence d'un mot : par exemple « -$(MAKEFLAGS) ».

La fonction findstring détermine si une chaîne apparaît comme sous-chaîne d'une autre. Si vous voulez tester le drapeau « -t », utilisez « t » comme première chaîne et le premier mot de MAKEFLAGS comme autre chaîne.

Par exemple, voici comment faire en sorte d'utiliser « ranlib -t » pour achever de marquer un fichier d'archive comme étant à jour :

archive.a: …
ifneq (,$(findstring t,$(firstword -$(MAKEFLAGS))))
        +touch archive.a
        +ranlib -t archive.a
else
        ranlib archive.a
endif

Le préfixe « + » marque ces lignes de recette comme « récursives », de sorte qu'elles seront exécutées malgré l'emploi du drapeau « -t ». Voir Utilisation récursive de make.


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