Laboratoire MSSMat Formation Makefiles GNU Make, niveau débutant A.-S. Mouronval Mars 2005 1
A quoi sert make? Utilitaire permettant d'exécuter des instructions pour construire une application Permet d'optimiser la génération de l'application : ne pas reconstruire les parties (étapes) à jour de l'application Structure de l'application décrite dans un fichier : le makefile Plusieurs versions de make existent : ici nous parlerons de GNU make (car disponible pour Unix, Linux, Mac et Windows et très répandu) 2
Rappel de compilation (i) : cas simple 1 étape: gcc hello.c ou gcc -o hello hello.c ou 2 étapes : Fichier source hello.c Compilateur : gcc -c hello.c Editeur de liens (linker) : gcc -o hello hello.o Programme exécutable hello Fichier entête hello.h Fichier objet hello.o Librairie libperso.a ou libperso.so Rqs : -- en fortran 77 : source=.f, compilateur=g77... -- sans l'option '-o', l'exécutable aurait pour nom a.out 3
Rappel de compilation (ii) : exemple de projet main.c #include <stdio.h> #include <stdlib.h> #include "hello.h" int main(void) { Hello(); return EXIT_SUCCES ; } hello.c #include <stdio.h> #include <stdlib.h> void Hello(void) { printf("hello World\n"); } hello.h #ifndef H_GL_HELLO #define H_GL_HELLO void Hello(void); #endif 4
Rappel de compilation (iii) : plusieurs fichiers source 1 étape : gcc -o hello main.c hello.c ou 2 étapes main.c gcc -c main.c gcc -o hello main.o hello.o hello.h hello.c gcc -c hello.c main.o hello.o hello 5
Graphe des dépendances hello hello dépend de hello.o et main.o Il s'obtient par : gcc -Wall -o hello hello.o main.o main.o hello.o hello.o dépend de hello.c Il s'obtient par : gcc -Wall -c hello.c main.c hello.h hello.c main.o dépend de main.c et hello.h Il s'obtient par : gcc -Wall -c main.c Rq: ici, -Wall est une option qui active tous les messages d'avertissement 6
Ecrire le fichier Makefile L'élément principal d'un makefile est la règle (rule). Celle-ci se compose d'une ou plusieurs cibles (target), de dépendances ou pré-requis (dependencies) et de commandes : cible: [dépendances...] <tab> commande1 <tab> commande2 Caractères spéciaux -- Un commentaire débute par # -- Retour à la ligne : \ -- Le caractère @ permet de ne pas afficher la commande exécutée sur la sortie standard 7
Exemple simple Makefile1_mod # Edition des liens hello: hello.o main.o <tab> gcc -Wall-o hello hello.o main.o <tab> @echo Fin edition des liens #Création des fichiers objets hello.o: hello.c <tab> gcc -Wall -c hello.c Attention à la tabulation dans les commandes! Vérification : cat -v -t -e Makefile1_mod ou vi Makefile1_mod + set list etc main.o: main.c\ hello.h <tab> gcc -Wall -c main.c 8
Lancer make gmake ou gmake <cible> traite la cible par défaut ou la cible indiquée, à partir : -- du fichier GnuMakefile, Makefile ou makefile s'il en existe un -- de règles internes, dans le cas contraire et si make le peut (cf. Formation niveau avancé) Options utiles : -f <Monfichier> ou Monfichier est le nom du fichier makefile -n : affiche les commandes à exécuter pour mettre à jour la cible -k : ne pas s'arrêter en cas d'erreur 9
Comment procède make? make s'intéresse à la cible par défaut ou à celle qui lui est donnée sur la ligne de commande Il construit le graphe des dépendances de cette cible Une fois arrivé aux feuilles du graphe, make regarde, pour chacune des feuilles, si celle-ci est un fichier qui existe -- Si ce n'est pas le cas, il retourne une erreur et ne poursuit pas -- Dans le cas contraire, make regarde si la cible associée existe : ---- Si ce n'est pas le cas, il exécute la commande associée ---- Dans le cas contraire, il ne met à jour la cible que si une de ses dépendances est plus récente qu'elle 10
Exemple simple Makefile1 hello: hello.o main.o <tab> gcc -Wall -o hello hello.o main.o hello.o: hello.c <tab> gcc -Wall -c hello.c Application Cas 1 : 1ère compilation $ gmake -f Makefile1 gcc -Wall -c hello.c gcc -Wall -c main.c gcc -Wall -o hello hello.o main.o main.o: main.c hello.h <tab> gcc -Wall -c main.c Cas 2 : Relance immédiate $ gmake -f Makefile1 gmake: `hello' is up to date. Cas 3 : Modification de hello.c $ touch hello.c $ gmake -f Makefile1 gcc -Wall -c hello.c gcc -Wall -o hello hello.o main.o 11
Cibles (précisions) Par défaut, make exécute la premiere cible ne commençant pas par. Une cible peut être : - un fichier ; - une action (imprimer des fichiers, effacer les fichiers.o etc). Une cible peut ne pas avoir de dépendances. 12
Cibles : exemple Makefile2 #cible par défaut all: hello hello: hello.o main.o <tab> gcc -Wall -o hello hello.o main.o Cet exemple contient 4 cibles dont 2 ne sont pas des fichiers : all et clean all est la cible par défaut hello.o: hello.c <tab> gcc -Wall -c hello.c main.o: main.c hello.h <tab> gcc -Wall -c main.c # Effacer clean: <tab> rm -rf *.o hello Application : gmake -f Makefile2 clean rm -rf *.o hello 13
Cibles :.PHONY Makefile2bis Que se passe-t-il si votre répertoire contient un fichier clean? $ gmake -f Makefile2 clean gmake: `clean' is up to date. La cible clean est reconnue comme étant un fichier qui existe déjà et qui est à jour par rapport à ses dépendances! Solution : la cible.phony Elle indique à make que la cible n'est pas un fichier $ gmake -f Makefile2bis clean rm -rf *.o hello #cible par défaut all: hello hello: hello.o main.o <tab> gcc -Wall -o hello hello.o main.o hello.o: hello.c <tab> gcc -Wall -c hello.c main.o: main.c hello.h <tab> gcc -Wall -c main.c # Effacer les.o et exe.phony:clean clean: <tab> rm -rf *.o hello 14
Variables Leur utilisation facilite l'évolution du makefile Définition : variable = valeur - valeur : une liste de noms séparés par des espaces - variable: une chaîne de caractères (#, = et espace exclus) En général, elles sont écrites en majuscules make fait la distinction entre majuscules et minuscules Utilisation : $(variable) ou ${variable} 15
Variables : utilisation Lorsque $(variable) doit être évaluée, make parcourt tout le fichier makefile à la recherche de la définition de variable En principe, il ne doit y avoir qu'une définition pour une variable donnée. Dans le cas contraire, c'est la dernière définition (du fichier) qui prime sur les autres Exemple : TUTU = coucou TITI = $(TUTU) all: echo $(TITI) TUTU=Au revoir affichera : Au revoir 16
Variables : exemple Makefile3 CC=gcc CFLAGS=-Wall LDFLAGS=-Wall EXEC=hello all: $(EXEC) hello: hello.o main.o <tab> $(CC) $(LDFLAGS) -o hello hello.o main.o hello.o: hello.c <tab> $(CC) $(CFLAGS) -c hello.c main.o: main.c hello.h <tab> $(CC) $(CFLAGS) -c main.c.phony:clean clean: <tab> rm -rf *.o $(EXEC) Dans cet exemple : CC désigne le compilateur C (CC Compiler), de même FC désignerait le compilateur Fortran CFLAGS sont les options de compilation en C (FFLAGS en Fortran) LDFLAGS représente les options de l'édition des liens (LD est l'éditeur de liens) EXEC contient le nom des exécutables 17
Variables automatiques Les variables automatiques sont des variables définies automatiquement par make en cours d'exécution $@ : nom de la cible courante $< : nom de la première dépendance courante $^ : liste des dépendances courantes $? : liste des dépendances plus récentes que la cible 18
Variables automatiques : exemple Makefile4 CC=gcc CFLAGS=-Wall LDFLAGS=-Wall EXEC=hello all: $(EXEC) hello: hello.o main.o <tab> $(CC) $(LDFLAGS) -o $@ $^ ---> ici $@ correspond à hello, $^ à hello.o et main.o hello.o: hello.c <tab> $(CC) $(CFLAGS) -c $< ---> ici $< correspond à hello.c main.o: main.c hello.h <tab> $(CC) $(CFLAGS) -c $< ---> et là à main.c.phony:clean clean: <tab> rm -rf *.o $(EXEC) 19
Règles génériques (Pattern rules) Les règles génériques permettent d'écrire en une seule règle un ensemble de règles très semblables Elles reposent sur l'utilisation du caractère % 20
Règles génériques : exemple Makefile5 CC=gcc CFLAGS=-Wall LDFLAGS=-Wall EXEC=hello all: $(EXEC) hello: hello.o main.o <tab> $(CC) $(LDFLAGS) -o $@ $^ %.o: %.c <tab> $(CC) $(CFLAGS) -c $<.PHONY:clean clean: <tab> rm -rf *.o $(EXEC) La règle %.o: %.c <tab> $(CC) $(CFLAGS) -c $< signifie que «chaque fichier.o dépend du fichier.c de même nom et peut être généré en utilisant la commande indiquée. Forme équivalente obsolète mais encore très utilisée (Suffixes rules) :.SUFFIXES:.SUFFIXES:.c.o.c.o: <tab> $(CC) $(CFLAGS) -c $< 21
Règles génériques : exemple (ii) Makefile6 L'exemple précédent ne fonctionne pas correctement si hello.h est modifié : $ touch hello.h $ gmake -f Makefile5 gmake: Nothing to be done for `all'... alors que main.o et hello ne sont pas à jour! Solution : ajouter la dépendance main.o: hello.h $ touch hello.h $ gmake -f Makefile6 gcc -Wall -c main.c gcc -Wall -o hello hello.o main.o CC=gcc CFLAGS=-Wall LDFLAGS=-Wall EXEC=hello all: $(EXEC) hello: hello.o main.o <tab> $(CC) $(LDFLAGS) -o $@ $^ main.o: hello.h %.o: %.c <tab> $(CC) $(CFLAGS) -c $<.PHONY:clean clean: <tab> rm -rf *.o $(EXEC) 22
! " # Utilisation des fonctions internes make dispose de fonctions internes permettant de manipuler facilement des chaines de caractères La syntaxe générale d'une fonction est : $(nom fonction param1, param2,...) Deux d'entre-elles sont très utiles : -- La fonction patsusbt : elle permet d'effectuer une substitution. Utilisation : $(patsubst motif, motifsubstitution, variable) -- La fonction wildcard : elle indique qu'il faut réaliser une expansion explicite de la variable Utilisation : $(wildcard masque) 23
Utilisation des fonctions internes : exemple (i) Makefile7 CFLAGS=-Wall LDFLAGS=-Wall EXEC=hello SRC= $(wildcard *.c) OBJ= $(patsubst %.c, %.o, $(SRC)) all: $(EXEC) -----> équivalent à SRC=hello.c main.c ---- > équivalent à OBJ=hello.o main.o hello: $(OBJ) $(CC) $(LDFLAGS) -o $@ $^ main.o: hello.h %.o: %.c $(CC) $(CFLAGS) -c $< Rq : OBJ= $(patsubst %.c, %.o, $(SRC)) peut aussi s'écrire : OBJ= $(SRC:.c=.o).PHONY: clean clean: rm -rf *.o $(EXEC) 24
$ % & Instructions conditionnelles Similaires aux directives de compilation en C Syntaxe générale : directive conditionnelle faire (...) si vrai else faire (...) si faux endif Directives conditionnelles ifeq(arg1,arg2) ifneq(arg1,arg2) ifdef name # vrai si name est non-vide ifndef name 25
Instructions conditionnelles : exemple Makefile8 DEBUG=yes CC=gcc ifeq ($(DEBUG),yes) CFLAGS=-Wall -g LDFLAGS=-Wall -g else CFLAGS=-Wall LDFLAGS=-Wall endif EXEC=hello SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) all: $(EXEC) ifeq ($(DEBUG),yes) @echo "Génération en mode debug" else @echo "Génération en mode release" endif hello: $(OBJ) $(CC) $(LDFLAGS) -o $@ $^ main.o: hello.h %.o: %.c $(CC) $(CFLAGS) -c $<.PHONY: clean clean: rm -rf *.o $(EXEC) Cas 1 : DEBUG=yes $ gmake -f Makefile8... gcc -Wall -g hello.c... Génération en mode debug Cas 2: DEBUG=No $ gmake -f Makefile8... Génération en mode release 26
' ( ) * Conclusions, points importants A l'issu de cette formation, vous devez : Connaître les principales étapes d'une compilation Identifier les dépendances d'une cible Ecrire des règles simples Ecrire un makefile simple et lancer make 27
+, -. / 0 1 2 Formation avancée Régles implicites Directives include, define... Inclusion automatique des dépendances Provenance des variables... Projet dans des répertoires différents (VPATH, vpath...) Fonctions (manipulation de texte, nom de fichier...) Librairies Etc 28
3 4 5 6 7 8 Références Techniques de production de programmes, M. Herrb, 1999, http://users.skynet.be/structure/informatique/software/lgc/tpp.pdf Managing project with make, A. Oram & S. Talbot, Ed. O'Reilly (disponible au labo) GNU make : http://www.gnu.org/software/make Managing project with GNU make, R. Mecklenburg, Ed. O'Reilly, 2004 (disponible au labo, plus difficile pour débuter) Introduction à Makefile, http://gl.developpez.com/tutoriel/outil/makefile (présentation inspirée de ce document) Création de header et compilation séparée, http://prografix.games-creator.org/document187 29
30