Makefile


1. Utilisation simple

La commande make sait comment fabriquer certains fichiers : par exemple, elle saura créer toto à partir de toto.c ou toto.cc. Dans ces cas, un Makefile n'est même pas nécessaire.
make toto

Néanmoins, la plupart du temps, on a besoin d'un Makefile : soit pour indiquer les dépendances entre les différents fichiers, soit car les commandes à lancer pour la compilation ne sont pas très standard. Voici un exemple de Makefile. La première ligne comporte le nom du « but », suivi de deux points. Les lignes suivantes (qui doivent impérativement commencer par une tabulation --- make est un logiciel vraiment archaïque) contiennent les commandes à lancer pour atteindre ce but. Pour lancer la compilation, on tapera make ou make all (le nom all n'a rien de particulier : c'est juste le premier but qui apparait dans le fichier).
all:
        latex 1
        dvips -E -o 1.ps 1.dvi

On peut indiquer les dépendance entre les différents fichiers : dans l'exemple suivant, on veut obtenir un fichier 1.ps, qui provient d'un fichier 1.dvi qui provient d'un fichier 1.tex. La commande make va comparer les dates de dernière modification de ces fichiers (s'ils existent) et ainsi juger s'il est nécessaire de recréer tout ou partie de ces fichiers.
all: 1.ps

1.ps: 1.dvi
        dvips -E -o 1.ps 1.dvi

1.dvi: 1.tex
        latex 1

Les commandes doivent tenir sur une ligne. Si on a besoin de plusieures lignes, on mettra un antislash à la fin. (Trouver un exemple utile...)
all:
        latex qksjfdlqkshdf.tex ; bibtex qksjfdlqkshdf ; latex qksjfdlqkshdf.tex ; latex qksjfdlqkshdf.tex ; dvips -o qksjfdlqkshdf.ps qksjfdlqkshdf.dvi 

all:
        latex qksjfdlqkshdf.tex ;  \
        bibtex qksjfdlqkshdf ;     \
        latex qksjfdlqkshdf.tex ;  \
        latex qksjfdlqkshdf.tex ;  \
        dvips -o qksjfdlqkshdf.ps qksjfdlqkshdf.dvi 

Dans les deux exemples (équivalents) précédents, tout était lancé en une seule fois, à l'aide d'une unique commande system(). Par contre, dans l'exemple suivant, on lance une commande à la fois.
all:
        latex qksjfdlqkshdf.tex
        bibtex qksjfdlqkshdf
        latex qksjfdlqkshdf.tex
        latex qksjfdlqkshdf.tex
        dvips -o qksjfdlqkshdf.ps qksjfdlqkshdf.dvi 

Comme le caractère $ est interprété par make, il faut le doubler si on veut qu'il le soit par le shell. (Nous reverrons plus loin commen réécrire plus proprement cet exemple.)
all:
        for i in `find . -name "*.tex"`   ;\
        do                                ;\
          latex $$i                       ;\
        done

On peut rajouter certains signes cabalistiques devant les commandes qui seront données au shell. Un @ signifie que la commande ne sera pas affichée.
all:
        @echo Je commence
        latex 1.tex
        @echo J\'ai terminé

Un - demande de continuer même s'il y a des erreurs.
all:
        @echo Expect this to go wrong
        -python 1.py
        @echo Nonetheless, we are going on        

2. Options de la commande make

make -n
Ne pas lancer la compilation, mais juste dire ce que l'on ferait.

make -k
Continuer même s'il y a des erreurs.

make -j 5
On peut lancer jusqu'à 5 commandes à la fois (pertinent sur les machines multi-processeurs). Si le Makefile a un drôle de nom, on peut le préciser

make -f Makefile.linux all

3. Utilisation des variables

On définit et utilise une variable comme cela. ne pas oublier les parenthèses.
TEX    =  1.tex 
LATEX  =  latex -interaction=nonstopmode -shell-escape

all: 
        $(LATEX) $(TEX)

On peut utiliser une variable dans la définition d'une autre variable.
MORE = A.tex B.tex
TEX  = 1.tex 2.tex 3.tex 4.tex $(MORE)

L'exemple suivant est équivalent (mais ne le copiez pas pour autant).
TEX  = 1.tex 2.tex 3.tex 4.tex $(MORE)
MORE = A.tex B.tex

Par conséquent, l'exemple suivant provoque une boucle récursive infinie...
TEX = 1.tex
TEX = $(TEX) A.tex B.tex

