Leçon 4 : L’outil makefile

Page 1 sur 9Lecteur de document UniversityLib

Leçon 4 : L’outil makefile

Computer Science - Software Compilation · notes

Voir tous les documents en programmation

Leçon 4 : L’outil makefile

Objectifs de la leçon 4 « l’outil makefile »

- Déduire les avantages de la compilation conditionnelle. - Construire un graphe de dépendance des fichiers constituant une

application.

- Reconnaitre les règles d’écriture d’un fichier makefile. - Utiliser l’outil makefile pour mettre en place un mécanisme de

compilation conditionnelle.

-

Identifier les commandes qui permettent l’exécution d’un fichier

makefile.

Plan de la leçon 4 « l’outil makefile »

o 4.1 Introduction o 4.2 Règles d’écriture d’un fichier makefile o 4.3 Exemple o 4.4 Exécution d’un fichier makefile o 4.5 Utilisation des variables et des macro-commandes

59

4.1 Introduction :

L’outil Makefile est un moyen pratique pour compiler et lier des programmes repartis sur

plusieurs fichiers. Le principe du Makefile est de construire un graphe de dépendance des

fichiers constituant l’application, surchargé pour chaque fichier par la commande qui permet

de le régénérer à partir des fichiers dont il dépend. On lance l’interpréteur de Makefile par la

commande « make ». La différence entre make et un script Shell classique est que make

connait si une cible est à jour ou non, ce qui lui permet de décider si cette cible doit être

reconstruite, ou peut être conservée (on parle alors de compilation conditionnelle). Lorsqu’on

demande à « make » de refaire une cible, il détermine quelles sont les sous-cibles qui ne se

sont pas à jour, les régénérera avant de régénérer la cible principale. En effet, lorsqu’un seul

fichier parmi les fichiers sources est modifié, il n’est pas nécessairement utile de recompiler

tous les fichiers sources pour régénérer l’exécutable final. Ceci permet de gagner du temps en

ne recompilant que les fichiers sources sur lesquels la modification du code effectuée a un réel

impact. En particulier, la commande « make » permet d’assurer la compilation séparée grâce à

la commande « gcc », et ceci en ne recompilant que le code modifié (compilation

conditionnelle), tout en ayant la possibilité d’utiliser des commandes Shell (ce qui très utile,

surtout pour réaliser des installations).

4.2 Règles d’écriture d’un fichier makefile :

Les fichiers makefile sont structurés selon des règles qui définissent ce qui doit être exécuté et

qui permettent de compiler un programme. En effet, une règle est une suite d’instructions qui

seront exécutées pour construire un cible, mais uniquement si des dépendances sont plus

récentes (par rapport à l’exécution précédente de la cible). La syntaxe d’une règle est comme

suit :

cible : dépendances

Publicité

commandes

60

Avec :

- La « cible » désigne généralement le nom d’un fichier qui va être généré par les

« commandes » qui vont suivre, sachant que l’espace qui précède « commandes » est

obligatoire et doit être fait avec une tabulation.

- Les « dépendances » désignent les fichiers ou les règles nécessaires à la création de

la « cible ». Par exemple, pour la compilation C, les dépendances peuvent contenir un

fichier d’en-tête ou un fichier source.

- Les « commandes » représentent généralement les commandes Shell qui seront

exécutées au moment de la création de la « cible ». Il est possible de faire appel sur la

même ligne à une suite de commandes séparées par un caractère point-virgule « ; ».

Dans le cas où les commandes dépassent une ligne, il est obligatoire de signaler la fin

de ligne avec un caractère antislash « \ ».

Notons que les lignes qui débutent par le caractère « # » sont des lignes de commentaires. Par

exemple, soit le graphe suivant de dépendance des fichiers constituant l’application

(l‘exécutable résultat dans cet exemple est intitulé « sum »), tels que les flèches représentent

les inclusions.

Figure 4.1 Exemple de graphe de dépendance des fichiers constituant une application.

61

Le fichier makefile correspondant à ce graphe est comme suit :

sum: main.o sum.o

gcc –o sum main.o sum.o

main.o: main.c sum.h

gcc –c main.c

sum.o: sum.c sum.h

gcc –c sum.c

Figure 4.2 Contenu du fichier « makefile ».

4.3 Exemple :

Soit le fichier makefile suivant, sachant que « all », qui débute généralement les fichiers

makefile, regroupe dans ces dépendances l’ensemble des exécutables à produire et « clean »

permet de supprimer tout les fichiers intermédiaires. Notons que « mrproper » permet de

supprimer tout ce qui peut être régénéré et permet une reconstruction complète du projet.

all : executable executable : file1.o file2.o

gcc -o executable file1.o file2.o

file1.o : file1.c file1.h gcc -c file1.c file2.o : file2.c file1.h file2.h gcc -c file2.c

Publicité

clean : rm file1.o file2.o executable core

Figure 4.3 Contenu du fichier « makefile ».

