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