Si on tient à ce que le symbole = se comporte comme on s'y attend, on peut le remplacer par :=. L'exemple précédent peut se corriger ainsi.
TEX := 1.tex
TEX := $(TEX) A.tex B.tex

Quand make sait comment créer un fichier à partir d'un autre sans qu'on lui dise (par exemple, un fichier *.o à partir d'un fichier *.c), on peut quand même contrôler son comportement à l'aide de certaines variables.
CC       = gcc
CXX      = g++
CPP      = $(CC) -E
CFLAGS   = -Wall -O2 -I/usr/X11R6/include 
CXXFLAGS = 
LDFLAGS  = 

Il y a d'autres variables qui sont souvent utilisées dans les Makefiles (mais pas dans les règles par défaut).
LDLIBS= -L/usr/X11R6/lib -lXaw3d -lXmu -lXt  -lSM -lICE -lXext -lX11 -lm

On peut changer la valeur des variables du Makefile depuis la ligne de commande.
make CC=gcc all
make CC="gcc -Wall" all

4. Créer ses propres règles

Nous avons vu que make savait comment obtenir un exécutable à partir d'un source C. Elle ne sait pas comment obtenir un fichier PostScript à partir d'un source LaTeX, mais on peut le lui apprendre. Dabs l'exemple suivant, le caractère % est un caractère générique (wildcard, en anglais). La variable $< représente le fichier de départ (le premier, s'il y en a plusieurs), la variable $@ représente le fichier d'arrivée, la variable $* représente le contenu du caractère générique. (Il y en a plein d'autres : voir la page d'info, rubrique « pattern rules ».)
%.dvi : %.tex ; latex  $< 

%.ps : %.dvi ; dvips -E -f $< > $@

%.gif : %.ps ; pstogif -scale 2 $<

all: 1.ps

5. Fonctions

On aimerait pouvoir demander à make de compiler tous les fichiers LaTeX du répertoire courrant. Si on essaye de faire comme sous le shell, avec *.tex, on constate que ça marche parfois (pas toujours). La fonction wildcard permet de faire cela.
TEX_FILES = $(wildcard *.d)

Maintenant, on aimerait bien avoir le nom des fichiers PostScript correspondants, i.e., ceux que l'on aimerait créer. La fonction patsubst fait ce genre de chose.
%.dvi : %.tex ; latex  $< 

%.ps : %.dvi ; dvips -E -f $< > $@

%.gif : %.ps ; pstogif -scale 2 $<

TEX = $(wildcard *.tex)
PS  = $(patsubst %.tex,%.ps,  $(TEX))
GIF = $(patsubst %.tex,%.gif, $(TEX))

all: $(GIF)
        -rm -f -- *.aux *.log

clean:
        rm -f -- $(PS) $(GIF) *.log *.dvi *.aux *\~

On peut même lancer des commandes du shell. La commande suivante essaye d'obtenir la liste des répertoires contenus dans le répertoire courrant.
DIR = $(shell find . -type d -print)

La fonction filter-out permet d'enlever certains éléments d'une liste : en particulier, ici, on ne veut ni le répertoire courrant, ni le répertoire Template.
DIR = $(filter-out . ./Template,$(shell find . -type d -print))

6. Récursivité

Si on veut que les commande make, make all ou make clean agissent récursivement dans tous les répertoires contenus dans $(DIR), on peut procéder ainsi. Rappelons que la variable $@ contient le nom du but (ici, le nom d'un répertoire) et la variable $(MAKECMDGOALS) contient l'argument de la commande make (ici : rien, all ou clean).
all: $(DIR)

clean: $(DIR)

.PHONY: $(DIR)
$(DIR):
        $(MAKE) -C $@ $(MAKECMDGOALS)

7. Exemples divers

On peut vérifier qu'une variable a été définie.
ifdef FOOBAR
TEX := $(TEX) foobar.tex
endif

La fonction wildcard permet de ne retenir, dans une liste de noms de fichiers, que ceux qui sont effectivement là.
SCSI_SRCS = $(wildcard $(patsubst %.o,%.c, $(L_OBJS)))

Pour inclure un fichier, s'il existe.
ifeq (.config,$(wildcard .config))
include .config
else
OPTIONS =
endif


Vincent Zoonekynd (zoonek@math.jussieu.fr)
(Janvier 2000) Last modified: Fri Jan 21 21:50:31 CET 2000