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
GNU make est un outil qui permet :
Nombreux cas d'utilisation
Remarque : nos exemples seront basés sur des projets en C
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
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'
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
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
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
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
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
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'
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 $@ $^
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 $@ $^
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 $@ $^
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}
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}