I. Introduction▲
I-A. Qu'est-ce qu'une bibliothèque ?▲
Les bibliothèques peuvent être comparées à des boites à outils. En effet, celles-ci apportent des fonctionnalités supplémentaires que le programmeur va pouvoir utiliser pour réaliser son projet. Imaginons que vous souhaitiez afficher une image dans une fenêtre. Le langage C, ou même C++ en lui-même ne propose rien pour faire une telle chose. Sachant que d'autres programmeurs ont déjà travaillé dessus, il est préférable de réutiliser ce qu'ils ont fait plutôt que de perdre du temps à le refaire. Pour cela, une bibliothèque qui se spécialise dans l'affichage d'images nous fournira toutes les fonctions nécessaires pour lire une image et pour l'afficher. Au lieu d'effectuer les appels aux fonctions bas niveau comme fopen(), fread(), ou autres, il suffira d'appeler une fonction de la bibliothèque, qui lira directement le fichier sans même que l'on se préoccupe des détails.
L'autre bonne nouvelle, c'est que ce que vous voulez faire a sûrement été déjà réalisé et donc, qu'il existe une bibliothèque répondant à vos besoins.
Même les fonctions printf() ou pow() proviennent de bibliothèques. La première fonction provient de la bibliothèque C (libc), incluse par défaut dans tous projets et la seconde provient de la bibliothèque mathématiques (libm).
I-B. Comment une bibliothèque s'insère-t-elle dans le processus de compilation ?▲
Revoyons rapidement les étapes de la création d'un programme :
Les fichiers source (les fichiers .c/.cpp/…) sont d'abord lus par le préprocesseur qui ne s'occupera que de traiter les commandes #XXX (#define, #ifdef…) pour produire un fichier source où ces directives ont été exécutées.
Ensuite, le compilateur transforme le source en un fichier intermédiaire (.o/.obj), compilé, mais qui n'est pas exécutable. Le langage utilisé est spécifique au compilateur. Pour valider votre code, le compilateur a besoin de connaître tous les symboles que vous utilisez. Le nom d'une fonction, autant que le nom d'une variable sont des symboles. Si vous utilisez une fonction qui est inconnue, il va vous le faire savoir avec un message d'erreur. En effet, pour lui, si la fonction est inconnue, il est incapable de traiter le symbole et donc, incapable de compiler le code. En C et C++, les fichiers .h/.hpp permettent de déclarer les symboles au compilateur et donc de lui dire : « cette fonction existe, voici les paramètres qu'elles acceptent, le type de valeur qu'elle retourne ».
Finalement, l'éditeur de liens prend les fichiers objets du programme pour les assembler et produire un exécutable. Ici, le code de toutes les fonctions doit être présent et sera inséré dans le programme. Si le code des fonctions que vous utilisez n'est pas dans vos fichiers objets, il va les chercher dans les bibliothèques que vous lui aurez indiquées. S'il ne trouve toujours pas la fonction, il ne peut pas continuer (le programme est incomplet) et l'édition des liens échouera.
Ainsi, pour pouvoir rajouter une bibliothèque à un projet, il faut :
- les fichiers d'entêtes (.h/.hpp) décrivant les fonctions utilisables pour le compilateur ;
- les fichiers de code compilé de la bibliothèque (.a/.so/.lib) pour l'éditeur de liens ;
- les fichiers pour l'exécution (.so/.dll).
I-C. Quelle est la différence entre bibliothèque statique et bibliothèque dynamique ?▲
La bibliothèque statique sera compilée avec le programme et directement intégrée dans l'exécutable final. Il n'y aura donc pas besoin de .so/.dll lors de l'exécution. L'avantage de la compilation statique est de rendre l'exécutable indépendant (il peut fonctionner sur n'importe quelle plateforme compatible, sans pour autant nécessiter l'installation de la bibliothèque), toutefois, il est, de par ce fait, plus lourd et vous ne pourrez pas mettre à jour les bibliothèques sur lesquelles il repose, sans le recompiler.
La bibliothèque dynamique fera que l'exécutable ira chercher les fonctions à exécuter dans des fichiers séparés (.so/.dll). L'exécutable sera plus léger et les bibliothèques pourront être mises à jour sans recompiler l'exécutable. De plus, une bibliothèque peut être chargée à la volée (durant l'exécution d'un programme) et ainsi fournir des fonctionnalités en plus, si elle est présente : les systèmes de modules ou ajouts sont souvent implémentés sous la forme de bibliothèques chargées lorsque nécessaires.
Il est vrai que lors d'une compilation avec les bibliothèques dynamiques, celles-ci peuvent être mises à jour en remplaçant simplement les .so/.dll, toutefois, il faut que les changements entre les versions ne modifient en rien l'interface binaire de l'application (ABI). Soit, il ne faut pas que les signatures des fonctions changent.
I-D. Détails sur les bibliothèques▲
I-D-1. Organisation des fichiers▲
Sachant que les bibliothèques doivent toujours mettre à disposition les mêmes fichiers, celles-ci sont toujours présentées de la même façon. Au minimum, dans l'archive d'une bibliothèque, vous trouverez :
- un dossier include, contenant les fichiers d'entêtes ;
- un dossier lib, contenant les fichiers de code compilé à indiquer à l'éditeur de liens ;
- un dossier bin (ou, directement à la racine), contenant les fichiers pour l'exécution d'un programme compilé avec cette bibliothèque (.dll/.so).
Bien entendu, vous pouvez aussi trouver un fichier README, un fichier de licence et de la documentation.
I-D-2. S'y retrouver dans les fichiers à télécharger▲
Le site officiel d'une bibliothèque vous proposera de nombreux fichiers à télécharger. Chaque fichier porte un nom bien précis permettant de trouver rapidement celui correspondant à vos besoins.
La raison de cette multitude de fichiers est que chaque compilateur nécessite un fichier compilé par un même compilateur à la même version. En effet, les .lib compilés avec Visual Studio 2012, ne seront pas compatibles avec le compilateur MinGW, ou même, ils ne seront pas compatibles avec Visual Studio 2008.
Voici les fichiers proposés sur le site de la SDL (bibliothèque de jeux 2D) :
Classification |
Fichier |
Description |
Binaire pour l'exécution (« Runtime Binaries ») |
SDL2-2.0.1-win32-x86.zip |
Fichier contenant la DLL pour Windows 32 bits. |
SDL2-2.0.1-win32-x64.zip |
Fichier contenant la DLL pour Windows 64 bits. |
|
SDL2-2.0.1.dmg |
Fichier de développement et d'exécution pour OS X. |
|
Fichiers pour le développement (« Development Libraries ») |
SDL2-devel-2.0.1-VC.zip |
Fichiers de développement pour Visual C++ (Visual Studio). |
SDL2-devel-2.0.1-mingw.tar.gz |
Fichiers de développement pour MinGW. |
|
SDL2-2.0.1.dmg |
Fichier de développement et d'exécution pour Mac OS. |
|
Sources |
SDL2-2.0.1.zip |
Code source de la bibliothèque. En la compilant, vous générerez les fichiers .lib/.so/.a/.dll/… |
Il se peut que les fichiers proposés diffèrent quelque peu, mais généralement, les bibliothèques se ressemblent.
Sous Linux, il est possible d'installer les bibliothèques à l'aide du gestionnaire de paquets.
Une erreur commune, provoquant l'apparition d'« undefined referenceUndefined reference » malgré la bonne configuration du projet, est d'utiliser des bibliothèques 64 bits alors que le compilateur ne supporte que le 32 bits. En effet, certains compilateurs ne sont pas capables de gérer les formats 64 bits et donc ne les liront simplement pas (et ce bien que vous ayez un système d'exploitation 64 bits). Comme les fichiers de la bibliothèque ne sont pas lus, les fonctions définies dans le fichier ne seront pas reconnues, provoquant des erreurs de type « undefined reference ».
De plus, sous Windows, vous aurez l'erreur 0xc000007b lorsque vous aurez un projet compilé en 32 bits, tout en ayant les .DLL en 64 bits.
I-D-2-a. Convention de nommage▲
Les fichiers à télécharger se différencient par l'architecture, mais aussi par les spécificités de compilation donc voici la convention de nommage :
Nommage |
Description |
x86 |
Bibliothèque 32 bits. |
x86-64 |
Bibliothèque 64 bits. |
d (ou debug) |
Bibliothèque compilée avec les informations de débogage. |
mt (ou t) |
Bibliothèque utilisant du multithread. |
static |
Bibliothèque statique. |
monolith |
Bibliothèque où tous les modules ont été regroupés en un bloc. |
II. Configuration du projet▲
II-A. Arborescence du projet▲
Afin que votre projet soit organisé et facilement distribuable, nous allons créer les dossiers suivants :
- lib : pour les fichiers des bibliothèques (.a/.so/.lib) ;
- include : pour les fichiers d'entêtes (notamment, ceux de la SDL) ;
- src : pour vos fichiers de code source.
Cette organisation permet d'éviter que vous ayez à installer (ou faire installer) la bibliothèque sur chacun des postes que vous utiliserez : elle est intégrée dans le projet. De plus, l'installation de la bibliothèque dans les répertoires des éditeurs n'est pas recommandée (même si certains sites font de cette façon :(), notamment, car vous forcez l'utilisateur recevant votre projet à installer lui-même la bibliothèque et à reconfigurer le projet, mais aussi, parce que cela entraîne des oublis. D'autant plus que sous Windows, il n'y a pas d'emplacement déterminé pour mettre les bibliothèques.
Ce qui nous donne l'arborescence suivante :
/
include
– Nom bibliothèque 1
– – …
– – tous les fichiers .h de la première bibliothèque
– – …
– Nom bibliothèque 2
– – …
– – tous les fichiers .h de la deuxième bibliothèque
– – …
– …
lib
– libXXX.so (dynamique) ou libXXX.a (statique) (pour Linux)
– libXXX.lib (pour Windows) ou libXXX*.a (pour MinGW)
– …
src
– main.c
XXX.dll (pour Windows)
les fichiers de projets (.cbp (pour Code::Blocks), .sln (pour Visual Studio)…)
Lorsque vous voulez distribuer le code de votre projet, il suffit de compresser le dossier et c'est tout. L'utilisateur à qui vous donnez votre projet n'aura qu'à décompresser le fichier et il pourra directement l'utiliser.
II-B. Mise en place de la bibliothèque▲
La mise en place d'une bibliothèque se fait en trois étapes :
- spécification d'un nouveau répertoire de fichiers d'entêtes pour le compilateur ;
- spécification d'un nouveau répertoire de fichiers de bibliothèques pour l'éditeur de liens ;
- spécification des fichiers de bibliothèques à utiliser.
II-B-1. GCC/G++ en ligne de commande▲
Dans votre ligne de compilation :
- rajoutez une ou plusieurs options -I pour indiquer le répertoire des fichiers d'entêtes ;
- rajoutez une ou plusieurs options -L pour indiquer le répertoire des fichiers des bibliothèques ;
- rajoutez une ou plusieurs options -l pour indiquer les fichiers de bibliothèques à utiliser. Généralement, le nom est formé avec le motif suivant : libXXX.so (XXX étant le nom de la bibliothèque). Le nom à passer à l'option est simplement XXX : -lXXX.
Ce qui donne :
gcc -Wall -Wextra -L./lib -I./include src/main.c -o main -lXXX
Pour compiler le fichier src/main.c avec la bibliothèque XXX.
II-B-2. Code::Blocks▲
Dans votre projet Code::Blocks, dans les options de compilation (« build options ») :
Ensuite, pensez bien à sélectionner votre projet dans son intégralité et non pas l'une des cibles (par défaut « debug » ou « release »), cela permettra d'appliquer la configuration à l'intégralité du projet et de ne pas avoir de problème lors d'un changement de cible :
Maintenant, vous pouvez ajouter le répertoire des fichiers d'entêtes dans l'onglet « répertoires de recherche » (« search directories »), sous onglet « compilateur » (« compiler »). Cliquez sur le bouton « ajouter » (« Add ») et tapez : ./include puis « OK ».
Il faut maintenant faire de même pour le répertoire des bibliothèques, dans le sous-onglet « Éditeur de liens » (« Linker »), ajoutez ./lib :
Finalement, vous pouvez rajouter les bibliothèques, dans l'onglet « paramètres de l'éditeur de liens » (« Linker settings ») :
libXXX.so est un exemple. Vous devez rajouter les noms des fichiers présents dans le dossier lib.
Votre projet est finalement prêt pour utiliser la bibliothèque.
II-B-3. Visual Studio▲
Dans les propriétés du projet « Projet » → « Propriétés » (« Project » → « Properties ») :
Cliquez sur « Propriétés de la configuration » (« Configuration Properties ») afin de déplier l'arborescence et de déverrouiller les deux options en haut de la fenêtre. Sélectionnez la configuration « Toutes plateformes » (« All Configurations ») afin que vos modifications s'appliquent sur la cible « Debug » et la cible « Release » :
Dans le menu C/C++, il est conseillé de changer l'entrée « Niveau d'avertissement » (« Warning Level ») du niveau 3 au niveau 4 afin que le compilateur vous renvoie le maximum d'informations sur votre code et ses possibles erreurs.
Dans le menu C/C++, rajoutez ./include pour l'entrée « Répertoires d'inclusion supplémentaires » (« Additional Include Directories ») :
Dans le menu « Éditeur de liens » (« Linker »), ajoutez ./lib pour l'entrée « Répertoires de bibliothèques supplémentaires » (« Additional Library Directories ») :
Dans le sous-menu « Entrée » (« Input »), pour l'entrée « Dépendances supplémentaires » (« Additional Dependencies »), insérez « libXXX.lib » :
libXXX.lib est un exemple. Vous devez rajouter les noms des fichiers présents dans le dossier lib.
Pour valider chaque modification des propriétés, cliquez sur le bouton « Appliquer » (« Apply »).
Afin que vous puissiez copier votre projet d'un répertoire à un autre et que Visual Studio continue de trouver les fichiers, il est recommandé d'utiliser des chemins relatifs (par exemple : ./lib et ./include).
Vous devriez maintenant pouvoir compiler le projet utilisant la bibliothèque.
II-B-4. Xcode▲
Dans Xcode, cliquez sur votre projet dans l'arborescence se trouvant à gauche de l'éditeur afin de faire apparaître les propriétés. Ensuite cliquez sur « Paramètres de compilation » (« Build Settings ») et rajoutez /Libraries/Frameworks à l'entrée « Chemin de recherche des frameworks » (« Framework Search Paths ») permettant ainsi à Xcode de trouver le fichier Framework de la bibliothèque. Dans l'onglet « Étapes de compilation » (« Build Phases ») et dans la sous-section « Lier binaire avec des bibliothèques » (« Link Binary With Libraries »), cliquez sur le bouton « Ajouter autre… » (« Add Other… ») puis dans le dossier /Library/Frameworks, trouvez la framework de votre bibliothèque. Votre projet est maintenant prêt à être compilé.
II-B-5. Qt Creator (QMake)▲
La configuration d'un projet Qt Creator utilisant QMake se fait à travers la modification du fichier .pro de votre projet.
Pour rajouter un dossier de fichiers d'entêtes, vous devez utiliser la variable INCLUDEPATH :
INCLUDEPATH += ./include
Pour rajouter un dossier de bibliothèques, vous devez utiliser la variable LIBS :
LIBS += -L./lib
Finalement, pour rajouter une bibliothèque à utiliser lors de l'édition des liens, vous devez utiliser une nouvelle fois la variable LIBS :
LIBS += -lXXX
N'oubliez pas d'exécuter le programme qmake pour que votre projet prenne en compte les modifications du .pro.
II-B-6. Eclipse CDT▲
Dans Eclipse CDT, vous pouvez accéder aux propriétés du projet en passant par le menu « Projet » → « Propriétés » (« Project » → « Properties »), ou en faisant un clic droit sur votre projet puis « Propriétés » (« Properties ») :
Afin de modifier toutes les configurations en une seule fois, vous devez choisir la configuration « Toutes les configurations » (« All configurations ») :
Ensuite, vous devez accéder par l'arborescence sur la gauche à « Compilation C/C++ » → « Paramètres » (« C/C++ Build » → « Settings »).
Dans l'onglet « Paramètrages des outils » (« Tool Settings »), vous devez sélectionner « Entêtes » (« Includes ») pour ajouter le dossier des fichiers d'entêtes :
La configuration du dossier des bibliothèques et des bibliothèques à inclure dans votre projet se trouve dans l'entrée « Bibliothèques » (« Libraries ») :
Afin d'obtenir le chemin sous la forme « "${workspace_loc:/${ProjName}/lib}" » il suffit de cliquer sur le bouton « Workspace » lors de l'ajout d'un répertoire et de sélectionner le répertoire voulu du workspace comme le montrent ces captures d'écrans :
Pour finir, il suffit de valider toutes les fenêtres de configuration et votre projet est prêt.
II-B-7. CMake▲
CMake permet de générer les fichiers pour compiler un projet (fichiers de Visual Studio, Code::Blocks…) en écrivant un unique fichier descriptif du projet à compiler.
II-B-7-a. Cas d'une bibliothèque installée sur le système▲
Si la bibliothèque est installée (ou doit être installée) sur le système, CMake peut rechercher lui-même la bibliothèque et configurer le projet en adéquation avec votre système grâce à la commande FindPackage :
FindPackage(XXX)
Toutefois, la commande FindPackage nécessite la présence d'un fichier descriptif indiquant à CMake comment trouver et configurer la bibliothèque. Généralement, l'installation de CMake s'accompagne d'une série de ces fichiers pour les bibliothèques les plus courantes.
II-B-7-b. Cas d'une bibliothèque accompagnant le projet▲
Pour spécifier un répertoire de fichiers d'entêtes, il faut utiliser include_directories :
include_directories(./include)
Pour spécifier un répertoire de bibliothèques, il faut utiliser link_directories :
link_directories(./lib)
Finalement, pour ajouter une bibliothèque, il faut utiliser :
target_link_libraries(
executable
XXX)
Voilà, vous pouvez générer les fichiers de compilation de votre projet.
II-B-8. Autotools▲
Autotools permet lui aussi de générer les fichiers de compilation pour un projet. Pour rajouter une bibliothèque à votre projet, il faut éditer le fichier Makefile.am.
II-B-8-a. Cas d'une bibliothèque installée sur le système▲
Si la bibliothèque est installée (ou doit être installée) sur le système, autotools propose une macro qui vérifiera la présence de la bibliothèque et configurera le compilateur en adéquation :
AC_CHECK_LIB([XXX], [functionXXX], [AC_SUBST(LIBDL,-lXXX)])
Vous pouvez aussi utiliser AC_SEARCH_LIB :
AC_SEARCH_LIBS(functionXXX, XXX, [], [
AC_MSG_ERROR([XXX est introuvable])
])
II-B-8-b. Cas d'une bibliothèque accompagnant le projet▲
Si la bibliothèque accompagne votre projet et n'est donc pas disponible pour le système, pour ajouter un répertoire de fichiers d'entêtes, il faut utiliser la variable _CFLAGS (pour du C) ou _CPPFLAGS (pour du C++) :
exec_CFLAGS=-I./include
ou
exec_CPPFLAGS=-I./include
Pour ajouter un répertoire de bibliothèques et une bibliothèque, il faut utiliser la variable _LDADD :
exec_LDADD=-L./lib -lXXX
(Dans le cas, où votre exécutable final s'appelle « exec »)
III. Erreurs▲
III-A. Implicit declaration of function… ▲
L'erreur « Implicit declaration of function… » apparaît lorsque vous n'avez pas rajouté le fichier d'entêtes dans votre code source à l'aide d'un #include. En effet, comme tout autre programme, il faut inclure le fichier d'entêtes dans le code pour avoir accès aux fonctions (sinon, le compilateur ne connaît pas ces symboles).
III-B. Undefined reference▲
L'erreur « undefined reference » (référence indéfinie) indique que l'éditeur de liens ne trouve pas une fonction. Cela arrive si :
- vous n'avez pas ajouté la bibliothèque contenant la fonction mentionnée par l’éditeur de lien au projet (ou il manque une autre bibliothèque, Google pourra vous aider à trouver de laquelle dont il s'agit, en recherchant « undefined reference le_nom_de_la_fonction ») ;
- si le fichier de la bibliothèque n'est pas trouvé ou qu'il n'est pas lu (il n'est pas lu, lorsqu'il n'est pas compatible avec votre compilateur).
IV. Conclusion▲
Vous savez maintenant installer et configurer une bibliothèque dans votre projet. Vous pouvez continuer le développement de votre programme tout en utilisant les fonctionnalités de cette bibliothèque.