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
logo imprimer
Licence : CC by-nd

 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 :

  1. all: mon_livre
  2. mon_livre: chapitre1 chapitre2
  3. chapitre1: figures-chap1
  4. chapitre2: figures-chap2

Télécharger

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 :

  1. objet1: source1
  2. <tabulation> bigloo -c source1 -o objet1

Télécharger

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 :

  1. # il est souvent préférable de définir les outils
  2. # par des variables comme ceci :
  3.  
  4. BIGLOO = bigloo
  5.  
  6. # et de les utiliser ainsi, pour des raisons expliquées plus bas :
  7.  
  8. <tabulation> $(BIGLOO) -c source1 -o objet1

Télécharger

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 :

  1. lire-outils.o: lire-outils.scm

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 :

  1. lire-swissprot: lire-outils.o lire-swiss-seq.o choix-fichier.o

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 :

  1. all: lire-swissprot

 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 :

  1. (cd <mon repertoire> ; ${MAKE} [<ma cible>])

${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 :

  1. bigloo -afile access-file.scm -c lire-outils.scm

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) :

  1. bigloo lire-outils.o lire-swiss-seq.o choix-fichier.o \
  2. -o lire-swissprot

Télécharger

sans oublier la tabulation.

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

  1. all: lire-swissprot
  2. choix-fichier.o : choix-fichier.scm
  3. bigloo -afile access-file.scm -c choix-fichier.scm
  4. lire-swiss-seq.o : lire-swiss-seq.scm
  5. bigloo -afile access-file.scm -c lire-swiss-seq.scm
  6. lire-outils.o : lire-outils.scm
  7. bigloo -afile access-file.scm -c lire-outils.scm
  8. lire-swissprot: lire-outils.o lire-swiss-seq.o choix-fichier.o
  9. bigloo lire-swiss-seq.o lire-outils.o choix-fichier.o -o lire-swissprot

Télécharger

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 :

  1. %.o: %.scm
  2. bigloo -afile access-file.scm -c $< -o $@

Télécharger

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 :

  1. all: lire-swissprot
  2. %.o: %.scm
  3. bigloo -afile access-file.scm -c $< -o $@
  4. lire-swissprot: lire-outils.o lire-swiss-seq.o choix-fichier.o
  5. bigloo lire-swiss-seq.o choix-fichier.o lire-outils.o -o lire-swissprot

Télécharger

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

  1. Makefile choix-fichier.scm lire-swiss-seq.scm
  2. access-file.scm lire-outils.scm

Télécharger

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 :

  1. .SUFFIXES : .o .c .cc .scm
  2. BGL_SOURCES = peuple-iter.scm call-os.scm
  3. BGL_OBJECTS = peuple-iter.o call-os.o
  4. SOURCES = $(BGL_SOURCES) create_db.cc read_db.cc \
  5. write_db.cc copy_seq.cc \
  6. xalloc.c liste_db.cc query_db.cc stub.cc
  7. CXX_OBJECTS = read_db.o create_db.o write_db.o \
  8. copy_seq.o liste_db.o query_db.o stub.o
  9. C_OBJECTS = xalloc.o
  10. OBJECTS = $(CXX_OBJECTS) $(C_OBJECTS) $(BGL_OBJECTS)
  11. EXECUTABLE = iter-os
  12. CCC =/usr/ucb/cxx
  13. BIGLOO = bigloo
  14. CFLAGS = -O3
  15. CPPFLAGS =
  16. BGL_FLAGS = -Obench -farithmetic -rm
  17. BGL_LDFLAGS = -lstatic-bigloo
  18. CC=gcc
  19. all: $(EXECUTABLE) Makefile
  20. .cc.o:
  21. $(CCC) $(CPPFLAGS) -c $< -o $@
  22. .c.o:
  23. $(CC) $(CFLAGS) -c $< -o $@
  24. .scm.o:
  25. $(BIGLOO) -afile access - file.scm $(BGL_FLAGS) -c $< -o $@
  26. $(EXECUTABLE): $(OBJECTS)
  27. $(BIGLOO) $(BGL_LDFLAGS) -o $@ $(OBJECTS)
  28. clean :
  29. rm -f $(EXECUTABLE) $(OBJECTS)

Télécharger

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.

Notes :

[1Voir le manuel d’Oram [1]. Des outils similaires à make sont disponibles pour d’autres systèmes. Certains sont dotés d’une interface graphique, mais ils ont l’inconvénient de ne pas être aussi programmables que make. Les systèmes de programmation destinés au langage Java utilisent des outils spécifiques, notamment Maven et Ant, qui réalisent des fonctions analogues.

[2L’enregistrement de répertoire qui permet d’accéder à un fichier pointe sur une structure de données appelée i-nœud qui contient les caractéristiques statiques du fichier, notamment sa date de création, sa date de dernière modification et sa date de dernière consultation. Le fonctionnement de make repose sur ces informations.

[3Notons que le signe $ a, ici et en d’autres circonstances, une valeur syntaxique pour make. Or il a aussi une valeur syntaxique pour le shell Unix, par exemple nous pouvons désigner notre répertoire racine par $HOME, ce qui peut être utile notamment dans un Makefile. Pour éviter une ambiguïté dans ce cas, et par convention, dans un Makefile tout emploi du signe $ pour une commande du shell se fera en écrivant $$ (deux signes $).

Forum
Répondre à cet article


pucePlan du site puceContact puceMentions légales puceEspace rédacteurs puce

RSS

2004-2017 © Site WWW de Laurent Bloch - Tous droits réservés
Site réalisé sous SPIP
avec le squelette ESCAL-V3
Version : 3.86.35