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

2 Introduction aux Makefiles

Pour indiquer à make ce que vous voulez lui faire faire, il vous faut un fichier appelé makefile. La plupart du temps, le makefile indique à make comment compiler et lier un programme.

Dans ce chapitre, nous présentons un makefile simple qui décrit comment compiler et lier un éditeur de texte composé de huit fichiers source en C et de trois fichiers d'en-tête. Le makefile peut aussi indiquer à make comment exécuter d'autres commandes lorsqu'on le lui demande explicitement (par exemple, supprimer certains fichiers en guise de nettoyage). Pour voir un exemple de makefile plus complexe, consultez Exemple de Makefile complexe.

Lorsque make recompile l'éditeur, chaque fichier source en C qui a été modifié doit être recompilé. Si un fichier d'en-tête a changé, chaque fichier source en C qui inclut (#include) ce fichier d'en-tête doit être recompilé par sécurité. Chaque compilation produit un fichier objet correspondant au fichier source. Enfin, si l'un quelconque des fichiers source a été recompilé, tous les fichiers objets — qu'ils soient nouvellement créés ou conservés depuis des compilations précédentes — doivent être liés ensemble pour produire le nouvel éditeur exécutable.

2.1 À quoi ressemble une règle

Un makefile simple est constitué de « règles » ayant la forme suivante :

target … : prerequisitesrecipe
        …
        …

Une cible (target) est en général le nom d'un fichier produit par un programme ; des fichiers exécutables ou des fichiers objets en sont des exemples. Une cible peut aussi être le nom d'une action à accomplir, comme clean (voir Cibles factices (Phony)).

Un prérequis (prerequisite) est un fichier utilisé comme entrée pour créer la cible. Il est fréquent qu'une cible dépende de plusieurs fichiers.

Une recette (recipe) est une action que make réalise. Une recette peut comporter plusieurs commandes, sur la même ligne ou chacune sur sa propre ligne. [Important] vous devez placer un caractère de tabulation en début de chaque ligne de recette ! C'est une subtilité obscure qui piège les inattentifs. Si vous préférez préfixer vos recettes par un caractère autre que la tabulation, vous pouvez affecter un autre caractère à la variable .RECIPEPREFIX (voir Autres variables spéciales).

Habituellement, une recette se trouve dans une règle dotée de prérequis et sert à créer un fichier cible lorsque l'un des prérequis change. Toutefois, la règle qui spécifie une recette pour la cible n'a pas forcément de prérequis. Par exemple, la règle contenant la commande de suppression associée à la cible clean n'a pas de prérequis.

Une règle, donc, explique comment et quand refaire certains fichiers qui sont les cibles de cette règle. make exécute la recette sur les prérequis afin de créer ou de mettre à jour la cible. Une règle peut aussi expliquer comment et quand accomplir une action. Voir Écriture des règles.

Un makefile peut contenir d'autres textes que des règles, mais un makefile simple peut se contenter de ne contenir que des règles. Les règles réelles peuvent paraître un peu plus compliquées que dans ce gabarit, mais elles s'inscrivent toutes plus ou moins dans ce schéma.

2.2 Un Makefile simple

Voici un makefile direct qui décrit comment un fichier exécutable appelé edit dépend de huit fichiers objets qui, à leur tour, dépendent de huit fichiers source en C et de trois fichiers d'en-tête.

Dans cet exemple, tous les fichiers C incluent (#include) defs.h, mais seuls ceux qui définissent des commandes d'édition incluent command.h, et seuls les fichiers de bas niveau qui modifient le tampon de l'éditeur incluent buffer.h.

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit main.o kbd.o command.o display.o \
           insert.o search.o files.o utils.o

Ici, nous coupons chaque ligne longue en deux à l'aide d'une barre oblique inverse suivie d'un saut de ligne ; cela revient à écrire une seule longue ligne, mais c'est plus lisible. Voir Découper les lignes longues.

Pour utiliser ce makefile afin de créer le fichier exécutable appelé edit, tapez :

make

Pour utiliser ce makefile afin de supprimer du répertoire le fichier exécutable et tous les fichiers objets, tapez :

make clean

Dans le makefile de l'exemple, les cibles comprennent le fichier exécutable edit ainsi que les fichiers objets main.o, kbd.o, etc. Les prérequis sont des fichiers tels que main.c et defs.h. En réalité, chaque fichier .o est à la fois une cible et un prérequis. Les recettes comprennent cc -c main.c et cc -c kbd.c.

Lorsqu'une cible est un fichier, elle doit être recompilée ou reliée si l'un de ses prérequis change. De plus, tout prérequis qui est lui-même généré automatiquement doit être mis à jour en premier. Dans cet exemple, edit dépend de chacun des huit fichiers objets ; le fichier objet main.o dépend du fichier source main.c et du fichier d'en-tête defs.h.

Une recette peut suivre chaque ligne qui contient une cible et ses prérequis. Ces recettes indiquent comment mettre à jour le fichier cible. Un caractère de tabulation (ou tout caractère spécifié par la variable .RECIPEPREFIX ; voir Autres variables spéciales) doit figurer au début de chaque ligne de la recette, afin de distinguer les recettes des autres lignes du makefile. (Gardez à l'esprit que make ne sait rien de la manière dont fonctionnent les recettes. C'est à vous de fournir des recettes qui mettront correctement à jour le fichier cible. Tout ce que fait make, c'est exécuter la recette que vous avez spécifiée lorsque le fichier cible doit être mis à jour.)

La cible clean n'est pas un fichier, mais simplement le nom d'une action. Comme on ne souhaite pas en temps normal accomplir les actions de cette règle, clean n'est le prérequis d'aucune autre règle. Par conséquent, make n'en fait jamais rien à moins que vous ne le lui demandiez explicitement. Notez que non seulement cette règle n'est le prérequis d'aucune autre, mais qu'elle n'a elle-même aucun prérequis ; le seul but de cette règle est donc d'exécuter la recette spécifiée. Les cibles qui ne renvoient pas à des fichiers mais ne sont que des actions sont appelées cibles factices (phony). Pour en savoir plus sur ce type de cible, voir Cibles factices (Phony) ; pour savoir comment amener make à ignorer les erreurs de rm ou de toute autre commande, voir Erreurs dans les recettes.

2.3 Comment make traite un Makefile

Par défaut, make commence par la première cible (à l'exception des cibles dont le nom commence par ., sauf si elles contiennent aussi un ou plusieurs /). C'est ce qu'on appelle le but par défaut. (Les buts sont les cibles que make s'efforce en fin de compte de mettre à jour.) Vous pouvez modifier ce comportement à l'aide de la ligne de commande (voir Arguments pour spécifier les buts) ou de la variable spéciale .DEFAULT_GOAL (voir Autres variables spéciales).

Dans l'exemple simple de la section précédente, le but par défaut est de mettre à jour le programme exécutable edit ; c'est pourquoi nous avons placé cette règle en premier.

Ainsi, lorsque vous donnez la commande :

make

make lit le makefile du répertoire courant et commence par traiter la première règle. Dans l'exemple, cette règle sert à relier edit ; mais avant que make puisse traiter complètement cette règle, il doit traiter les règles des fichiers dont edit dépend — ici les fichiers objets. Chacun de ces fichiers est traité selon sa propre règle. Ces règles indiquent de mettre à jour chaque fichier .o en compilant son fichier source. La recompilation doit avoir lieu si le fichier source, ou l'un des fichiers d'en-tête nommés comme prérequis, est plus récent que le fichier objet, ou si le fichier objet n'existe pas.

Les autres règles sont traitées parce que leurs cibles apparaissent comme prérequis du but. Si une autre règle n'est pas une dépendance du but (ni de ce dont il dépend, etc.), cette règle n'est pas traitée, sauf si vous demandez à make de le faire (avec une commande telle que make clean).

Avant de recompiler un fichier objet, make envisage de mettre à jour ses prérequis, à savoir le fichier source et les fichiers d'en-tête. Ce makefile ne spécifie rien à faire pour eux — les fichiers .c et .h ne sont les cibles d'aucune règle —, aussi make ne fait rien pour ces fichiers. En revanche, make mettrait à jour les programmes C générés automatiquement, comme ceux produits par Bison ou Yacc, selon leurs propres règles à ce moment-là.

Une fois recompilés les fichiers objets qui en avaient besoin, make décide s'il faut relier edit. Cela doit être fait si le fichier edit n'existe pas, ou si l'un des fichiers objets est plus récent que lui. Si un fichier objet vient d'être recompilé, il est désormais plus récent que edit, donc edit est relié.

Ainsi, si nous modifions le fichier insert.c et exécutons make, make compilera ce fichier pour mettre à jour insert.o, puis liera edit. Si nous modifions le fichier command.h et exécutons make, make recompilera les fichiers objets kbd.o, command.o et files.o, puis liera le fichier edit.

2.4 Les variables simplifient les Makefiles

Dans l'exemple précédent, nous avons dû énumérer deux fois tous les fichiers objets dans la règle de edit (rappelée ici) :

edit : main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
        cc -o edit main.o kbd.o command.o display.o \
                   insert.o search.o files.o utils.o

Une telle duplication est source d'erreurs ; si un nouveau fichier objet est ajouté au système, on risque de l'ajouter à une liste et de l'oublier dans l'autre. On peut éliminer ce risque et simplifier le makefile en utilisant une variable. Les variables permettent de définir une chaîne de texte une seule fois et de la substituer ensuite à plusieurs endroits (voir Comment utiliser les variables).

C'est une pratique courante que tout makefile possède une variable nommée objects, OBJECTS, objs, OBJS, obj ou OBJ, qui est une liste de tous les noms de fichiers objets. On définirait une telle variable objects à l'aide d'une ligne comme celle-ci dans le makefile :

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

Ensuite, à chaque endroit où l'on veut placer une liste des noms de fichiers objets, on peut substituer la valeur de la variable en écrivant $(objects) (voir Comment utiliser les variables).

Voici à quoi ressemble le makefile simple complet lorsqu'on utilise une variable pour les fichiers objets :

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

edit : $(objects)
        cc -o edit $(objects)
main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
insert.o : insert.c defs.h buffer.h
        cc -c insert.c
search.o : search.c defs.h buffer.h
        cc -c search.c
files.o : files.c defs.h buffer.h command.h
        cc -c files.c
utils.o : utils.c defs.h
        cc -c utils.c
clean :
        rm edit $(objects)

2.5 Laisser make déduire les recettes

Il n'est pas nécessaire d'écrire en détail les recettes de compilation de chacun des fichiers source en C, car make peut les déduire lui-même. Il dispose d'une règle implicite pour mettre à jour un fichier .o à partir d'un fichier .c portant le nom correspondant, à l'aide d'une commande cc -c. Par exemple, make utilisera la recette cc -c main.c -o main.o pour compiler main.c en main.o. On peut donc omettre les recettes dans les règles des fichiers objets. Voir Utiliser les règles implicites.

Lorsqu'un fichier .c est ainsi utilisé automatiquement, il est aussi automatiquement ajouté à la liste des prérequis. On peut donc omettre les fichiers .c des prérequis, à condition d'omettre la recette.

Voici l'exemple complet, avec ces deux modifications et la variable objects suggérée plus haut :

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

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

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
        rm edit $(objects)

C'est ainsi que nous écririons le makefile dans la pratique réelle. (Les complications liées à clean sont décrites ailleurs. Voir Cibles factices (Phony) et Erreurs dans les recettes.)

Parce que les règles implicites sont si commodes, elles sont importantes. Vous les verrez utilisées fréquemment.

2.6 Un autre style de Makefile

Lorsque les objets d'un makefile sont créés uniquement par des règles implicites, un autre style de makefile devient possible. Dans ce style, on regroupe les entrées par prérequis plutôt que par cible. En voici un exemple :

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o

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

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

Ici, defs.h est donné comme prérequis de tous les fichiers objets ; command.h et buffer.h sont les prérequis des fichiers objets spécifiques énumérés pour chacun d'eux.

Que cela soit préférable est affaire de goût : c'est plus compact, mais certaines personnes n'aiment pas ce style car elles trouvent plus clair de regrouper en un seul endroit toutes les informations concernant chaque cible.

2.7 Des règles pour nettoyer le répertoire

Compiler un programme n'est pas la seule chose pour laquelle on peut vouloir écrire des règles. Les makefiles indiquent couramment comment effectuer quelques autres tâches que la compilation d'un programme : par exemple, comment supprimer tous les fichiers objets et exécutables pour que le répertoire soit clean (propre).

Voici comment nous pourrions écrire une règle make pour nettoyer notre éditeur d'exemple :

clean:
        rm edit $(objects)

En pratique, on pourrait vouloir écrire la règle d'une manière un peu plus élaborée afin de gérer des situations imprévues. On procéderait ainsi :

.PHONY : clean
clean :
        -rm edit $(objects)

Cela empêche make d'être perturbé par un fichier réel nommé clean et le fait poursuivre malgré les erreurs de rm. (Voir Cibles factices (Phony) et Erreurs dans les recettes.)

Une règle de ce genre ne devrait pas être placée au début du makefile, car nous ne voulons pas qu'elle s'exécute par défaut ! Ainsi, dans le makefile de l'exemple, nous voulons que la règle de edit, qui recompile l'éditeur, reste le but par défaut.

Comme clean n'est pas un prérequis de edit, cette règle ne s'exécutera pas du tout si l'on lance la commande make sans argument. Pour faire s'exécuter la règle, il faut taper make clean. Voir Comment exécuter make.


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