Date de publication : 6 janvier 2009
Ce document est un cours d'initiation à CMake, un système de construction logicielle libre, multilangage et multiplate-forme.
Ce cours vous enseignera les bases nécessaires au bon usage de cet outil. Une fois ces informations assimilées, vous serez capable d'utiliser CMake dans les cas les plus simples. Vous pourrez alors poursuivre vers une utilisation avancée avec l'unique aide de la documentation officielle.
Qu'un logiciel soit libre ou propriétaire, son code source n'a que peu de chances de rester exclusivement entre les mains de son auteur d'origine. Un projet propriétaire développé en entreprise pourra être maintenu par une multitude de personnes tout au long de son existence. Un logiciel libre, quant à lui, peut compter plusieurs dizaines voire plusieurs centaines de développeurs éparpillés à travers le monde, sans compter les utilisateurs souhaitant compiler le code source eux-mêmes.
Pour compiler le code source, toutes ces personnes ont besoin d'un ensemble de fichiers contenant les paramètres de compilation. En effet, construire un programme à partir de son unique code source relève de la rétro-ingénierie. Or, au vu des conditions précitées, il est tout à fait impensable de fournir avec le code source un tel fichier dont le format serait lié à un EDI particulier, à un compilateur particulier ou encore à un système d'exploitation particulier. De plus, les paramètres de compilation listés dans ce fichier ne doivent évidemment pas dépendre d'une configuration spécifique de la machine effectuant la compilation (telle que le chemin d'installation d'une bibliothèque tierce).
En conséquence, il devient évident que le partage du fichier de projet de l'EDI ou encore du ou des fichiers makefile n'est pas une solution acceptable.
En réponse à ce besoin est née une nouvelle catégorie d'outils : les systèmes de construction logicielle (ou build systems, en anglais). Comme son nom l'indique, un tel système a pour but de permettre, directement ou indirectement, la compilation d'un code source.
Tous les systèmes de construction ont leurs spécificités, mais leur fonctionnement est globalement le même. Dans un premier temps, le
développeur écrit dans un fichier la description du projet (incluant la liste des fichiers sources, les bibliothèques à lier au binaire final,
etc.) de façon totalement indépendante de la configuration logicielle de la machine (qui inclut le compilateur utilisé, le
chemin vers les fichiers des bibliothèques à lier, etc.). Ensuite, les personnes souhaitant compiler le projet appellent le système de
construction en lui passant en paramètre le fichier écrit par le développeur. Le système analyse alors le fichier d'une part et tente de
récupérer des informations sur la configuration logicielle de la machine l'exécutant d'autre part. Une fois ces opérations accomplies, il sera
enfin en mesure de produire un script de compilation de bas niveau (tel qu'un fichier Makefile, comprenant des appels
explicites au compilateur, dont les paramètres seront propres à la configuration logicielle) :

Certains systèmes effectueront plutôt des appels directs au compilateur afin de produire le binaire final, mais l'idée générale est la même.
Le fichier décrivant le projet, accompagné du code source (et éventuellement de la documentation et d'autres ressources diverses), est donc tout ce qui constitue le paquetage partagé par les différents contributeurs du projet (et par les utilisateurs si le projet est libre). Étant donné que ce fichier est utilisable par tous quelle que soit leur configuration logicielle, les systèmes de construction constituent donc une réponse viable à la problématique exposée ci-dessus.
Il existe un grand nombre de systèmes de construction logicielle différents, tels que les fameux GNU Autotools, SCons, Jam ou encore qmake. Le système qui nous intéresse dans ce document est le non moins connu CMake.
CMake est un système de construction logicielle. C'est un logiciel libre (licence BSD), multilangage et multiplate-forme.
À partir du fichier descriptif du projet (nommé CMakeLists.txt) et des informations sur la configuration logicielle de la
machine effectuant la compilation (listées dans le fichier CMakeCache.txt, prérempli lors de la phase d'analyse de la
configuration), CMake est capable de générer les types de fichiers suivants :
Makefile ;
Makefile).
C'est le générateur qui permet de définir le type de fichier à générer. Il existe plusieurs générateurs pour les makefiles, ainsi qu'un générateur par EDI supporté :

