Automake, variables et cibles
Signature : | Mis en ligne le : 18/04/2008
Catégorie(s) :
  • GNU/Linux Magazine
  • | Domaine :
    Commentez

    Retrouvez cet article dans : Linux Magazine 79

    Les variables d’automake suivent une grammaire bien définie. Cet article vous montrera comment conjuguer les radicaux, préfixes et suffixes, puis abordera quelques cibles des fichiers Makefile générés.

    Introduction

    Le mois dernier, l’article qui aurait du paraître a montré une lacune en ce qui concerne les conventions de nommage des variables. Plutôt que de réparer cela à la dernière minute en effleurant le sujet, nous avons préféré prendre notre temps pour le traiter un peu plus en détail, en le limitant aux variables d’automake, ce qui est déjà pas mal. Vous verrez dans cet article que ces variables permettent de définir ce que vous voulez compiler et comment. Mais la réalisation de cette compilation passe par l’outil make et ses cibles, ce qui compose la seconde partie de l’article. Nous y développons les cibles principales, celles qui permettent de lancer la compilation, créer une archive ou faire le ménage.

    Les variables dans Makefile.am

    Les variables sont composées d’un radical et d’un suffixe, parfois d’un préfixe. A l’aide des différents suffixes, vous pouvez associer à un radical des fichiers sources, un répertoire d’installation, une liste de programmes ou bibliothèques à compiler ou lier...

    La variable la plus connue est probablement bin_PROGRAMS. Le radical est bin, le suffixe PROGRAMS. Cette variable contient des programmes à compiler, comme l’indique le suffixe, et lors de l’installation, à placer dans le répertoire ${bindir} comme ne l’indique pas le radical. Si cette variable est bien connue, c’est parce que son suffixe est un suffixe dit " primaire ". Une variable à suffixe primaire indique une liste d’objets et leur type. En fonction de celui-ci, certains objets seront compilés, d’autres installés tels quels... Il existe plusieurs suffixes primaires. La liste actuelle en est donnée dans le tableau 1.

    /img-articles/lm/79/art-7/fig-1.jpg

    Notez que certains outils peuvent ajouter leurs propres suffixes primaires. C’est entre autres le cas de libtool qui amène LTLIBRARIES.

    /img-articles/lm/79/art-7/fig-2.jpg

    Lorsque ces variables à suffixe primaire ont permis de définir la liste des objets à construire, il nous faut, pour certains, indiquer à automake comment le faire. Pour un programme en C ou C++, vous devez indiquer les fichiers sources. Vous pouvez aussi ajouter quelques options au compilateur ou à l’éditeur de liens. Dans tous les cas, le principe consiste à utiliser le nom de l’objet comme radical et à lui ajouter le suffixe adéquat. Si vous avez par exemple déclaré l’objet plouf avec bin_PROGRAMS=plouf, vous pouvez utiliser plouf comme radical et indiquer les fichiers sources avec le suffixe SOURCES ainsi : plouf_SOURCES=plouf1.c plouf2.c. C’est ici l’occasion pour tenter de clarifier l’utilisation des suffixes servant à indiquer des options au compilateur et à l’éditeur de liens. Voyez les tableaux 2 et 3 à ce sujet. Nous allons, pour l’exemple, compiler un programme avec l’option -ansi, l’option d’optimisation -O2, et le lier à libtruc dont les fichiers se trouvent dans /opt/libtruc/include et /opt/libtruc/lib. Voici à quoi pourrait bien ressembler notre fichier Makefile.am :

    Remarquez l’utilisation de la variable LIBTRUC_PREFIX qui n’a rien à voir avec automake. Son utilité ici est simplement d’éviter la redondance de /opt/libtruc et par la suite, hors du cadre de cet article, de pouvoir la définir dans le fichier configure.ac en interrogeant l’utilisateur avec AC_ARG_WITH. Vous pouvez noter d’autre part que nous avons " oublié " d’indiquer l’option d’optimisation -O2. La raison en est toute simple : ce n’est pas à vous d’indiquer cette option, mais à l’utilisateur qui lancera la commande suivante :

    Sur la ligne de commande du compilateur, vous pourrez ainsi trouver, dans l’ordre, ceci : ${plouf_CFLAGS} ${CFLAGS}. Vous savez maintenant conjuguer les radicaux et les suffixes. Nous allons garder les préfixes pour les paragraphes suivants car contrairement aux suffixes qui créent de nouvelles cibles spécifiques à ce que vous voulez générer, les préfixes influent directement sur les cibles génériques.

    Les cibles générées par automake

    Automake prépare plusieurs cibles dans le fichier Makefile.in (transformé en Makefile par le script configure). Parmi celles-ci, nous allons aborder plusieurs familles de cibles, celles de compilation (all), d’installation (install), de distribution (dist et distcheck) et de nettoyage (clean et distclean).

    Cibles de compilation

    Les cibles de compilation génériques sont all et all-am. La seconde est une dépendance de la première, ce qui permet d’ajouter éventuellement les vôtres (ce qui est rare, et déconseillé si vous n’avez pas atteint les limites d’automake). La cible all-am sert à générer le fichier Makefile, puis tous les objets que vous avez pu définir à l’aide de suffixes primaires. En d’autres termes, avec cette cible, vous lancez la compilation de vos programmes (suffixe PROGRAMS), bibliothèques (suffixe LIBRARIES ou, avec libtool, LTLIBRARIES) et ainsi de suite. Automake s’occupe bien évidemment des dépendances telles que de compiler chaque fichier source pour pouvoir ensuite les lier dans un programme ou une bibliothèque. Le fichier Makefile est une dépendance de all-am ce qui peut paraître surprenant. En l’absence de ce fichier, donc de l’indication de la dépendance, quel est l’intérêt de l’indiquer à make ? En l’absence de ce fichier, il est nul. Par contre, en sa présence, la date est testée, et make sait regénérer le fichier Makefile s’il est moins récent que Makefile.in ou config.status. En d’autres termes, si vous touchez aux fichiers relatifs aux auto-tools, make sait s’en apercevoir et faire le nécessaire avant de passer à vos objets.

    Cibles d’installation

    La cible la plus connue pour l’installation est install. Cette cible ne présente pas grand intérêt pour le programmeur, car c’est en général l’utilisateur qui en fait le plus grand usage. Néanmoins, le programmeur peut s’interroger sur ce qui sera installé. Normalement, tous les objets définis à l’aide des suffixes primaires et seulement ceux-ci sont installés. Si vous souhaitez installer des fichiers non cités via des variables à suffixes primaires, le plus simple est de... les citer dans une variable à suffixe primaire. Le meilleur exemple est le fichier d’en-tête proposé avec une bibliothèque, par exemple truc.h. Si vous l’indiquez via truc_SOURCES=truc1.c truc2.c truc.h, ce fichier ne sera pas installé. Au lieu d’écrire cela, indiquez include_HEADERS=truc.h. Mais où vont être installés mes fichiers ? Prenez le radical de la variable à suffixe primaire. Ajoutez-lui dir (sans le tiret bas), et si cette nouvelle variable est définie par défaut, vous savez où sera installé votre objet. Appliquons cela à bin_PROGRAMS : extrayez le radical bin, ajoutez-lui dir et vous obtenez la variable bindir qui est définie ainsi : bindir=${exec_prefix}/bin, la variable exec_prefix étant par défaut égale à prefix, le répertoire d’installation. Les variables par défaut peuvent être consultées avec un configure –help :

    L’interprétation en français donne le tableau 4. Nous y ajoutons trois répertoires que configure --help n’indique pas : $pkglibdir, $pkgincludedir et $pkgdatadir. Dans ce tableau, PKG désigne le nom du projet défini dans autoconf. Ces différents répertoires standards correspondent aux recommandations du Filesystem Hierarchy Standard (FHS) (Tableau 4). Si vous voulez installer des fichiers ne rentrant dans aucune de ces catégories, vous pouvez très facilement créer la vôtre. Pour cela, créez une variable avec le radical que vous allez utiliser ensuite avec un suffixe primaire. Ajoutez-lui une terminaison dir (sans tiret bas), et donnez-lui le répertoire d’installation en guise de contenu. Pour installer le fichier hop.readme dans le répertoire ${prefix}/share/hop, rien de plus simple :

    Nous aurions pu utiliser ici ${prefix}/share/hop/docs, mais avec un peu de réflexion, nous nous rendons bien compte qu’il s’agit en fait d’un sous-répertoire de ${pkgdatadir}, ce pourquoi nous utilisons cette variable. Nous vous invitons à utiliser au maximum les variables existantes. Que faire si vous ne souhaitez pas installer un objet défini dans une variable à suffixe primaire ? Vous pouvez préfixer cette variable par noinst. Voici ce que cela donne avec un fichier readme à installer et un fichier readme.tmp à ne pas installer (en reprenant l’exemple avec hopdocsdir ci-dessus) :

    Il existe une autre cible d’installation intéressante : install-strip. Celle-ci sert à exécuter strip avant d’installer les binaires. Cette commande permet de supprimer la table des symboles, ce qui fait gagner un peu de place sur le disque. Elle est en général appréciée de ceux qui créent des paquets d’installation (rpm, deb...), car lorsque vous multipliez les binaires sur votre disque dur, les octets de gagnés s’accumulent et à force, cela s’en ressent par une place moindre requise pour installer le système d’exploitation.

    Cibles de distribution

    La distribution consiste en réalité à créer une archive au format .tar.gz (ou autre comme nous allons le voir ensuite) contenant tous les fichiers utiles à la compilation du projet. Pour un programme en C, cela revient à faire une archive contenant les fichiers sources et les scripts de compilation. La cible s’appelle dist, et nous la déconseillons au profit de la cible distcheck que nous allons traiter plus en détail. Cette cible sert non seulement à créer une archive, mais aussi à vous donner un peu de temps pour vous préparer un café (un thé, un cocktail... à votre convenance). En effet, elle crée cette archive et la teste. Cela signifie qu’elle ouvre l’archive, lance configure avec des options qui peuvent être différentes des vôtres, compile de façon à écrire les fichiers résultants ailleurs que dans les répertoires sources, puis installe, avant de faire le nettoyage.

    /img-articles/lm/79/art-7/fig-3.jpg

    Si tout cela se passe bien, tant mieux. Sinon, make distcheck vous met en évidence un problème qui serait probablement survenu plus tard. Un des problèmes récurrent est l’absence d’un fichier, qui est pourtant dans votre répertoire de travail. La question qui se pose alors est : qu’est-ce donc qui fait partie des fichiers distribués et qu’est-ce qu’il faut explicitement indiquer ? Pour avoir une liste exhaustive, référez-vous à la documentation d’automake. Voici cependant une liste qui devrait vous servir dans la plupart des cas :
    • Les fichiers de base pour les auto-tools (inclus : configure, les Makefile.am, les Makefile.in, AUTHORS, LICENSE, README, ChangeLog...) ;
    • Les fichiers sources (déclarés dans un fichier Makefile.am avec une variable à suffixe _SOURCES) ;
    • Les fichiers indiqués dans un fichier Makefile.am avec la variable EXTRA_DIST ;
    • Les fichiers indiqués dans un fichier Makefile.am avec une variable que vous avez préfixée par dist_.
    Si un fichier manque à l’appel, le plus simple est d’utiliser la variable EXTRA_DIST, qui fonctionne toujours. Cependant, cette variable fait un peu bric-à-brac. L’utiliser à tort et à travers n’est pas forcément élégant et peut être signe d’une mauvaise organisation du projet. Il est préférable de faire appel à une variable à préfixe dist_ à chaque fois que possible, car cette variable indiquera la nature des fichiers via son radical et leur fonction via son suffixe. Le problème d’EXTRA_DIST ici est d’être muette à ce sujet. Voici un exemple de fichier Makefile.am dont tous les fichiers cités seront tous inclus dans la distribution à l’exception du binaire plouf : Ne seront pas distribués les fichiers indiqués avec une variable à suffixe primaire. Par exemple, si vous voulez utiliser la variable man_MANS pour installer vos pages de manuel (ce qui est recommandé), vous devez savoir qu’il faut indiquer explicitement qu’il faut inclure ces fichiers dans la distribution, par exemple avec le préfixe dist_ comme vous pouvez le voir dans l’exemple ci-dessus. La raison pour laquelle ces fichiers ne sont pas inclus automatiquement est qu’il est considéré que vous pouvez non pas inclure ces fichiers, mais les générer, par exemple à partir de fichiers au format docbook. Sans bonne raison (et sauf exception), un fichier généré n’est jamais inclus dans une archive. Ne sont pas distribués non plus les fichiers dont vous forcez explicitement la non-distribution avec le préfixe nodist_. Ainsi, si vous générez des fichiers sources à l’aide de lex ou bison (ou yacc), vous ne souhaitez peut-être pas inclure les fichiers résultants de code C (ceci est une exception de fichier généré inclus dans l’archive). Vous pouvez ainsi écrire nodist_plouf_SOURCES=plouf_lex.c. Lorsque vous exécutez make distcheck, vous pouvez souhaiter indiquer des options à utiliser lors de l’exécution de configure. Cela est déconseillé, pour rester dans des conditions les plus proches des standards possibles, mais voici un cas où vous n’avez pas le choix. Soit un programme P nécessitant une bibliothèque libP. Les deux sont distincts et vous devez d’abord installer libP puis P. Par ailleurs, vous n’avez pas les droits administrateurs. Donc pas question d’installer libP dans les répertoires où se trouvent habituellement les bibliothèques. Vous allez donc installer libP dans /home/votre_compte/local/lib avec ./configure --prefix=/home/votre_compte/local. Après avoir travaillé sur le programme P, dont la ligne configure comprend l’option --with-libP=/home/votre_compte/local, vous lancez make distcheck. Celui-ci devrait échouer lamentablement, en pleurant l’absence de libP, ne connaissant pas l’endroit où vous l’avez placé. Pas question de bricoler vos scripts pour prendre en compte cet emplacement. Vous allez tout simplement profiter de la variable DISTCHECK_CONFIGURE_FLAGS. Voici votre commande : Si maintenant la création de votre archive devait échouer, vous n’auriez plus à chercher du côté de libP. Comment créer une archive dans un format différent de .tar.gz ? Vous pourriez bien exécuter une commande gouroutesque comme celle-ci :

    Cependant, cela devient plus complexe pour d’autres types d’archives, et surtout, il existe ce qu’il faut avec les auto-tools ! Vous disposez des cibles indiquées dans le tableau 5. Ces cibles s’utilisent comme make dist.

    Cibles de nettoyage

    Nous terminons cet article avec les cibles de nettoyage. Vous connaissez probablement make clean qui supprime les fichiers issus de l’exécution de la commande make. Il existe aussi make distclean qui supprime également les fichiers générés lors de l’exécution du script configure, comme les fichiers Makefile.

    /img-articles/lm/79/art-7/fig-4.jpg

    Si vous souhaitez ajouter un fichier à la liste des fichiers à effacer avec make clean, le plus simple est de l’ajouter à la variable CLEANFILES. De la même façon, si make distclean ne parvient pas à supprimer un fichier, utilisez la variable DISTCLEANFILES.

    Conclusion

    Nous espérons que cette introduction à la grammaire des variables d’automake vous poussera à mieux tirer profit des fichiers Makefile.am pour vos projets. Cet article ne pouvant pas être exhaustif, nous vous invitons à vous référer au manuel d’automake qui, si un exemple ne vous donne pas immédiatement la solution de votre problème, vous proposera au moins quelques pistes. En ce qui concerne les cibles, nous n’avons abordé que les plus simples, en partant du principe qu’une variable pouvait les conditionner. Il en existe d’autres, et vous pouvez compléter avec les vôtres. Ceci fera probablement l’objet d’un article prochainement. Mais le mois prochain, nous repartirons à l’assaut des bibliothèques avec pour prétexte, la création d’une macro M4. Remerciements Remerciements à Guillaume Rousse pour sa relecture et ses avis éclairés.

    Liens :

     

    Retrouvez cet article dans : Linux Magazine 79

    Vous souhaitez commenter cet article ?
    Brèves Flux RSS
    Édito : GNU/Linux Magazine Hors-Série N°72
    Édito : Linux Pratique N°84
    Édito : MISC N°74
    Édito : GNU/Linux Magazine N°173
    Édito : MISC Hors-Série N°9
    Communication RSS Com. RSS Presse
    HACKITO ERGO SUM
    GNU/Linux Magazine, partenaire du SymfonyLive Paris
    Opensilicium, partenaire de RTS EMBEDDED
    Linux Pratique et Linux Essentiel, Partenaire de l’Open World Forum
    Gnu/Linux Magazine, Partenaire des JDEV 2013
    Rechercher un article dans notre base documentaire :
    En kiosque Flux RSS

    Le tout nouveau GNU/Linux Magazine HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Pratique est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau GNU/Linux Magazine est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Open Silicium est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...