L'Utilitaire MAKE
sous UNIX

Najib TOUNSI


Najib TOUNSI (ntounsi@emi.ac.ma )
Dernière mise à jour: 02/01/1999





1. Principe de Base

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.

2. Dépendances Entre Fichiers

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.

3. Make et Makefile

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

4. Makefile avec Plusieurs Dépendances

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.

5. Paramétrisation de Makefile

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.

6. Les Macros Standards

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

7. Options de la Commande make

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

8. Remarques de Conclusion

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
ntounsi@emi.ac.ma
  Dernière MàJ Sept 2000