Par défaut, la première dépendance rencontrée tentera d’être résolue : « all ». Cette cible

nécessite de remettre à jour toutes les conditions situées à droite des « : » et « make » passe

donc à l’unique cible suivante « executable ». D’après la deuxième ligne du makefile, on voit

62

que « executable » dépend des deux fichiers objets « file1.o » et « file2.o ». Récursivement,

les fichiers dont dépendent « file1.o » et « file2.o » sont recherchés. Si un des fichiers

dépendants est plus récent qu’un des fichiers cibles au cours de cette recherche, le fichier

cible doit être régénéré, et la règle de compilation (« commandes ») associée est exécutée.

4.4 Exécution d’un fichier makefile :

Pour exécuter un fichier makefile, on fait recours à la commande « make » comme suit :

make all

La commande « make » interprète le fichier makefile et exécute les commandes contenues

dans la règle « all », une fois les dépendances ont été vérifiées. Si ces dépendances ont été

modifiées depuis la dernière compilation, la compilation de ces fichiers modifiés entraîne la

mise à jour des cibles. Dans le cas où la commande « make » est utilisée sans argument, elle

exécute la première règle rencontrée. En effet, pour exécuter un ensemble d’actions, la

commande « make » requiert par défaut un fichier nommé makefile. Si le fichier qui contient

l’ensemble d’actions est intitulé autrement (par exemple « actions »), il faut utiliser l’option

« –f » avec la commande « make », tout en spécifiant le nom du fichier, comme suit :

make –f actions

Toutefois, il est possible de ne générer qu’un cible particulier et ceci en spécifiant son nom

en tant que paramètre pour la commande « make ». Par exemple, pour ne générer que le

fichier « file2.o » dans l’exemple précèdent, il suffit d’écrire :

make file2.o

63

Par exemple, soit le fichier makefile suivant :

L’objectif de ce makefile est de créer un exécutable « res » qui dépend de trois fichiers objets

« walmain.o », « walfact.o » et « aff_bjr.o ». Le fichier objet « walmain.o » est généré à partir

du fichier « walmain.c » (par la commande «gcc –c walmain.c » ).

Notons que le fichier « walmain.c » fait appel au fichier « walfct.h » suivant (c.f. Leçon 2.) :

64

Le fichier objet « walfact.o » est généré à partir du fichier « walfact.c » (par la commande

«gcc –c walfact.c » ).

De même, le fichier objet « aff_bjr.o » est généré à partir du fichier « aff_bjr.c » (par la

Publicité

commande «gcc –c aff_bjr.c » ).

Ainsi, pour exécuter ce fichier makefile, on fait recours à la commande « make » sans

arguments, ce qui permet d’interpréter le fichier makefile et ceci en exécutant la première

règle rencontrée (« all »). Cependant, la commande « make clean » ne permet d’exécuter que

la ligne dont la cible est « clean ». En lançant de nouveau la commande « make », le message

«Rien à faire pour « all » » est affiché, vu qu’aucune des dépendances n’a été modifiée entre

temps.

65

4.5 Utilisation des variables et des macro-commandes :

La commande « make » utilise des variables prédéfinies et des macro-commandes, tels que les

variables sont considérées comme des macro-commandes (comme « #define » en langage C).

La déclaration d’une variable doit être placée en début du fichier makefile et elle se fait

comme suit :

Variable=Valeur

Il est aussi possible d’utiliser les variables d’environnement du Shell, qui seront exportées

comme des macros. Ensuite, pour faire appel à une variable, il suffit de précéder son nom

entre parenthèses par le caractère dollar « $ » (comme en Shell). Par exemple, la macro

« $(HOME) » correspondent à la variable d’environnement « HOME ». De même, si on

déclare une variable comme suit « CC = gcc », il est possible par la suite d’utiliser « $(CC)

prog.c » pour compiler le programme « prog.c ». En effet, on adopte souvent les conventions

de nommage suivantes :

66

Variable/Macro- commande

CC CXX ou CPP CFLAGS CXXFLAGS LDFLAGS EXEC RM $@ $< $^ $?

Rôle

Compilateur du langage C (cc ou gcc) Compilateur du langage C++ (c++ ou g++) Paramètres (ou options) à passer au compilateur du langage C Paramètres (ou options) à passer au compilateur du langage C++ Paramètres (ou options) de l’édition des liens Nom de l’exécutable à générer Commande utilisée pour effacer un fichier Nom de la cible Nom de la première dépendance Liste des dépendances Liste des dépendances plus récentes que la cible

Table 4.1 Principales conventions du nommage.

Notons enfin qu’il est aussi possible d’utiliser les caractères jokers (comme en Shell). Par

exemple, l’expression « prog?.c » représente tous les fichiers commençant par « prog » et

finissant par l’extension « .c » avec une lettre entre ces deux chaînes. De même, le caractère

antislash « \ » permet d’inhiber l’action des caractères jokers.

67