source .notebooksrc
Makefile
.Pour les gros projets, GNU proprose un ensemble d'outils regroupés sous le nom d'autotools (Make
en fait partie).
Il s'agit d'une chaîne assez complexe d'outils qui vise à écrire un Makefile
qui s'adapte l'architecture et au système d'exploitation :
CMake
(pour cross platform make) est un moteur de production multiplate-forme.
À partir d'un fichier de configuration CMakeLists.txt
(généralement un par projet), CMake génère des fichiers fichiers descriptifs qui permettent de construire le projet.
Par dédaut, le fichier descriptif est Makefile
mais CMake
peut générer d'autres types de fichiers destinés aux IDE comme Visual Studio.
Dans le cadre de ce cours, nous allons explorer Make
et CMake
.
Makefile
(à la main)¶L'invocation de la commande make
seule (sans cible) va chercher à réaliser la première règle rencontrée dans le fichier Makefile
.
Un fichier Makefile
ressemblant à ça :
# Définition des variables
variable1 = valeur1
variable2 = valeur2
# Définition des règles
# règle 1
cible1: dépendance1
recette1
# règle 2
cible2: dépendance2
recette2
Important :
recette1
est précédée d'une tabulation.
La règle 1
donne plusieurs informations :
cible1
(en général un fichier) est obsolète et doit être regénérée si elle n'existe pas ou si la dépendance1
(un autre fichier) est plus récente que cible1
cible1
en invoquant la recette1
.Dans le cas où cible2 = dépendence1
, alors la règle 2
va vérifier que la cible2
est bien réalisée avant de réaliser la règle 1
.
maillage
¶Make fonctionne avec un système de dépendances chaînées donc il est plus facile d'écrire un Makefile
en commençant par la cible finale :
cd $ROOTDIR/maillage
# On affiche les 3 premières lignes de Makefile_1
pygmentize -l make Makefile | head -n3
# Options de compilation CXX := g++ CXXFLAGS := -std=c++11 -Wall
Les dépendances de main.e
sont obtenues en compilant chaque fichier source pour produire un objet :
# On affiche les lignes suivantes de Makefile_1
pygmentize -l make Makefile_1 |sed -n '5,9p'
# On veut un objet compilé à partir d'un fichier .cpp # et qui dépend aussi de deux fichiers d'entête main.o: main.cpp maillage.hpp quadrangle.hpp g++ -std=c++11 -Wall -o main.o -c main.cpp
Il faut répéter ce type de règles pour tous les fichiers objets :
# On affiche les autres lignes de Makefile_1
pygmentize -l make Makefile_1 |sed -n '10,24p'
point.o: point.cpp point.hpp g++ -std=c++11 -Wall -o point.o -c point.cpp segment.o: segment.cpp segment.hpp g++ -std=c++11 -Wall -o segment.o -c segment.cpp triangle.o: triangle.cpp triangle.hpp g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp quadrangle.o: quadrangle.cpp quadrangle.hpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp polygone.o: polygone.cpp polygone.hpp g++ -std=c++11 -Wall -o polygone.o -c polygone.cpp
On ajoute également une régle pour effacer les fichiers produits par Make
pygmentize -l make Makefile_1 | tail -n 4
# On efface les fichiers compilés clean: rm -f main.e *.o *.a
Le fichier complet compile correctement :
make -f Makefile_1 clean
make -f Makefile_1
rm -f main.e *.o *.a g++ -std=c++11 -Wall -o main.o -c main.cpp g++ -std=c++11 -Wall -o point.o -c point.cpp g++ -std=c++11 -Wall -o segment.o -c segment.cpp g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp g++ -std=c++11 -Wall -o polygone.o -c polygone.cpp g++ -std=c++11 -Wall -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o
Note : remarquez la syntaxe
make -f Makefile_1
pour utiliser le fichierMakefile_1
.
mais il contient beaucoup de répétitions, sources potentielles d'erreurs :
pygmentize -l make Makefile_1
# On veut l'exécutable main.e produit en liant les objets main.e: main.o point.o segment.o triangle.o quadrangle.o polygone.o g++ -std=c++11 -Wall -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o # On veut un objet compilé à partir d'un fichier .cpp # et qui dépend aussi de deux fichiers d'entête main.o: main.cpp maillage.hpp quadrangle.hpp g++ -std=c++11 -Wall -o main.o -c main.cpp point.o: point.cpp point.hpp g++ -std=c++11 -Wall -o point.o -c point.cpp segment.o: segment.cpp segment.hpp g++ -std=c++11 -Wall -o segment.o -c segment.cpp triangle.o: triangle.cpp triangle.hpp g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp quadrangle.o: quadrangle.cpp quadrangle.hpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp polygone.o: polygone.cpp polygone.hpp g++ -std=c++11 -Wall -o polygone.o -c polygone.cpp # On efface les fichiers compilés clean: rm -f main.e *.o *.a
On commence par définir les variables suivantes :
pygmentize -l make Makefile_2
# Options de compilation CXX := g++ CXXFLAGS := -std=c++11 -Wall # On veut l'exécutable main.e produit en liant les objets main.e: main.o point.o segment.o triangle.o quadrangle.o polygone.o maillage.hpp $(CXX) $(CXXFLAGS) -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o main.o: main.cpp maillage.hpp $(CXX) $(CXXFLAGS) -o main.o -c main.cpp point.o: point.cpp point.hpp $(CXX) $(CXXFLAGS) -o point.o -c point.cpp segment.o: segment.cpp segment.hpp $(CXX) $(CXXFLAGS) -o segment.o -c segment.cpp triangle.o: triangle.cpp triangle.hpp $(CXX) $(CXXFLAGS) -o triangle.o -c triangle.cpp quadrangle.o: quadrangle.cpp quadrangle.hpp $(CXX) $(CXXFLAGS) -o quadrangle.o -c quadrangle.cpp polygone.o: polygone.cpp polygone.hpp $(CXX) $(CXXFLAGS) -o polygone.o -c polygone.cpp clean: rm -f main.e *.o *.a
On compile à nouveau :
make -f Makefile_2 clean
make -f Makefile_2
rm -f main.e *.o *.a g++ -std=c++11 -Wall -o main.o -c main.cpp g++ -std=c++11 -Wall -o point.o -c point.cpp g++ -std=c++11 -Wall -o segment.o -c segment.cpp g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp g++ -std=c++11 -Wall -o polygone.o -c polygone.cpp g++ -std=c++11 -Wall -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o
On peut aller plus loin en utilisant :
%
Voici les principales variables automatiques fournies par make
:
$@
: la cible$<
: la première dépendance$?
: toutes les dépendances plus récentes que la cible$^
: toutes les dépendancesOn va les utiliser pour construire la règle générique qui capture tous les fichiers .o
:
%.o: %.cpp %.hpp
$(CXX) $(CXXFLAGS) -o $@ -c $<
Ca simplifie beaucoup le Makefile
!
pygmentize -l make Makefile_3
# Options de compilation CXX := g++ CXXFLAGS := -std=c++11 -Wall # On veut l'exécutable main.e produit en liant les objets main.e: main.o point.o segment.o triangle.o quadrangle.o polygone.o $(CXX) $(CXXFLAGS) -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o # main.o dépend de main.cpp mais aussi des fichiers d'en-tête qu'il inclut main.o: main.cpp maillage.hpp quadrangle.hpp $(CXX) $(CXXFLAGS) -o $@ -c $< # les autres objets ne dépendent que de leur source et en-tête respectives %.o: %.cpp %.hpp $(CXX) $(CXXFLAGS) -o $@ -c $< clean: rm -f main.e *.o *.a
On vérifie la compilation :
make -f Makefile_3 clean
make -f Makefile_3
rm -f main.e *.o *.a g++ -std=c++11 -Wall -o main.o -c main.cpp g++ -std=c++11 -Wall -o point.o -c point.cpp g++ -std=c++11 -Wall -o segment.o -c segment.cpp g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp g++ -std=c++11 -Wall -o polygone.o -c polygone.cpp g++ -std=c++11 -Wall -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o
On peut faire encore plus générique et plus clair en remplaçant la règle en utilisant $^
pour désigner toutes les dépendances (et en regroupant la liste des objets dans une variable).
pygmentize -l make Makefile_4
# Options de compilation CXX := g++ CXXFLAGS := -std=c++11 -Wall objects = point.o segment.o triangle.o quadrangle.o polygone.o # On veut l'exécutable main.e produit en liant les objets main.e: main.o $(objects) $(CXX) $(CXXFLAGS) -o $@ $^ # main.o dépend de main.cpp mais aussi des fichiers d'en-tête qu'il inclut main.o: main.cpp maillage.hpp quadrangle.hpp $(CXX) $(CXXFLAGS) -o $@ -c $< # les autres objets ne dépendent que de leur source et en-tête respectives %.o: %.cpp %.hpp $(CXX) $(CXXFLAGS) -o $@ -c $< clean: rm -f main.e *.o *.a
On compile pour vérifier :
make -f Makefile_4 clean
make -f Makefile_4
rm -f main.e *.o *.a g++ -std=c++11 -Wall -o main.o -c main.cpp g++ -std=c++11 -Wall -o point.o -c point.cpp g++ -std=c++11 -Wall -o segment.o -c segment.cpp g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp g++ -std=c++11 -Wall -o polygone.o -c polygone.cpp g++ -std=c++11 -Wall -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o
La gestion manuelle des dépendances est délicate et source d'erreurs, même pour les petits projets. Make permet de générer automatiquement les dépendances entre les fichiers :
pygmentize Makefile
# Options de compilation CXX := g++ CXXFLAGS := -std=c++11 -Wall srcs = point.cpp segment.cpp triangle.cpp quadrangle.cpp polygone.cpp # On construit la liste des objets par substitution objects = $(srcs:.cpp=.o) # On veut l'exécutable main.e produit en liant les objets main.e: main.o $(objects) $(CXX) $(CXXFLAGS) -o $@ $^ # La règle qui produit les objets à partir des sources %.o: %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< # On génère des fichiers makefile contenant les dépendances %.d: %.cpp @set -e; rm -f $@; \ $(CXX) -M $(CXXFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ # On inclut les fichiers makefile de dépendances -include $(srcs:.cpp=.d) # On efface tout y compris les dépendances clean: rm -f main.e *.o *.a *.d
On compile une première fois :
make clean
make
rm -f main.e *.o *.a *.d g++ -std=c++11 -Wall -o main.o -c main.cpp g++ -std=c++11 -Wall -o point.o -c point.cpp g++ -std=c++11 -Wall -o segment.o -c segment.cpp g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp g++ -std=c++11 -Wall -o polygone.o -c polygone.cpp g++ -std=c++11 -Wall -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o
On vérifie que la compilation prend en compte les dépendances en modifiant triangle.hpp
qui est inclus dans quadrangle.cpp
:
touch triangle.hpp
make
g++ -std=c++11 -Wall -o triangle.o -c triangle.cpp g++ -std=c++11 -Wall -o quadrangle.o -c quadrangle.cpp g++ -std=c++11 -Wall -o main.e main.o point.o segment.o triangle.o quadrangle.o polygone.o
Dans le répertoire maillage
, modifier le fichier Makefile
pour :
libmaillage.a
libmaillage.a
pour construire l'exécutable main.e
voir fichier Makefile_arch
.
Remarque : la variable automatique
$?
désigne la liste des dépendances plus récentes que la cible.
pygmentize -l make Makefile_arch
# Options de compilation CXX := g++ CXXFLAGS := -std=c++11 -Wall srcs = point.cpp segment.cpp triangle.cpp quadrangle.cpp polygone.cpp # On construit la liste des objets par substitution objects = $(srcs:.cpp=.o) # main.e est produit en liant main.o avec la bibliothèque libmaillage.a main.e: main.o libmaillage.a $(CXX) $(CXXFLAGS) -o $@ $< -L. -lmaillage # La bibliothèque libmaillage.a est produite à partir des objets libmaillage.a: $(objects) ar r $@ $? # La règle qui produit les objets à partir des sources %.o: %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< # On génère des fichiers makefile contenant les dépendances %.d: %.cpp @set -e; rm -f $@; \ $(CXX) -M $(CXXFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ # On inclut les fichiers makefile de dépendances -include $(srcs:.cpp=.d) # On efface tout y compris les dépendances clean: rm -f main.e *.o *.a *.d
make -f Makefile_arch
./main.e
ar r libmaillage.a point.o segment.o triangle.o quadrangle.o polygone.o ar: creating libmaillage.a g++ -std=c++11 -Wall -o main.e main.o -L. -lmaillage Test cout sur maillage : Type de mailles : triangles Nombre max de voisins : 3 Nombre de noeuds : 4 Nombre de mailles : 2 Tests unitaires réussis, maillage : 4/4
Make
¶Ca complique l'écriture du Makefile !
Dans le répertoire maillage_struct
, on propose un Makefile
adapté à l'organisation suivante des fichiers :
cd $ROOTDIR
make -s -C maillage_struct clean
tree maillage_struct
maillage_struct ├── include │ ├── maillage.hpp │ ├── point.hpp │ ├── polygone.hpp │ ├── quadrangle.hpp │ ├── segment.hpp │ └── triangle.hpp ├── main.cpp ├── Makefile ├── src │ ├── point.cpp │ ├── polygone.cpp │ ├── quadrangle.cpp │ ├── segment.cpp │ └── triangle.cpp └── test └── test_maillage.cpp 3 directories, 14 files
Le Makefile
correspondant est sensiblement plus compliqué :
cd $ROOTDIR/maillage_struct
pygmentize Makefile
# Compiler options CXX := g++ CXXFLAGS := -std=c++11 -Wall INC := include/ # .hpp files will be in this directory CXXFLAGS := $(CXXFLAGS) -I$(INC) # Files definition exec := main.e libname := maillage libfile := lib$(libname).a # Build list of cpp files: srcs := $(wildcard src/*.cpp) # Build objects list from src/ content objects := $(srcs:.cpp=.o) # Build tests list from test/ content testsrcs := $(wildcard test/*.cpp) tests = $(testsrcs:.cpp=.e) # Build list of dependencies depssrcs := $(srcs) $(testsrcs) main.cpp deps = $(depssrcs:.cpp=.d) # These dependencies are not files .PHONY: all clean tests # Build main.e and test/test_*.e all: $(exec) $(tests) # Produce main.e by linking with main.o and libmaillage.a $(exec): main.o $(libfile) $(CXX) $(CXXFLAGS) -o $@ $< -L. -l$(libname) # Produce test exec by linking with libmaillage.a test/%.e: test/%.o $(libfile) $(CXX) $(CXXFLAGS) -o $@ $< -L. -l$(libname) # The static library libmaillage.a is built from object files $(libfile): $(objects) ar r $@ $? # A rule to build dependencies %.d: %.cpp @set -e; rm -f $@; \ $(CXX) -M $(CXXFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ # Include dependency makefiles -include $(deps) # Say how to compile an .o file %.o: %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< clean: rm -f $(exec) $(objects) $(libfile) $(tests) $(deps) *.o
L'avantage est que si l'on rajoute un fichier source de test, il est automatiquement pris en compte par le Makefile
.
On rajoute par exemple le fichier test/test_triangle.cpp
:
# On compile d'abord le projet :
make clean
make
# Puis on rajoute le fichier source :
cat >test/test_triangle.cpp <<EOL
#include <iostream>
#include "triangle.hpp"
int main() {
triangle t;
t.all_testsu();
return 0;
}
EOL
rm -f main.e src/polygone.o src/triangle.o src/segment.o src/quadrangle.o src/point.o libmaillage.a test/test_maillage.e src/polygone.d src/triangle.d src/segment.d src/quadrangle.d src/point.d test/test_maillage.d main.d *.o g++ -std=c++11 -Wall -Iinclude/ -o main.o -c main.cpp g++ -std=c++11 -Wall -Iinclude/ -o src/polygone.o -c src/polygone.cpp g++ -std=c++11 -Wall -Iinclude/ -o src/triangle.o -c src/triangle.cpp g++ -std=c++11 -Wall -Iinclude/ -o src/segment.o -c src/segment.cpp g++ -std=c++11 -Wall -Iinclude/ -o src/quadrangle.o -c src/quadrangle.cpp g++ -std=c++11 -Wall -Iinclude/ -o src/point.o -c src/point.cpp ar r libmaillage.a src/polygone.o src/triangle.o src/segment.o src/quadrangle.o src/point.o ar: creating libmaillage.a g++ -std=c++11 -Wall -Iinclude/ -o main.e main.o -L. -lmaillage g++ -std=c++11 -Wall -Iinclude/ -o test/test_maillage.o -c test/test_maillage.cpp g++ -std=c++11 -Wall -Iinclude/ -o test/test_maillage.e test/test_maillage.o -L. -lmaillage rm test/test_maillage.o
On vérifie qu'il est pris en compte :
make
./test/test_triangle.e
g++ -std=c++11 -Wall -Iinclude/ -o test/test_triangle.o -c test/test_triangle.cpp g++ -std=c++11 -Wall -Iinclude/ -o test/test_triangle.e test/test_triangle.o -L. -lmaillage rm test/test_triangle.o Tests unitaires réussis, triangle : 3/3
make
connaît les dépendances donc il peut déduire les compilations indépendantes et les exécuter en parallèle.
Pour laisser le système choisir le nombre de processus parallèles :
make -j
Pour forcer à n processus :
make -j 4
Comparons les temps de compilation en mode séquentiel et parallèle :
cd $ROOTDIR/maillage
make clean
echo "Temps séquentiel [s] :"
time make -s
rm -f main.e *.o *.a *.d Temps séquentiel [s] : 2.73
make clean
echo "Temps parallèle [s] :"
time make -s -j
rm -f main.e *.o *.a *.d Temps parallèle [s] : 0.72
L'accélération est appréciable, surtout pendant les phases de développement !
Cette section n'est qu'un aperçu de l'utilisation de Make
.
On pourrait aller bien plus loin en suivant la documentation officielle : https://www.gnu.org/software/make/manual/.
Toutefois, on a constaté que l'écriture et la maintenance d'un Makefile pouvait se révéler délicate et longue, même pour de petits projets.
On va maintenant voir comment simplifier cet travail en utilisant CMake
.
CMake possède son propre langage de programmation. Dans cette partie, nous allons découvrir les rudiments de ce langage. Pour aller plus loin, on peut se référer au site CMake qui fournit :
Dans le répertoire cmake_point/
, on a rassemblé 3 fichiers sources :
cd $ROOTDIR
cd cmake_point
ls *pp
main.cpp point.cpp point.hpp
point.*
sont des copies des fichiers du même nom dans le répertoire maillage/
.main.cpp
contient l'exécution des tests unitaires de la classe point
:pygmentize main.cpp
#include <iostream> #include "point.hpp" int main() { point p; p.all_testsu(); return 0; }
On commence par écrire le fichier CMakeLists.txt
qui décrit les étapes pour générer l'exécutable point.e
à partir des fichiers sources.
# On crée un lien vers le fichier CMakeLists.txt_basic
rm -f CMakeLists.txt && ln -s CMakeLists.txt_basic CMakeLists.txt
pygmentize ./CMakeLists.txt
cmake_minimum_required(VERSION 3.3) project(Point) add_executable(point.e main.cpp point.cpp)
CMake est conçu pour compiler dans un répertoire séparé des sources.
C'est une bonne pratique qui permet d'isoler les fichiers générés par la compilation des fichiers sources.
On crée donc le sous-répertoire build/
:
rm -rf build # nettoyage préalable
mkdir build
cd build
On invoque la commande cmake
en indiquant le chemin du répertoire où se trouve le fichier CMakeLists.txt
:
cmake ..
-- The C compiler identification is GNU 9.3.0 -- The CXX compiler identification is GNU 9.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - 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 -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/cmake_point/build
ls -l
total 32 -rw-r--r-- 1 jovyan users 13833 Feb 16 15:05 CMakeCache.txt drwxr-xr-x 5 jovyan users 4096 Feb 16 15:05 CMakeFiles -rw-r--r-- 1 jovyan users 1556 Feb 16 15:05 cmake_install.cmake -rw-r--r-- 1 jovyan users 5529 Feb 16 15:05 Makefile
CMakeCache.txt
et le répertoire CMakeFiles
contiennent des informations propres au projet et à la plateformecmake_install.cmake
indique où seront installés les exécutables générésMakefile
permet à make
de gérer la compilationCMakeCache.txt
et CMakeFiles
ne sont pas regénérés entièrement à chaque invocation de la commande cmake
.
cmake ..
-- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/cmake_point/build
Si on veut reprendre la génération de zéro, il faut les effacer.
rm -rf CMakeCache.txt CMakeFiles # On efface CMakeCache.txt et CMakeFiles
cmake ..
-- The C compiler identification is GNU 9.3.0 -- The CXX compiler identification is GNU 9.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - 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 -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/cmake_point/build
Grâce à la présence du Makefile
, on peut maintenant constuire le projet :
make
Scanning dependencies of target point.e [ 33%] Building CXX object CMakeFiles/point.e.dir/main.cpp.o [ 66%] Building CXX object CMakeFiles/point.e.dir/point.cpp.o [100%] Linking CXX executable point.e [100%] Built target point.e
puis exécuter le fichier point.e
:
./point.e
Tests unitaires réussis, point : 3/3
On peut obtenir le détail des commandes de compilation utilisée par make
avec make VERBOSE=1
:
make clean
make VERBOSE=1
/usr/bin/cmake -S/builds/boileau/outils-dev-log/notebooks/cmake_point -B/builds/boileau/outils-dev-log/notebooks/cmake_point/build --check-build-system CMakeFiles/Makefile.cmake 0 /usr/bin/cmake -E cmake_progress_start /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/progress.marks make -f CMakeFiles/Makefile2 all make[1]: Entering directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' make -f CMakeFiles/point.e.dir/build.make CMakeFiles/point.e.dir/depend make[2]: Entering directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' cd /builds/boileau/outils-dev-log/notebooks/cmake_point/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /builds/boileau/outils-dev-log/notebooks/cmake_point /builds/boileau/outils-dev-log/notebooks/cmake_point /builds/boileau/outils-dev-log/notebooks/cmake_point/build /builds/boileau/outils-dev-log/notebooks/cmake_point/build /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/point.e.dir/DependInfo.cmake --color= make[2]: Leaving directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' make -f CMakeFiles/point.e.dir/build.make CMakeFiles/point.e.dir/build make[2]: Entering directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' [ 33%] Building CXX object CMakeFiles/point.e.dir/main.cpp.o /usr/bin/g++ -o CMakeFiles/point.e.dir/main.cpp.o -c /builds/boileau/outils-dev-log/notebooks/cmake_point/main.cpp [ 66%] Building CXX object CMakeFiles/point.e.dir/point.cpp.o /usr/bin/g++ -o CMakeFiles/point.e.dir/point.cpp.o -c /builds/boileau/outils-dev-log/notebooks/cmake_point/point.cpp [100%] Linking CXX executable point.e /usr/bin/cmake -E cmake_link_script CMakeFiles/point.e.dir/link.txt --verbose=1 /usr/bin/g++ -rdynamic CMakeFiles/point.e.dir/main.cpp.o CMakeFiles/point.e.dir/point.cpp.o -o point.e make[2]: Leaving directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' [100%] Built target point.e make[1]: Leaving directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' /usr/bin/cmake -E cmake_progress_start /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles 0
CMakeLists.txt
¶Debug
, Release
, etc.)# On crée un lien vers le fichier CMakeLists.txt_options
cd ..
rm -f CMakeLists.txt && ln -s CMakeLists.txt_options CMakeLists.txt
pygmentize ./CMakeLists.txt
cmake_minimum_required(VERSION 3.3) project(Point) # On fixe la version du compilateur set(CMAKE_C_COMPILER "clang++") # On fixe un type de compilation par défaut if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() # Options de compilation communes set(CMAKE_CXX_FLAGS "-std=c++11") # Options de compilation pour le type Release set(CMAKE_CXX_FLAGS_RELEASE "-O3") # Options de compilation pour le type Debug set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") # Messages lors de la génération message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(" Compiler CXX flags: ${CMAKE_CXX_FLAGS}") message(" Compiler CXX debug flags: ${CMAKE_CXX_FLAGS_DEBUG}") message(" Compiler CXX release flags: ${CMAKE_CXX_FLAGS_RELEASE}") add_executable(point.e main.cpp point.cpp)
On génère avec le type de construction par défaut Release
:
cd $ROOTDIR/cmake_point/build
rm -rf CMakeCache.txt CMakeFiles # On efface CMakeCache.txt et CMakeFiles
cmake ..
-- The C compiler identification is GNU 9.3.0 -- The CXX compiler identification is GNU 9.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - 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 -- Detecting CXX compile features -- Detecting CXX compile features - done -- Build type: Release Compiler CXX flags: -std=c++11 Compiler CXX debug flags: -O0 -g Compiler CXX release flags: -O3 -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/cmake_point/build
Ou avec le type Debug
en spécifiant l'option en ligne de commande :
rm -rf CMakeCache.txt CMakeFiles # On efface CMakeCache.txt et CMakeFiles
cmake -D CMAKE_BUILD_TYPE=Debug ..
-- The C compiler identification is GNU 9.3.0 -- The CXX compiler identification is GNU 9.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - 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 -- Detecting CXX compile features -- Detecting CXX compile features - done -- Build type: Debug Compiler CXX flags: -std=c++11 Compiler CXX debug flags: -O0 -g Compiler CXX release flags: -O3 -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/cmake_point/build
On vérifie que les options de compilation pour Debug
sont bien prises en compte :
make VERBOSE=1
/usr/bin/cmake -S/builds/boileau/outils-dev-log/notebooks/cmake_point -B/builds/boileau/outils-dev-log/notebooks/cmake_point/build --check-build-system CMakeFiles/Makefile.cmake 0 /usr/bin/cmake -E cmake_progress_start /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/progress.marks make -f CMakeFiles/Makefile2 all make[1]: Entering directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' make -f CMakeFiles/point.e.dir/build.make CMakeFiles/point.e.dir/depend make[2]: Entering directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' cd /builds/boileau/outils-dev-log/notebooks/cmake_point/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /builds/boileau/outils-dev-log/notebooks/cmake_point /builds/boileau/outils-dev-log/notebooks/cmake_point /builds/boileau/outils-dev-log/notebooks/cmake_point/build /builds/boileau/outils-dev-log/notebooks/cmake_point/build /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/point.e.dir/DependInfo.cmake --color= Dependee "/builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/point.e.dir/DependInfo.cmake" is newer than depender "/builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/point.e.dir/depend.internal". Dependee "/builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles/point.e.dir/depend.internal". Scanning dependencies of target point.e make[2]: Leaving directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' make -f CMakeFiles/point.e.dir/build.make CMakeFiles/point.e.dir/build make[2]: Entering directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' [ 33%] Building CXX object CMakeFiles/point.e.dir/main.cpp.o /usr/bin/g++ -std=c++11 -O0 -g -o CMakeFiles/point.e.dir/main.cpp.o -c /builds/boileau/outils-dev-log/notebooks/cmake_point/main.cpp [ 66%] Building CXX object CMakeFiles/point.e.dir/point.cpp.o /usr/bin/g++ -std=c++11 -O0 -g -o CMakeFiles/point.e.dir/point.cpp.o -c /builds/boileau/outils-dev-log/notebooks/cmake_point/point.cpp [100%] Linking CXX executable point.e /usr/bin/cmake -E cmake_link_script CMakeFiles/point.e.dir/link.txt --verbose=1 /usr/bin/g++ -std=c++11 -O0 -g -rdynamic CMakeFiles/point.e.dir/main.cpp.o CMakeFiles/point.e.dir/point.cpp.o -o point.e make[2]: Leaving directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' [100%] Built target point.e make[1]: Leaving directory '/builds/boileau/outils-dev-log/notebooks/cmake_point/build' /usr/bin/cmake -E cmake_progress_start /builds/boileau/outils-dev-log/notebooks/cmake_point/build/CMakeFiles 0
CMake se révèle particulièrement intéressant pour les projets plus complexes. Comme on l'a vu précédemment :
include/
, src/
, etc.)Prenons par exemple le projet maillage_cmake/
cd $ROOTDIR
tree -L 1 maillage_cmake
maillage_cmake ├── CMakeLists.txt_base ├── CMakeLists.txt_test ├── include ├── src └── test 3 directories, 2 files
Ce projet contient un fichier CMakeLists.txt
que nous allons détailler :
cd maillage_cmake
rm -f test/test_echec.cpp
rm -f CMakeLists.txt
ln -s CMakeLists.txt_base CMakeLists.txt
pygmentize CMakeLists.txt
cmake_minimum_required(VERSION 3.3) project(Maillage) # On indique où se trouvent les fichiers d'en-tête include_directories(include) # On construit la liste des fichiers sources à partir du contenu de src/ file(GLOB_RECURSE SOURCE_FILES src/*.cpp) # On indique les options de compilations set(CMAKE_CXX_FLAGS "-std=c++11") # On crée la bibliothèque libmaillage.a add_library(maillage ${SOURCE_FILES}) # On génère l'exécutable maillage.e à partir du fichier src/main.cpp add_executable(maillage.e src/main.cpp) # On lie l'exécutable avec la bibliothèque libmaillage.a target_link_libraries(maillage.e maillage)
Générons le projet CMake :
rm -rf build
mkdir build
cd build
cmake ..
-- The C compiler identification is GNU 9.3.0 -- The CXX compiler identification is GNU 9.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - 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 -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build
Compilons :
make -j
Scanning dependencies of target maillage [ 11%] Building CXX object CMakeFiles/maillage.dir/src/main.cpp.o [ 22%] Building CXX object CMakeFiles/maillage.dir/src/point.cpp.o [ 33%] Building CXX object CMakeFiles/maillage.dir/src/quadrangle.cpp.o [ 44%] Building CXX object CMakeFiles/maillage.dir/src/segment.cpp.o [ 55%] Building CXX object CMakeFiles/maillage.dir/src/polygone.cpp.o [ 66%] Building CXX object CMakeFiles/maillage.dir/src/triangle.cpp.o [ 77%] Linking CXX static library libmaillage.a [ 77%] Built target maillage Scanning dependencies of target maillage.e [ 88%] Building CXX object CMakeFiles/maillage.e.dir/src/main.cpp.o [100%] Linking CXX executable maillage.e [100%] Built target maillage.e
Nous avons créé la bibliothèque libmaillage.a
et l'exécutable maillage.a
:
tree -L 1
. ├── CMakeCache.txt ├── CMakeFiles ├── cmake_install.cmake ├── libmaillage.a ├── maillage.e └── Makefile 1 directory, 5 files
./maillage.e
Test cout sur maillage : Type de mailles : triangles Nombre max de voisins : 3 Nombre de noeuds : 4 Nombre de mailles : 2 Tests unitaires réussis, maillage : 4/4
Avec un fichier descripteur CMakeLists.txt
de seulement 8 lignes, CMake construit l'ensemble du projet et gère automatiquement ses dépendances en ne recompilant que les fichiers nécessaires :
touch ../include/point.hpp
make -j
Scanning dependencies of target maillage [ 11%] Building CXX object CMakeFiles/maillage.dir/src/main.cpp.o [ 22%] Building CXX object CMakeFiles/maillage.dir/src/segment.cpp.o [ 33%] Building CXX object CMakeFiles/maillage.dir/src/point.cpp.o [ 44%] Building CXX object CMakeFiles/maillage.dir/src/triangle.cpp.o [ 55%] Building CXX object CMakeFiles/maillage.dir/src/polygone.cpp.o [ 66%] Building CXX object CMakeFiles/maillage.dir/src/quadrangle.cpp.o [ 77%] Linking CXX static library libmaillage.a [ 77%] Built target maillage Scanning dependencies of target maillage.e [ 88%] Building CXX object CMakeFiles/maillage.e.dir/src/main.cpp.o [100%] Linking CXX executable maillage.e [100%] Built target maillage.e
touch ../include/quadrangle.hpp
make -j
Scanning dependencies of target maillage [ 11%] Building CXX object CMakeFiles/maillage.dir/src/main.cpp.o [ 22%] Building CXX object CMakeFiles/maillage.dir/src/quadrangle.cpp.o [ 33%] Linking CXX static library libmaillage.a [ 77%] Built target maillage Scanning dependencies of target maillage.e [ 88%] Building CXX object CMakeFiles/maillage.e.dir/src/main.cpp.o [100%] Linking CXX executable maillage.e [100%] Built target maillage.e
cd $ROOTDIR/maillage_cmake
tree test
test ├── test_maillage.cpp ├── test_point.cpp ├── test_polygone.cpp ├── test_quadrangle.cpp ├── test_segment.cpp └── test_triangle.cpp 0 directories, 6 files
Chaque fichier source est un programme principal qui exécute les tests unitaires de la classe du même nom. Par exemple :
pygmentize test/test_point.cpp
#include "point.hpp" int main() { point p; return p.all_testsu(); }
Pour utiliser CTest, on ajoute les lignes suivantes au fichier CMakeLists.txt
:
cd $ROOTDIR/maillage_cmake
rm -f CMakeLists.txt
ln -s CMakeLists.txt_test CMakeLists.txt
tail -n 15 CMakeLists.txt |pygmentize -l cmake
enable_testing() # On construit la liste des fichiers de test message(STATUS "Ajout des tests :") file(GLOB_RECURSE TEST_FILES test/*.cpp) foreach(TEST_FILE ${TEST_FILES}) # On récupère le nom sans l'extension get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) # On crée l'exécutable test_toto.e à partir du fichier test_toto.cpp add_executable(${TEST_NAME}.e ${TEST_FILE}) target_link_libraries(${TEST_NAME}.e maillage) # On ajoute le test "test_toto" qui utilise l'exécutable "test_toto.e" add_test(${TEST_NAME} ${TEST_NAME}.e) message(" - ${TEST_NAME}") endforeach()
On génère le projet CMake correspondant :
rm -rf build_ctest
mkdir build_ctest
cd build_ctest
cmake ..
-- The C compiler identification is GNU 9.3.0 -- The CXX compiler identification is GNU 9.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - 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 -- Detecting CXX compile features -- Detecting CXX compile features - done -- Ajout des tests : - test_maillage - test_point - test_polygone - test_quadrangle - test_segment - test_triangle -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest
On compile :
make -j
Scanning dependencies of target maillage [ 4%] Building CXX object CMakeFiles/maillage.dir/src/point.cpp.o [ 14%] Building CXX object CMakeFiles/maillage.dir/src/polygone.cpp.o [ 14%] Building CXX object CMakeFiles/maillage.dir/src/quadrangle.cpp.o [ 19%] Building CXX object CMakeFiles/maillage.dir/src/main.cpp.o [ 23%] Building CXX object CMakeFiles/maillage.dir/src/triangle.cpp.o [ 28%] Building CXX object CMakeFiles/maillage.dir/src/segment.cpp.o [ 33%] Linking CXX static library libmaillage.a [ 33%] Built target maillage Scanning dependencies of target test_triangle.e Scanning dependencies of target test_segment.e Scanning dependencies of target test_polygone.e Scanning dependencies of target test_point.e Scanning dependencies of target test_quadrangle.e Scanning dependencies of target test_maillage.e Scanning dependencies of target maillage.e [ 38%] Building CXX object CMakeFiles/test_segment.e.dir/test/test_segment.cpp.o [ 42%] Building CXX object CMakeFiles/test_polygone.e.dir/test/test_polygone.cpp.o [ 47%] Building CXX object CMakeFiles/test_triangle.e.dir/test/test_triangle.cpp.o [ 52%] Building CXX object CMakeFiles/test_point.e.dir/test/test_point.cpp.o [ 57%] Building CXX object CMakeFiles/test_quadrangle.e.dir/test/test_quadrangle.cpp.o [ 61%] Building CXX object CMakeFiles/test_maillage.e.dir/test/test_maillage.cpp.o [ 66%] Building CXX object CMakeFiles/maillage.e.dir/src/main.cpp.o [ 71%] Linking CXX executable test_segment.e [ 76%] Linking CXX executable test_polygone.e [ 80%] Linking CXX executable test_point.e [ 85%] Linking CXX executable test_triangle.e [ 90%] Linking CXX executable test_quadrangle.e [ 90%] Built target test_segment.e [ 90%] Built target test_polygone.e [ 90%] Built target test_point.e [ 90%] Built target test_triangle.e [ 90%] Built target test_quadrangle.e [ 95%] Linking CXX executable maillage.e [100%] Linking CXX executable test_maillage.e [100%] Built target maillage.e [100%] Built target test_maillage.e
On lance CTest :
ctest
Test project /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest Start 1: test_maillage 1/6 Test #1: test_maillage .................... Passed 0.00 sec Start 2: test_point 2/6 Test #2: test_point ....................... Passed 0.00 sec Start 3: test_polygone 3/6 Test #3: test_polygone .................... Passed 0.00 sec Start 4: test_quadrangle 4/6 Test #4: test_quadrangle .................. Passed 0.00 sec Start 5: test_segment 5/6 Test #5: test_segment ..................... Passed 0.00 sec Start 6: test_triangle 6/6 Test #6: test_triangle .................... Passed 0.00 sec 100% tests passed, 0 tests failed out of 6 Total Test time (real) = 0.02 sec
Pour afficher la sortie des tests, on peut exécuter CTest en mode verbeux :
ctest -V
UpdateCTestConfiguration from :/builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/DartConfiguration.tcl UpdateCTestConfiguration from :/builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/DartConfiguration.tcl Test project /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest Constructing a list of tests Done constructing a list of tests Updating test list for fixtures Added 0 tests to meet fixture requirements Checking test dependency graph... Checking test dependency graph end test 1 Start 1: test_maillage 1: Test command: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/test_maillage.e 1: Test timeout computed to be: 10000000 1: Test cout sur maillage : 1: Type de mailles : triangles 1: Nombre max de voisins : 3 1: Nombre de noeuds : 4 1: Nombre de mailles : 2 1: 1: Tests unitaires réussis, maillage : 4/4 1/6 Test #1: test_maillage .................... Passed 0.00 sec test 2 Start 2: test_point 2: Test command: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/test_point.e 2: Test timeout computed to be: 10000000 2: Tests unitaires réussis, point : 3/3 2/6 Test #2: test_point ....................... Passed 0.00 sec test 3 Start 3: test_polygone 3: Test command: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/test_polygone.e 3: Test timeout computed to be: 10000000 3: Tests unitaires réussis, polygone : 3/3 3/6 Test #3: test_polygone .................... Passed 0.00 sec test 4 Start 4: test_quadrangle 4: Test command: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/test_quadrangle.e 4: Test timeout computed to be: 10000000 4: Tests unitaires réussis, quadrangle : 3/3 4/6 Test #4: test_quadrangle .................. Passed 0.00 sec test 5 Start 5: test_segment 5: Test command: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/test_segment.e 5: Test timeout computed to be: 10000000 5: Tests unitaires réussis, segment : 4/4 5/6 Test #5: test_segment ..................... Passed 0.00 sec test 6 Start 6: test_triangle 6: Test command: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest/test_triangle.e 6: Test timeout computed to be: 10000000 6: Tests unitaires réussis, triangle : 3/3 6/6 Test #6: test_triangle .................... Passed 0.00 sec 100% tests passed, 0 tests failed out of 6 Total Test time (real) = 0.02 sec
Créons un test qui échoue :
cat > ../test/test_echec.cpp <<EOL
#include "point.hpp"
int main() {return 1;}
EOL
On met à jour le projet :
cmake ..
make -j
-- Ajout des tests : - test_echec - test_maillage - test_point - test_polygone - test_quadrangle - test_segment - test_triangle -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest [ 30%] Built target maillage Scanning dependencies of target test_echec.e [ 43%] Building CXX object CMakeFiles/test_echec.e.dir/test/test_echec.cpp.o [ 43%] Built target test_segment.e [ 60%] Built target test_polygone.e [ 60%] Built target test_triangle.e [ 78%] Built target test_quadrangle.e [ 78%] Built target maillage.e [ 86%] Built target test_point.e [ 95%] Built target test_maillage.e [100%] Linking CXX executable test_echec.e [100%] Built target test_echec.e
L'exécution de CTest fait apparaître un échec mais ne s'interrompt pas :
ctest
Test project /builds/boileau/outils-dev-log/notebooks/maillage_cmake/build_ctest Start 1: test_echec 1/7 Test #1: test_echec .......................***Failed 0.00 sec Start 2: test_maillage 2/7 Test #2: test_maillage .................... Passed 0.00 sec Start 3: test_point 3/7 Test #3: test_point ....................... Passed 0.00 sec Start 4: test_polygone 4/7 Test #4: test_polygone .................... Passed 0.00 sec Start 5: test_quadrangle 5/7 Test #5: test_quadrangle .................. Passed 0.00 sec Start 6: test_segment 6/7 Test #6: test_segment ..................... Passed 0.00 sec Start 7: test_triangle 7/7 Test #7: test_triangle .................... Passed 0.00 sec 86% tests passed, 1 tests failed out of 7 Total Test time (real) = 0.02 sec The following tests FAILED: 1 - test_echec (Failed) Errors while running CTest
CMake possède un système de recherche de paquets externes (bibliothèques, fichiers d'en-tête, etc.) qui permet de gérer l'installation des dépendances sur différents systèmes.
Dans le cadre de ce cours, nous allons prendre l'exemple de la bibliothèque matplotlib-cpp qui permet de tracer des graphes directement depuis le programme C++.
Ce choix présente plusieurs avantages :
matplotlib-cpp
s'appuie sur la bibliothèque matplotlib
pour Python qui est largement utiliséeNous allons utiliser matplotlib
pour représenter un maillage généré par notre bibliothèque.
Note : Il s'agit seulement d'un exercice et non d'une utilisation optimale de
matplotlib
!
L'exemple se trouve dans le projet maillage_plot
qui contient l'en-tête matplotlibcpp.h
dans le sous-répertoire ext/
:
cd $ROOTDIR
tree -L 2 maillage_plot/ext
maillage_plot/ext └── matplotlibcpp.h 0 directories, 1 file
Par rapport au CMakeLists.txt
du projet maillage_cmake
, on ajoute les lignes suivantes :
cd maillage_plot
pygmentize CMakeLists.txt |sed '5,28!d'
# Recherche des dépendances Python message(STATUS "Find Python dependencies using CMake ${CMAKE_VERSION}") if(CMAKE_VERSION VERSION_LESS 3.12) # Pour CMake < 3.12 find_package(PythonInterp) find_package(PythonLibs) set(Python_NumPy_INCLUDE_DIRS "/opt/conda/lib/python3.7/site-packages/numpy/core/include") set(Python_LIBRARIES ${PYTHON_LIBRARIES}) set(Python_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} ${Python_NumPy_INCLUDE_DIRS}) else() if(APPLE) # Pour MacOS seulement set(CMAKE_FIND_FRAMEWORK NEVER) endif() # Nécessite CMake >= 3.12 find_package(Python3 COMPONENTS Interpreter Development required) set(Python_LIBRARIES ${Python3_LIBRARIES}) set(Python_INCLUDE_DIRS ${Python3_INCLUDE_DIRS}) endif() message(" Python_INCLUDE_DIRS : ${Python_INCLUDE_DIRS}") message(" Python_LIBRARIES : ${Python_LIBRARIES}") # On ajoute les en-têtes des biliothèques internes include_directories(ext ${Python_INCLUDE_DIRS}) # On ajoute les en-têtes du projet
On ajoute la fonction trace()
dans le fichier include/maillage.hpp
:
pygmentize include/maillage.hpp | sed '176,213!d'
// Trace le maillage template<class T, int n> void maillage<T, n>::trace() { T une_maille = getMaille(0); int nb_sommets = une_maille.get_nb_sommets(); std::vector<double> x(m_nb_noeuds); std::vector<double> y(m_nb_noeuds); std::vector<double> xs(nb_sommets+1); std::vector<double> ys(nb_sommets+1); plt::figure(); // sinon: "segmentation fault" sur MacOS // Tracé des arêtes for(int i = 0; i < m_nb_mailles; i++) { T maille = getMaille(i); for(int j = 0; j < nb_sommets; j++) { xs[j] = maille.getPoint(j).get_x(); ys[j] = maille.getPoint(j).get_y(); }; // dernier sommet = premier sommet xs[nb_sommets] = maille.getPoint(0).get_x(); ys[nb_sommets] = maille.getPoint(0).get_y(); plt::plot(xs, ys); } // Tracé des noeuds for(int i = 0; i < m_nb_noeuds; i++) { x[i] = m_noeuds[i].get_x(); y[i] = m_noeuds[i].get_y(); } // On veut des symboles circulaires plt::plot(x, y, "o"); // Finalisation du tracé plt::title("Maillage"); plt::axis("equal"); plt::xlabel("x"); plt::ylabel("y"); plt::save("maillage.png"); //plt::show(); cout << "Tracé sauvé dans maillage.png" << endl;
On appelle la fonction trace()
depuis le programme principal :
pygmentize src/main.cpp
#include <iostream> #include "quadrangle.hpp" #include "maillage.hpp" int main() { // On crée un maillage triangle régulier maillage<triangle, 3> m; m.maillage_regulier(5); cout << m << endl; // On trace le maillage m.trace(); return 0; }
Comme d'habitude avec CMake, on compile dans un répertoire build/
séparé :
rm -rf build
mkdir build
cd build
Sur Mac, on aide CMake à trouver l'installation Python :
cmake -D Python3_ROOT_DIR=~/anaconda3 ..
-- The C compiler identification is GNU 9.3.0 -- The CXX compiler identification is GNU 9.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - 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 -- Detecting CXX compile features -- Detecting CXX compile features - done -- Find Python dependencies using CMake 3.16.3 -- Could NOT find Python3 (missing: required) (found version "3.9.1") Python_INCLUDE_DIRS : /opt/conda/envs/outils-dev-log/include/python3.9 Python_LIBRARIES : /opt/conda/envs/outils-dev-log/lib/libpython3.9.so -- Configuring done -- Generating done -- Build files have been written to: /builds/boileau/outils-dev-log/notebooks/maillage_plot/build
On compile :
make -j
Scanning dependencies of target maillage [ 4%] Building CXX object CMakeFiles/maillage.dir/src/main.cpp.o [ 14%] Building CXX object CMakeFiles/maillage.dir/src/polygone.cpp.o [ 14%] Building CXX object CMakeFiles/maillage.dir/src/point.cpp.o [ 19%] Building CXX object CMakeFiles/maillage.dir/src/segment.cpp.o [ 23%] Building CXX object CMakeFiles/maillage.dir/src/triangle.cpp.o [ 28%] Building CXX object CMakeFiles/maillage.dir/src/quadrangle.cpp.o [ 33%] Linking CXX static library libmaillage.a [ 33%] Built target maillage Scanning dependencies of target test_triangle.e Scanning dependencies of target test_quadrangle.e Scanning dependencies of target maillage.e Scanning dependencies of target test_segment.e Scanning dependencies of target test_polygone.e Scanning dependencies of target test_maillage.e Scanning dependencies of target test_point.e [ 38%] Building CXX object CMakeFiles/test_triangle.e.dir/test/test_triangle.cpp.o [ 42%] Building CXX object CMakeFiles/test_quadrangle.e.dir/test/test_quadrangle.cpp.o [ 47%] Building CXX object CMakeFiles/test_segment.e.dir/test/test_segment.cpp.o [ 57%] Building CXX object CMakeFiles/test_polygone.e.dir/test/test_polygone.cpp.o [ 57%] Building CXX object CMakeFiles/test_point.e.dir/test/test_point.cpp.o [ 61%] Building CXX object CMakeFiles/maillage.e.dir/src/main.cpp.o [ 66%] Building CXX object CMakeFiles/test_maillage.e.dir/test/test_maillage.cpp.o [ 71%] Linking CXX executable test_triangle.e [ 76%] Linking CXX executable test_quadrangle.e [ 85%] Linking CXX executable test_segment.e [ 85%] Linking CXX executable test_polygone.e [ 85%] Built target test_triangle.e [ 90%] Linking CXX executable test_point.e [ 90%] Built target test_quadrangle.e [ 90%] Built target test_segment.e [ 90%] Built target test_point.e [ 90%] Built target test_polygone.e [ 95%] Linking CXX executable test_maillage.e [100%] Linking CXX executable maillage.e [100%] Built target test_maillage.e [100%] Built target maillage.e
On exécute le programme principal qui crée un maillage triangulère régulier et le trace dans le fichier maillage.png
:
./maillage.e
Type de mailles : triangles Nombre max de voisins : 3 Nombre de noeuds : 36 Nombre de mailles : 50 Tracé sauvé dans maillage.png
On affiche le résultat :
cat maillage.png | display
Make
qui permet de constuire un projet en gérant les dépendances entre fichiers.Make
seul est compliqué à mettre en oeuvre et à maintenir pour de gros projets et pour des plateformes multiples.CMake
, un outil de plus haut niveau :