Retrouvez cet article dans : Linux Magazine 81
Apprenez à utiliser pkg-config pour intégrer vos bibliothèques aux auto-tools.
Le mois dernier, nous disposions d’une bibliothèque, que nous voulions lier à un programme, et nous nous interrogions sur son numéro de version. La solution proposée était de disposer d’une fonction de la bibliothèque qui, avec un numéro de version fourni en argument, testait si elle était compatible avec ce qu’on lui demandait. Une macro M4 pour autoconf se chargeait de faire appel à cette fonction.
Dans cet article, nous allons poser la même problématique, mais proposer une solution différente, à base de pkg-config. En effet, à l’installation de la bibliothèque, il est possible d’inclure un fichier contenant ce numéro de version, mais aussi son chemin d’installation, les options du compilateur et de l’éditeur de liens...
Ce fichier pourrait être un script qui retournerait les options du compilateur si nous le lancions avec l’option --cflags par exemple. Mais chacun proposerait ainsi son propre script, réinventant la roue à chaque fois.
L’objectif du projet pkg-config est de proposer un outil unique permettant de connaître toutes ces options nécessaires à la compilation, en se basant sur des fichiers de définitions au format relativement simple.
Nos exemples
Comme le mois dernier, nous allons partir sur deux projets. Le premier est une bibliothèque, libmytoolkit, dans laquelle vous pourrez trouver une fonction afficher(). Le second est un programme très simple, qui fait appel à cette fonction pour afficher un message. Nous vous laissons découvrir lequel à la lecture de l’exemple.
libmytoolkit
Nous commençons par la bibliothèque libmytoolkit qui n’a rien de particulier, à part peut-être l’utilisation de Glib. Pourquoi utiliser la fonction g_printf() alors que printf() faisait l’affaire ? Cette dépendance permettra de montrer comment la signifier à pkg-config un peu plus loin dans l’article. Voici les éléments principaux de notre projet de bibliothèque :
src/afficher.c
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
int afficher(char*msg) {
g_printf("%s\n", msg);
}
src/libmytoolkit.h
#ifndef LIBMYTOOLKIT_H #define LIBMYTOOLKIT_H int afficher(char*msg); #endif
configure.ac
AC_PREREQ(2.59) AC_INIT(libmytoolkit, 0.1, ymettier@libertysurf.fr) AC_CONFIG_SRCDIR([src/afficher.c]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE LIBMYTOOLKIT_AC=1 LIBMYTOOLKIT_REV=0 LIBMYTOOLKIT_ANC=0 AC_SUBST(LIBMYTOOLKIT_AC) AC_SUBST(LIBMYTOOLKIT_REV) AC_SUBST(LIBMYTOOLKIT_ANC) # Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET AC_PROG_LIBTOOL # 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. AM_PATH_GLIB_2_0( 2.0.0, [], [AC_MSG_FAILURE([Glib not found])]) AC_CONFIG_FILES([ Makefile src/Makefile ]) AC_OUTPUT
Makefile.am
SUBDIRS=src
src/Makefile.am
lib_LTLIBRARIES=libmytoolkit.la
libmytoolkit_la_SOURCES=afficher.c
libmytoolkit_la_CPPFLAGS=@GLIB_CFLAGS@
libmytoolkit_la_LIBADD=@GLIB_LIBS@
libmytoolkit_la_LDFLAGS=-version-info ${LIBMYTOOLKIT_AC}:${LIBMYTOOLKIT_REV}:${LIBMYTOOLKIT_ANC}
libmytoolkit.pc
C’est ici que nous faisons connaissance avec pkg-config. Cet outil est utilisé pour obtenir des informations sur les bibliothèques installées sur le système. Ceux qui ont déjà programmé avec glib savent qu’il faut ajouter `pkg-config glib-2.0 --cflags --libs` sur la ligne de commande du compilateur. Cette commande typique demande à pkg-config de nous indiquer ce qu’il faut ajouter comme options du compilateur et de l’éditeur de liens lorsque nous souhaitons utiliser glib-2.0.
Il existe plusieurs options pour pkg-config. Par exemple, pkg-config --list-all vous indique la liste des bibliothèques installées et leur identifiant pour pkg-config. Si vous voulez connaître le numéro de version de l’une d’elle, utilisez l’option --modversion (à ne pas confondre avec --version qui indique le numéro de version de pkg-config lui-même). Pour la bibliothèque libxml2, voici ce que nous obtenons :
$ pkg-config libxml-2.0 --modversion 2.6.17
Ce qui nous intéresse néanmoins le plus souvent sont ce qu’indiquent ces deux options : --cflags et --libs. Nous allons définir cela dans un fichier, dont l’extension est .pc, et qui devra être installé dans le répertoire par défaut de pkgconfig, généralement /usr/lib/pkgconfig. Voici un premier jet :
# Variables
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${exec_prefix}/include
# Definitions
Name: libmytoolkit
Description: Il faudrait penser à une meilleure description ici
Version: 0.1
Requires: glib-2.0 >= 2.0.0
Libs: -L${libdir} -lmytoolkit
Cflags: -I${includedir}
La syntaxe de ce fichier est très simple. Deux types de lignes cohabitent. Celles qui définissent des variables commencent par un nom de variable, sont suivies du signe =, et terminent par le contenu de la variable. Nous avons ici une syntaxe habituelle, aussi habituelle que l’utilisation des variables avec le signe $ et les accolades.
L’autre type de ligne consiste en des définitions, composées d’un mot-clé, d’un double point et d’un contenu. Les mots-clés sont indiqués dans le tableau 1 :

Comme vous pouvez vous en douter, il est très souhaitable que nous profitions d’autoconf pour générer ce fichier. Pour cela, renommez-le en libmytoolkit.pc.in et revoyez son contenu :
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libmytoolkit
Description: Il faudrait vraiment penser à une meilleure description ici
Version: @VERSION@
Requires: glib-2.0 >= 2.0.0
Libs: -L${libdir} -lmytoolkit
Cflags: -I${includedir}
Nous avons presque fini avec notre bibliothèque. Il ne nous reste plus d’une part qu’à demander à autoconf de générer notre fichier, ce que vous pouvez faire simplement en ajoutant libmytoolkit.pc dans la liste AC_CONFIG_FILES du fichier configure.ac.
D’autre part, nous devons encore déclarer ce fichier à automake dans le fichier Makefile.am. Pour cela, rappelez-vous il y a deux mois, nous avions défini de nouvelles variables, l’une de suffixe dir et l’autre _DATA. C’est ce que nous allons effectuer ici pour indiquer que ce fichier doit atterrir dans ${libdir}/pkgconfig. Ajoutez ceci au fichier Makefile.am :
pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libmytoolkit.pc
Nous avons ainsi ajouté le support de pkg-config dans notre bibliothèque. Il ne vous reste plus qu’à en créer une archive avec la suite habituelle aclocal; autoconf; autoheader; automake -a -c, suivi de ./configure; make distcheck. Si, à cet endroit, vous préférez utiliser un répertoire d’installation non standard, par exemple /tmp/libmytoolkit, pour effectuer vos tests sans avoir besoin des droits administrateurs, ne vous privez pas. Nous allons voir comment nous en sortir malgré cette petite difficulté.
pkg-config libmytoolkit --modversion
Essayez maintenant ce que nous avons vu plus haut pour d’autres bibliothèques :
$ pkg-config --list-all | grep libmytoolkit libmytoolkit libmytoolkit - Il faudrait vraiment penser à une meilleure description ici $ pkg-config libmytoolkit --modversion 0.1 $ pkg-config libmytoolkit --cflags -I/usr/include -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include $ pkg-config libmytoolkit --libs -L/usr/lib -L/usr/lib -lmytoolkit -lglib-2.0 -lintl -liconv
Avez-vous remarqué la présence de glib ici ? La raison en est simple : nous avons défini glib comme une dépendance. Notre outil pkg-config prend donc les mesures qui s’imposent.
Si vous avez installé libmytoolkit.pc dans un répertoire autre que celui où pkg-config va chercher ses définitions, vous devez indiquer votre répertoire à l’aide de la variable PKG_CONFIG_PATH. Cela donne par exemple :
PKG_CONFIG_PATH=/tmp/libmytoolkit/lib/pkgconfig pkg-config --list-all
En dehors des tests, nous vous déconseillons de définir une variable d’environnement (export PKG_CONFIG_PATH=/tmp/libmytoolkit/lib/pkgconfig). Le problème est qu’elle n’est définie que pour vous, et que dans la limite de votre session ou de votre environnement. Limitez ce genre de pratiques à vos tests.
bonjour_le_monde
Le programme bonjour_le_monde en lui-même est extrêmement simple. Voyez plutôt le fichier src/main.c :
#include <stdio.h>
#include <stdlib.h>
#include <libmytoolkit.h>
int main(int argc, char**argv) {
afficher(“Bonjour le monde”);
exit(EXIT_SUCCESS);
}
Ce programme nécessite notre libmytoolkit pour fonctionner. Nous allons écrire le fichier src/Makefile.am ainsi :
bin_PROGRAMS=bonjour_le_monde bonjour_le_monde_SOURCES=main.c bonjour_le_monde_CPPFLAGS=@LIBMYTOOLKIT_CFLAGS@ bonjour_le_monde_LDFLAGS=@LIBMYTOOLKIT_LIBS@
Ce fichier suppose que les variables autoconf LIBMYTOOLKIT_CFLAGS et LIBMYTOOLKIT_LIBS soient définies. Sans cela, notre fichier configure.ac serait rapide à écrire. C’est ici que nous allons faire appel à pkg-config. Nous pourrions définir ces variables ainsi dans le fichier configure.ac :
LIBMYTOOLKIT_CFLAGS=`pkg-config libmytoolkit --cflags` LIBMYTOOLKIT_LIBS=`pkg-config libmytoolkit --libs`
Cependant, il existe une macro M4 qui évite un peu la redondance de ces deux lignes. PKG_CHECK_MODULES peut prendre quatre arguments :
- Un identifiant qui sert de préfixe de variable. Il sert à définir les deux variables qui nous intéressent en y accolant les suffixes
_CFLAGSet_LIBS. - La liste des bibliothèques relatives à cet identifiant. Il est possible d’indiquer des contraintes de version.
- Une action à réaliser si les bibliothèques sont dispo-nibles.
- Une action à réaliser si une bibliothèque manque à l’appel.
Voici maintenant le fichier configure.ac de notre projet bonjour_le_monde :
AC_PREREQ(2.59) AC_INIT(bonjour_le_monde, 0.1, ymettier@libertysurf.fr) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE # Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET # Checks for libraries. PKG_CHECK_MODULES(LIBMYTOOLKIT, libmytoolkit >= 0.1, [], [AC_MSG_FAILURE([libmytoolkit not found])] ) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h]) # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_CONFIG_FILES([ Makefile src/Makefile ]) AC_OUTPUT
Vous lisez ici que, ayant besoin de définir les variables LIBMYTOOLKIT_CFLAGS et LIBMYTOOLKIT_LIBS, notre identifiant est naturellement LIBMYTOOLKIT. Au niveau des bibliothèques, remarquez deux points. D’abord, nous n’indiquons ici aucune référence à glib dont libmytoolkit a besoin.
En effet, libmytoolkit le sait, l’indique à pkg-config et ce dernier fait le nécessaire. Vous n’avez donc heureusement pas à vous préoccuper des dépendances des bibliothèques que vous utilisez !
Ensuite, nous vérifions que la bibliothèque libmytoolkit a bien un numéro de version supérieur ou égal à 0.1. Ceci est intéressant lorsque vous souhaitez utiliser une fonctionnalité récente d’une bibliothèque.
C’est ici que vous indiquez la plus vieille version compatible avec ce que vous voulez. En troisième argument, nous n’indiquons rien du tout. Nous n’avons pas grand-chose à dire si tout se passe bien et préférons continuer. En cas d’erreur, par contre, nous nous mettons en situation d’échec et affichons un message via le quatrième argument.
Conclusion
Pkg-config est aux bibliothèques ce qu’apt-get ou urpmi sont aux logiciels. Contrairement à ces derniers, pkg-config a l’avantage d’être le seul de ce genre à être à la fois aussi avancé et aussi utilisé.
Mais il souffre de quelques inconvénients majeurs. D’abord, il n’est pas installé partout. Lorsque votre système n’en dispose pas, vous devez, en tant que développeur, proposer une autre solution. En tant qu’utilisateur, si vous cherchez à compiler un logiciel y faisant appel, vous n’avez d’autre choix que de l’installer.
Dans ce cas, un autre inconvénient apparaît de manière flagrante : peu, voire pas du tout, de bibliothèques y seront déclarées. Par exemple, si glib a été installé auparavant, vous ne disposez pas forcément de ses définitions pour les indiquer au pkg-config que vous venez d’installer.
De manière moins flagrante, si pkg-config est installé avec le système, tel que les distributions GNU/Linux récentes, vous pouvez compter sur un grand nombre de bibliothèques définies.
Cependant, elles ne le seront pas toutes, les développeurs n’ayant pas fait la démarche de les y intégrer. En tant que développeurs de bibliothèques, si vous pouvez, ajoutez un fichier de définitions pour pkg-config à vos projets.
Utilisateurs de bibliothèques, considérez que pkg-config n’est pas utilisable partout, et proposez, dans la mesure du possible, une solution de secours aux pauvres dinosaures de l’informatique !
Liens :
- Le manuel d’autoconf : http://www.gnu.org/manual/autoconf/index.html
- Le manuel d’automake : http://www.gnu.org/manual/automake/index.html
- Le manuel de libtool : http://www.gnu.org/software/libtool/manual/
- La page de manuel de pkg-config.
- C en action, O’Reilly, Chapitre 20.
Retrouvez cet article dans : Linux Magazine 81