Alors que d'autres systèmes de construction ne sont utilisables que sous un ensemble restreint de systèmes d'exploitation, CMake est disponible aussi bien sous GNU/Linux que sous Windows et MacOS, pour ne citer qu'eux.
À l'heure où cet article est publié, CMake gère déjà les langages de programmation les plus populaires, tels que C, C++ et Java.
De plus, comme vous pourrez vous-même en juger dans la suite de ce cours, CMake est très facile à utiliser (ce qui est loin d'être le cas de
tous les outils de cette catégorie) et la syntaxe du fichier CMakeLists.txt est à la fois simple et efficace.
Le paquetage livré avec CMake contient une interface graphique pour assister son fonctionnement. Cependant, cette interface n'est à l'heure actuelle pas disponible sous tous les systèmes d'exploitation. De plus, son utilisation n'est pas indispensable, voire nuisible d'un point de vue pédagogique ; il est en effet toujours intéressant de savoir ce qui se passe derrière ce type d'interfaces. Par conséquent, l'utilisation des interfaces graphiques de CMake ne sera pas traitée dans ce cours.
Son indépendance vis à vis du système d'exploitation, du compilateur et des outils de développement, sa simplicité d'utilisation, ses performances et ses nombreuses fonctionnalités font de CMake le système de prédilection de plus en plus d'utilisateurs. Pour plus d'informations sur CMake, dirigez-vous vers le site officiel.
Le site officiel de CMake propose des installateurs pour un vaste ensemble de systèmes d'exploitation. Vous pouvez également télécharger son code source si vous souhaitez le compiler vous-même. Dans tous les cas, rendez-vous sur la page du site officiel consacrée à l'installation de CMake.
CMake est également disponible dans les dépôts de nombreuses distributions GNU/Linux, telles que Debian, Fedora et Gentoo.
Passons directement à la pratique avec un projet simple. Par projet simple, comprenez un exécutable dont le code est constitué de quelques classes et n'utilisant pas de bibliothèque tierce.
Nous considérerons tout au long du chapitre que le projet est organisé de cette façon :
Le contenu des fichiers sources importe peu, pourvu que le code ne comporte pas d'erreur.
Une fois le code source écrit, occupons-nous de notre fichier CMakeLists.txt.
La syntaxe du fichier CMakeLists.txt est on ne peut plus simple. Elle est exclusivement constituée d'appels de
commandes. Les arguments à passer aux commandes sont placés entre parenthèses et séparés par des espaces ou des sauts de ligne. Les
différents appels de commandes sont séparés de la même façon :
nom_de_la_commande(argument1 argument2 argument3)nom_d_une_autre_commande( argument1 argument2 )
À noter qu'aucun saut de ligne n'est toléré entre le nom de la commande et la parenthèse ouvrante.
Si un argument doit lui-même contenir des espaces, il pourra être entouré de guillemets doubles :
commande(argument"argument avec des espaces")
Enfin, les éternels commentaires, quant à eux, sont marqués par un # en début de ligne :
#ceci est un commentairecommande(argument1 argument2)
Écrivez le code suivant dans le fichier CMakeLists.txt du projet :
#Déclaration du projetproject(MyProject)#Déclaration de l'exécutableadd_executable( my_executable src/bird.h src/duck.cpp src/duck.h src/main.cpp src/sparrow.cpp src/sparrow.h )
La commande project() est utilisée pour déclarer un projet.
Le premier paramètre est le nom que vous voulez donner à votre projet. Il s'agit entre autres du nom que portera le projet dans votre EDI.
Cette commande prend également un second paramètre optionnel. Il s'agit du langage dans lequel le code du programme à compiler est écrit. Dans la majorité des cas, vous pouvez simplement ignorer cet argument, CMake étant capable de reconnaître lui-même le langage en analysant les sources.
Nous devons ensuite définir nos cibles, c'est-à-dire l'ensemble des exécutables et des bibliothèques que nous voulons générer à partir du
code source. Pour le moment, nous ne souhaitons créer qu'un unique exécutable à partir de l'ensemble de notre code source. Pour ce faire,
nous écrivons la commande add_executable().
Le premier paramètre est le nom de l'exécutable. Inutile de spécifier une éventuelle extension de fichier (telle que
.exe) : rappelez-vous que CMake est conçu pour être utilisé sous n'importe quel système d'exploitation. En
outre, lorsque arrivera le moment de générer le fichier exécutable, il décidera lui-même des caractères à ajouter au nom que vous lui
avez passé (en l'occurrence .exe sous Windows).
Vous l'avez très certainement deviné : les arguments suivants constituent la liste des fichiers sources qui seront passés au
compilateur. Remarquez toutefois la présence apparemment inopportune des fichiers d'en-tête (*.h) au sein de
cette liste. En effet, on ne passe jamais directement un fichier d'en-tête à un compilateur. Ces fichiers ne sont lus qu'au moment
de la résolution des directives d'inclusion (#include) par le préprocesseur. Par bonheur, CMake traite spécifiquement
ces fichiers de façon à ce qu'ils n'apparaissent jamais dans une commande d'appel au compilateur. Vous n'avez donc pas à vous
soucier de ce qui se passe sous le capot à ce niveau-là.
Dans ce cas, me direz-vous, pourquoi prendre la peine de lister les fichiers d'en-tête s'ils ne sont même pas passés au compilateur ? La raison est simple : vous voudrez certainement qu'ils soient présents parmi les fichiers sources du projet dans votre EDI, afin de pouvoir les ouvrir et les éditer rapidement. Par ailleurs, même si vous n'utilisez pas d'EDI pouvant gérer des projets, imaginez que cela pourrait être le cas de la personne qui reprendra votre code dans le futur.
En bref, ne vous posez pas de question : listez tous les fichiers sources sans faire de distinction.
![]() | |
Quel que soit votre système d'exploitation, c'est la notation UNIX qui est utilisée pour l'écriture de l'adresse d'un fichier. Il faudra
donc écrire |
Notre fichier CMakeLists.txt est maintenant complet ; lançons CMake sans plus attendre. Placez-vous dans le dossier racine
de votre projet et entrez une commande cmake de cette forme :
cmake.-G"nom_du_générateur_de_mon_choix"
Le premier paramètre désigne le répertoire où se situe le fichier CMakeLists.txt du projet. Dans notre cas, il s'agit du
dossier courant.
L'option -G permet de définir le générateur. Du générateur dépend le type de fichier(s) que CMake va générer (fichier
Makefile, fichier de projet d'EDI, etc.), comme il a été évoqué dans la présentation de
CMake. Remplacez nom_du_générateur_de_mon_choix par l'un des générateurs de CMake. Pour obtenir la liste des générateurs
disponibles sur votre plate-forme, affichez l'aide de la commande cmake :
cmake --help
Cela ne semble pas forcément évident au premier abord, mais il faut écrire en toutes lettres le nom du générateur dans la commande
cmake. Par exemple, si vous voulez générer un fichier de projet pour Code::Blocks et que votre compilateur est MinGW, entrez
la commande suivante :
cmake.-G"CodeBlocks - MinGW Makefiles"
Si tout se passe bien, la commande devrait retourner quelque chose comme ceci :
-- The C compiler identification isnom_de_votre_compilateur_C-- The CXX compiler identification isnom_de_votre_compilateur_C++-- Check for working C compiler:chemin_vers_votre_compilateur_C-- Check for working C compiler:chemin_vers_votre_compilateur_C-- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler:chemin_vers_votre_compilateur_C++-- Check for working CXX compiler:chemin_vers_votre_compilateur_C++-- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Configuring done -- Generating done -- Build files have been written to:adresse_du_dossier_de_votre_projet
CMake devrait avoir généré les fichiers suivants :
CMakeFiles : dossier contenant les fichiers intermédiaires de compilation, des fichiers temporaires et
autres fichiers de configuration qui ne regardent que CMake ;CMakeCache.txt : fichier définissant un ensemble de variables de configuration et d'informations sur votre
système (nous nous pencherons davantage sur ce fichier plus loin dans le cours) ;cmake_install.cmake : script CMake d'installation de votre programme (dont nous ne parlerons pas dans ce
cours) ;MyProject.xxx : fichier de projet de votre EDI (selon le générateur choisi) ;Makefile (selon le générateur choisi).
Remarquez que votre dossier src reste inchangé. Aucun fichier d'aucune sorte n'y a été créé. C'est un des aspects qui
explique pourquoi CMake est autant apprécié chez les maniaques du rangement tel que moi : il ne s'introduit pas là où on ne lui a pas dit
d'entrer et se contente de son dossier personnel (CMakeFiles) pour l'écriture de fichiers temporaires et de
configuration interne.
Vous avez maintenant tout ce qu'il faut pour lancer une compilation. Procédez comme vous le faîtes habituellement : ouvrez votre projet avec
votre EDI préféré et cliquez sur le bouton , ou bien lancez make depuis votre terminal. Si
tout va bien, votre compilateur aura créé un exécutable dans le dossier racine de votre projet, aux côtés du fichier
CMakeLists.txt. Lancez-le pour vérifier qu'il n'y a pas de problème.
Au fur et à mesure que votre projet évolue, de nouvelles classes, de nouveaux fichiers sources apparaissent, tandis que d'autres
disparaissent. Chaque modification de l'arborescence vous contraint à modifier le fichier CMakeLists.txt encore et
encore.
CMake possède toute la panoplie de commandes nécessaires à la résolution de ce type de désagréments. Pour ce cas précis, utilisons la
commande file() :
cmake_minimum_required(VERSION2.6)#Déclaration du projetproject(MyProject)#Génération de la liste des fichiers sourcesfile( GLOB_RECURSE source_files src/*)#Déclaration de l'exécutableadd_executable( my_exe${source_files})
file() est une commande pouvant effectuer un vaste nombre d'opérations différentes.
Le premier paramètre est le type d'opération à exécuter. L'opération GLOB récupère une liste de fichiers à partir
d'une expression. La variante GLOB_RECURSE cherche dans les sous-dossiers des dossiers spécifiés.
Le troisième paramètre est une expression à laquelle le chemin de chaque fichier doit correspondre pour que celui-ci soit ajouté à la liste.
src/* désigne tous les fichiers situés dans le dossier src, ainsi que tous les fichiers situés dans
chaque sous-dossier de src (spécificité de la variante GLOB_RECURSE).
Le deuxième paramètre est la variable (ici source_files) dans laquelle la liste de fichiers est écrite.
La suite est évidente : parmi les arguments de la commande add_executable(), en lieu et place de la liste de
fichiers sources se trouve désormais la variable contenant la liste des fichiers récupérés par la commande file().
Comme vous le voyez, pour accéder à la valeur d'une variable, il faut entourer son nom d'accolades et faire précéder le tout par le
symbole $.
L'utilisation de la commande file() (ainsi que beaucoup d'autres) nécessite de spécifier une version minimale de
CMake, ce qui explique la ligne ajoutée au début du script. Si vous l'omettez, pas de panique : un message d'erreur vous le rappellera
explicitement.
![]() | Il est nécessaire de relancer cmake chaque fois que la liste des fichiers change ! |
|---|---|
En effet, même si le fichier |
Définissez le dossier où vos exécutables doivent être produits via la variable EXECUTABLE_OUTPUT_PATH. Optons pour un
nouveau dossier nommé bin :
set(EXECUTABLE_OUTPUT_PATH bin)
Mieux encore : placez vos exécutables dans des dossiers portant le nom du type de compilation (Release, Debug, ...). Ce dernier est
défini par la variable CMAKE_BUILD_TYPE (qui n'a d'effet seulement si le générateur choisi est basé sur Make) :
cmake_minimum_required(VERSION2.6)#Configuration du projetproject(MyProject)set(EXECUTABLE_OUTPUT_PATH bin/${CMAKE_BUILD_TYPE})#Génération de la liste des fichiers sourcesfile( GLOB_RECURSE source_files src/*)#Déclaration de l'exécutableadd_executable( my_exe${source_files})
Ne vous inquiétez pas si aucun sous-dossier n'est créé dans bin : la variable CMAKE_BUILD_TYPE
doit être définie par vous-même. Par défaut, cette variable est vide ; dans ce cas, CMake crée un script de compilation en mode
Debug.
Avant de faire l'erreur de la définir directement dans le fichier CMakeLists.txt, lisez plutôt ce qui suit.
Les fichiers sources et la documentation mis à part, CMakeLists.txt est le seul fichier que vous partagerez avec
les autres contributeurs du projet (et éventuellement avec les utilisateurs finaux qui souhaitent compiler les sources eux-mêmes). Par
conséquent, ce fichier doit décrire la construction du projet de façon totalement indépendante du système et du cas d'utilisation.
En effet, les contributeurs n'utiliseront pas tous le même système avec les mêmes paramètres ; votre système lui-même pourrait très bien évoluer au cours du projet. De plus, vous ne compilerez pas toujours pour les mêmes raisons (pour tester ou déboguer votre code, pour publier une version de votre logiciel, etc.).
En bref, ne définissez rien dans CMakeLists.txt qui puisse varier d'un ordinateur à l'autre ou d'un cas
d'utilisation à l'autre. Voici quelques exemples d'informations qui ne doivent pas figurer dans le fichier :
Tous ces paramètres personnels doivent être définis ailleurs : dans le fichier
CMakeCache.txt.
Exerçons-nous avec un exemple typique ; nous allons modifier le mode de compilation, caractérisé par la variable
CMAKE_BUILD_TYPE. Ouvrez le fichier CMakeCache.txt et trouvez la ligne définissant cette variable.
La ligne en question devrait être dans cet état :
CMAKE_BUILD_TYPE:STRING=
Toutes les définitions de variable ont cette forme :
NOM_DE_LA_VARIABLE:TYPE=valeur
Le type de la variable n'a d'autre intérêt que de permettre à l'interface graphique de CMake d'afficher le contrôle adéquat (case à
cocher pour BOOL, champ de texte pour STRING, boite de dialogue d'ouverture de fichier pour
FILEPATH, etc.). Toutefois, ne modifiez en aucun cas le type des variables ; CMake s'y perdrait.
Optons pour une compilation en mode Release (la majuscule est prise en compte) :
CMAKE_BUILD_TYPE:STRING=Release
Relancez la commande cmake et compilez. Si le niveau d'optimisation est élevé et que la macro NDEBUG est définie,
alors l'opération est un succès.
Remarquez que vous pouvez également modifier les variables définies par CMakeCache.txt directement via la commande
cmake grâce à l'option -D. Voici la commande correspondant à la modification que nous venons d'effectuer
(l'absence d'espace entre le -D et la définition de la variable est requise) :
cmake.-G"nom_du_générateur_de_mon_choix"-DCMAKE_BUILD_TYPE:STRING=Release
Cela peut en effet être beaucoup plus commode que d'éditer le fichier manuellement si la variable en question est souvent modifiée. On
pourrait par exemple imaginer un script cmake_debug.xxx et un autre cmake_release.xxx
(extension selon le système d'exploitation) ; un simple double-clic sur le script (ou un appel depuis le terminal) permettrait de
modifier rapidement le mode de compilation.
Nous allons maintenant configurer la compilation d'un nouvel exécutable faisant usage d'une bibliothèque. Dans cet exemple, nous aimerions inclure l'une des célèbres bibliothèques de Boost dans notre programme : Boost.Signals. Si vous voulez inclure une autre bibliothèque, l'idée générale est bien entendu la même.
Le projet est organisé comme ceci :
L'unique fichier source est main.cpp, visible ci-dessous. Une fois ce code correctement compilé, l'exécutable résultant doit
afficher Hello, World! sur la sortie standard.
#include <iostream>#include <boost/signals.hpp>structHelloWorld{voidoperator()()const{std::cout<<"Hello, World!"<<std::endl;}};intmain(intargc,char**argv){boost::signal0<void>sig; HelloWorld hello; sig.connect(hello);sig();return0;}
Nous débutons avec le fichier CMakeLists.txt suivant :
cmake_minimum_required(VERSION2.6)#Configuration du projetproject(MyProject)set(EXECUTABLE_OUTPUT_PATH bin/${CMAKE_BUILD_TYPE})#Configuration de l'exécutablefile( GLOB_RECURSE source_files src/*)add_executable( my_exe${source_files})
Il y a deux méthodes pour inclure une bibliothèque avec CMake : la méthode manuelle et la méthode assistée. La méthode assistée est celle qui nécessite le moins de travail de votre part. Toutefois, il n'est possible de l'utiliser qu'avec un ensemble restreint de bibliothèques. C'est pourquoi nous allons passer en revue les deux méthodes.
De la même façon que lors d'une compilation en ligne de commande ou via un EDI, nous devons :
Pour remplir la première condition, nous utiliserons la commande include_directories() :
include_directories(/chemin/vers/boost_x_xx_x)
La commande link_directories() nous permettra de répondre à la deuxième condition :
link_directories(/chemin/vers/boost_x_xx_x/stage/lib)
Enfin, la commande target_link_libraries(), qui sert à spécifier les différentes bibliothèques à lier à une cible (indiquée
en premier argument), remplira la dernière :
target_link_libraries(
my_exe
boost_signals-mt
)![]() | |
Sous Windows, les adresses absolues doivent être notées sous cette forme : |
![]() | |
Remarquez qu'il n'est pas nécessaire (même si cela reste possible) de spécifier le nom exact du fichier de bibliothèque. Par exemple, dans
le cas d'un fichier sous GNU/Linux portant le nom |
Voici le contenu du fichier CMakeLists.txt après ces modifications :
cmake_minimum_required(VERSION2.6)#Configuration du projetproject(MyProject)set(EXECUTABLE_OUTPUT_PATH bin/${CMAKE_BUILD_TYPE})#Inclusion de Boostinclude_directories(/chemin/vers/boost_x_xx_x)link_directories(/chemin/vers/boost_x_xx_x/stage/lib)#Configuration de l'exécutablefile( GLOB_RECURSE source_files src/*)add_executable( my_exe${source_files})#Configuration de l'édition de lienstarget_link_libraries( my_exe boost_signals-mt )
Appelez la commande cmake, compilez et testez le programme. Tout fonctionne à merveille ? Alors ajoutons la touche finale à
notre fichier CMakeLists.txt.
Comme expliqué dans le chapitre précédent, le fichier CMakeLists.txt ne devrait pas comporter d'informations relatives à
votre système. Utilisons donc des variables à la place des chemins et des noms de fichiers spécifiés :
cmake_minimum_required(VERSION2.6)#Configuration du projetproject(MyProject)set(EXECUTABLE_OUTPUT_PATH bin/${CMAKE_BUILD_TYPE})#Inclusion de Boostinclude_directories(${boost_include_dir})link_directories(${boost_lib_dir})#Configuration de l'exécutablefile( GLOB_RECURSE source_files src/*)add_executable( my_exe${source_files})#Configuration de l'édition de lienstarget_link_libraries( my_exe${boost_signals_lib_name})
Encore une fois, définissez la valeur de ces variables dans le fichier CMakeCache.txt. Ajoutez-y les lignes suivantes :
//Chemin vers le dossier des fichiers d'en-tête de Boostboost_include_dir:FILEPATH=/chemin/vers/boost_x_xx_x//Chemin vers le dossier des fichiers bibliothèques de Boostboost_lib_dir:FILEPATH=/chemin/vers/boost_x_xx_x/stage/lib//Nom du fichier de la bibliothèque Boost.Signalsboost_signals_lib_name:STRING=boost_signals-mt
Effectuez un ultime test. Si celui-ci est toujours concluant, alors votre fichier CMakeLists.txt est prêt à être distribué
aux autres contributeurs du projet.
CMake est livré avec un ensemble de modules dont certains sont destinés à vous assister pour l'inclusion de bibliothèques dans votre projet. Les bibliothèques Boost font partie des quelques paquetages à bénéficier d'un tel module.
Tout d'abord, incluez le module correspondant (nommé FindBoost, dans notre cas) dans CMakeLists.txt :
include(FindBoost)
Utilisez ensuite la commande find_package(), désormais utilisable de cette manière pour Boost, grâce à l'inclusion du
module FindBoost :
find_package( Boost1.36.0REQUIRED signals )
Boost, le premier argument, est le nom du paquetage à trouver.1.36.0, le deuxième argument définit la version du paquetage à trouver.REQUIRED, le troisième argument, indique que le ou les éléments de la bibliothèque qui vont suivre sont
requis.signals, le quatrième et dernier argument, est l'un de ces éléments.
Enfin, il est possible de spécifier des critères de recherche plus précis, tels que l'utilisation des versions statiques ou dynamique des
bibliothèques ou encore de leurs versions optimisées multithread ou non. Ces variables sont à définir avant l'appel de la commande
find_package() :
set(Boost_USE_STATIC_LIBS ON)set(Boost_USE_MULTITHREAD OFF)
Si la commande find_package() parvient à trouver les paquetages spécifiés, elle définit un ensemble de variables
comprenant, entre autres :
Boost_LIBRARY_DIRS, répertoire contenant les fichiers d'en-tête de Boost ;Boost_INCLUDE_DIRS, répertoire contenant les fichiers de bibliothèques de Boost ;Boost_LIBRARIES, chemin des fichiers des bibliothèques recherchées (ici, Boost.Signals uniquement).
Ces variables sont à utiliser comme arguments des commandes respectives include_directories(),
link_directories() et target_link_libraries(). Leurs définitions se situent dans la partie consacrée à
l'inclusion manuelle d'une bibliothèque.
![]() | |
Vous aimeriez être certain qu'une variable soit définie comme vous le souhaitez ? Vous pouvez utiliser la commande
|
Le fichier CMakeLists.txt complet est le suivant :
cmake_minimum_required(VERSION2.6)#Configuration du projetproject(MyProject)set(EXECUTABLE_OUTPUT_PATH bin/${CMAKE_BUILD_TYPE})#Inclusion de Boostinclude(FindBoost)find_package( Boost1.36.0REQUIRED signals )link_directories(${Boost_LIBRARY_DIRS})include_directories(${Boost_INCLUDE_DIRS})#Configuration de l'exécutablefile( GLOB_RECURSE source_files src/*)add_executable( my_exe${source_files})#Configuration de l'édition de lienstarget_link_libraries( my_exe${Boost_LIBRARIES})
Dans un environnement de type Unix où l'organisation des fichiers est standard, la recherche devrait être concluante sans avoir à fournir plus d'informations à CMake. Toutefois, dans le cas d'une installation exotique ou de l'utilisation d'un autre système d'exploitation, il peut s'avérer nécessaire de spécifier dans quel dossier chercher.
Dans un tel cas de figure, vous devez ajouter dans la section EXTERNAL cache entries du fichier CMakeCache.txt
un ensemble de définitions de variables. Pour Boost, les variables à définir sont BOOST_ROOT,
BOOST_INCLUDEDIR et BOOST_LIBRARYDIR, définissant respectivement le dossier racine de votre installation
de Boost, le dossier contenant les fichiers d'en-tête et le dossier contenant les fichiers de bibliothèques :
//Chemin vers le dossier racine de BoostBOOST_ROOT:FILEPATH=/chemin/vers/boost_x_xx_x//Chemin vers le dossier des fichiers d'en-tête de BoostBOOST_INCLUDEDIR:FILEPATH=/chemin/vers/boost_x_xx_x//Chemin vers le dossier des fichiers bibliothèques de BoostBOOST_LIBRARYDIR:FILEPATH=/chemin/vers/boost_x_xx_x/stage/lib
Lancez la commande cmake. Celle-ci devrait renvoyer la sortie suivante :
-- Boost version: x.xx.x -- Found the following Boost libraries: -- signals -- Configuring done -- Generating done -- Build files have been written to: [adresse_du_dossier_de_votre_projet]
Compilez votre programme et vérifiez qu'il affiche bien Hello, World!.
Pour une description détaillée de l'ensemble des modules livrés avec CMake, rendez-vous sur la documentation officielle.
Poursuivons notre mise en pratique avec la compilation d'une bibliothèque.
Restons dans la simplicité et imaginons une bibliothèque permettant de dire bonjour dans la langue de notre choix.
L'organisation d'un projet de bibliothèque est quelque peu différente de celle d'un exécutable :
Le dossier include contient les en-têtes publics dans un sous-dossier portant le nom de la bibliothèque. Ainsi, les
programmes utilisant ladite bibliothèque incluront les en-têtes nécessaires de cette façon :
#include <iostream>#include <libhello/french.h>intmain(intargc,char**argv){std::cout<<"In France, people say hello that way: "; std::cout<<libhello::say_hello_in_french(); std::cout<<std::endl;return0;}
Le dossier src, quant à lui, contient le reste des fichiers sources. Si l'un de ces fichiers doit inclure l'un des
en-têtes publics, la directive d'inclusion sera similaire à celle de l'exemple ci-dessus. Par exemple, le contenu du fichier
src/french.cpp est le suivant :
#include <iostream>#include <libhello/french.h>namespacelibhello{conststd::stringsay_hello_in_french(){return"Bonjour, tout le monde !";}}//namespace libhello
L'en-tête correspondant, include/libhello/french.h, est la suivante :
#ifndef LIBHELLO_FRENCH_H#define LIBHELLO_FRENCH_Hnamespacelibhello{conststd::stringsay_hello_in_french();}//namespace libhello#endif
Les fonctions correspondant aux autres langues sont déclarées et définies de la même façon.
Comme d'habitude, on commence par déclarer le projet :
project(Hello)
On définit ensuite le dossier dans lequel le fichier de bibliothèque sera généré. Puisqu'il s'agit d'une bibliothèque, optons pour
lib :
set(LIBRARY_OUTPUT_PATH lib/${CMAKE_BUILD_TYPE})
Les fichiers sources de la bibliothèque incluent les en-têtes publics avec une directive d'inclusion du type #include<>. Le dossier include doit donc être ajouté à la liste des dossiers parcourus
par le préprocesseur lors de l'inclusion d'un en-tête externe. On retrouve la commande que l'on a utilisée pour l'inclusion d'une bibliothèque :
include_directories(include)
Comme toujours suit la génération de la liste des fichiers sources. On liste les fichiers du dossier src ainsi que ceux de
include :
file( GLOB_RECURSE source_files src/*include/*)
Enfin, nous déclarons la bibliothèque elle-même, avec la commande add_library() :
add_library( hello SHARED${source_files})
Tout comme pour la commande add_executable(), le premier paramètre est le nom de la bibliothèque et le dernier la liste des
fichiers sources. Le second paramètre, optionnel, indique le type de bibliothèque à générer :
STATIC, pour générer une bibliothèque à lier statiquement ;SHARED, pour générer une bibliothèque à lier dynamiquement.
Par défaut, ce paramètre est défini à STATIC.
Voici le contenu du fichier CMakeLists.txt au complet :
cmake_minimum_required(VERSION2.6)#Configuration du projetproject(Hello)set(LIBRARY_OUTPUT_PATH lib/${CMAKE_BUILD_TYPE})#Inclusion des en-têtes publicsinclude_directories(include)#Configuration de la bibliothèquefile( GLOB_RECURSE source_files src/*include/*)add_library( hello SHARED${source_files})
Générez le script de compilation et compilez. Votre compilateur devrait avoir généré un fichier de bibliothèque partagée dans le dossier
lib (ou dans l'un de ses sous-dossiers).
Il est maintenant temps de tester notre bibliothèque. Pour ce faire, nous allons coder un exécutable effectuant l'ensemble des tests
nécessaires. Commençons par mettre en place le projet CMake correspondant à cet exécutable dans un nouveau dossier (que nous nommerons
test) de notre hiérarchie :
Le fichier main.cpp se contente de vérifier que les fonctions de la bibliothèque renvoient bien les chaînes de caractères
attendues :
#include <cassert>#include <iostream>#include <libhello/english.h>#include <libhello/french.h>intmain(intargc,char**argv){assert(libhello::say_hello_in_english() =="Hello everyone!");assert(libhello::say_hello_in_french() =="Bonjour, tout le monde !"); std::cout<<"Test OK."<<std::endl;return0;}
Le fichier CMakeLists.txt ne comporte aucune nouvelle notion. En fait, il s'agit du même fichier que celui de la
Section 4-1, « Méthode manuelle », à l'exception que les variables boost_include_dir, boost_lib_dir
et boost_signals_lib_name sont directement remplacées par les paramètres correspondant à notre bibliothèque :
cmake_minimum_required(VERSION2.6)#Configuration du projetproject(TestHello)set(EXECUTABLE_OUTPUT_PATH bin/${CMAKE_BUILD_TYPE})#Inclusion de la bibliothèque Helloinclude_directories(../include)link_directories(../lib/${CMAKE_BUILD_TYPE})#Configuration de l'exécutablefile( GLOB_RECURSE source_files src/*)add_executable( test_hello${source_files})#Configuration de l'édition de lienstarget_link_libraries( test_hello hello )
Prêtez attention à la commande link_directories(). Si vous spécifiez le type de compilation
(CMAKE_BUILD_TYPE) à Debug (par exemple) pour cet exécutable de test, le fichier de la bibliothèque Hello sera recherché dans
../lib/Debug. Veillez donc à ce que la bibliothèque ait été préalablement compilée en Debug avant de tenter une compilation
de l'exécutable de test de ce type. Bien évidemment, cette remarque importe quel que soit le type de compilation.
Lancez CMake, compilez et vérifiez que le test se déroule sans échec d'assertion.
Vous voilà fin prêt à utiliser CMake pour la gestion de vos propres projets logiciels. Bien entendu, il y a nombre de commandes que nous n'avons pas passées en revue dans ce cours, mais les exemples étudiés couvrent l'essentiel des cas que vous pourrez rencontrer. Quoi qu'il en soit, la documentation officielle reste le compagnon idéal pour l'initié que vous êtes désormais.
Merci à Alp Mestan, Matthieu Brucher, Come David, Philippe Dunski et surtout 3DArchi pour leurs encouragements, leurs conseils et leur participation à la relecture de ce document. Merci également à ram-0000 pour ses corrections d'ordre grammatical. Si vous souhaitez vous aussi me faire part de vos suggestions, n'hésitez pas à me contacter.
Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2008 Florian Goujeon. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée.
Copyright © 2000-2012 - www.developpez.com