Le compilateur gcc vous permet aussi de compiler les bibliothèques (certains disent "librairies", car "library" en anglais).
Il existe deux types de bibliothèques : les bibliothèques statiques et les bibliothèques dynamiques.
Les bibliothèques statiques
Une bibliothèque statique (généralement d'extension
.a) est une bibliothèque qui sera intégrée à l'exécutable lors de la compilation.
L'avantage est que l'exécutable produit est autonome et ne nécessite rien de plus pour fonctionner. La bibliothèque se comporte comme un autre fichier objet.
Si vous voulez faire une bibliothèque statique avec
machin.c et
machin.h, il suffit de faire :
Code : Console | gcc -c machin.c -o machin.o
ar -q libmachin.a machin.o |
La première commande crée le fichier objet (ça, on connait). La commande
ar archive tout simplement ce fichier.
Pour plus de détails, lisez
man ar en français.
La bibliothèque se liera comme n'importe quel fichier objet :
Code : Console | gcc bidule1.o bidule2.o bidule3.o libmachin.a -o Programme |
Les bibliothèques dynamiques
Les bibliothèques dynamiques -
.so (Sharing Object) sous Linux ou
.dll (Dynamic Link Library) sous Windows - sont des bibliothèques qui ne sont pas intégrées à l'exécutable lors de l'édition de liens. L'exécutable appelle alors la bibliothèque pour exécuter les fonctions.
Il en ressort plusieurs avantages :
- si la bibliothèque est utilisée par plusieurs programmes, elle n'est chargée qu'une fois en mémoire ;
- l'exécutable est plus léger ;
- on peut la mettre à jour sans recompiler le programme (à condition de ne pas modifier le header).
En revanche, il faudra fournir la bibliothèque, sans quoi le programme ne pourra pas fonctionner.
Pour créer une bibliothèque dynamique, il faut utiliser
Code : Console | gcc -c -fPIC truc -o truc.o
gcc -shared -fPIC truc.o -o libtruc.so |
L'option
-fPIC (Position Independent Code) compile sans indiquer d'adresse mémoire dans le code, car en fonction du programme qui l'utilisera, les adresses pourront être différentes. Ceci évitera des conflits entre les bibliothèques.
L'option
-shared indique que c'est une bibliothèque partagée (autre façon de dire dynamique).
On compilera encore de la même manière (avec
-fPIC en plus, par sécurité) :
Code : Console | gcc -fPIC bidule1.o bidule2.o bidule3.o libtruc.so -o Programme |
Hélas, l'exécutable n'est pas prêt à être utilisé. En effet, lorsqu'un programme appelle une bibliothèque, Linux cherche si la bibliothèque est installée dans un répertoire par défaut, mais pas dans le répertoire courant.
Pour pouvoir utiliser ce programme, il y a 2 solutions :
- LA MAUVAISE : copier la bibliothèque dans /lib ou dans /usr/libCode : Console(nécessite d'être root)
On laisse ces répertoires aux programmes fournis et suivis par la distribution Linux. On ne pollue pas le système avec nos programmes personnels.
Pour les programmes personnels (ou du voisin, ou que-sais-je), on utilise le répertoire /usr/local.
On copiera donc les bibliothèques dans /usr/local/lib !
Hélas, ce répertoire ne sera pas trouvé non plus par le système lorsque le programme appellera la bibliothèque ! On utilisera donc :
- LA BONNE MÉTHODE : utiliser la variable LD_LIBRARY_PATH. Cette variable donne les autres chemins où sont appelés les bibliothèques. Elle est sous la forme chemin1:chemin2:chemin3. Vous pouvez voir sa valeur en utilisant :Code : ConsoleGénéralement par défaut, elle n'est pas définie, la commande affichera alors un blanc. Pour ajouter un dossier, il suffit de mettreCode : Console
| export LD_LIBRARY_PATH=chemin:$LD_LIBRARY_PATH |
Pour indiquer le répertoire courant, utilisez le point (.).
Si vous ne voulez pas refaire la manip après chaque démarrage du système, éditez le fichier
/home/moi/.profile (n'affecte que l'utilisateur "moi") ou bien
/etc/profile (affecte tous les utilisateurs, nécessite donc d'être root pour le modifier).
Rajoutez la ligne
export LD_LIBRARY_PATH=chemin:$LD_LIBRARY_PATH, et voilà !
Remarquez, vous pouvez le faire en une ligne de commande :
Code : Console | echo export LD_LIBRARY_PATH=chemin:$LD_LIBRARY_PATH >> ~/.profile |
Cependant, ceci rajoutera la ligne à la fin du fichier. Les fichiers
.profile (s'il existe) et
/etc/profile étant structurés, il est préférable de les éditer à la main.
Les noms usuels
J'ai donné comme nom à mes exemples
libmachin.a et
libtruc.so. J'aurais très bien plus les appeler
machin.lib et
truc.dll : on est sous Linux, tout est permis !
Cependant, on utilisera plutôt par convention, des noms du même type que mes exemples. De plus, cela vous permettra de compiler en utilisant les options
-l et
-L :
- -lmachin pour libmachin.a ou -ltruc pour libtruc.so,
-l rajoute automatiquement lib devant et .a ou .so derrière ;
- -Lchemin pour indiquer le chemin ;
non nécessaire si le chemin est /lib ou /usr/lib (mais ça ne devrait pas !) ou dans LD_LIBRARY_PATH.
Exemple :
Code : Console | gcc -fPIC bidule.o -L. -lbidule -o Programme |
gcc créera l'exécutable
Programme en liant l'objet
bidule.o avec la bibliothèque
libbidule.so (ou
libbidule.a s'il ne trouve pas le so), située dans le répertoire courant.
Mettre à jour une bibliothèque
ld -soname
Maintenant, vous savez faire des bibliothèques dynamiques ; mais comment différencier deux versions de la même bibliothèque et faire en sorte que la nouvelle version fonctionne avec un programme compilé avec la première version ?
Par exemple, je compile le programme
bidule avec la bibliothèque
libtruc.so.1.1. Plus tard, je veux installer (pour un autre programme)
libtruc.so.1.2. Mais je veux que le programme
bidule puisse l'utiliser.
On utilise pour cela l'option
-Wl,-soname, pour définir le lien
libtruc.so.1 comme cela :
Code : Console | gcc -Wl,-soname, libtruc.so.1 -o libtruc.so.1.1 |
Ceci créera la bibliothèque
libtruc.so.1.1, à partir de
libtruc.so.1, et cette bibliothèque sera reconnue comme
libtruc.so.1 !
Quelques explications
Je ne vous l'avais pas dit, mais gcc ne fait pas l'édition de liens ! Il appelle pour cela la commande
ld !
Pour passer des options à
ld avec gcc on utilise
-Wl,option,
L'option
-soname libtruc.so.1 permettra à l'OS de reconnaître une bibliothèque comme s'appelant
libtruc.so.1.
Le nom
libtruc.so.1 sera intégré dans la cible (
libtruc.so.1.1) et sera lu par
ldconfig.
ldconfig
ldconfig crée des liens symboliques entre les
-soname et les bibliothèques concernées.
Il inspecte les bibliothèques dans les emplacements suivants :
- /lib
- /usr/lib
- les chemins indiqués dans /etc/ld.so.conf (vous pouvez l'éditer si vous êtes root) ;
- les chemins de LD_LIBRARY_PATH.
Il crée pour chaque bibliothèque un lien ayant comme nom le
-soname et ayant comme cible la bibliothèque. Si deux bibliothèques ont le même
-soname, il fera le lien vers la version la plus récente.
Faites attention aux points suivants :
- le nom de la bibliothèque doit être du type libnom.so.version pour être reconnue par ldconfig ;
- si vous éditez /etc/ld.so.conf, vous n'avez plus besoin de LD_LIBRARY_PATH ;
- une bibliothèque de ce type ne sera pas reconnue avec l'option -l, qui nécessite un type libnom.so (sans rien derrière) ;
- il existe certaines conventions : pour mettre à jour une bibliothèque il faut que les prototypes des fonctions soient identiques, si vous rajoutez une fonction à cette bibliothèque, il faudra mettre à votre bibliothèque -soname libtruc.so.2 ! Elle sera alors différente de libtruc.so.1 !
- il faudra exécuter ldconfig en console avant de lancer le programme.
Note : la commande
ldd Programme liste les bibliothèques utilisées par votre programme.
Compiler avec des bibliothèques tierces
Si vous avez installé une bibliothèque tierce (GTK ou SDL par exemple), vous vous demandez sûrement où se trouve les fameux
.so !
La réponse est qu'il n'y a pas à se poser la question !
On utilise dans ce cas la commande
pkg-config.
pkg-config est un utilitaire qui donne des informations sur les bibliothèques installées.
Ainsi :
Code : Console | pkg-config --cflags [bibliothèque] |
donne la liste des dossiers des headers de [bibliothèque]
Code : Console | pkg-config --libs [bibliothèque] |
donne la liste des fichiers de [bibliothèque]
Certaines bibliothèques fournissent un outil spécifique pour elle-mêmes. Par exemple, avec sdl, on peut utiliser sdl-config.
sdl-config --cflags équivaut à pkg-config --cflags sdl
pkg-config donne la liste telle qu'elle peut être comprise par gcc (avec des
-I et des
-l). On peut donc l'intégrer directement à une commande gcc.
Donc, pour compiler un programme
main.c qui utilise GTK+, on fera :
Code : Console | gcc -c main.c $(pkg-config --cflags gtk+-2.0) -o main.o
gcc main.o $(pkg-config --libs gtk+-2.0) -o Programme |
(Le «
$ » permet de renvoyer la valeur de ce qu'il y a entre parenthèses à la commande)
On peut aussi le faire en une seule commande :
Code : Console | gcc main.c $(pkg-config --cflags --libs gtk+-2.0) -o Programme |
Et comment je fais pour savoir que c'est gtk+-2.0 et non gtk2.0 ou encore gtk+ ?
pkg-config cherche dans /usr/lib/pkgconfig le fichier
[bibliothèque].pc.
Il vous suffit de trouver le fichier de votre bibliothèque dans ce dossier.
Vous pouvez même rajouter vos propres
.pc si vous constuisez des bibliothèques, en vous inspirant de la syntaxe des fichiers présents sur votre système. Vous pouvez mettre vos
.pc dans un autre dossier (par exemple /usr/local/lib/pkgconfig), il faut pour celà définir le chemin dans la variable
PKG_CONFIG_PATH.
Petit problème de compréhension ? Envie d'en savoir plus ?
man ldd
man ld.so
man ldconfig
man pkg-config (désolé, je ne l'ai pas trouvé en français celui-là)
Ça y est ! Vous maîtrisez gcc !
Ouah ! Mais j'ai un big programme à compiler, moi !
Faut que je me tape toutes ces commandes ?
Heureusement non, ce serait le comble du programmeur de taper sans cesse les mêmes commandes !
Il existe un outil permettant d'automatiser un peu tout ça. J'ai nommé :
make !