Introduction � Makefile

Date de publication : 04/04/2004

Date de mise a jour : 31/08/2004

Par gl
 

Cet article constitue une introduction � l'utilisation des Makefiles dans le cadre de la compilation de projets en lignes de commandes.


Avant-propos
Remerciement
Avertissement
1. Le projet exemple : "hello world"
2. Pr�sentation de Makefile
2.1. Makefile minimal
2.2. Makefile enrichi
3. D�finition de variables
3.1. Variables personnalis�es
3.2. Variables internes
4. Les r�gles d'inf�rence
5. La cible .PHONY
6. G�n�ration de la liste des fichiers objets
7. Construction de la liste des fichiers sources
8. Commandes silencieuses
9. Les Makefiles conditionnels
10. sous-Makefiles
11. Bibliographie et liens


Avant-propos


Les Makefiles sont des fichiers, g�n�ralement appel�s makefile ou Makefile, utilis�s par le programme make pour ex�cuter un ensemble d'actions, comme la compilation d'un projet, l'archivage de document, la mise � jour de site, etc. Cet article pr�sentera le fonctionnement de makefile au travers de la compilation d'un petit projet en C.


Remerciement


Merci � Alacazam, Anomaly, et � Pomalaix pour la correction orthographique.
Merci �galement � Nyal, Emmanuel Delahaye et _Anne_ pour leurs suggestions et corrections.


Avertissement


Il existe une multitude d'utilitaires de Makefile fonctionnant sur diff�rents syst�mes (gmake, nmake, tmake, etc.). Les Makefiles n'�tant malheureusement pas normalis�s, certaines syntaxes ou fonctionnalit�s peuvent ne pas fonctionner sur certains utilitaires.
Le pr�sent article se base sur l'utilitaire GNU make. Toutefois les notions abord�es devraient �tre utilisables avec la majorit� des utilitaires.


1. Le projet exemple : "hello world"


Le projet utilis� ici sera un classique "hello world" d�coup� en trois fichiers:

 
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
 
main.c
#include <stdio.h> #include <stdlib.h> #include "hello.h" int main(void) { Hello(); return EXIT_SUCCESS; }

2. Pr�sentation de Makefile


Un Makefile est un fichier constitu� de plusieurs r�gles de la forme :

cible: dependance
	commandes
Chaque commande est pr�c�d�e d'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.


2.1. Makefile minimal


Le Makefile minimal du projet est donc :

 
Makefile
hello: hello.o main.o gcc -o hello hello.o main.o hello.o: hello.c gcc -o hello.o -c hello.c -Wall -ansi main.o: main.c hello.h gcc -o main.o -c main.c -Wall -ansi
Regardons de plus pr�s sur cet exemple comment fonctionne un Makefile :
Nous cherchons � cr�er le fichier ex�cutable hello, la premi�re d�pendance est la cible d'une des r�gles de notre Makefile, nous �valuons donc cette r�gle. Comme aucune d�pendance de hello.o n'est une r�gle, aucune autre r�gle n'est � �valuer pour compl�ter celle-ci.
Deux cas se pr�sentent ici : soit le fichier hello.c est plus r�cent que le fichier hello.o, la commande est alors ex�cut�e et hello.o est construit, soit hello.o est plus r�cent que hello.c est la commande n'est pas ex�cut�e. L'�valution de la r�gle hello.o est termin�e.
Les autres d�pendances de hello sont examin�es de la m�me mani�re puis, si n�cessaire, la commande de la r�gle hello est ex�cut�e et hello est construit.


2.2. Makefile enrichi


Plusieurs cas ne sont pas g�r�s dans l'exemple pr�c�dent :

  • Un tel Makefile ne permet pas de g�n�rer plusieurs ex�cutables distincts.
  • Les fichiers interm�diaires restent sur le disque dur m�me lors de la mise en production.
  • Il n'est pas possible de forcer la reg�n�ration int�grale du projet

Ces diff�rents cas conduisent � l'�criture de r�gles compl�mentaires :

  • all : g�n�ralement la premi�re du fichier, elle regroupe dans ces d�pendances l'ensemble des ex�cutables � produire.
  • clean : elle permet de supprimer tout les fichiers interm�diaires.
  • mrproper : elle supprime tout ce qui peut �tre r�g�n�r� et permet une reconstruction compl�te du projet.

En ajoutant ces r�gles compl�mentaires, notre Makefile devient donc :

 
Makefile
all: hello hello: hello.o main.o gcc -o hello hello.o main.o hello.o: hello.c gcc -o hello.o -c hello.c -Wall -ansi main.o: main.c hello.h gcc -o main.o -c main.c -Wall -ansi clean: rm -rf *.o mrproper: clean rm -rf hello

