|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Le problème Tant qu'on écrit de petits programmes qui se compilent vite, on peut se contenter d'un unique fichier source par programme, et taper une ligne de commande dans le Shell pour les compiler. Cependant, plus les programmes deviennent grands et complexes, plus il devient important de séparer ses sources en plusieurs fichiers afin de les clarifier, éventuellement permettre la réutilisation de certaines parties telles-quelles dans d'autres projets, et diminuer les temps de compilation. Un besoin d'automatisation se fait alors sentir pour ne pas avoir à taper une ligne de commande par fichier. La solution Si vous utilisez un IDE comme StormC, ce problème ne devrait pas se poser car l'IDE s'occupe de tout (ou presque). Avec un compilateur en ligne de commande comme GCC ou vbcc, vous pouvez utiliser la commande "make" pour exécuter des fichiers makefiles automatisant la compilation de votre projet. En plus de n'avoir qu'une ligne à taper dans le Shell pour construire l'exécutable, cela permet de ne compiler que les fichiers source ayant été modifiés depuis la dernière compilation, ce qui diminue grandement le temps de compilation sur les gros projets. Les outils Il existe différentes variantes de make, dont notamment smake (livré avec SAS/C), dmake (livré avec DICE C), GNU make version GeekGadgets et d'autres portages de ce même GNU make pour Amiga que l'on peut trouver sur Aminet. Je ne parlerai pas de smake et dmake qui ont leur propre syntaxe, et sont peu utilisés (ou presque introuvable légalement en ce qui concerne smake). Deux versions de GNU make seront abordées :
Le contenu d'un makefile Ce qu'il faut mettre dans un makefile dépend grandement de votre projet. Un makefile peut servir à bien d'autres choses que la compilation d'un programme ! Mais nous n'en parlerons pas ici :-). Commençons par réaliser un makefile très simple destiné à compiler un programme composé de trois fichiers source. Voici les fichiers source : toto.c
titi.c
titi.h
Si vous vous demandez la raison de l'inclusion de "titi.h" dans "titi.c", sachez que c'est pour être sûr que le prototype de "ma_jolie_fonction()" se trouvant dans le ".h" est bien conforme à la définition de la fonction dans le ".c". En effet, il arrive parfois que l'on modifie les paramètres d'une fonction dans un fichier en oubliant de les modifier dans l'autre... Et voici le makefile correspondant :
Les "#" servent aux commentaires. Pour le reste, nous avons ici trois règles. Les règles sont la base d'un makefile. Elles sont de la forme :
Une cible est souvent le nom d'un fichier à générer, comme ici "programme_executable", "toto.o" et "titi.o" ; cependant, il peut aussi s'agir du simple nom d'une action à effectuer. Nous verrons cela par la suite. Une dépendance est le nom d'un fichier dont dépend la cible de cette règle. Si la cible est un fichier, alors la règle ne sera exécutée que si une des dépendances est plus récente que la cible. Par exemple, ici, la première règle ne sera exécutée que si "toto.o" ou "titi.o" ont une date plus récente que "programme_executable". Quant aux commandes, il s'agit simplement de commandes telles qu'on pourrait les taper dans un Shell AmigaDOS. Il peut y avoir plusieurs commandes par règle, sur plusieurs lignes consécutives, tout comme il peut n'y avoir aucune commande. Attention : les lignes de commande doivent commencer par une tabulation (et pas par des espaces !). Pensez-y si vous utilisez un vieil éditeur de texte pourri comme GoldEd 5. ;-) Différences entre le make de GeekGadgets et celui d'Aminet : le make de GeekGadgets lance chaque commande dans un Shell Unix "sh", tandis que le portage disponible sur Aminet utilise un Shell AmigaDOS normal. En pratique, cela a surtout une influence sur l'utilisation des jokers. Si vous voulez par exemple lancer la commande suivante :
...il n'y aura pas de problème avec le make d'Aminet ; mais cela ne marchera pas avec celui de GeekGadgets. Pour ce dernier, il vous faudra écrire :
De plus, le make de GeekGadgets ne reconnaît pas les chemins d'accès du type "VOLUME:chemin". Il faut écrire "/VOLUME/chemin", et ceci aussi bien dans les cibles que les dépendances et les commandes. Les règles en détail Examinons maintenant une par une les règles de notre makefile :
Cette règle dit à make comment générer la cible "programme_executable". Les dépendances étant "toto.o" et "titi.o", la commande ne sera exécutée que si au moins un de ces deux fichiers est plus récent que "programme_executable", ou bien sûr si ce dernier n'existe pas encore. Quant à la commande, c'est un simple appel à vc (l'interface de vbcc) servant à lier "toto.o" avec "titi.o" pour former l'exécutable. Passons à la deuxième règle :
Celle-ci décrit la façon de générer "toto.o". Elle sera déclenchée si "toto.c" ou "titi.h" sont plus récents que "toto.o", c'est-à-dire si les fichiers source ont été modifiés depuis la dernière compilation. C'est très intéressant car cela signifie que seuls les fichiers modifiés seront recompilés, évitant par là une grosse perte de temps. Puisque le fichier "toto.c" inclut "titi.h", "toto.o" peut changer si l'un ou l'autre de ces fichiers source a été modifié. C'est pourquoi ils sont tous deux dans les dépendances. La commande "vc -c -o toto.o toto.c" crée "toto.o" à partir de "toto.c". Le "-c" dit à vbcc de ne pas générer d'exécutable, mais au contraire de juste générer le fichier objet (GCC fonctionne pareil). La troisième et dernière règle est identique à celle que nous venons de voir, hormis les noms de fichiers qui diffèrent. Vous pouvez essayer ce makefile en tapant tout simplement "make" dans un Shell, en étant bien sûr dans le répertoire où se trouvent le makefile et les différents fichiers source. make devrait lancer la compilation de "toto.o", puis de "titi.o", puis finalement le lien de ces deux fichiers pour obtenir "programme_executable". Améliorations Ce makefile est parfaitement fonctionnel, mais il a néanmoins plusieurs défauts :
Nous allons améliorer ce makefile en utilisant des variables et une règle dite "de motif" ("pattern rule" en anglais).
La première variable rajoutée ici est CC. C'est la variable standard utilisée pour représenter le nom du compilateur. Nous lui donnons donc tout naturellement la valeur "vc" pour utiliser vbcc. Pour ensuite utiliser cette variable, nous écrirons "$(CC)". Chaque occurrence de "$(CC)" sera remplacée par "vc". Passons à la première règle :
La cible et les dépendances n'ont pas changé ; par contre, la ligne de commande utilise maintenant trois variables. Nous avons déjà vu "$(CC)". Les deux autres, "$@" et "$^", sont des variables dites automatiques car elles sont créées automatiquement par make. "$@" va tout simplement être remplacé par la cible de la règle en cours (ici, "programme_executable"). "$^" va elle être remplacé par toutes les dépendances (ici, "toto.o titi.o"). Cette ligne est donc équivalente à :
La deuxième règle introduit la notion de règle implicite :
Cette règle (de type règle de motif ou "pattern rule") définit une règle implicite disant à make comment créer un fichier ".o" à partir de son ".c" correspondant. Nous avons déjà vu la variable automatique "$@", qui sera remplacée par la cible, c'est-à-dire ici le fichier ".o". "$<" est aussi une variable automatique, et sera elle remplacée par la première dépendance, c'est-à-dire le fichier ".c". Les deux règles suivantes sont les mêmes que dans la première version du makefile, mais sans les commandes associées :
En l'absence de commande spécifique, make va utiliser la règle implicite que nous venons de définir. Ces deux règles couplées à la règle de motif donnent donc un résultat identique aux deux dernières règles du premier makefile. Avec ce nouveau makefile, vous pouvez :
Nettoyage Une cible "clean" est généralement incluse dans un makefile pour effacer tous les fichiers objet, afin de forcer la recompilation de tout le projet :
(attention, vous devez écrire "\#\?.o" si vous utilisez le make de GeekGadgets) Comme vous pouvez le constater, une règle n'a pas forcément de dépendance, et sa cible n'est pas forcément un nom de fichier. Pour exécuter cette règle, vous n'avez qu'à taper "make clean" dans le Shell (en l'absence d'argument, make exécute par défaut la première règle du makefile). Le mot de la fin Cette introduction devrait vous avoir appris le nécessaire pour faire des makefiles basiques, permettant de compiler des programmes composés de nombreux fichiers source sans souci. Il reste cependant de nombreuses possibilités d'extension : faire un makefile qui gère plusieurs compilateurs, des options de compilation différentes (avec ou sans débogage, pour 68k ou PowerPC...), la création de l'archive lha contenant votre programme et sa documentation, etc. Si le besoin s'en fait sentir, un deuxième article sur les makefiles pourrait voir le jour ; mais en attendant vous pouvez examiner les makefiles qui vous passeront sous les yeux et consulter la documentation AmigaGuide fournie avec le make de GeekGadgets pour plus de précisions.
|