L'Utilitaire MAKE
sous UNIX
Najib TOUNSI
Table des Matières
make est un programme UNIX (commande make) qui permet d'effectuer une certaine tâche à la suite d'un événement. Par exemple, si un morceau de programme a été modifié alors il faut refaire l'exécutable où il intervient, et donc déclencher les compilations/éditions nécessaires. Ce travail parfois laborieux à faire "à la main", make peut s'en charger de façon automatique.
Make obéit à des instructions du type
si événement alors action
décrites dans un fichier de nom Makefile dans le répertoire courant.
Les actions sont des commandes UNIX, et les événements en cause concernent l'état des fichiers. Plus exactement, un événement concerne le date de fichier. Celle-ci, peut avoir une importance quand à l'actualité de ce dernier. Si un fichier est crée à partir d'un auttre fichier, alors ce dernier dépend du premier. En générale, un fichier mis à jour peut avoir une conséquence sur les autres fichiers qui en dépendent. L'exemple de la modification d'un programme source qui entraîne sa recompilation est typique.
Make gère la date de la dernière modification d'un fichier[1]. En comparant ces dates pour deux fichiers, il en déduit lequel est plus récent que l'autre. Information qui peut avoir de l'importance. Si par exemple, un fichier source est plus récent qu'un fichier exécutable qui en dépend, ce dernier n'est pas à jour et il faut probablement le refaire. Il y a donc une notion conceptuelle qui est la dépendance entre deux (ou plusieurs) fichiers. make s'appuie en fait sur cette relation de dépendance entre fichiers, traduites en termes de dates, pour réaliser un travail. Ces dépendances sont définies par l'utilisateur, et make peut en gérer plusieurs. Entre un source et son binaire, il y a la relation de dépendance compilation. Entres des objets et un exécutable il y a la dépendance édition de lien.
![]() |
Fig-1. Exemple de Relations de
Dépendances
entre Fichiers.
Exemple: Soit pgme.c un programme C, et pgme le résultat de sa compilation/édition. On peut dire que: pgme dépend_de pgme.c . Avec make, n écrit:
pgme : pgme.c
Le caractère ':' exprime la dépendance. Dans ce cas, si la date de dernière modification de pgme.c est postérieure à celle de pgme (qui est celle de sa création en fait) alors on doit recompiler pgme.c pour reconstituer (to make) pgme. La commande UNIX pour cela est
cc -o pgme pgme.c
On met cette règle de dépendance dans un fichier Makefile sous la forme de suivante:
pgme : pgme.c
cc -o pgme
pgme.c
La première ligne exprime la dépendance, et celle qui suit, l'action à mener dans le cas où la dépendance est violée.
Un fichier Makefile est une liste de règles de dépendance de la forme:
<cible> :
<listeDépendance>
<TAB> <commande UNIX>
où la première ligne spécifie un (ou plusieurs) fichier cible, ensuite le caractère ':' et ensuite, séparée par espaces, la liste des fichiers dont la cible dépend. Les lignes qui suivent et qui commencent par le caractère TAB, indiquent les commandes UNIX à exécuter dans le cas où la dépendance est violée, c'est à dire un des fichiers de dépendance est de date plus récente que le fichier cible. En fait, la commande UNIX make exécute le fichier Makefile qui se trouve sous son répertoire de lancement.
Exemple:
% cat Makefile
pgme : pgme.c
cc -o pgme
pgme.c
Ici, on a un fichier Makefile très simple où pgme dépend de pgme.c, et si pgme.c est de date plus récente que pgme, alors il faut reconstituer ce dernier. C'est ce que fait la ligne commande qui suit. Pour exécuter ce Makefile, on fait
% make
Le système répond alors:
cc -o pgme pgme.c
Le caractère <TAB> est significatif. Il distingue les lignes contenant les actions, des lignes contenant les dépendances. (Des lignes commentaires peuvent être insérées dans le texte; elles commencent par le caractère #).
# Exemple de fichier Makefile
#
pgme : pgme.c
cc -o pgme pgme.c # On
recompile
Si un fichier cible est de date plus récente que ceux dont il dépend, c'est qu'il est à jour. La dépendance n'est pas violée. Dans ce cas rien ne se fait et un message apparaît.
% make
'pgme' is up to date
Quand un fichier cible est absent(n'existe pas), c'est comme si sa date est très ancienne et la règle est déclenchée.
% rm pgme
% make
cc -o pgme pgme.c
On peut mettre plusieurs règles de dépendances dans un Makefile. Considérons l'exemple d'un programme application constitué par plusieurs morceaux, part1.o et part2.o, compilés séparément.

Figure-2. Graphe de dépendance make (sans cycle).
Comme le montre la figure, application dépend de part1.o et de part2.o qui, eux-mêmes, dépendent respectivement de part1.c et part2.c. Un Makefile est particulièrement utile dans ce cas.
application : part1.o part2.o
cc -o
application part1.o part2.o
part1.o : part1.c
echo
"Recompilation de part1.c"
cc -c
part1.c
part2.o : part1.c
echo
"Recompilation de part2.c"
cc -c
part2.c
Maintenant, la commande make peut avoir comme paramètre le (ou les) fichiers cibles à constituer selon le besoin du moment (par défaut, la première cible).
% make part2.o
ne vérifie que la règle de dépendance relative à cette cible (la troisième ici). Elle sera déclenchée si part2.c a été touché dernièrement. Ce qui générera un nouveau part2.o . On pourra par la suite reconstituer le fichier application,
% make application
vue qu'il dépend de part2.o. Ainsi, de proche en proche, on peut automatiquement relancer les commandes utiles dans le cas où des modifications sont apportées à un fichier source. On peut résumer toutes les étapes en une seule, avec
% make application
make se chargeant lui-même de vérifier toutes les règles nécessaires pour reconstituer son argument. make vérifie si l'un des fichiers dont il dépend, e.g. part1.o, a besoin d'être reconstitué. Si oui, il lui applique le même principe, et ainsi de suite. make descend ainsi, dans l'arbre de dépendance, en reconstituant tous les chemins relatif à son argument.
Par un mécanisme simple de substitution, on peut paramétrer le programme make. Un fichier Makefile peut contenir des macros qui définissent les substitutions à faire. Une macro est une ligne de la forme
<idfMacro>=<chaîne>
A l'exécution, et après la rencontre de la macro, une expression de la forme
$(idfMacro)
sera remplacée par la chaîne associée.
Ce mécanisme est très utile, et permet d'adapter un Makefile à des usages variés. On peut par exemple choisir entre cc le compilateur natif du système ou gcc celui de GNU ; Il suffit pour cela d'écrire:
# Mettre ici le compilateur approprié.
Défaut cc
COMPILO=cc
...
$(COMPILO) p1.c p2.c ...
L'utilisateur choisira alors d'éditer ce fichier et de fixer la valeur de COMPILO comme il le souhaite. On peut aussi choisir un répertoire d'installation. Le fichier Makefile suivant copie une liste de filchiers file1, file2etc ... dans le repertoire d'installation donné par la variable REP. A charge pour l'utilisateur de la changer.
# Mettre ici le répertoire approprié.
Défaut /usr/local
REP=/usr/local
install : file1 file2
cp file1 file2
$(REP)
...
cd $(REP)
...
Dans ce dernier exemple, on a le principe de constante nommée qui permet de changer "/usr/local" à un seul endroit au lieu de plusieur. Les macros rendent les makefile très utilisés dans les installations de logiciels.
Un certains nombres de macros sont prédéfinies:
$@ correspond au fichier cible courant.
$? correspond au fichier plus récent que la cible en cours de traitement
$* est le nom du fichier cible courant privé du suffixe.
$< est le nom du fichier ayant provoqué l'opération
Une macro peut être définie dans le fichier Makefile ou être fournie sur la ligne commande make. La valeur fournie en ligne commande, est prioritaire sur celle définie dans le fichier. Dans l'exemple juste ci-dessus, la ligne commande
make install
exécutera
cp file1 file2 /usr/local
et la ligne
make REP=/home install
exécutera
cp file1 file2 /home
Il est parfois commode de maintenir un fichier uniquement pour sa date de dernière mise à jour. Pour, au besoin, lancer une commande appropriée. L'exemple suivant permet d'imprimer les listings qu'il faut au besoin. Il suffit de taper make print.
print : f1, f2, f3 ... fn
lpr $? # imprime les
fichiers de date plus récente
#
que le fichier print
touch print #
Remet à "maintenant" la date de print
L'exécution de
make print
ne cherchera a imprimer que les nouveaux fichiers par rapport au dernier make print. C'est à dire tous les fichiers plus récent que le fichier print. lequel est touché par make à chaque fois.
Si le nom d'un fichier inexistant est rencontré dans une cible, make exécute les commandes associées. Ainsi la première exécution de make print imprimera tous les fichiers. touch créera le fichier print ensuite.
Par ailleurs, si un fichier qui n'existe pas n'est pas créé par les commandes lancées, make exécute ces comandes à chaque invocation. Un usage typique de cela est:
install : x y z
cp x y z /dir
qui (re) copie les fichiers x, y et z dans un répertoire dir. Si, toutefois, le fichier install n'existe pas..
Il est, parfois possible de générer automatiquement le fichier Makefile lui-même. D'ailleurs beaucoup de logiciels échangés dans internet viennent avec un Makefile d'installation qui est créé au début de l'installation.
Makefile : source1 source2
# Commandes de recréation du "Vrai" Makefile
make, lit d'abord tout le fichier avant de commancer son exécution bien sûr.
[1] UNIX garde pour chaque fichiers la date du dernier accès en lecture et en écriture. C'est cette dernière qui est considérée par make
| ©Najib
TOUNSI (EMI) Original: http://www.emi.ac.ma/~ntounsi/COURS/UNIX/make.html |