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.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
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
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} |
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é.
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.
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=<code>pkg-config libmytoolkit --cflags</code>
LIBMYTOOLKIT_LIBS=<code>pkg-config libmytoolkit --libs</code> |
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 _CFLAGS et _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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
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 :
Retrouvez cet article dans : Linux Magazine 81