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)

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épertoire build, 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

Date: 4 Décembre 2015

Author: Marc Fuentes

Created: 2017-12-20 az. 14:57

Validate