Compilation séparée et Make Benjamin Drieu v1.0 - 30 Avril 1997 Cette documentation explique comment utiliser make pour la compilation séparée: comment écrire un fichier Makefile, l'utiliser, et quelles sont les conventions utilisées dans les fichiers Makefile. La version de make à laquelle je ferais référence au cours de ce document est Gnu Make. Introduction Comment marche la compilation

*.c *.h (Fichiers source) | COMPILATEUR | *.o (Fichier objet) | Librairies --> LIEUR <-- Fichiers objet | Fichier executable

Compilation séparée Utilité

L'utilité est triple: La programmation est modulaire, donc plus compréhensible La séparation en plusieurs fichiers produit des listings plus lisibles La maintenance est plus facile car seule une partie du code est recompilée

Ce que fait make

make assure la compilation séparée grâce à gcc make utilise des macro-commandes et des variables make permet de ne recompiler que le code modifié make permet d'utiliser des commandes shell, et ainsi d'effectuer une installation

Make est essentiel lorsque l'on veut effectuer un portage, car la plupart des logiciels libres UNIX (c'est-à-dire des logiciels qui sont fournis avec le code source) l'utilisent pour leur installation.

Makefile ?

Le fichier Makefile est un fichier nécessaire à make. Un fichier Makefile indique à make comment exécuter les instructions nécessaires à l'installation d'un logiciel ou d'une librairie.

Le fichier Makefile doit se trouver dans le répertoire courant lorsqu'on appelle make à l'invite du shell.

Les instructions contenues dans un fichier Makefile obéissent à une syntaxe particulière un peu stupide.

Règles

Les fichiers Makefile sont structurés grâce aux règles. Ce sont elles qui définissent ce qui doit être exécuté ou non, et qui permettent de compiler un programme de différentes façons.

Qu'est-ce qu'une règle ?

Une règle est une suite d'instructions qui seront exécutées pour construire une cible, mais uniquement si des dépendances sont plus récentes.

La syntaxe d'une règle est la suivante: cible: dependances commandes ...

Cible

La cible est généralement le nom d'un fichier qui va être généré par les commandes qui vont suivre, ou une action gérée par ces mêmes commandes, par exemple clean ou install (Voir chapitre IX [Cibles] pour plus de détails sur les conventions utilisées dans l'attribution d'un nom à une règle).

Dépendances

Les dépendances sont les fichiers ou les règles nécessaires à la création de la cible. Par exemple un fichier en-tête ou un fichier source dans le cas d'une compilation C. Dans le cas d'un fichier, la cible n'est construite que si ce fichier est plus récent que la cible.

Commandes

C'est une suite de commandes shell qui seront exécutées au moment de la création de la cible. Une étrangeté de la syntaxe des fichiers Makefile oblige l'utilisateur de make à insérer une tabulation au début de chaque ligne, faute de quoi make affichera une erreur au moment de son exécution.

La syntaxe de make oblige aussi l'utilisateur à ajouter une caractère "backslash" ('\') à la fin de chaque ligne dès que les commandes à exécuter dépassent une ligne de texte.

Mon premier Makefile !

Maintenant que nous connaissons la syntaxe d'un fichier Makefile, nous allons en créer un pour apprendre à les utiliser.

Fichier exemple Makefile

# Mon premier Makefile all: foobar.o main.c gcc -o main foobar.o main.c foobar.o: foobar.c foobar.h gcc -c foobar.c -o foobar.o

Et maintenant ?

Si vous avez enregistré l'exemple ci-dessus dans un fichier Makefile, il ne nous reste plus qu'a exécuter make dans le même répertoire que celui où vous avez enregistré le fichier.

Make s'exécute tout simplement en lançant la commande:

$ make all

Make va alors interpréter le fichier Makefile et exécuter les commandes contenues dans la règle all, une fois que les dépendances foobar.o et main.c seront vérifiées.

C'est à dire dire que si foobar.o ou main.c sont plus récents que le fichier main, make recompilera main.

Notez que si j'avais simplement tapé: "make" le résultat serait le même car quand make est exécuté sans argument, make exécute la première règle recontrée.

Macro-commandes et variables

