1. Introduction au C++




1.11. CMake

cmake est un générateur de fichier de configuration qui permet d'automatiser la compilation et l'édition de lien d'un ensemble de fichiers source de manière à obtenir un exécutable.

On peut, entre autre, générer un makefile, un fichier projet Eclipse ou KDE.

Sous Ubuntu 14.04, installer les packages suivants : cmake et cmake-qt-gui qui est une interface graphique mais qui n'est pas très intuitive.

Dans la partie qui suit on s'intéresse à la génération d'un makefile.

1.11.1. Exemple simple avec un fichier

On désire à partir d'un fichier situé dans le répertoire courant main.cpp générer l'exécutable main.exe.

La première étape consiste à écrire un fichier CMakeLists.txt qui décrit les étapes à réaliser pour aller des sources à l'exécutable.

  1.  
  2. project(MyProject)
  3.  
  4. # will generate the binary ./main.exe
  5.     # binary
  6.     main.exe
  7.     # fichiers sources
  8.     main.cpp
  9. )
  10.  
  11.  

Pour obtenir un makefile, exécuter la commande suivante :

\$ cmake -G "Unix Makefiles"

-- The C compiler identification is Clang 3.6.0
-- The CXX compiler identification is Clang 3.6.0
-- Check for working C compiler: /usr/bin/clang-3.6
-- Check for working C compiler: /usr/bin/clang-3.6 -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/clang++-3.6
-- Check for working CXX compiler: /usr/bin/clang++-3.6 -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: [...]/cmake1

Durant l'exécution de la commande, plusieurs fichiers sont générés :

Pour obtenir l'exécutable, il suffit de taper :

make
Scanning dependencies of target main.exe
[100%] Building CXX object CMakeFiles/main.exe.dir/main.cpp.o
Linking CXX executable main.exe
[100%] Built target main.exe

On rencontre cependant deux problèmes:

Pour répondre au premier problème, il suffit d'ajouter les lignes suivantes au fichier CMakeLists.txt :

set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_CXX_COMPILER "/usr/bin/g++")

Il faut relancer cmake, mais parfois cela n'est pas suffisant, il faut supprimer le fichier CMakeCache.txt et le sous-répertoire CMakeFiles :

\$ rm -rf CMakeFiles CMakeCache.txt
\$ cmake -G "Unix Makefiles"

-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/g++
-- Check for working CXX compiler: /usr/bin/g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: [...]/cmake1_1

Pour répondre au second problème, il suffit de lancer make avec l'option VERBOSE=1 :

make VERBOSE=1
...
[100%] Building CXX object CMakeFiles/main.exe.dir/main.cpp.o
/usr/bin/g++     -o CMakeFiles/main.exe.dir/main.cpp.o -c /home/richer/dev/cpp/cmake1_1/main.cpp
Linking CXX executable main.exe
/usr/bin/cmake -E cmake_link_script CMakeFiles/main.exe.dir/link.txt --verbose=1
/usr/bin/g++       CMakeFiles/main.exe.dir/main.cpp.o  -o main.exe -rdynamic 
...

On s'aperçoit qu'aucune option de compilation (optimisation ou débugage) n'est utilisée.

Il faut choisir un "type de build" (build type en anglais) que l'on peut traduire en français par le mot distribution, on modifie CMakeLists.txt en conséquence :

set(CMAKE_BUILD_TYPE Release)

On a le choix entre :

Par exemple pour définir sa propre distribution :

project(MyProject)

set(CMAKE_BUILD_TYPE ma_distribution)
set(CMAKE_CXX_FLAGS_MA_DISTRIBUTION "-O3 -ftree-vectorize -msse2")
set(CMAKE_C_FLAGS_MA_DISTRIBUTION "-O3 -ftree-vectorize -msse2")

Attention les trois dernières instructions doivent être placées après la directive project, sinon elles ne seront pas prises en compte.

On peut également ajouter des flags à une distribution existante :

project(MyProject)

set(CMAKE_BUILD_TYPE release)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall -ftree-vectorize -msse2 -std=c++11")

On compilera alors avec -O3 -DNDEBUG -Wall -ftree-vectorize -msse2 -std=c++11.

1.11.2. Exemple src et bin

Dans cet exemple on place les fichiers sources dans le sous-répertoire src et l'exécutable final dans le répertoire bin.

  1.  
  2. # define compiler in order not to take clang
  3. # and clang++ as default compilers
  4. # do rm -rf CMakeFiles/* CMakeCache.txt
  5. # if it is not working
  6.     CMAKE_C_COMPILER
  7.     "/usr/bin/gcc"
  8. )
  9.     CMAKE_CXX_COMPILER
  10.     "/usr/bin/g++"
  11. )
  12.  
  13.  
  14. project(MyProject)
  15.  
  16.     CMAKE_BUILD_TYPE
  17.     release
  18. )
  19.     EXECUTABLE_OUTPUT_PATH
  20.     bin/${CMAKE_BUILD_TYPE}
  21. )
  22.  
  23.     GLOB_RECURSE
  24.     source_files
  25.     src/*.cpp
  26. )
  27.  
  28. # will generate the binary ./main.exe
  29.     main.exe
  30.     ${source_files}
  31. )
  32.  
  33.  

Attention, l'exécutable main.exe sera situé dans le sous-répertoire bin/Release car on a utilisé la directive set(CMAKE_BUILD_TYPE release).

1.11.3. Exemple création et utilisation d'une librairie utilisateur

On dispose de l'arborescence suivante pour les sources :

src
├── file1.cpp
├── file1.h
├── main.cpp
└── maths
    ├── statistics.cpp
    └── statistics.h

On veut faire en sorte de créer une librairie avec les fichiers du sous-répertoire maths.

  1.  
  2. # define compiler in order not to take clang
  3. # and clang++ as default compilers
  4. # do rm -rf CMakeFiles/* CMakeCache.txt
  5. # if it is not working
  6. set(CMAKE_C_COMPILER "/usr/bin/gcc")
  7. set(CMAKE_CXX_COMPILER "/usr/bin/g++")
  8.  
  9. project(MyProject)
  10.  
  11. set(CMAKE_BUILD_TYPE Release)
  12. set(EXECUTABLE_OUTPUT_PATH bin/${CMAKE_BUILD_TYPE})
  13. set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
  14.  
  15. # tell where to find the .h files
  16. include_directories(src src/maths)
  17.  
  18. # find .cpp files for the maths library
  19. file(GLOB_RECURSE source_files_maths src/maths/*.cpp)
  20. # tell to create a library
  21. add_library(maths SHARED ${source_files_maths})
  22.  
  23. file(GLOB_RECURSE source_files src/*.cpp)
  24.  
  25. # will generate the binary ./main.exe
  26. add_executable(main.exe ${source_files})
  27.  
  28. # link with library maths
  29. target_link_libraries(main.exe maths)
  30.  
  31.  

On ajoute les 4 lignes suivantes :

Au final on obtient une lirairie dans le répertoire principal du projet (libmaths.so) qui est liée à l'exécutable main.exe.