======Makefile====== //Pour compiler automatiquement et efficacement un projet de programmation, le Makefile est une solution élégante.// Un fichier //Makefile// est un simple fichier texte qui définit un ensemble de régles, chaque règle permettant de générer un fichier cible à partir de ses dépendances (une liste de fichiers). Voici la syntaxe d'un règle : cible1: dep1 dep2 dep3 ... commande Attention, il faut une tabulation avant d'écrire la //commande// qui réalise la cible ! Notons que la commande n'est exécutée que si le fichier //cible1// n'existe pas, ou que la cible est périmée, c'est-à-dire qu'il y a au moins une dépendance qui est plus récente que la cible. Dans ce dernier cas, il faut mettre à jour la cible. Par ailleurs, les dépendances peuvent également être la cible d'autres règles. Dans ce cas, si le fichier d'une dépendance comme //dep1// n'existe pas (ou qu'elle est périmée), alors on va appliquer cette autre règle en cascade pour mettre à jour cette dépendance, avant de mettre à jour //cible1//. dep1: autredep1 autredep2 ... autre_commande Au final, grâce à un chaînage précis des dépendances, le Makefile permet de re-compiler un projet en ne mettant à jour que les cibles qui dépendent des fichiers modifiés. Voici un exemple type de fichier Makefile pour le programme C précédent. # variable standard pour la compilation CC=gcc # compilateur CFLAGS=-Wall -g -std=c99 # options de compilation LDFLAGS= # options de link LDLIBS=-lm # bibliothèques CPPFLAGS= # options de preprocessing # cible principale (par défaut) all: toto # règle spécifique pour générer l'exécutable toto: toto.o tutu.o tata.o $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) # règle générique de compilation des fichiers C (implicite) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ .PHONY: clean clean: rm -f *.o *~ toto Dans ce Makefile, il y a quelques notions à comprendre : * Les commentaires sont précédés du symbole #. * La variable %%$%%@ représente la cible courante. * La variable %%$%%^ représente la liste des dépendances. * La variable %%$%%< représente la première dépendance. * La cible .PHONY permet d'indiquer des cibles particulières qui ne sont pas des fichiers, comme par exemple //clean//. Par ailleurs, il n'est pas nécessaire d'indiquer la règle générique "%.o: %.c" ici, car cette règle est implicite dans les Makefile ! Pour fabriquer une cible particulière, on fait //make cible//. Par défault, l'appel à //make// fabrique la première cible (//all// dans notre exemple. $ make gcc -Wall -g -std=c99 -c toto.c -o toto.o gcc -Wall -g -std=c99 -c tutu.c -o tutu.o gcc -Wall -g -std=c99 -c tata.c -o tata.o gcc toto.o tutu.o tata.o -o toto -lm $ make clean rm -f *.o *~ toto L'utilisation de la règle générique (implicitement ou pas) est pratique, mais elle ne prend pas en compte toutes les dépendances, comme les fichiers auxiliaires (*.h). Pour améliorer notre Makefile, il faut ajouter les règles de dépendance explicitement, comme ci-dessous. # variable standard pour la compilation CC=gcc # compilateur CFLAGS=-Wall -g -std=c99 # options de compilation LDFLAGS= # options de link LDLIBS=-lm # bibliothèques CPPFLAGS= # options de preprocessing # cible principale (par défaut) all: toto # règle spécifique pour générer l'exécutable toto: toto.o tutu.o tata.o $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) # dépendances explicites toto.o: toto.c tata.h tutu.h tata.o: tata.c tata.h tutu.o: tutu.c tutu.h .PHONY: clean clean: rm -f *.o *~ toto Plus d'info sur les régles implicites : https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html#Catalogue-of-Rules ==== Pour aller un peu plus loin ==== L'écriture des dépendances peut être fastidieux dans un gros projet, on peut alors utiliser la commande "gcc -MM". $ gcc -MM *.c toto.o: toto.c tata.h tutu.h tata.o: tata.c tata.h tutu.o: tutu.c tutu.h Pour aller encore plus loin, on peut inclure les dépendances dans le Makefile, que l'on générère explicitement avec la cible dep. # variable standard pour la compilation CC=gcc # compilateur CFLAGS=-Wall -g -std=c99 # options de compilation LDFLAGS= # options de link LDLIBS=-lm # bibliothèques CPPFLAGS= # options de preprocessing # cible principale (par défaut) all: toto # règle spécifique pour générer l'exécutable toto: toto.o tutu.o tata.o $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) .PHONY: clean dep # génération des dépendances dep: $(CC) -MM *.c > depends.txt clean: rm -f *.o *~ toto # inclusion des dépendances -include depends.txt Dans ce cas, il faut commencer par faire "make dep" pour générer le fichier des dépendances, puis après on peut faire "make". Il faut penser à remettre à jour les dépendances avec "make dep" quand on ajoute de nouveaux fichiers ou que l'on modifie les //includes// ! ==== Pour aller encore plus loin ==== //Parler de Makefile recursif avec "make -C subdir" ; parler des fonctions %%$%%(wildcard *.c) ; etc.// SOURCES := $(wildcard *.c) INCLUDES := $(wildcard *.h) OBJECTS := $(SOURCES:.c=.o) ==== Nota Bene ==== //Quelques astuces...// * @ suppresses the normal 'echo' of the command that is executed. * - means ignore the exit status of the command that is executed (normally, a non-zero exit status would stop that part of the build). * + means 'execute this command under make -n' (or 'make -t' or 'make -q') when commands are not normally executed. See also the POSIX specification for make and also §9.3 of the GNU Make manual. Plus d'info : https://stackoverflow.com/questions/3477292/what-do-and-do-as-prefixes-to-recipe-lines-in-make