Site WWW de Laurent Bloch
Slogan du site

ISSN 2271-3905
Cliquez ici si vous voulez visiter mon autre site, orienté vers des sujets moins techniques.

Pour recevoir (au plus une fois par semaine) les nouveautés de ce site, indiquez ici votre adresse électronique :

Systèmes de configuration
Make et Makefile
Article mis en ligne le 8 juillet 2010
dernière modification le 2 mai 2016

par Laurent Bloch

make

Le système Unix (comme d’autres) possède un système de construction de programmes (on dit aussi un configurateur) nommé make. Au moyen de ce système on peut décrire les fichiers source et autres nécessaires à la construction d’un programme, les dépendances entre fichiers, les commandes nécessaires à la compilation des sources et à la construction des exécutables, ainsi que beaucoup d’autres choses.

L’usage habituel de make consiste à déterminer automatiquement quelles parties d’un grand programme (ou d’un texte composé avec LaTeX) doivent être recompilées après que certains fichiers source aient été modifiés, et à lancer les commandes appropriées pour ce faire.

Make est en fait un système plus général que seulement un configurateur de programmes. C’est un langage de programmation adapté à la résolution de systèmes de contraintes. Un programme make (généralement contenu dans un fichier nommé Makefile) comporte essentiellement deux types d’instructions : des règles et des commandes.

Une règle énonce le nom d’une cible à atteindre (par exemple le nom d’un fichier exécutable) suivi des noms des cibles préliminaires dont elle dépend et qu’il faudra avoir atteintes avant d’espérer atteindre la cible courante. Cela s’écrit par exemple ainsi, le nom de la cible est en début de ligne suivi de :, les noms des cibles intermédiaires sont dans la suite de la ligne :

Une ligne qui contient une instruction de type règle est suivie de lignes d’instructions de type commande qui formulent les actions à effectuer pour atteindre la cible de la règle :

Attention une ligne de commande commence toujours par le caractère tabulation, c’est un des moindres pièges de make que de donner une signification syntaxique à un caractère invisible.

Il y a aussi des définitions de variables et des lignes de commentaires :

Make peut être utilisé pour configurer d’autres objets que des programmes : toute situation où certains fichiers-cibles doivent être mis à jour automatiquement à partir de fichiers-origines à chaque modification de ces derniers et selon certaines règles est susceptible d’être automatisée au moyen de make. Une cible n’est d’ailleurs pas nécessairement un fichier, il peut s’agir plus généralement d’un ensemble de tâches à réaliser désigné par un nom symbolique.

Le langage de make est loin d’être simple et sa description complète excéderait les limites de ce document [1]. Nous nous contenterons d’indiquer quelques moyens propres à rédiger un programme make simple.

Un Makefile simple pour construire un programme

Un programme make est contenu dans un fichier nommé par convention Makefile. En général on place dans un même répertoire les fichiers qui servent à la construction du programme et le Makefile qui indique comment le construire. Les exemples ci-dessous supposent cette condition remplie, et que ce répertoire est le répertoire courant quand on tape la commande make.

Nous prendrons comme exemple la construction d’un programme en Scheme compilé avec le compilateur Bigloo. Notre Makefile va comporter des lignes de texte essentiellement de deux sortes, décrites par les deux alinéas suivants.

Lignes de règles

Des lignes de règles, ou lignes de dépendance, énumèrent, pour une cible à produire, les cibles préalables (souvent des fichiers) dont elle dépend. Si un des fichiers dont dépend la cible est modifié depuis sa dernière construction [2], il faudra la reconstruire. Ainsi, la règle de dépendance pour lire-outils.o est très simple :

Le fichier-objet lire-outils.o dépend du fichier-source lire-outils.scm. Si ce dernier est modifié (la création est un cas particulier de modification) il faut reconstruire l’objet.

Une ligne de règle se reconnaît au fait qu’elle comporte le signe «  : », qui sépare la cible à sa gauche des pré-requis à sa droite :

La première règle du Makefile joue un rôle particulier, elle mentionne une cible toujours visée. Il convient de mettre en tête soit la règle qui déclenche la construction du programme résultant, soit une règle qui se contente d’énumérer les exécutables souhaités :

Lignes de commandes

Des lignes de commandes énumèrent les actions à effectuer pour produire la cible. Dans le cas simple que nous allons décrire ce seront des compilations et des éditions de liens, mais toute commande Unix serait acceptable.

