Makefile redacteur : Emmanuel Bébrune
Modifs d. Dupont
Introduction :
Le système du makefile permet de simplifier la compilation. Ce système
se résume en un simple fichier “Makefile”. Ce fichier liste tous les
fichiers du projet et permet d’organiser ce dernier en morceaux
indépendants.
Le fichier Makefile est lu par la commande make, ainsi pour compiler un
projet, avec un fichier Makefile qui_va_bien, il suffit de taper la
commande “make” sans aucun arguments.
Structure du fichier Makefile :
Un fichier Makefile est divisé en structures telles que :
cible: dépendances
commandes
Il faut absolument précéder chaque commande par une tabulation.
Lors de l'utilisation d'un tel fichier via la commande make la première
règle rencontrée, ou la règle dont le nom est spécifié, est évaluée.
L'évaluation d'une règle se fait en plusieurs étapes :
Les dépendances sont analysées, si une dépendance est la cible d'une
autre règle du Makefile, cette règle est à son tour évaluée.
Lorsque l'ensemble des dépendances est analysé et si la cible ne
correspond pas à un fichier existant ou si un fichier dépendance est
plus récent que la règle, les différentes commandes sont exécutées.
Application sur un exemple basique:
illustration
Pour illustrer un fichier Makefile, j’ai fait un petit programme
composé de deux fichiers .h et trois .c. Ce programme a pour simple but
de demander à l’utilisateur de saisir une chaîne de caractères, puis
l’inscrit à l’écran et quitte.
La partie saisie est composée des fichiers saisie.h et saisie.c, dans
le Makefile, nous aurons donc une règle :
saisie.o: saisie.c saisie.h
gcc -c saisie.c saisie.h
La partie affichage est composée des fichiers affiche.h et affiche.c,
dans le Makefile, nous aurons donc une règle :
affiche.o: affiche.c affiche.h
gcc -c affiche.c affiche.h
Le fichier principal est composé juste du fichier main.c. Nous aurons
donc la règle suivante dans le fichier Makefile :
main.o: main.c
gcc -o main.o -c main.c
Pour finir, il faut définir une règle pour faire l’édition des liens
entre tout les fichiers .o créés par les autres règles. On obtient donc
:
main: main.o affiche.o saisie.o
gcc -o main main.o affiche.o saisie.o
En général, on ajoute les règles all et clean pour, respectivement,
compiler tout le projet et nettoyer le projet, c’est à dire, supprimer
les fichiers compilés et pré-compilés (exécutables et .o). La règle all
est généralement placée en tête du fichier pour être la règle appelée
par défaut (sans paramètres à l’exécution de make) et la règle clean en
dernière.
On ajoute donc les règles suivantes :
all: main
clean:
rm -rf *.o
rm -rf main
Variables et paramètres dans le Makefile:
Le système du Makefile est très puissant, ainsi on peut définir des
variables afin que l’on ait très peu de modifications à apporter au
fichier afin de s’adapter à l’environnement.
Pour cela, il suffit d’ajouter au début du fichier la définition des
variables telle que :
CC=gcc
CFLAGS=-Wall -ansi
LDFLAGS=-Wall -ansi
EXEC=main
Ici, j’ai choisi les variables
CC
pour définir le compilateur (ici
gcc),
CFLAGS pour les
paramètres de compilation,
LDFLAGS
pour les
paramètres d’édition des liens, et enfin
EXEC pour le nom de
l’exécutable.
Certaines variables sont implicites, et donc utilisables sans
définition particulière. Nous avons par exemple : $@ qui représente la
cible de la règle courante et $^ pour la ou les dépendances.
On arrive donc aux règles de compilation suivantes :
all: $(EXEC)
main: main.o saisie.o affiche.o
$(CC) -o $@ $^ $(LDFLAGS)
main.o: main.c
$(CC) -c $^ $(CFLAGS)
saisie.o: saisie.h saisie.c
$(CC) -c $^ $(CFLAGS)
affiche.o: affiche.h affiche.c
$(CC) -c $^ $(CFLAGS)
clean:
rm -rf *.o
rm -rf $(EXEC)
Les fichiers d’exemple :
Le fichier principal (main.c) :
#include "saisie.h"
#include "affiche.h"
int main(){
char* texte;
affiche("Veuillez saisir quelques caractères\n");
texte = saisie();
affiche("Vous avez saisi : ");
affiche(texte);
affiche("\n");
return 0;
}
Le fichier affiche.h :
#ifndef _AFFICHE_H_
#define _AFFICHE_H_
#include <stdio.h>
#include <stdlib.h>
int affiche(char*);
//rem int affiche(char*){} redéfinie la fonction est crée une erreur de
compil
#endif
Le fichier affiche.c :
#include "affiche.h"
int affiche(char* texte){
printf("%s", texte);
return 0;
}
Le fichier saisie.h :
#ifndef _SAISIE_H_
#define _SAISIE_H_
#include <stdio.h>
#include <stdlib.h>
char* saisie();
#endif
Le fichier saisie.c :
#include "saisie.h"
char* saisie(){
char* texte = (char*) malloc(10*sizeof(char));
scanf("%s", texte);
return texte;
free(texte);
}
Et enfin,
le Makefile :
CC=gcc
CFLAGS=-Wall -ansi
LDFLAGS=-Wall -ansi
EXEC=main
all: $(EXEC)
main: main.o saisie.o affiche.o
<TAB>$(CC) -o $@ $^ $(LDFLAGS)
main.o: main.c
<TAB>$(CC) -c $^ $(CFLAGS)
saisie.o: saisie.h saisie.c
<TAB>$(CC) -c $^ $(CFLAGS)
affiche.o: affiche.h affiche.c
<TAB>$(CC) -c $^ $(CFLAGS)
clean:
<TAB>rm -rf *.o
<TAB>rm -rf $(EXEC)
//rem
<TAB> n'est pas à saisir, il faut mettre une tab
pour approfondir :
On peut aussi définir des règles génériques afin de ne pas
avoir à
lister tous les couples de fichiers .h et .c, je n’en parlerai pas ici
mais le site suivant détaille bien cette fonction et bien d’autres :
http://gl.developpez.com/tutoriel/outil/makefile/
C’est la page la plus claire et complète que j’ai trouvée sur le sujet.