Makefiles et GNU Make

Demandez le programme!

Retour à la page Systèmes

Motivation

L'écriture de fichiers Makefile, pour compiler un projet Unix/Linux, est un exercice négligé pour de plus ou moins bonnes raisons:

Mais parfois on vous demande d'en écrire un tout neuf, et c'est l'occasion de s'y replonger.

Un Makefile de débutant

Rappelons d'abord quelques problèmes posés par les Makefile simplistes de débutants (i.e. ceux de votre serviteur en école d'ingé):

Pièce à conviction (souvenir d'école!):

#Makefile élémentaire: "make" pour compiler, "make clean" pour nettoyer

#===== Définition des variables =====

# Le nom des variables importe peu.
# On y met les bibliothèques, les fichiers objets, les headers,
# les options de compilation et celles d'édition de liens

LIBS 		= -lgraphic -lXm -lXmu -lXt -lX11 -lm -lvolmgt -lsocket
OBJETS          = labyrinthe.o tracer.o graphiques.o main.o
HEADERS         = utils.h globals.h
OFLAGS          = -g -c -Wall
CFLAGS          = -g
# nom du fichier exécutable final
EXECUTABLE      = essai

#==== Règles de production des fichiers ====

# Astuce : en l'absence de fichier 'all' ou 'clean',
# chaque exécution de Make, exécute les instructions
#   des lignes 'all' et 'clean' par défaut.

all: $(EXECUTABLE) clean

main.o: main.c $(HEADERS)               # main.o dépend de main.c et de HEADERS
	gcc $(OFLAGS) main.c

graphiques.o: graphiques.c $(HEADERS)   # fichiers objet intermédiaires
	gcc $(OFLAGS) graphiques.c

...

$(EXECUTABLE): $(OBJETS)               # fabrique l'exécutable
	gcc $(CFLAGS) $(OBJETS) -o $(EXECUTABLE) $(LIBS)

clean:					# efface les fichiers intermédiaires
	rm -f *.o *~

Un Makefile pour gros projet

Après quelques années de vie professionnelle, de gros projets info, et enfin de lecture de manuel (cherchez l'erreur de priorité) mes Makefile ont gagné en intelligence:

#Makefile d'exemple

# ----- D'abord, les flags avec lesquels compiler -----

CXX:=clang++
CXX_DBG:=-g
CXX_OPTIM:=-O3
CXXFLAGS:=-c -Wall $(CXX_DBG) $(CXX_OPTIM)

LD:=clang++
LDFLAGS:=-g

# ----- Ensuite, les produits de compilation souhaités -----

#Les exécutables à produire, et les objets dont ils dépendent
bit_map_perf.exe: bit_map_perf.o util.o
vecteur.exe: vecteur.o util.o

#Indiquer que 'clean' est juste une action, pas un fichier
.PHONY: clean

# ----- Variables pour les dépendances entre fichiers -----

#Les noms des fichiers sources .cpp donnent ceux des fichiers objet .o et
# des fichiers de prérequis .d à produire au besoin

SRC  := bit_map_perf.cpp util.cpp vecteur.cpp
OBJ  := $(patsubst %.cpp, %.o, $(filter %.cpp, $(SRC)))
DEPS := $(OBJ:.o=.d)

# ----- Règles de construction des fichiers -----

#Fichier .o: dépendances listées dans le .d à produire avant à partir du .cpp
%.o: %.cpp %.d
	$(CXX) $(CXXFLAGS) $< -o $@

#Fichier .d: dépend des mêmes fichiers que le .o correspondant:
#invoquer le compilateur avec -MM -MG donne les dépendances du .o,
#puis avec 'sed' on change la règle "f.o: ..." en "f.d f.o: ..."
%.d: %.cpp
	g++ -MM -MG $(CXXFLAGS) $*.cpp | sed -e "s@^\(.*\)\.o:@\./\1.d \./\1.o:@" > $@

#Edition de liens: les .o requis sont à indiquer explicitement auparavant.
%.exe:
	$(LD) $(LDFLAGS) $^ -o $@

#Nettoyage: non seulement les fichiers .o, mais aussi les .d:
clean:
	rm -f $(OBJ) $(DEPS)

# ----- Utiliser les fichiers de règles produits -----

#Les prérequis de chaque fichier .o figurent dans le fichier .d associé:
include $(DEPS)

# ----- Fin du Makefile ------

Débogage et astuces courantes

Quelques commandes élémentaires courantes lors de la mise au point d'un Makefile (tirées de discussions comme StackOverflow 1745939):

Si vraiment vous n'avez pas de chance et devez chercher plus profondément, quelques pistes figurent dans l'article DrDobbs: debugging Makefiles:

Aspects plus ésotériques des Makefile

Cet points ne sont mentionnés que pour la culture générale: les détails sont dans le manuel.

Espaces: comptés seulement à partir de la première référence, mais jusqu'à la fin de ligne:

Quelques moyens supplémentaires de définir des recettes:

Et des façons moins courantes de manipuler les variables:

Retour à la page Systèmes

Dernière modification: 13 février 2017