Créer des Makefiles : un mini-tutoriel

Gazette Linux n°83 — Octobre 2002

Tedi Heriyanto

Article paru dans le n°83 de la Gazette Linux d'octobre 2002.

Traduction française par Joëlle Cornavin .

Relecture de la traduction française par Encolpe Degoute .

Article publié sous Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.


Table des matières
1. Introduction
2. Pourquoi avons-nous besoin d'un Makefile ?
3. Structure du Makefile
4. Un exemple de Makefile
5. Commentaire
6. Cible particulière (phony target) [2]
7. Variable

Cet article offre des indications pour créer votre propre Makefile. Il explique pourquoi nous avons besoin d'un Makefile et dresse la liste de plusieurs éléments à prendre en considération lorsqu'on le crée.


1. Introduction

Imaginez que vous développez un programme appelé foo, qui se compose de cinq fichiers d'en-tête, 1.h, 2.h, 3.h, 4.h et 5.h, six fichiers de code source en langage C, 1.cpp à 5.cpp et un fichier main.cpp.

Note

Il n'est pas recommandé d'utiliser un tel système de nommage de fichiers dans la réalité.

Supposez que vous trouviez un bogue dans le fichier 2.cpp, et que vous deviez le corriger. Pour pouvoir obtenir un nouveau programme foo, il faut recompiler tous les fichiers, les en-têtes et le code source, même si vous changez juste un fichier.

Alors que faire ? Y a-t-il une solution à ce problème ?

Ne vous faites pas de souci. Les informaticiens y sont confrontés depuis des années. Ils ont donc développé un programme appelé make. Au lieu de compiler la totalité du code source, ce programme ne construit que le code source qui a subi des changements. Si en vous faites dans le fichier 2.cpp, alors make construira uniquement celui-ci. Sympathique, n'est-ce pas ?

Voici d'autres raisons pour lesquelle nous avons besoin de make [1] :


2. Pourquoi avons-nous besoin d'un Makefile ?

Bien que la commande make soir très utile, elle ne peut fonctionner sans les instructions que nous, programmeurs, lui donnons. Les instructions make sont stockées dans un fichier texte, habituellement appelé makefile et contient les commandes que make doit traiter.

Ce fichier est normalement nommé makefile ou Makefile. Par convention, les programmes GNU ont appelé leur makefile, Makefile, car il est facile de voir (si vous faites un ls, ce fichier est en principe au début de la liste). Si vous lui donnez un autre nom, veillez simplement à ajouter l'option d'inclusion -f à make pour faire savoir à la commande que vous l'utilisez.

Par exemple, si nous avons un makefile appelé bejo, alors la commande que nous utilison pour donner l'ordre à make de traiter ce fichier est :



make -f bejo


3. Structure du Makefile

Un makefile se compose d'une section cible, dépendances et règles. Les dépendances sont les éléments ou le code source nécessaires pour créer une cible ; la cible est le plus souvent un nom d'exécutable ou de fichier objet. Les règles sont les commandes nécessaires pour créer la cible.

Ce qui suit est une description simple d'un makefile :


target: dependencies
        command
        command
        ...

4. Un exemple de Makefile

Voici un exemple simple de makefile (les numéros de ligne ont été ajoutés pour les besoins de cet article) :


1 client: conn.o 
2       g++ client.cpp conn.o -o client
 
3 conn.o: conn.cpp conn.h
4   g++ -c conn.cpp -o conn.o

Dans le makefile ci-dessus, les dépendances se trouvent dans la ligne client conn.o, alors que les règles se trouvent dans la ligne g++ client.cpp conn.o -o client. Notez que chaque ligne de règle commence par une tabulation, non des espaces. Oublier d'insérer une tabulation au début de la ligne de règles est une des erreurs les plus courantes lorsqu'on construit des makefiles. Heureusement, ce genre d'erreur est très facile à repérer du fait que le programme make le signalera.

Voici une description détaillée du makefile évoqué ci-dessus :

  1. Créez un fichier exécutable appelé client à titre de cible, qui dépend du fichier conn.o.

  2. Les règles permettant de créer la cible sont dans la troisième ligne.

  3. Dasn la troisième ligne, pour créer la cible conn.o, make a besoin des fichiers conn.cpp et conn.h.

  4. Les règles permettant de créer la cible conn.o sont dans la quatrième ligne.


5. Commentaire

Pour ajouter un commentaire dans le makefile, placez simplement un # dans la première colonne de chaque ligne à commenter.

Voici maintenant un exemple de makefile déjà commenté :


# Créer un fichier "client" exécutable
1 client: conn.o
2   g++ client.cpp conn.o -o client

# Créer un fichier objet "conn.o"
3 conn.o: conn.cpp conn.h
4   g++ -c conn.cpp -o conn.o

6. Cible particulière (phony target) [2]

Une cible particluière (phony target) est juste un nom de fichier fictif pour les commandes qui seront exécutées quand vous lancez une requête explicite. Il y a deux raisons à l'emploi d'une cible particulière : pour éviter des conflits avec un fichier du même nom et pour améliorer les performances du makefile.

Si vous écrivez une règle dont la commande n'est pas censée créer un fichier cible, ces commandes seront exécutées lors de chaque réexécution de la cible. Par exemple :


clean:
        rm *.o temp

Du fait que la commande rm ne crée pas de fichier nommé clean, ce fichier n'existera jamais. La commande rm est toujours exécutée chaque fois que vous saisissez :



make clean

parce que make part du principe que le fichier clean est toujours nouveau.

La cible ci-dessus arrêtera de fonctionner s'il existe un fichier nommé clean dans le répertoire actuel. Comme il n'exige pas de dépendances, le fichier clean sera considéré comme étant à jour, et la commande rm *.o temp ne sera pas exécutée. Pour résoudre ce problème, vous pouvez déclarer explicitement une cible comme « particulière » à l'aide de la commande .PHONY. Par exemple :


.PHONY : clean

Dans le makefile ci-dessus, si nous indiquons l'instruction make clean sur la ligne de commande, la commande rm *.o temp sera toujours exécutée, qu'il existe ou non un fichier nommé clean dans le répertoire actuel.


7. Variable

Pour définir une variable dans un makefile, vous pouvez utiliser la commande suivante :



$VAR_NAME=value

Par convention, un nom de variable est écrit en majuscules, par exemple :



$OBJECTS=main.o test.o

Pour obtenir la valeur d'une variable, placez le symbole $ avant le nom de la variable, comme suit :



$(VAR_NAME)

Dans un makefile, on trouve deux sortes de variables, les variables étendues récursivement et les variables étendues simplement.

Dans la variable étendue récursivement, make continuera à étendre cette variable jusqu'à ce qu'elle ne puisse plus être étendue, par exemple :



TOPDIR=/home/tedi/project
SRCDIR=$(TOPDIR)/src

La variable SRCDIR sera étendue, d'abord en étendant la variable TOPDIR. Le résultat final est /home/tedi/project/src.

Mais la variable étendue récursivement ne conviendra pas pour la commande suivante :



CC = gcc -o
CC = $(CC) -O2

Grâce à une variable étendue récursivement, cette commande entrera dans une boucle infinie. Pour contourner ce problèmme, nous utilisons une variable étendue simplement :



CC := gcc -o
CC += $(CC) -O2

Le symbole := crée la variable CC et se voit affecter sa valeur, gcc -o. Le symbole += se place à la fin de la valeur -O2 de CC.

Notes

[1]

Kurt Wall, et.al., Linux Programming Unleashed, 2001. (N. d. T.).

[2]

GNU Make Documentation File, info make. (N. d. T.).