Make

Transcription

Make
Licence Professionnelle A SRALL
Outils Libres
Séance no 4 : Outils de construction d’applications
1
1.1
Présentation des outils de développement
Introduction
Lorsque les logiciels ne sont pas distribués sous forme de package, dont le format dépend
de la distribution utilisée, ils sont généralement toujours accessibles sous formes de tarball. Un
tarball correspond à une archive compressée des sources d’un logiciel donné. Pour pouvoir être
utilisé, ce logiciel doit bien souvent être recompilé, sauf lorsque les sources correspondent à un
langage de script, auquel cas ils peuvent être directement interprétés.
L’étape de recompilation en elle-même n’est pas toujours simple. Les sources d’un logiciel
sont bien souvent susceptibles d’être utilisés sur des plates-formes différentes, en utilisant des
bibliothèques extérieures, des ressources particulières... Pour le concepteur d’un logiciel, ces
différents paramètres doivent être considérés dès les premières étapes de la conception. Si un
logiciel donné est sensé être portable, c’est-à-dire utilisable sur des plates-formes et des environnements différents, le support de cette portabilité doit être intégré de façon rigoureuse et
méthodique.
Dans certains cas, la portabilité est facilitée par l’adoption de standards. Cela peut correspondre à :
— Des normes au niveau des systèmes d’exploitation, permettant de normaliser certaines
ressources. On retrouve par exemple de grandes similitudes au niveau des différents
UNIX, mais également certaines différences notables, parfois même au sein d’un même
système, comme cela se constate avec les différentes distributions Linux. Certains efforts, comme la norme POSIX ou encore LSB (Linux Standard Base, http://www.linuxbase.org/)
permettent de fédérer la normalisation de certaines ressources, respectivement au niveau
des UNIX ou des distributions Linux. Mais ces efforts ne permettent pas de s’affranchir
de tous les problèmes de compatibilité pouvant survenir.
— Des langages de programmation, qui sont généralement décrits par des normes ne reposant pas sur une plate-forme particulière. Mais là encore, des problèmes de compatibilité
peuvent intervenir, souvent pas tant au niveau des langages eux-mêmes que des compilateurs qui les implémentent. Ainsi, si certains langages sont relativement exempts de
problèmes de compatibilité, comme c’est le cas pour Java par exemple, d’autres comme
C++ posent plus de problèmes. La diversité des compilateurs, ainsi que l’évolution des
différentes normes de ce langage — la dernière version n’est toujours pas complètement
supportée par la majeure partie des compilateurs —, rendent délicat la compilation d’un
logiciel dans des environnements différents.
— Enfin, des bibliothèques, dépendant elles-mêmes des systèmes d’exploitation et des langages de programmation, et donc « héritant » des problèmes soulevés ci-dessus, possèdent leurs particularités. Dans certains environnements, une partie de leurs fonctionnalités peut ne pas être accessible, ou elles peuvent avoir des politiques de diffusion
1
(licence typiquement) ou des disponibilités différentes suivant les plates-formes considérées.
Pour un développeur, le support de toutes ces particularités peut facilement devenir un
casse-tête difficile à résoudre. Heureusement, certains outils de développement permettent de
faciliter la gestion de ces contraintes.
1.2
Make
Même si make n’est pas directement un outil permettant de s’affranchir des problèmes de
compatibilité, il est à la base d’un certain nombre de solutions décrites ci-dessous. make est
utilisé pour la construction de logiciels, qu’il est capable de mener automatiquement en s’appuyant sur des règles de dépendance.
Ces règles sont décrites dans un fichier Makefile, qui doit être adapté à chaque plate-forme
visée. Selon les cas, et en particulier selon l’usage ou non d’outils additionnels tels que ceux
décrits ci-dessous, le fichier Makefile :
— peut contenir des règles relatives à chaque plate-forme supportée. Dans ce cas de figure,
les fichiers Makefile peuvent vite devenir très volumineux et difficile à maintenir ;
— peuvent être générés pour une plate-forme particulière par des outils de plus haut niveau,
contenant de ce fait ce qu’on peut qualifier de métarègles. Ce deuxième cas de figure
est plus proche des habitudes de développement observées les dernières années.
Les principes de fonctionnement de make sont présentées au paragraphe 2.
1.3
Les outils liés à configure
Les outils présentés dans ce paragraphe sont des surcouches de make qui se sont créées et
qui ont été étoffées progressivement. En les considérant tous, cela représente 5 outils différents
qui continuent encore aujourd’hui à être utilisés conjointement pour gérer plus facilement la
création et la maintenance des fichiers Makefile. En dépit des avantages qu’ils procurent, tous
ces outils de haut niveau ne s’attachent qu’aux problèmes de portabilité liés aux différents
UNIX et n’offrent aucune solution intéressante pour le support de Windows.
1.3.1
Configure
configure est un programme, écrit en shell script, qui est créé par un développeur pour
générer des fichiers Makefile, permettant ainsi de produire le fichier adéquat par rapport à un
environnement particulier. Historiquement, ces fichiers configure étaient écrits complètement
par les développeurs de logiciel. Mais devant le nombre de particularités propres à chaque
composante logicielle utilisée pour la conception d’un programme, il est rapidement devenu
lui-même généré par les outils présentés ci-dessous.
configure reste actuellement le principal outil de configuration des logiciels dont les fichiers sources sont diffusés dans un tarball. Lors de la récupération d’un tel tarball, l’utilisateur
lance le script configure (après avoir décompressé l’archive), qui va générer le Makefile
correspondant à la plate-forme détectée. Il n’y a plus alors qu’à compiler le programme et à
l’installer.
2
1.3.2
Autoconf
Les fichiers configure devaient auparavant être mis à jour à chaque nouvel UNIX supporté, ou à chaque fois qu’une fonctionnalité était ajoutée. Le but de autoconf est de permettre
la génération de fichiers configure à partir d’un ensemble de règles décrites dans un fichier
makefile.in. Ces règles permettent de décrire comment construire un programme donné. Les
fichiers configure générés permettent de tenir compte de ces règles pour générer au final le
fichier makefile adéquat pour un environnement donné.
autoconf est donc un outil utilisé par les développeurs. Ceux-ci s’en servent pour créer
le script configure qu’ils incluront dans le tarball qui permettra de diffuser les sources d’un
programme.
1.3.3
Automake
Étant donné que beaucoup de programmes sont construits de la même manière, une partie
non négligeable du contenu des différents fichiers makefile.in est assez similaire d’un projet
à un autre. Par exemple, on y retrouve quasi systématiquement des ensembles de (méta-)règles
permettant de compiler un programme donné, de l’installer, de le tester, de faire le ménage après
la compilation... Pour factoriser ces ensembles de lignes de codes qui finalement deviennent
standards, automake a été conçu. Comme autoconf, automake est un programme exécuté par
le développeur. Il interprète des règles décrites dans un fichier makefile.am, dont la syntaxe
est nettement plus simple que dans des Makefiles ou des fichiers makefile.in.
automake considère le fichier makefile.am d’un projet pour générer un fichier makefile.in, lui-même permettant de générer un fichier configure grâce à autoconf. Le fichier
configure permet in fine de générer le fichier makefile permettant de gérer la compilation
du projet.
1.3.4
Libtool
Libtool est un outil permettant de prendre en considération les particularités liées à la création, à l’édition de liens et à la gestion des bibliothèques statiques et dynamiques d’un système
donné. Il offre des opérations de haut-niveau permettant ainsi de s’affranchir de particularités
très techniques. Il peut être utilisé seul, mais est généralement utilisé via les outils autoconf
et automake présentés ci-dessus.
1.4
Cmake
CMake est un outil nettement moins utilisé que les outils présentés ci-dessus, mais il représente certainement une des solutions les plus abouties pour assurer la portabilité de la compilation d’un programme sous UNIX, sous Windows et sous Mac OS X. CMake est un système
d’automatisation de compilation multi plates-formes. Comme les outils présentés jusqu’ici, il
est basé sur la définition de règles, regroupées dans un fichier CMakeLists.txt. À partir de
ces règles, CMake peut générer soit un Makefile, pour assurer la compilation d’un programme
sous UNIX, soit un projet exploitable par Visual C++ pour assurer la compilation de ce même
programme sous Windows.
CMake est un outil mature, utilisé par exemple par KDE pour gérer les 4 millions de lignes
de ce projet. CMake a été préféré à SCons, autre solution de compilation multi plates-formes,
3
justement pour sa maturité, sa simplicité et sa puissance et son support. Il est donc a priori,
actuellement, une des meilleures solutions pour répondre à ce besoin. Les autres solutions «
sérieuses » envisageables sont :
— qmake(Successeur de tmake, voir http://doc.trolltech.com/3.0/qmake-manual.html)
— Ant (http://freshmeat.net/projects/ant/)
— Cons/SCons (http://freshmeat.net/projects/cons/ et http://freshmeat.net/projects/sc
Par ailleurs, même si les outils liés à make et configure présentés ci-dessus ont été conçus
pour des plates-formes UNIX et ne marchent pas directement sous Windows, ils peuvent toutefois être utilisés après l’installation de Cygwin, qui propose une surcouche POSIX pour l’environnement Windows. Mais cette solution montre parfois vite des limites...
2
Make
make, comme la majeure partie des outils présentés dans ce cours, est un outil très complet
qui pourrait faire l’objet d’un cours entier. Nous ne présentons ici que certains principes de
base permettant d’en comprendre le fonctionnement.
2.1
Exemple introductif
Un fichier makefile, utilisé par l’utilitaire make, permet de spécifier les dépendances entre
certains fichiers et des règles décrivant comment résoudre ces dépendances pour atteindre un
objectif donné, appelé cible. Ainsi, sur l’exemple suivant :
Exemple de Makefile minimal.
1
2
3
4
5
6
7
8
9
10
all: monappli
mod1.o: mod1.c mod1.h
gcc -c mod1.c
mod2.o: mod2.c
gcc -c mod2.c
monappli: mod1.o mod2.o
gcc -o monappli mod1.o mod2.o
— Les lignes 1, 3, 6 et 9 définissent des cibles et des dépendances. Le premier mot de
chaque ligne (respectivement all, mod1.o, mod2.o et monappli) désignent des cibles,
tandis que le reste de la ligne, après le :, exprime les dépendances liées à cette cible.
Les cibles sont soit des noms de fichiers à produire, soit des mots réservés par make.
Les dépendances correspondent à d’autres cibles et/ou à des fichiers.
— Les lignes 4, 7 et 10 décrivent les commandes permettant d’atteindre la cible. Chacune
de ces commandes est précédée d’une tabulation.
L’exécution de la commande make prend une cible comme paramètre (cette cible prend
la première valeur de cible — all dans l’exemple — si aucun paramètre n’est fourni). Pour
construire cette cible, ou la reconstruire si elle existe déjà, make va examiner les dépendances
de cette cible. Si les dépendances de cette cible sont plus récentes que la cible elle-même, la
4
cible sera (re)construite. Dans le cas contraire, make ne fera rien du tout, considérant que les
dépendances sont satisfaites.
Si les dépendances d’une cible correspondent elles-mêmes à d’autres cibles, make ne cherchera par à résoudre la cible demandée dans l’immédiat, mais va d’abord déterminer le statut
des sous-cibles, et ceci récursivement, toujours selon les mêmes principes. Si une sous-cible
n’existe pas à un moment donné, une règle devra être trouvée dans le Makefile pour construire
cette sous-cible, sinon make terminera en erreur...
2.2
Un exemple plus avancé
Les fichiers Makefile peuvent également contenir d’autres informations : des variables
d’environnement (dont il est possible de surcharger la valeur en ligne de commande), des métas
règles... L’exemple suivant montre un exemple de fichier Makefile plus sophistiqué.
Fichier Makefile plus sophistiqué.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CC = gcc
CFLAGS = -Wall
LDFLAGS = -O2
OBJS = mod1.o mod2.o
all: monappli
monappli: $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
strip $@
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
clean:
\rm monappli *.o
Ce listing se base sur l’utilisation d’un certain nombres de variables propres à make :
— $@ : 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
On peut noter aussi que certaines cibles peuvent ne pas avoir de dépendances associées
(comme c’est le cas pour la dépendance clean). Si une telle cible est invoquée via la commande
make, les commandes associées sont automatiquement exécutées. On remarque aussi lignes 9
et 10 que plusieurs commandes peuvent être associées à une cible donnée.
3
3.1
Configure
Généralités
Les scripts configure supportent généralement un grand nombre d’options. Ces options
peuvent être variables d’un logiciel à un autre, mais un certain nombre d’entre elles sont tou5
jours présentes. Les options disponibles sont accessibles en tapant ./configure --help .
Parmi les options standards, certaines permettent de changer les répertoires d’installation (usuellement, des répertoires comme /usr/local ou /opt sont utilisés) pour les parties du logiciel
dépendant ou non de l’architecture du système, pour les binaires, les bibliothèques, les pages
de manuel, etc...
configure génère un certain nombre de fichiers qui peuvent être consultés en cas de problème de configuration :
— config.cache : gère un cache des résultats des tests effectués afin d’améliorer les
performances de l’exécution du script configure dans le cas où celui-ci ait à être exécuté plusieurs fois. Dans certains types de problèmes de configuration, ce mécanisme
de cache peut être gênant. Il suffit alors de supprimer ce fichier pour que configure
réexécute la totalité des tests.
— config.log : permet de garder une trace de chacun des tests effectués, de manière
généralement plus verbeuse que ce qui est affiché à la console. Quand un problème est
rencontré, la visualisation de ce fichier permet d’accéder à des informations plus fine
sur le problème en question (quelles commandes ont été exécutées, avec quel chemin
par exemple) et ainsi de faciliter la localisation du problème.
— config.status : ce fichier est un script permettant de reproduire la configuration actuelle, telle qu’elle a été demandée par la personne installant le logiciel.
— config.h : utile pour les programmes écrits en C ou en C++, ce fichier défini un certain
nombre de constantes qui peuvent ensuite être utilisée pour la phase de compilation, si
les fichiers sources du logiciel incluent ce fichier d’en-tête.
— Makefile : c’est la finalité visée par l’exécution du script configure. Il y en a un par
répertoire contenant des fichiers sources à compiler. Chaque Makefile permet alors de
gérer la compilation des fichiers sources du répertoire où il se trouve.
3.2
Cibles générées
Les cibles standards des fichiers Makefile générés sont :
— make all : pour construire l’intégralité du logiciel,
— make check : pour lancer les tests liés au logiciel, si celui-ci en contient (c’est-à-dire si
le concepteur du logiciel en a intégré dans la distribution de son logiciel),
— make install : pour installe le logiciel une fois que celui-ci a été compilé. Les répertoires concernés par cette installation sont ceux qui ont été désignés par les options
passées au script configure (comme --prefix par exemple ou les options similaires),
ou les répertoires par défaut dans le cas inverse,
— make clean : supprime tous les fichiers générés par les compilations.
— make distclean : supprime tous les fichiers générés par les compilations, ainsi que
ceux qui ont été générés par l’utilisation des outils automake, autoconf et configure.
4
4.1
Exercices
Makefile
1. Que fait la commande make si on la lance alors qu’aucun fichier Makefile n’est présent
dans le répertoire courant ?
6
2. Que fait la commande make toto dans les mêmes conditions que la question précédente ?
3. Créez un fichier avec des mots, chacun sur une ligne, et enregistrez le dans un fichier
mots.dico.
(a) Déterminez une commande UNIX permettant de trier les mots de ce fichier et les
enregistrant dans un fichier mots.trie.
(b) Créez un fichier Makefile permettant de gérer automatiquement cette opération
quand le fichier mots.dico est modifié.
(c) Testez votre fichier Makefile.
(d) Si on souhaite maintenant gérer des fichiers francais.dico, anglais.dico...,
comment pouvez-vous modifier votre Makefile pour qu’il permette de créer automatiquement les fichiers triés correspondant à ces fichiers (il est possible que vous
ayez besoin d’utiliser une pattern rule pour cela – reportez-vous à la documentation) ?
4.2
Configure
Installez Apache HTTPD sur votre machine en le recompilant à partir de ses sources. Testez
le programme une fois compilé.
7