Ceux qui ont déjà installé un logiciel en
recompilant ses sources ont probablement déjà
rencontré la fameuse formule magique ./configure; make; make install. L'art et la manière de générer le script configure
qui fait tout a fait l'objet d'un article dans le numéro 24 et est
disponible sur internet. Depuis, autoconf a vu son numéro de version
passer de 2.13 à 2.5X (2.59 lors de la rédaction de ces lignes) et
automake est passé de 1.4 à 1.9. Si la compatibilité antérieure a été
souhaitée par les développeurs, ceux-ci ont aussi suggéré de perdre les
anciennes habitudes. Cet article a pour objectif de reprendre les chose
en montrant, à partir d'un exemple trivial, comment générer les
fichiers nécessaires à la commande magique.
Que sont et que font au juste les auto-tools ? Les auto-tools sont les outils autoconf, automake, libtool et ceux qui gravitent autour (nous évoquerons aclocal
plus loin). Au premier abord, ils permettent de générer des fichiers
Makefile que l'on peut paramétrer avec les options fournies au script configure.
Si leur intérêt se limitait à cela, les
développeurs préféreraient faire leurs propres
fichiers Makefile et expliquer comment modifier les options par exemple dans un fichier que les Makefile pourraient inclure.
Les auto-tools permettent de générer des fichiers Makefile
de façon portable, ce qui signifie qu'un programme, tant qu'il ne fait
pas appel aux fonctionnalités propres de la plateforme sur laquelle il
doit tourner, pourra être compilé partout où les auto-tools (et les
dépendances de votre programme) fonctionnent. Notre exemple, conçu sur
Darwin (Mac OS X) fonctionnera aussi bien sur Linux que sur Cygwin (MS
Windows). Vous n'avez pas à tenir compte vous-même de la version de make et implicitement du format des fichiers Makefile et de leurs particularités par exemple.
Mieux, les auto-tools respectent les standards GNU et Unix, en particulier le FHS. Cela fait entre autre la joie des personnes qui créent des paquets pour nos distributions favorites lorsqu'elles respectent aussi le FHS. Ces personnes n'auront aucun mal à adapter les répertoires que votre programme utilise à l'arborescence du système.
Parmi les avantages des auto-tools par rapport à de simples fichiers Makefile, nous citerons encore le fait que les cibles (pour make) clean, distcheck
et d'autres sont systématiquement générées, sans le moindre paramétrage
supplémentaire de votre part. Il est aussi possible de paramétrer la
compilation avec des options --enable-fonctionnalité ou --with-option pour le script configure de notre commande magique. Et j'en passe...
Qui fait quoi ? Nous avons trois commandes et deux types de fichiers (configure.ac et les fichiers Makefile.am). Au résultat, nous obtenons un fichier configure et en l'exécutant, des fichiers Makefile. C'est touffu !