Attention, chaque ligne de commande s’exécute dans un environnement propre au sens Unix du terme ; si par exemple nous voulons visiter des sous-répertoires où des commandes make déclencheront d’autres actions, et ainsi de suite récursivement, placer la commande cd appropriée sur une ligne et passer à la ligne suivante pour décrire les actions ne produira pas le résultat escompté. Il faut utiliser la syntaxe du shell pour la composition séquentielle des processus :

${MAKE} est une variable standard de make qui désigne make. À cet endroit nous voulons exécuter make, mais si nous écrivons simplement make nous n’aurons la garantie ni que nous invoquons bien la même version de make que celle qui a lancé le Makefile, ni surtout qu’elle va s’exécuter avec les mêmes paramètres. ${MAKE} nous donne ces garanties [3].

Une ligne de commande commence par un caractère de tabulation. Comme make reconnaît les lignes de commande à ce caractère, il faut prendre garde à ne pas l’oublier pour les lignes de commande et à ne pas en mettre ailleurs. Et avoir remarqué que le « copier-coller » à la souris de votre interface graphique préférée transforme parfois les tabulations en espaces blancs.

La ligne de commande pour la cible lire-outils.o sera :

Voici la ligne de commande pour l’édition de liens qui construit le programme (la règle pour le programme complet est plus longue, pour aller à la ligne il faut placer un caractère « \ » à la fin, immédiatement suivi d’un retour-chariot) :

sans oublier la tabulation.

Notre Makefile complet peut donc s’écrire comme suit :

Généraliser et factoriser

Règles génériques

Les trois règles de construction des objets sont extrêmement similaires. L’esprit de la programmation rechigne devant l’inélégance de cette répétition. Make nous offre la possibilité d’écrire des règles génériques :

ce qui signifie : pour tout fichier-cible avec un nom de la forme %.o, % étant un préfixe quelconque, le fichier %.scm s’il existe est un bon prérequis, et alors l’action décrite par la ligne de commande suivante est exécutée (si quelque-chose a été modifié dans le prérequis). $< est une variable dont la valeur est la liste des prérequis qui ont déclenché la règle (ici le fichier %.scm), $@ est une variable qui désigne la cible courante. Nous obtenons donc le Makefile suivant :

Ceci rédigé il nous suffit de nous placer dans un répertoire contenant les fichiers suivants :

et taper la commande make suffira à construire notre programme lire-swissprot.

Règles de suffixes

Une autre façon d’écrire pour make des règles plus générales (et donc moins nombreuses) est d’utiliser les suffixes des noms de fichiers pour déterminer les règles à leur appliquer. En général sous Unix les fichiers source sont suffixés par .scm pour les programmes Bigloo, .c pour les programmes C, .cc pour les programmes C++, .f pour les programmes Fortran, etc. Les compilateurs produisent des fichiers objet suffixés par .o. Ceci nous permet d’écrire des règles selon une syntaxe que nous allons illustrer par un exemple.

Supposons que nous voulions construire un programme nommé iter-os à partir de fichiers source Bigloo, C et C++. C’est l’objet du Makefile ci-dessous :

Ce Makefile commence à ressembler à du travail professionnel. La première ligne définit les suffixes que nous allons utiliser (en fait cette ligne n’est indispensable que pour définir le suffixe des fichiers Bigloo, les autres sont définis par défaut). Les listes de fichiers objet sont définis par des variables pour éviter les définitions multiples qui aboutissent à des incohérences. La variable définie par BGL_OBJECTS = ... est invoquée ultérieurement sous le nom ${BGL_OBJECTS}. Les accolades { et } peuvent être remplacées (sauf cas particulier) par des parenthèses ( et ).

Les drapeaux des compilateurs sont définis par des variables vides par méfiance à l’égard de make, qui souvent prévoit des valeurs par défaut. Les noms des compilateurs sont définis par des variables pour les mêmes raisons que nous utilisions $MAKE ci-dessus pour désigner make.

La règle .cc.o décrit comment faire un fichier objet .o à partir d’un fichier source .cc. La variable $< désigne le fichier prérequis à partir duquel on construit la cible. $@ est comme ci-dessus une variable qui désigne la cible courante.

La règle clean permet de détruire les fichiers intermédiaires et finals pour repartir des sources quand on a fait des bêtises. make clean sera la façon de l’invoquer.

Ces quelques paragraphes sont loin d’épuiser le sujet make. Ils devraient vous permettre d’écrire des Makefiles simples et de lire ceux que vous trouverez dans les distributions de logiciels récupérées sur le réseau. Pour de plus amples détails, voir le manuel d’Oram. make

Références

[1]
Andrew Oram Steve Talbott. Managing Projects with make. O’Reilly, Sebastopol, USA, 1991.