Introduction à Cmake
Table of Contents
Salut à toi ! Bienvenue dans la bidouille !
Utilise « C-c C-o » (control+c puis control+o) pour suivre les liens (les trucs soulignés en bleu ci-dessous).
Ce fichier est disponible à http://sed.bordeaux.inria.fr/la-bidouille
1 Présentation de Cmake
1.1 À quoi ça sert :
1.1.1 Générer des Makefiles
(C, C++, Fortran)
1.1.2 Détecter l'environnement de compilation
1.1.3 Générer et exécuter des tests
1.1.4 Créer des packages
1.2 Outils similaires :
1.2.1 GNU Autotools
1.2.2 Scons
2 Compiler un projet
2.1 Projet minimal
2.1.1 fichier CMakeLists.txt
cmake_minimum_required (VERSION 3.0.2) project (haha) add_executable(monExec zut.cpp)
où zut.cpp
est votre fichier source principal (par exemple un «bonjour monde!». monExec
est le nom
de l'executable à produire. Après dans le répertoire où est
CMakeLists.txt
, on exécute la commande suivante
mkdir build && cd build && cmake .. && make
2.1.2 Normalement cela configure le projet et lance la compilation. Vous pouvez tester votre exécutable.
Remarque : Imaginons que l'on ait deux fichiers, la ligne add_executable
devient alors
add_executable(monExec fic1.cpp fic2.cpp fic2.hpp)
2.1.3 Expressions avec des caractères de remplacement
Si l'on souhaite utiliser des expressions avec des caracteres génériques,
par exemple *.cpp
, on peut utiliser l'instruction cmake file(GLOB nom_var glob_expr)
file(GLOB myVar "*.cpp") message(STATUS "fichiers sources = ${myVar}") add_executable(monExec ${myVar})
2.1.4 Documentation
pour obtenir à tout moment de la doc sur une commande, tapez dans le shell cmake --help-command maCommande
2.2 compilation d'une bibliothèque
On va maintenant mettre une fonction dans une bibliothèque et on liera [1] notre executable à cette bibliothèque
2.2.1 Sous-répertoire de la bibliothèque
mkdir lib && cd lib && echo "add_library(maSuperLib sourceLib.cpp) ">> CMakeLists.txt && cd ..
2.2.2 fichier d'en-tête
mkdir include && cd include && echo "void superFunction(); " >> superLib.hpp && cd ..
2.2.3 nouveau CMakeLists.txt
cmake_minimum_required (VERSION 3.0.2) project (haha) add_subdirectory(lib) include_directories(include) add_executable(monExec zut.cpp) target_link_libraries(monExec maSuperLib)
2.2.4 le fichier principal zut.cpp
pourra ressembler à cela
#include <iostream> #include "superLib.hpp" int main() { superFunction(); return 0; }
2.2.5 Remarques :
- Si l'on tape
make help
dans le répertoirebuild
, on obtient la liste des cibles disponibles - pour avoir les lignes réellement executées lors de la compilation par
Cmake, on peut utiliser
make VERBOSE=1
3 Détection de l'environnement
3.1 utilisation de find_package
supposons que nous voulons compiler le programme suivant zut.cpp
#include <iostream> #include <gtk/gtk.h> int main(int argc, char * argv[]) { gtk_init(&argc, &argv); GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(win), "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), nullptr); gtk_widget_show_all(win); gtk_main(); return 0; }
Nous avons un problème pour trouver les fichiers d'en-tête et les bonnes
bibliothèques. Pour cela on va utiliser find_package
cmake_minimum_required (VERSION 3.0.2) project (GtkTuto) find_package(GTK2) if (GTK2_FOUND) message(STATUS "GTK INCLUDES = ${GTK2_INCLUDE_DIRS}") message(STATUS "GTK LIBS = ${GTK2_LIBRARIES}") include_directories(${GTK2_INCLUDE_DIRS}) endif() add_definitions("-std=c++11") add_executable(oneWindow zut.cpp) target_link_libraries(oneWindow ${GTK2_LIBRARIES})
Remarques : on notera l'utilisation de add_definitions
pour passer des options sur la ligne de commande.
3.2 Recherche individuelle de fonction
On peut par exemple utiliser check_function_exists
(obsolète)
cmake_minimum_required (VERSION 3.0.2) project(checkfun) include(CheckFunctionExists) set(CMAKE_REQUIRED_LIBRARIES "m" ${CMAKE_REQUIRED_LIBRARIES}) check_function_exists(sqrt HEHE) if (${HEHE}) message(STATUS "j'ai trouve sqrt") else() message(FATAL_ERROR "je n'ai pas trouvé sqrt") endif()
3.3 passage de macros (#define) à l'aide d'un fichier de config
Selon que l'on a detecté ou pas une propriété du système hôte, on peut vouloir
definir une macro dans le code. Si l'on a beaucoup de macros à définir le
mécanisme basé sur add_defintions("-DMA_MACRO")
peut s'averer rapidement
lourd, on peut utiliser un fichier de config : pour cela, imaginons que l'on ne trouve pas la
fonction «sqrt» dans math.h, on va décider de l'emuler.
CMakeLists.txt
cmake_minimum_required (VERSION 3.0.2) project(checkfun) include(CheckFunctionExists) #set(CMAKE_REQUIRED_LIBRARIES "m" ${CMAKE_REQUIRED_LIBRARIES}) check_function_exists(sqrt SQRT_DEFINE) configure_file(config.hpp.in config.hpp) include_directories(${PROJECT_BINARY_DIR}) add_executable(main zut.cpp)
config.hpp.in
#cmakedefine SQRT_DEFINE
zut.cpp
#include <iostream> #include "config.hpp" #ifdef SQRT_DEFINE #include <cmath> #define MYSQRT(x) std::sqrt(x) #else #define MYSQRT(x) newton_sqrt(x) #endif double newton_sqrt(double x) { double x0 = x; auto flag = false; for(int i = 0; i < 10 && !flag; ++i) { double xp = x0 - (x0*x0 - x)/ (2. * x0); flag = ((xp - x0) * (xp - x0) < 1e-20); x0 = xp; } return x0; } int main() { std::cout << MYSQRT(2.) << std::endl; return 0; }
4 Tests
L'ajout de tests est très simple dans cmake, par contre le grain est plutôt
grossier, il suffit d'appeler enable_testing()
dans le fichier
CMakeLists.txt, puis d'ajouter chaque avec une commande add_test(nom_test command args)
Après on lance les tests avec make test
ou ctest
5 Bonus
Au travers des exemples, on a vu que le langage possède diverses structures de crontrôle : le programme suivant - qui n'a qu'une valeur illustrative - donne un aperçu des differentes structures de contrôle:
cmake_minimum_required(VERSION 3.0.2) function(carre x x2) math(EXPR sq "${x}*${x}") set(${x2} ${sq} PARENT_SCOPE) endfunction() set(somme 0) set(n $ENV{N}) foreach(i RANGE 1 ${n}) carre(${i} sq) math(EXPR somme "${somme}+${sq}") endforeach() message(STATUS ${somme})
[1] À ne pas confondre son homo nim[2] [2] notre ancêtre qui inventa les jeux d'allumettes