Introduction à GNU make

FSUGAr - 10/02/2007


Rémi Laurent

C'est quoi ?

Un des grands outil du monde UNIX,
remis au goût du jour et réécrit sous GPL pour le projet GNU.

Le tout avec des ajouts de fonctionnalités intéressantes
tout en respectant la norme POSIX en vigueur

À quoi ça cert ?

GNU make est un outil qui permet :

Quand l'utiliser ?

Nombreux cas d'utilisation

Remarque : nos exemples seront basés sur des projets en C

Makefile

make va utiliser le contenu du fichier makefile ou Makefile pour connaître les opérations à effectuer

- pour spécifier un autre fichier Makefile

make -f FILE

- pour s'exécuter dans le dossier DIR

make -C DIR

avec cette directive il est possible de réaliser des Makefile récursifs

Cible : Source 1/2

Construire une cible nécessite des sources

La cible construite suivant la ou les règles données

#Mon premier makefile
testprog: testprog.c
	echo "Compilation de testprog"
	gcc -o testprog testprog.c

pour construire la cible 'testprog' il nous faut 'testprog.c', si cette dépendance est disponible, on exécute 'gcc -o testprog testprog.c'

Cible : Source 2/2

On peut avoir plusieurs sources dont certaines ne sont pas encore construites

testprog: testprog.c testlib.o testlib.h
	echo "Compilation de testprog"
	gcc -o testprog testprog.c testlib.o

testlib.o: testlib.c testlib.h
	echo "Compilation de la lib"
	gcc -c testlib.c

Dépendance : 'testlib.o' n'existe pas encore, make repère une règle permettant de le construire, il va donc l'exécuter avant de revenir à ce qu'il était en train de réaliser

Variable 1/2

L'écriture d'un makefile peut rapidement devenir fastidieuse avec ces nombreuses répétitions.
On fera donc usage de variables, comme en shell

TARGET=testprog
OBJ=testlib.o sharedlib.so proprietarylib.o
SOURCE=testprog.c

${CIBLE}: ${SOURCE} ${OBJ} testlib.h
	gcc -o ${CIBLE} ${SOURCE} ${OBJ}

testlib.o: testlib.c testlib.h
	gcc -c testlib.c

Variable 2/2

Il existe aussi des variables prédéfinies, pour gérer le compilateur à utiliser, les options à employer par exemple

CC=gcc
CFLAGS=-Wall -std=c99
LDFLAGS=-I/usr/local/include/foobar -lpthreads

Variables implicites

Pour encore simplifier l'écriture on peut utiliser des variables implicites, fonctionnalité de GNU make

Il en existe plein d'autres, plus complexes ou pour des cas particuliers ne nous intéressant que peu dans le cadre de cette introduction

Lien : Variables automatiques

Règles génériques

Plutôt que des spécifier les commandes à exécuter, une par une pour chaque type de traîtement, on peut définir des règles génériques

%.o:%.c
	gcc -o $@ -c $^

Désormais, chaque fois qu'un fichier objet, un .o sera nécessaire et non disponible, 'make' essaiera d'appliquer cette règle

$< : est la liste des fichiers sources, les sources .c ici
$@ : est la cible, l'objet .o ici

Macros 1/2

Pour encore simplifier la chose, on peut utiliser des macros

Ici nous allons écrire une macro reprenant tous les fichiers .c présents dans le dossier

TARGET=monprog
${TARGET}:$(wildcard *.c)
	gcc -o $@ $^

Problème : ici on recompile à chaque fois tous les fichiers .c pour créer notre exécutable 'monprog'

Macros 2/2

Une autre macro permet de réaliser des remplacements de chaînes, des 'pattern substitutions'

Reprenons l'exemple précédent

TARGET=monprog
${TARGET}: $(patsubst %.c,%.o,$(wildcard *.c))
        gcc -o $@ $^

Règles implicites

Certaines règles sont dors et déjà définies dans GNU make, les créer ne suffirait qu'à les personnaliser, ainsi l'exemple précédent est fonctionnel comme tel, pas besoin de définir une règle générique pour la compilation de sources .c en fichiers objets .o

TARGET=monprog
${TARGET}: $(patsubst %.c,%.o,$(wildcard *.c))
        gcc -o $@ $^

Pas de superflu

Dans l'exemple, les fichiers source .c sont d'abord compilés en fichiers objet .o, toute l'astuce réside dans le fait que make va vérifier si le .c est plus récent que le .o et réaliser la compilation que dans ce cas la, limitant ainsi le temps utilisé à la compilation des fichiers modifiés depuis la dernière compilation

TARGET=monprog
${TARGET}: $(patsubst %.c,%.o,$(wildcard *.c))
        gcc -o $@ $^

Nettoyage

Et si l'on veut tout nettoyer et compiler de zéro, ou bien ne garder que l'exécutable créé ?

Il suffit de créer une règle pour celà, avec un petit ajout si la règle ne correspond à aucun fichier réel

TARGET=monprog
OBJS=$(patsubst %.c, %.o, $(wildcard *.c))
all:${PROJECT}

${PROJECT}:${OBJS}

.PHONY:clean
clean:
        rm -f ${OBJS}

Chut !

Toutes les commandes spécifiées dans le makefile sont affichées à l'écran en même temps que d'être exécutées, on peut donc éventuellement limiter l'affichage à ce que ces commandes affichent réellement, ceci en les précédant de @

Un exemple :

.PHONY:clean
clean:
	@echo "Suppression des fichiers objets"
        rm ${OBJS}

Les liens