Les habitué(e)s de la programmation C ne seront pas dépaysé(e)s par le concept des variables de make. En fait, il faut plutôt considérer les variables comme des macro-commandes (#define en C).

Déclaration

La déclaration se fait tout simplement avec la syntaxe ci-dessous: NOM = VALEUR Les espaces insérés ici ne sont pas obligatoires, mais facilitent la lisibilité du Makefile. La valeur affectée à la variable peut comme pour les macro-commandes du C comporter n'importe quels caractères, elle peut aussi être une autre variable.

Appel

La syntaxe de l'appel de la macro-commande est la suivante: $(NOM)

Exemple

prefix = /usr/local bindir = $(prefix)/bin

Un Makefile un peu plus complexe et commenté

# $(BIN) est la nom du binaire généré BIN = foo # $(OBJECTS) sont les objets qui seront générés après la compilation OBJECTS = main.o foo.o # $(CC) est le compilateur utilisé CC = gcc # all est la première règle à être exécutée car elle est la première # dans le fichier Makefile. Notons que les dépendances peuvent être # remplacées par une variable, ainsi que n'importe quel chaine de # caractères des commandes all: $(OBJECTS) $(CC) $(OBJECTS) -o $(BIN) # ensuite les autres règles main.o: main.c main.h $(CC) -c mainc foo.o: foo.c foo.h main.h $(CC) -c foo.c

Caractères jokers

Les caractères jokers s'utilisent comme sous shell. Les caractères valides sont * ? [...]. Par exemple, toto?.c représente tous les fichiers commençant par toto, finissant par .c, avec une lettre entre ces deux chaînes.

Comme sous shell, le caractère '\' permet d'inhiber l'action des caractères jokers. Par exemple 's\*r.c' fait référence à st*r.c et non pas à tous les fichiers commençant par st et finissant par r.c.

Exemples: clean: # ici le caractère joker * est géré par le shell et non pas # par make rm -f *.o print: *.c # le $? est une variable automatique (Voir chapitre VII # [Patterns et variables automatiques]) lpr -Php $?

Patterns et variables automatiques Pattern

Un pattern s'utilise un peu comme un caractère joker, mais uniquement dans le cas des cibles. Le caractère faisant office de joker est le caractère pourcent ('%').

L'intérêt est d'avoir plusieurs cibles. Les patterns permettent de filtrer les cibles pour savoir pour lesquelles d'entre elles les commandes qui suivent seront exécutées.

Exemple idiot

Pourquoi cet exemple est-il idiot ? Tout simplement parcequ'on met pèle-mêle tous les fichiers à compiler dans la même variable OBJECTS. Le plus simple aurait été d'utiliser plusieurs variables et de faire des règles différentes pour chaque type de compilation (normale, x11, athena).

# Notons que les objets sont à la fois des fichiers objets à lier et # des exécutables à compiler OBJECTS = text/main.o text/foo.o x11/main x11/bar athena/main # Le compilateur est bien sûr gcc CC = gcc # Les dépendances sont tous les objets all: $(OBJECTS) # On va quand même faire quelque chose dans cette règle echo DONE # Ici les règles pour tous les fichiers .o se trouvant dans le # répertoire 'text' text/%.o: text/%.c $(CC) -c text/$*.c # Ici les règles pour tous les fichiers se trouvant dans le répertoire # 'x11' x11/%: x11/%.c $(CC) -lX11 x11/$*.c # Ici les règles pour les fichiers se trouvant dans le répertoire # 'athena' athena/%: athena/%.c $(CC) -lXaw -lXext -lXmu -lXt -lX11 athena/$*.c

Liste des variables automatiques

Les variables automatiques sont des variables qui sont actualisées au moment de l'exécution de chaque règle, en fonction de la cible et des dépendances.

$@: nom de la cible $<: première dépendance de la liste des dépendances $?: les dépendances plus récentes que la cible $^: toutes les dépendances $*: dans le cas de l'utilisation des patterns, la chaine correspondant au %

Conventions d'appellation Noms d'exécutables et d'arguments

Entre parenthèses les valeurs par défaut

AR: programme de maintenance d'archive (ar) CC: compilateur C (cc) CXX: compilateur C++ (c++) RM: commande pour effacer un fichier (rm) TEX: programme pour créer un fichier TeX dvi à partir d'un source TeX (tex) ARFLAGS: paramètres à passer au programme de maintenance d'archives () CFLAGS: paramètres à passer au compilateur C () CXXFLAGS: paramètres à passer au compilateur C++ ()

Noms de répertoires de destination

Entre parenthèses les valeurs usuelles

prefix: racine du répertoire d'installation (/usr/local) exec_prefix: racine pour les binaires ($(prefix)) bindir: répertoire d'installation des binaires ($(exec_prefix)/bin) libdir: répertoire d'installation des librairies ($(exec_prefix)/lib) datadir: répertoire d'installation des données statiques pour le programme ($(exec_prefix)/lib) statedir: répertoire d'installation des données modifiables par le programme ($(prefix)/lib) includedir: répertoire d'installation des en-têtes ($(prefix)/include) mandir: répertoire d'installation des fichiers de manuel ($(prefix)/man) manxdir: répertoire d'installation des fichiers de la section x du manuel ($(prefix)/manx) infodir: répertoire d'installation des fichiers info ($(prefix)/info) srcdir: répertoire d'installation des fichiers source ($(prefix)/src)

Noms de cibles

Un utilisateur de make peut donner à ses cibles le nom qu'il désire. Mais pour des raisons de lisibilité, on donne toujours un nom standard à ses cibles selon leur comportement.

Quelques exemples de cibles standard:

all: compile tous les fichiers source pour créer l'exécutable principal install: exécute all, et copie l'exécutable, les librairies, les datas, et les fichiers en-tête s'il y en a dans les répertoires de destination uninstall: détruit les fichiers créés lors de l'installation, mais pas les fichiers du répertoire d'installation (où se trouvent les fichiers source et le Makefile) clean: détruit tout les fichiers créés par all info: génère un fichier info dvi: génère un fichier dvi dist: crée un fichier tar de distribution

Copyright

Ce document est placé sous copyright © 1997 de Benjamin Drieu, association APRIL. Ce document peut être reproduit et distribué dans son intégralité ou partiellement, par quelque moyen physique que ce soit. Il reste malgré tout sujet aux conditions suivantes : La mention du copyright doit être conservée, et la présente section préservée dans son intégralité sur toute copie intégrale ou partielle. Si vous distribuez ce travail en partie, vous devez mentionner comment obtenir une version intégrale de ce document et être en mesure de la fournir.