3. D�finition de variables



3.1. Variables personnalis�es


Il est possible de d�finir des variables dans un Makefile, ce qui rend les �volutions bien plus simples et plus rapides, en effet plus besoin de changer l'ensemble des r�gles si le compilateur change, seule la variable correspondante est � modifier.
Une variable se d�clare sous la forme NOM=VALEUR et se voir utiliser via $(NOM).
Nous allons donc d�finir quatre variables dans notre Makefile :

  • Une d�signant le compilateur utilis�e nomm�e CC (une telle variable est typiquement nomm� CC pour un compilateur C, CPP pour un compilateur C++).
  • CFLAGS regroupant les options de compilation (G�n�ralement cette variable est nomm�es CFLAGS pour une compilation en C, CXXFLAGS pour le C++).
  • LDFLAGS regroupant les options de l'�dition de liens.
  • EXEC contenant le nom des ex�cutables � g�n�rer.

Nous obtenons ainsi :

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello all: $(EXEC) hello: hello.o main.o $(CC) -o hello hello.o main.o $(LDFLAGS) hello.o: hello.c $(CC) -o hello.o -c hello.c $(CFLAGS) main.o: main.c hello.h $(CC) -o main.o -c main.c $(CFLAGS) clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)

3.2. Variables internes


Il existe plusieurs variables internes au Makefile, citons entre autres :

$@Le nom de la cible
$<Le nom de la premi�re d�pendance
$^La liste des d�pendances
$?La liste des d�pendances plus r�centes que la cible
$*Le nom du fichier sans suffixe
Notre Makefile ressemble donc maintenant � :

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello all: $(EXEC) hello: hello.o main.o $(CC) -o $@ $^ $(LDFLAGS) hello.o: hello.c $(CC) -o $@ -c $< $(CFLAGS) main.o: main.c hello.h $(CC) -o $@ -c $< $(CFLAGS) clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)

4. Les r�gles d'inf�rence


Makefile permet �galement de cr�er des r�gles g�n�riques (par exemple construire un .o � partir d'un .c) qui se verront appel�es par d�faut.
Une telle r�gle se pr�sente sous la forme suivante :

%.o: %.c
	commandes
Il existe une autre notation plus ancienne de cette r�gle :

.c.o:
	commandes
Il devient alors ais� de d�finir des r�gles par d�faut pour g�n�rer nos diff�rents fichiers

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello all: $(EXEC) hello: hello.o main.o $(CC) -o $@ $^ $(LDFLAGS) %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)
Comme le montre clairement l'exemple pr�c�dent, main.o n'est plus reconstruit si hello.h est modifi�. Il est possible de pr�ciser les d�pendances s�par�ment des r�gles d'inf�rence et de r�tablir le fonctionnement original, pour obtenir finalement :

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello all: $(EXEC) hello: hello.o main.o $(CC) -o $@ $^ $(LDFLAGS) main.o: hello.h %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)

5. La cible .PHONY


Dans l'exemple pr�sent, clean est la cible d'une r�gle ne pr�sentant aucune d�pendance. Supposons que clean soit �galement le nom d'un fichier pr�sent dans le r�pertoire courant, il serait alors forc�ment plus r�cent que ses d�pendances et la r�gle ne serait alors jamais ex�cut�e.
Pour pallier ce probl�me, il existe une cible particuli�re nomm�e .PHONY dont les d�pendances seront syst�matiquement reconstruites.
Nous obtenons donc :

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello all: $(EXEC) hello: hello.o main.o $(CC) -o $@ $^ $(LDFLAGS) main.o: hello.h %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)

6. G�n�ration de la liste des fichiers objets


Plut�t que d'�num�rer la liste des fichiers objets dans les d�pendances de la r�gle de construction de notre ex�cutable, il est possible de la g�n�rer automatiquement � partir de la liste des fichiers sources. Pour cela nous rajoutons deux variables au Makefile :

  • SRC qui contient la liste des fichiers sources du projet.
  • OBJ pour la liste des fichiers objets.

La variable OBJ est remplie � partir de SRC de la mani�re suivante :

OBJ= $(SRC:.c=.o)
Pour chaque fichier .c contenu dans SRC nous ajoutons � OBJ un fichier de m�me nom mais portant l'extension .o.
Nous obtenons alors :

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello SRC= hello.c main.c OBJ= $(SRC:.c=.o) all: $(EXEC) hello: $(OBJ) $(CC) -o $@ $^ $(LDFLAGS) main.o: hello.h %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)

7. Construction de la liste des fichiers sources


