Il est notoirement difficile d'obtenir des informations utiles sur CMake en tant que débutant. Jusqu'à présent, j'ai vu quelques tutoriels sur la façon de mettre en place un projet très basique ou un autre. Cependant, aucun de ceux-ci n'explique le raisonnement derrière tout ce qui y est montré, laissant toujours de nombreux trous à combler.
Qu'est-ce que l' appel CMake sur un CMakeLists signifie ? Est-il censé être appelé une fois par arbre de construction ou quoi? Comment utiliser des paramètres différents pour chaque build s'ils utilisent tous le même fichier CMakeLists.txt de la même source? Pourquoi chaque sous-répertoire a-t-il besoin de son propre fichier CMakeLists? Serait-il judicieux d'utiliser CMake sur un CMakeLists.txt autre que celui à la racine du projet? Si oui, dans quels cas? Quelle est la différence entre spécifier comment créer un exécutable ou une bibliothèque à partir du fichier CMakeLists dans son propre sous-répertoire et le faire dans le fichier CMakeLists à la racine de toutes les sources? Puis-je créer un projet pour Eclipse et un autre pour Visual Studio, en modifiant simplement l' -G
option lors de l'appel de CMake? Est-ce même ainsi qu'il est utilisé?
Aucun des didacticiels, pages de documentation ou questions / réponses que j'ai vus jusqu'à présent ne donne un aperçu utile pour comprendre comment utiliser CMake. Les exemples ne sont tout simplement pas exhaustifs. Quels que soient les tutoriels que je lis, j'ai l'impression de manquer quelque chose d'important.
Il y a beaucoup de questions posées par les débutants de CMake comme moi qui ne le demandent pas explicitement, mais qui rendent évident le fait qu'en tant que newbs, nous n'avons aucune idée de comment gérer CMake ou de quoi en faire.
Réponses:
À quoi sert CMake?
Selon Wikipedia:
Avec CMake, vous n'avez plus besoin de conserver des paramètres séparés spécifiques à votre environnement de compilation / build. Vous avez une configuration, et cela fonctionne pour de nombreux environnements.
CMake peut générer une solution Microsoft Visual Studio, un projet Eclipse ou un labyrinthe Makefile à partir des mêmes fichiers sans rien y changer.
Étant donné un tas de répertoires contenant du code, CMake gère toutes les dépendances, les commandes de construction et d'autres tâches dont votre projet a besoin avant de pouvoir être compilé. Il ne compile rien en fait. Pour utiliser CMake, vous devez lui indiquer (en utilisant des fichiers de configuration appelés CMakeLists.txt) quels exécutables vous avez besoin de compiler, à quelles bibliothèques ils sont liés, quels répertoires se trouvent dans votre projet et ce qu'ils contiennent, ainsi que tous les détails tels que les indicateurs ou tout ce dont vous avez besoin (CMake est assez puissant).
Si cela est correctement configuré, vous utilisez alors CMake pour créer tous les fichiers dont votre «environnement de construction natif» de choix a besoin pour faire son travail. Sous Linux, par défaut, cela signifie Makefiles. Donc, une fois que vous exécutez CMake, il créera un tas de fichiers pour son propre usage plus quelques
Makefile
s. Tout ce que vous avez à faire par la suite est de taper "make" dans la console à partir du dossier racine chaque fois que vous avez fini de modifier votre code, et bam, un exécutable compilé et lié est créé.Comment fonctionne CMake? Qu'est ce que ça fait?
Voici un exemple de configuration de projet que je vais utiliser tout au long:
Le contenu de chaque fichier est affiché et discuté plus tard.
CMake configure votre projet en fonction de la racine
CMakeLists.txt
de votre projet, et le fait dans le répertoire que vous avez exécutécmake
dans la console. Faire cela à partir d'un dossier qui n'est pas la racine de votre projet produit ce qu'on appelle une construction hors source , ce qui signifie que les fichiers créés lors de la compilation (fichiers obj, fichiers lib, exécutables, vous savez) seront placés dans ledit dossier , séparés du code réel. Il aide à réduire l'encombrement et est également préféré pour d'autres raisons, dont je ne parlerai pas.Je ne sais pas ce qui se passe si vous exécutez
cmake
sur un autre que la racineCMakeLists.txt
.Dans cet exemple, puisque je veux que tout soit placé dans le
build/
dossier, je dois d'abord y naviguer, puis passer à CMake le répertoire dans lequelCMakeLists.txt
réside la racine .Par défaut, cela configure tout en utilisant Makefiles comme je l'ai dit. Voici à quoi devrait ressembler le dossier de construction maintenant:
Quels sont tous ces fichiers? La seule chose dont vous devez vous soucier est le Makefile et les dossiers du projet .
Notez les dossiers
src/
etlib/
. Celles-ci ont été créées carsimple/CMakeLists.txt
elles les désignent à l'aide de la commandeadd_subdirectory(<folder>)
. Cette commande indique à CMake de rechercher dans ledit dossier un autreCMakeLists.txt
fichier et d'exécuter ce script, de sorte que chaque sous-répertoire ajouté de cette manière doit contenir unCMakeLists.txt
fichier. Dans ce projet,simple/src/CMakeLists.txt
décrit comment créer l'exécutable réel etsimple/lib/CMakeLists.txt
comment créer la bibliothèque. Chaque cibleCMakeLists.txt
décrite par a sera placée par défaut dans son sous-répertoire dans l'arborescence de construction. Alors, après un rapidedans la console done from
build/
, certains fichiers sont ajoutés:Le projet est construit et l'exécutable est prêt à être exécuté. Que faites-vous si vous voulez que les exécutables soient placés dans un dossier spécifique? Définissez la variable CMake appropriée ou modifiez les propriétés d'une cible spécifique . Plus d'informations sur les variables CMake plus tard.
Comment dire à CMake comment créer mon projet?
Voici le contenu, expliqué, de chaque fichier dans le répertoire source:
simple/CMakeLists.txt
:La version minimale requise doit toujours être définie, conformément à l'avertissement que CMake lance lorsque vous ne le faites pas. Utilisez quelle que soit votre version de CMake.
Le nom de votre projet peut être utilisé plus tard, et indique que vous pouvez gérer plus d'un projet à partir des mêmes fichiers CMake. Je ne vais pas approfondir cela, cependant.
Comme mentionné précédemment,
add_subdirectory()
ajoute un dossier au projet, ce qui signifie que CMake s'attend à ce qu'il ait un dossierCMakeLists.txt
, qu'il exécutera ensuite avant de continuer. Au fait, si vous avez défini une fonction CMake, vous pouvez l'utiliser à partir d'autresCMakeLists.txt
sous-répertoires, mais vous devez la définir avant de l'utiliseradd_subdirectory()
ou elle ne la trouvera pas. Cependant, CMake est plus intelligent sur les bibliothèques, c'est donc probablement la seule fois où vous rencontrerez ce genre de problème.simple/lib/CMakeLists.txt
:Pour créer votre propre bibliothèque, donnez-lui un nom, puis listez tous les fichiers à partir desquels elle est construite. Simple. S'il fallait un autre fichier,
foo.cxx
à compiler, vous écririez à la placeadd_library(TestLib TestLib.cxx foo.cxx)
. Cela fonctionne également pour les fichiers dans d'autres répertoires, par exempleadd_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx)
. Plus d'informations sur la variable CMAKE_SOURCE_DIR ultérieurement.Une autre chose que vous pouvez faire avec cela est de spécifier que vous voulez une bibliothèque partagée. L'exemple:
add_library(TestLib SHARED TestLib.cxx)
. N'ayez crainte, c'est là que CMake commence à vous faciliter la vie. Qu'elle soit partagée ou non, tout ce dont vous avez besoin pour utiliser une bibliothèque créée de cette manière est le nom que vous lui avez donné ici. Le nom de cette bibliothèque est maintenant TestLib et vous pouvez la référencer de n'importe où dans le projet. CMake le trouvera.Existe-t-il un meilleur moyen de répertorier les dépendances? Certainement oui . Vérifiez ci-dessous pour plus d'informations à ce sujet.
simple/lib/TestLib.cxx
:simple/lib/TestLib.h
:simple/src/CMakeLists.txt
:La commande
add_executable()
fonctionne exactement de la même manière queadd_library()
, sauf, bien sûr, qu'elle générera un exécutable à la place. Cet exécutable peut maintenant être référencé en tant que cible pour des choses commetarget_link_libraries()
. Étant donné que tutorial.cxx utilise le code trouvé dans la bibliothèque TestLib, vous le signalez à CMake comme indiqué.De même, tous les fichiers .h #inclus par des sources dans
add_executable()
qui ne sont pas dans le même répertoire que la source doivent être ajoutés d'une manière ou d'une autre. Si ce n'est pas pour latarget_include_directories()
commande,lib/TestLib.h
ne sera pas trouvé lors de la compilation du didacticiel, donc lelib/
dossier entier est ajouté aux répertoires d'inclusion à rechercher pour #includes. Vous pouvez également voir la commandeinclude_directories()
qui agit de la même manière, sauf qu'elle n'a pas besoin de spécifier une cible car elle la définit globalement, pour tous les exécutables. Encore une fois, j'expliquerai CMAKE_SOURCE_DIR plus tard.simple/src/tutorial.cxx
:Notez comment le fichier "TestLib.h" est inclus. Pas besoin d'inclure le chemin complet: CMake s'occupe de tout cela en coulisse grâce à
target_include_directories()
.Techniquement parlant, dans un simple arbre source comme celui-ci, vous pouvez vous passer du
CMakeLists.txt
s souslib/
etsrc/
et simplement ajouter quelque chose commeadd_executable(Tutorial src/tutorial.cxx)
àsimple/CMakeLists.txt
. Cela dépend de vous et des besoins de votre projet.Que dois-je savoir d'autre pour utiliser correctement CMake?
(Sujets AKA pertinents pour votre compréhension)
Trouver et utiliser des packages : la réponse à cette question l' explique mieux que je ne le pourrais jamais.
Déclarer des variables et des fonctions, utiliser le flux de contrôle, etc .: consultez ce tutoriel qui explique les bases de ce que CMake a à offrir, ainsi qu'une bonne introduction en général.
Variables CMake : il y en a beaucoup, donc ce qui suit est un cours intensif pour vous mettre sur la bonne voie. Le wiki CMake est un bon endroit pour obtenir des informations plus détaillées sur les variables et ostensiblement d'autres choses.
Vous souhaiterez peut-être modifier certaines variables sans reconstruire l'arborescence de construction. Utilisez ccmake pour cela (il édite le
CMakeCache.txt
fichier). N'oubliez pas dec
configurer une fois les modifications effectuées, puis d'g
énérer les fichiers makefiles avec la configuration mise à jour.Lisez le didacticiel référencé précédemment pour en savoir plus sur l'utilisation des variables, mais pour faire court:
set(<variable name> value)
pour modifier ou créer une variable.${<variable name>}
pour l'utiliser.CMAKE_SOURCE_DIR
: Le répertoire racine de la source. Dans l'exemple précédent, c'est toujours égal à/simple
CMAKE_BINARY_DIR
: Le répertoire racine de la construction. Dans l'exemple précédent, cela équivaut àsimple/build/
, mais si vous exécutez àcmake simple/
partir d'un dossier tel quefoo/bar/etc/
, toutes les références àCMAKE_BINARY_DIR
dans cet arbre de construction deviendraient/foo/bar/etc
.CMAKE_CURRENT_SOURCE_DIR
: Le répertoire dans lequel se trouve le courantCMakeLists.txt
. Cela signifie qu'il change d'un bout à l'autre: l'impression à partir dessimple/CMakeLists.txt
rendements/simple
et l'impression à partir dessimple/src/CMakeLists.txt
rendements/simple/src
.CMAKE_CURRENT_BINARY_DIR
: Vous avez eu l'idée. Ce chemin dépendra non seulement du dossier dans lequel se trouve la construction, mais également de l'CMakeLists.txt
emplacement du script actuel .Pourquoi sont-ils importants? Les fichiers sources ne seront évidemment pas dans l'arborescence de construction. Si vous essayez quelque chose comme
target_include_directories(Tutorial PUBLIC ../lib)
dans l'exemple précédent, ce chemin sera relatif à l'arbre de construction, c'est-à-dire que ce sera comme l'écriture${CMAKE_BINARY_DIR}/lib
, qui regardera à l'intérieursimple/build/lib/
. Il n'y a pas de fichiers .h là-dedans; tout au plus vous trouverezlibTestLib.a
. Vous voulez à la${CMAKE_SOURCE_DIR}/lib
place.CMAKE_CXX_FLAGS
: Indicateurs à transmettre au compilateur, dans ce cas le compilateur C ++. Il convient également de noterCMAKE_CXX_FLAGS_DEBUG
qui sera utilisé à la place siCMAKE_BUILD_TYPE
est défini sur DEBUG. Il y en a plus comme ceux-ci; consultez le wiki CMake .CMAKE_RUNTIME_OUTPUT_DIRECTORY
: Indiquez à CMake où placer tous les exécutables une fois construits. Il s'agit d'un paramètre global. Vous pouvez, par exemple, le régler surbin/
et tout y placer proprement.EXECUTABLE_OUTPUT_PATH
est similaire, mais obsolète, au cas où vous tomberiez dessus.CMAKE_LIBRARY_OUTPUT_DIRECTORY
: De même, un paramètre global pour indiquer à CMake où placer tous les fichiers de bibliothèque.Propriétés de la cible : vous pouvez définir des propriétés qui n'affectent qu'une seule cible, que ce soit un exécutable ou une bibliothèque (ou une archive ... vous voyez l'idée). Voici un bon exemple de son utilisation (avec
set_target_properties()
.Existe-t-il un moyen simple d'ajouter automatiquement des sources à une cible? Utilisez GLOB pour lister tout dans un répertoire donné sous la même variable. Un exemple de syntaxe est
FILE(GLOB <variable name> <directory>/*.cxx)
.Pouvez-vous spécifier différents types de build? Oui, même si je ne suis pas sûr de savoir comment cela fonctionne ou de ses limites. Cela nécessite probablement un certain if / then'ning, mais CMake offre un support de base sans rien configurer, comme les valeurs par défaut pour le
CMAKE_CXX_FLAGS_DEBUG
, par exemple. Vous pouvez définir votre type de construction à partir duCMakeLists.txt
fichier viaset(CMAKE_BUILD_TYPE <type>)
ou en appelant CMake depuis la console avec les indicateurs appropriés, par exemplecmake -DCMAKE_BUILD_TYPE=Debug
.Avez-vous de bons exemples de projets utilisant CMake? Wikipédia a une liste de projets open source qui utilisent CMake, si vous voulez vous pencher là-dessus. Les didacticiels en ligne ne m'ont jusqu'ici été qu'une déception à cet égard, mais cette question de Stack Overflow a une configuration CMake assez cool et facile à comprendre. Ça vaut le coup d'œil.
Utilisation de variables de CMake dans votre code : Voici un exemple rapide et sale (adapté d' un autre tutoriel ):
simple/CMakeLists.txt
:simple/TutorialConfig.h.in
:Le fichier résultant généré par CMake,
simple/src/TutorialConfig.h
:Avec une utilisation intelligente de ceux-ci, vous pouvez faire des choses intéressantes comme éteindre une bibliothèque, etc. Je recommande de jeter un coup d'œil à ce tutoriel car il y a des choses légèrement plus avancées qui seront sûrement très utiles sur des projets plus importants, tôt ou tard.
Pour tout le reste, Stack Overflow regorge de questions spécifiques et de réponses concises, ce qui est idéal pour tout le monde sauf les non-initiés.
la source
make
dans bash pour, eh bien, faire votre projet.