La partie précédente a montré aux naifs qui en doutaient encore qu'une gestion de projet nécessitait de mettre en oeuvre la compilation séparée des composantes de l'application. Le compilateur est invoqué pour chaque fichier source et une fois supplémentaire pour l'édition de liens, dans le cas où l'on ne génère pas de librairies intermédiaires.
Lorsqu'un seul des fichiers sources est modifié, il n'est pas forcément utile de recompiler tous les fichiers sources pour regénérer l'application. On gagnera du temps à ne recompiler que les fichiers sources sur lesquels la modification du code effectuée a un réel impact. C'est à ce moment-là que le Makefile entre en jeu.
Le principe du Makefile est de construire un graphe de dépendance des fichiers constituant l'application, surchargé pour chaque fichier par la commande qui permet de le regénérer à partir des fichiers dont il dépend.
Le Makefile le plus simple à comprendre est celui où toutes les dépendances apparaissent explicitement. Exemple:
Example 1. Un exemple simple de Makefile
all : executable executable : file1.o file2.o gcc -o executable file1.o file2.o file1.o : file1.c file1.h gcc -c file1.c file2.o : file2.c file1.h file2.h gcc -c file2.c clean : rm file1.o file2.o executable core |
On lance l'interpréteur de Makefile par la commande make. Par défaut, la première dépendance rencontrée tentera d'être résolue : all. Cette target nécessite de remettre à jour toutes les conditions situées à droite des :. make passe donc à l'unique target suivante, executable
D'après la deuxième ligne du Makefile, on voit que executable dépend des deux fichiers objets file1.o et file2.o. Récursivement, les fichiers dont dépendent file1.o et file2.o sont recherchés. Si un des fichiers dépendant est plus récent qu'un des fichiers cible au cours de cette recherche, le fichier cible doit être regénéré, et la règle de compilation associée est exécutée.
% make clean rm file1.o file2.o executable core rm: cannot remove `core': No such file or directory % make gcc -c file1.c gcc -c file2.c gcc -o executable file1.o file2.o % touch file2.h % make gcc -c file2.c gcc -o executable file1.o file2.o % touch file2.o % make gcc -o executable file1.o file2.o % touch file1.h % make gcc -c file1.c gcc -c file2.c gcc -o executable file1.o file2.o |
Des règles génériques évitent d'avoir à écrire ligne de Makefile pour chaque fichier source.
Example 2. Un autre exemple de Makefile
CC = gcc CFLAGS = -O2 -c OBJS = file1.o file2.o all : executable .c.o : $(CC) $(CFLAGS) $< executable : $(OBJS) $(CC) -o $@ $(OBJS) |
La règle .c.o: s'applique pour tous les
fichiers .o et ont comme unique dépendance
le fichier .c correspondant.
$<
est alors substitué par le nom
du fichier sur lequel cette règle est instanciée.
On englobe pratiquement le premier exemple de Makefile, à la seule différence que l'on ne tient pas compte des dépendances sur les fichiers .h. La commande makedepend permet d'automatiser la génération de ces dépendances supplémentaires.
Example 3. Les dépendances supplémentaires
% cat Makefile CC = gcc CFLAGS = -O2 -c OBJS = file1.o file2.o SRCS = file1.c file2.c all : executable .c.o : $(CC) $(CFLAGS) $< executable : $(OBJS) $(CC) -o $@ $(OBJS)CC = gcc depend : makedepend -I. $(SRC) % cat file1.c #include "file1.h" main() {} % cat file2.c #include "file2.h" #include "file1.h" % makedepend -I. file1.c file2.c % cat Makefile CC = gcc CFLAGS = -O2 -c OBJS = file1.o file2.o SRCS = file1.c file2.c all : executable .c.o : $(CC) $(CFLAGS) $< executable : $(OBJS) $(CC) -o $@ $(OBJS)CC = gcc depend : makedepend -I. $(SRC) # DO NOT DELETE file1.o: ./file1.h file2.o: ./file2.h ./file1.h |
Les makefiles peuvent servir à automatiser toutes les tâches nécessitant d'appliquer plusieurs traitements consécutifs à un document. A titre d'exemple, ce document a été généré en utilisant un Makefile. Le fichier source est un document SGML, il est traité par la commande jade pour produire un document HTML et un document TeX. Le document TeX est ensuite compilé à son tour. Le makefile utilisé dans ce cas est le suivant:
all : development.ps index.html index.html : development.sgml jade -ihtml -t sgml -d formation.dsl\#html development.sgml development.tex : development.sgml jade -t tex -d formation.dsl\#print development.sgml development.dvi : development.tex jadetex $< development.ps : development.dvi dvips -o $@ $< |