De la m�me mani�re, il peut �tre utile de g�rer la liste des fichiers sources de mani�re automatique (attention toutefois � la gestion des d�pendances vis � vis des fichiers d'ent�te). Pour ce faire nous allons faire remplir la variable SRC avec les diff�rents fichiers .c du r�pertoire.
La premi�re id�e qui vient g�n�ralement pour r�aliser cette t�che est l'utilisation du joker *.c, malheureusement il n'est pas possible d'utiliser ce joker directement dans la d�finition d'une variable. Nous devons utiliser la commande wildcard qui permet l'utilisation des caract�res joker.
Le Makefile correspondant est :

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) all: $(EXEC) hello: $(OBJ) $(CC) -o $@ $^ $(LDFLAGS) main.o: hello.h %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)

8. Commandes silencieuses


Lorsque le nombre de r�gles d'un Makefile augmente, il devient tr�s rapidement fastidieux de trouver les messages d'erreur affich�s au milieu des lignes de commandes. Les Makefiles permettent de d�sactiver l'�cho des lignes de commandes en rajoutant le caract�re @ devant la ligne de commande, notre Makefile devient alors :

 
Makefile
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi EXEC=hello SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) all: $(EXEC) hello: $(OBJ) @$(CC) -o $@ $^ $(LDFLAGS) main.o: hello.h %.o: %.c @$(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: @rm -rf *.o mrproper: clean @rm -rf $(EXEC)

9. Les Makefiles conditionnels


Les Makefiles nous offrent en plus une certaine souplesse en introduisant des directives, assez proches des directives de compilation du C, qui permettent d'ex�cuter conditionnellement une partie du Makefile en fonction de l'existence d'une variable, de sa valeur, etc.
Supposons, par exemple, que nous souhaitions compiler notre projet tant�t en mode debug, tant�t en mode release sans avoir � modifier plusieurs lignes du Makefile pour passer d'un mode � l'autre. Il suffit de cr�er une variable DEBUG et tester sa valeur pour changer de mode :

 
Makefile
DEBUG=yes CC=gcc ifeq ($(DEBUG),yes) CFLAGS=-Wall -ansi -g LDFLAGS=-Wall -ansi -g else CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi 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) -o $@ $^ $(LDFLAGS) main.o: hello.h %.o: %.c @$(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: @rm -rf *.o mrproper: clean @rm -rf $(EXEC)
Dans ce cas l'ex�cutable est g�n�r� en mode debug, il suffit de changer la valeur de la variable DEBUG pour revenir en mode release.


10. sous-Makefiles


Il arrive parfois que l'on souhaite cr�er plusieurs Makefiles correspondant � des parties distinctes d'un projet (par exemple : client/serveur, biblioth�ques de fonctions, sources r�parties dans plusieurs r�pertoires, etc.).
Toutefois il est souhaitable que ces Makefiles soient appel�s par un unique Makefile 'ma�tre' et que les options soient identiques d'un Makefile � l'autre.
Il est ainsi possible d'appeler un Makefile depuis un autre Makefile gr�ce � la variable $(MAKE) et de fournir � ce second Makefile des variables d�finies dans le premier en exportant celles-ci via l'instruction export, avant d'invoquer le second Makefile.
Voyons cela � travers notre petit projet, nous supposons qu'il se situe dans le sous-r�pertoire hello et que le Makefile ma�tre, situ� dans le r�pertoire principal, d�finira le compilateur et les options utilis�es, nous obtenons alors :

 
Makefile ma�tre
CC=gcc CFLAGS=-Wall -ansi LDFLAGS=-Wall -ansi HELLO_DIR=hello EXEC=$(HELLO_DIR)/hello all: $(EXEC) $(EXEC): @export CC @export CFLAGS @export LDFLAGS @(cd $(HELLO_DIR) && $(MAKE)) clean: @(cd $(HELLO_DIR) && $(MAKE) $@) mrproper: clean @(cd $(HELLO_DIR) && $(MAKE) $@)
 
Makefile
EXEC=hello SRC= $(wildcard *.c) OBJ= $(SRC:.c=.o) all: $(EXEC) hello: $(OBJ) @$(CC) -o $@ $^ $(LDFLAGS) %.o: %.c @$(CC) -o $@ -c $< $(CFLAGS) .PHONY: clean mrproper clean: @rm -rf *.o mrproper: clean @rm -rf $(EXEC)

11. Bibliographie et liens


  1. GNU make
  2. Le manuel de GNU Make



� 2004 Gr�gory Lerbret - Tous droits r�serv�s : Gr�gory Lerbret. Toute reproduction, utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour un usage personnel doit faire l'objet d'une autorisation �crite pr�alable de la part de : Gr�gory Lerbret , le propri�taire des droits intellectuels.