Autoconf a pour but de générer un fichier configure qui, lorsqu'on le lance, génère des fichiers à partir de fichiers modèles d'extension .in. Ainsi, ce script transforme les fichiers Makefile.in en fichiers Makefile. Voici un mystère d'éclairci.
Mais d'où viennent ces fichiers Makefile.in ? C'est ici le rôle d'automake. Cet outil prend en compte des fichiers Makefile.am et génère des fichiers Makefile.in aux mêmes endroits.
Le rôle d'aclocal est de générer un fichier aclocal.m4
dont nous parlons peu. En effet, il contient des macros (écrites dans
le langage m4), dont nous n'avons rarement besoin de connaître
l'existence. Sachez cependant qu'elles sont utilisées par le script configure pour effectuer certains tests et définir des variables dont les fichiers Makefile auront l'usage.
Enfin, le fichier configure.ac,
fichier de configuration d'autoconf, est utilisé par celui-ci, par
aclocal et indirectement par automake. Le premier génère le fichier configure en fonction du contenu de configure.ac. Le deuxième n'y fait que rechercher les noms des macros afin de copier celles nécessaires dans le fichier aclocal.m4 à partir de son dépôt (par exemple /usr/share/aclocal/*). Le dernier prévoit des variables pour ses fichiers Makefile.in en fonction des tests définis dans configure.ac.
L'ordre d'exécution des commandes est donc aclocal en premier, et ensuite indifféremment autoconf ou automake. Enfin, avant de compiler avec make, lancez le configure de la commande magique.
Afin d'aborder le sujet, nous allons nous servir d'un exemple simple, un programme qui affiche Bonjour le monde et prend fin, dont le code source est réparti sur deux fichiers plus celui des en-têtes. Voici ces trois fichiers :
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include "afficher.h"
int main(int argc, char**argv) {
afficher("Bonjour le monde");
exit(EXIT_SUCCESS);
}
/* afficher.c */
#include <stdio.h>
#include <stdlib.h>
#include "afficher.h"
int afficher(char*str) {
printf("%s\n", str);
return(0);
}
/* afficher.h */
#ifndef AFFICHER_H
#define AFFICHER_H
int afficher(char*str);
#endif
Le but de cet article est de compiler le programme bonjour à partir de ces sources.
Tout d'abord, démarrons dans un répertoire vide que nous appellerons racine, ou ${racine} dans nos scripts.
En général, pour un projet simple, créez un répertoire ${racine}/src/
et mettez-y tous les fichiers sources. Si votre projet doit contenir
deux exécutables indépendants, créez un répertoire pour chacun de ces
exécutables dans le répertoire racine, à la place du répertoire src/.
Si par contre les deux exécutables partagent certains fichiers sources,
vous devez les mettre dans le même répertoire à moins de créer une
bibliothèque avec le code commun. Nous voici donc ici avec trois
fichiers :
$ find .
./src
./src/afficher.c
./src/afficher.h
./src/main.c
Vous l'aurez compris, votre projet nécessite un fichier configure.ac à la racine, et un fichier Makefile.am à la racine ainsi que dans tous les sous-répertoires. Commençons par le fichier configure.ac; Vous pouvez le générer en lançant la commande autoscan. Vous obtiendriez un fichier configure.scan à renommer configure.ac. Voici ce fichier :
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_CONFIG_SRCDIR([src/afficher.c])
AC_CONFIG_HEADER([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT
Ce fichier fait appel à quelques notions que nous verrons dans des
articles suivants. Aussi, nous allons nous limiter au fichier configure.ac suivant :
AC_PREREQ(2.59)
AC_INIT(bonjour, 0.1, ymettier@libertysurf.fr)
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_PROG_MAKE_SET
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT
Pour insérer des commentaires, faites-les précéder d'un signe #. Vous pouvez aussi écrire dnl à la place, comme cela était le cas avec la version 2.13 d'autoconf.
La première ligne permet d'ôter toute ambiguité sur la version
d'autoconf à utiliser. Cela est utile sur les machines où autoconf-2.5X
cotoie encore l'ancienne version 2.13. La ligne AC_INIT
vous oblige à indiquer le nom de votre projet (notion différente du nom
de l'exécutable que nous allons générer), son numéro de version et une
adresse électronique que les utilisateurs pourront utiliser pour
envoyer des rapports de bogues (pensez à utiliser une liste de
diffusion dédiée lorsqu'elle existe). La troisième ligne indique à
automake qu'il faut s'initialiser.
Les deux lignes en AC_PROG_*
testent la présence de programmes et définissent des variables. C'est
grâce à elles que le script configure va vérifier la présence du
compilateur, en général gcc, et définir la variable $CC. Idem pour make dont la version et l'implémentation (ce n'est pas toujours GNU Make) varient d'une plateforme à l'autre.
Enfin la ligne AC_CONFIG_FILES
indique les fichiers à générer (à partir
des fichiers portant le même nom, mais avec l'extension
supplémentaire .in) et AC_OUTPUT lance la génération de ces fichiers.
L'étape suivante consiste à éditer des fichiers Makefile.am.
Dans notre cas d'école, celui de la racine ne contient qu'une ligne
définissant les sous-répertoires du projet (mettez une espace en guise
de séparateur entre les différents sous-répertoires) :
SUBDIRS=src
Pour les autres fichiers Makefile.am, définissez en premier la variable bin_PROGRAMS qui doit contenir le nom des binaires à générer. Notre programme s'appelant bonjour, nous y mettons bonjour. La variable suivante définit les fichiers sources nécessaires à la compilation du programme bonjour. Le nom de la variable est constitué du nom du binaire, suivi de l'extension _SOURCES. Le fichiers ${racine}/src/Makefile.am est le suivant :
bin_PROGRAMS=bonjour
bonjour_SOURCES= \
main.c \
afficher.c afficher.h
Nous verrons dans un article suivant des éléments de
syntaxe pour une utilisation un peu plus poussée de ces fichiers
Makefile.am.
Maintenant que nous avons les fichiers en place, nous pouvons exécuter quelques commandes, dans l'ordre :
$ aclocal
$ autoconf
$ automake -a -c
configure.ac: installing `./install-sh'
configure.ac: installing `./missing'
Makefile.am: installing `./INSTALL'
Makefile.am: required file `./NEWS' not found
Makefile.am: required file `./README' not found
Makefile.am: required file `./AUTHORS' not found
Makefile.am: required file `./ChangeLog' not found
Makefile.am: installing `./COPYING'
src/Makefile.am: installing `./depcomp'
La première fois que l'on invoque automake de la sorte, nous nous
rendons compte qu'il manque certains fichiers. Vous pouvez les créer
vides, mais il est préférable de mettre votre nom et/ou celui de
l'auteur du logiciel dans le fichier AUTHORS. Et quelques lignes dans le fichier README ne feront de mal à personne. Pour la postérité, indiquez dans le fichier ChangeLog
la date de vos manipulations pour ajouter le support
d'autoconf/automake ! Lorsque les fichiers manquants sont
créés, invoquez automake -a -c
à nouveau : il ne devrait plus rouspéter. Et vous venez de finir de
générer tout le nécessaire à la commande magique que vous pouvez lancer
:
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
[...]
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: executing depfiles commands
$ make
[...]
$ make install
Le projet bonjour va probablement prendre de l'ampleur. Voici quelques cas simples et courants que vous pouvez être amenés à rencontrer.
Ajouter des options pour modifier le comportement du compilateur ou
celui de l'éditeur de liens est chose courante. Vous pouvez ainsi
compiler avec l'option -g
afin de pouvoir déboguer plus facilement avec gdb. Ce genre d'options
ne doit pas apparaître dans le projet et c'est à la personne qui
compile de les indiquer. Pour cela, elle profite des variables
d'environnement CPPFLAGS pour le préprocesseur, CFLAGS pour le compilateur et LDFLAGS pour l'éditeur de liens. Cela donne :
$ ./configure
$ make CFLAGS="-O2"
$ make install-strip
Nous générons ainsi un exécutable optimisé, et l'installons avec la cible install-strip
qui supprime tout ce qui est inutile dans la table des symboles. Pour
en savoir plus sur cette opération, voyez la page de manuel de l'outil strip.
Il est néanmoins une option qu'il peut être
souhaitable d'indiquer systématiquement au compilateur :
l'option -Wall.
En présence de celle-ci, le compilateur affiche tous les messages
d'avertissement qu'il peut (ou presque, voyez la page de manuel de gcc et les options commençant par -W). Pour insérer cette option, nous modifions la variable CFLAGS via le fichier configure.ac et ajoutons ceci à la fin, avant la ligne AC_CONFIG_FILES :
if test "x$GCC" = "xyes"; then
CFLAGS="$CFLAGS -Wall"
fi
Cette opération peut ajouter deux fois ou plus l'option -Wall.
Mais le compilateur gcc ne vous en tiendra pas rigueur. C'est pourquoi
nous ne compliquons pas ces trois lignes avec un code qui teste la
présence de l'option dans CFLAGS.
Imaginons que nous ajoutions une nouvelle fonctionnalité, par exemple l'appel à la fonction plouff() définie dans le fichier src/plouf.c. Nous disposons aussi du fichier d'en-têtes src/plouf.h. Pour prendre en compte ces deux nouveaux fichiers, éditez le fichier src/Makefile.am
et ajoutez les noms des deux nouveaux fichiers à ceux existant
déjà, avant ou après, peu importe. Voici le
nouveau fichier src/Makefile.am :
bin_PROGRAMS=bonjour
bonjour_SOURCES= \
main.c \
plouf.c plouf.h \
afficher.c afficher.h
Comme vous venez de modifier un fichier Makefile.am, vous devez relancer automake. Avec les versions récentes d'automake, les fichiers Makefile sont générés de telle manière qu'une modification dans un fichier Makefile.am est détectée et en exécutant make,
vous regénérez tous les fichiers impactés par votre modification. La
suppression d'un fichier source est similaire à celle d'un ajout : vous
modifiez la variable bonjour_SOURCES et la suite ne change pas.
Ajouter le support d'une bibliothèque est très simple lorsque celle-ci
est placée dans un endroit standard. Il vous suffit d'ajouter une ligne
dans le fichier configure.ac. Voici celle qui vous permet d'utiliser la bibliothèque mathématique libm :
AC_SEARCH_LIBS(pow, m)
En général, l'absence d'une bibliothèque devrait provoquer l'arrêt du script configure. Le troisième argument de AC_SEARCH_LIBS
est l'action à effectuer si le test est positif, et le quatrième celle
si le test est négatif. Nous pouvons profiter de ce dernier ainsi :
AC_SEARCH_LIBS(pow, m, [], [exit])
Vous pouvez aussi afficher un message à la place d'une simple sortie. Pour cela, utilisez AC_MSG_ERROR; Voici le résultat pour libz :
AC_SEARCH_LIBS(gzopen, z, [], [
AC_MSG_ERROR([zlib est manquante])
])
Une modification de configure.ac nécessite la relance d'aclocal, d'autoconf et d'automake.
Qu'entendons-nous par nettoyage ? S'il s'agit de supprimer tout ce qui
a été compilé, principalement les fichiers objets (dont extension est .o), exécutez simplement make clean. SI vous voulez par contre supprimer aussi les fichiers générés suite à l'exécution de configure, lancez la commande make distclean.
Vous pouvez aussi vouloir faire le grand nettoyage et ne garder que les fichiers sources, ainsi que le strict nécessaire à autoconf et automake. Dans ce cas, supprimez tout sauf :
les sources de votre code (typiquement les fichiers d'extension .c et .h; notez que le fichier ${racine}/config.h est généré par configure : vous pouvez le supprimer aussi) ;
les fichiers Makefile.am ;
le fichier configure.ac ;
les fichiers ChangeLog, NEWS, README et AUTHORS ;
si vous les avez modifiés, les fichiers COPYING et INSTALL.
Après un tel ménage, il ne reste dans notre projet bonjour plus que les fichiers suivants :
$ find .
.
./AUTHORS
./ChangeLog
./configure.ac
./Makefile.am
./NEWS
./README
./src
./src/afficher.c
./src/afficher.h
./src/main.c
./src/Makefile.am
Pour générer un tel paquet, nous pensons à la commande tar du répertoire ${racine} renommée au préalable bonjour-0.1. Mais il y a beaucoup plus simple : exécutez make dist. Vous obtenez votre paquet, avec pour nom et numéro de version ce que vous avez défini dans configure.ac sur la ligne AC_INIT.
Cela n'est pas encore la meilleure méthode. En effet, le plus propre est de lancer la commande make distcheck.
Ainsi, non seulement vous générez le paquet souhaité, mais il est aussi
testé pour vous, dans un répertoire différent de celui de
développement. Vous mettez ainsi en évidence s'il manque des fichiers,
présents dans l'arborescence de développement, mais non déclarés dans
les fichiers Makefile.am.
Si vous voulez générer un fichier bonjour-0.1.tar.bz2, exécutez la commande make dist-bzip2. Vous pouvez obtenir les cibles possibles avec un simple grep "^dist-" Makefile. Celles d'automake-1.8.5 sont les suivantes : dist-gzip, dist-bzip2, dist-tarZ, dist-shar et dist-zip.