Voici un article qui présente une manière d’organiser son projet Qt de façon à avoir l’application, les librairies et les tests dans un seul et même projet. Il existe d’autres façons de faire, mais dans la jungle des possibilités (et des fichiers .pro), avoir un patron qui fonctionne, ça peut aider.
L’organisation du projet reprend les concepts décrits dans l’article sur la gestion des dépendances et la compilation répartie : SUBDIRS - handling dependencies. Le projet donné en exemple est donc découpé en une application app, une librairie lib et des tests tst_class1 et tst_class2, comme ceci :
/projet |--- projet.pro |--- app.pri `--- /src |--- /app | |--- app.pro | `--- ... |--- /lib | |--- lib.pro | `--- ... `--- /tests |--- /tst_class1 | |--- tst_class1.pro | `--- ... `--- /tst_class2 |--- tst_class2.pro `--- ...
L’application et les tests dépendent de la libraire. La présence d’une ou plusieurs librairies est très importante car cela permet d’isoler les composants et de les tester efficacement. Pour plus d’informations sur ce sujet, je vous conseille de lire “QtTest Unit Testing Framework” de Justin Noel de la société ICS.
Le projet
Le projet global contient un fichier project.pro très simple qui décrit simplement les sous-répertoires (subdirs) et les dépendances.fichier
project.pro
TEMPLATE = subdirs SUBDIRS = \ lib \ app \ tests # folders where to find the sub projects lib.subdir = src/lib app.subdir = src/app tests.subdir = src/tests # dependencies app.depends = lib tests.depends = lib
Les deux dernières lignes décrivent les dépendances de l’application et des tests vis à vis de la librairie. Celle-ci sera donc compilée en premier. Ensuite l’application et les tests pourront être compilés en parallèle.
La librairie
La librairie contient l’ensemble des classes utiles à l’application. Pour créer celle-ci, on peut s’aider du wizard de création de Qt Creator.Rien de particulier concernant le fichier .pro de la librairie. La seule chose importante, c’est la définition de la variable
DESTDIR
qui va copier le résultat de la compilation (la librairie, la DLL, les fichiers de symboles pdb) dans un répertoire “lib”
situé juste en dessous de la racine du projet. Les autres modules pourront ainsi facilement y accéder pour linker correctement leur exécutable.
fichier
lib.pro
QT -= gui TARGET = ProjectLib CONFIG (release, debug|release) { TARGET = ProjectLib DESTDIR = $$PWD/../../lib/debug } CONFIG (debug, debug|release) { TARGET = ProjectLibd DESTDIR = $$PWD/../../lib/release } TEMPLATE = lib CONFIG += dll DEFINES += PROJECTLIB_LIBRARY SOURCES += \ class1.cpp \ class2.cpp HEADERS += \ projectlib_global.h \ class1.h \ class2.h
La distinction debug/release n’est pas obligatoire, c’est une habitude de compilation sur plateforme Windows.
Le fichier commun "app.pri"
Comme nous avons plusieurs applications, l’application principale et les tests unitaires, nous allons écrire dans un seul fichier la configuration commune à toutes. Cela évite de dupliquer celle-ci dans chacun des fichiers .profichier
app.pri
PROJECTLIB = $$PWD/lib CONFIG(release, debug|release) { LIBS += -L$$PROJECTLIB/release -lProjectLib } CONFIG(debug, debug|release){ LIBS += -L$$PROJECTLIB/debug -lProjectLibd } INCLUDEPATH += $$PWD/src/lib
Pour que la librairie soit visible et accessible au niveau du linkage de l’application et des tests, on place celle ci dans le répertoire
$$PWD/lib
, qui se situe en dessous de la racine du projet. Au niveau de la librairie, cela est rendu possible grâce la variable DESTDIR
(voir chapitre précédent sur la librairie). /projet
|--- projet.pro
|--- app.pri
|--- /lib
| |--- /debug
| `--- /release
`--- /src
La définition de la variable
LIBS
sera différente pour les autres cibles comme Linux ou MacOS. L’exemple s’adresse à un projet construit sur une plateforme Windows.
La définition de la variable
INCLUDEPATH
permet au compilateur d’aller trouver les fichiers entêtes de la librairie. Encore une fois, on va au plus simple dans cet exemple, il existe d’autres façons de faire, et chaque plateforme a ses spécificités.
L’application
C’est elle qui contient la fonction main principale et qui va construire l’application à partir des classes fournies par la libraire. Dans ce schéma, les tests unitaires se font sur les classes de la librairie et les tests fonctionnels seront réalisés sur l’application.Le fichier .pro est relativement simple, on peut utiliser le wizard de Qt Creator pour créer une nouvelle application. L’instruction importante à retenir, c’est
include(../../app.pri)
qui va chercher la configuration commune pour linker correctement avec la librairie.fichier
app.pro
include(../../app.pri) QT += core TEMPLATE = app TARGET = Project SOURCES += main.cpp
Les Tests
Dans notre exemple, il y a deux tests, et chacun se comporte comme une application indépendant grâce à la macroQTEST_MAIN(TestClass)
qui crée une méthode main()
autonome et qui va exécuter les tests définis dans la classe passé en paramètre - voir Qt Documentation : Chapter 1: Writing a Unit Test sur l’écriture des tests unitaires.
On notera que la macro
QTEST_MAIN()
instancie un objet application de type QCoreApplication
. Si ce n’est pas nécessaire, il est possible de faire appel à la macro QTEST_APPLESS_MAIN()
.
Le fichier .pro est aussi relativement simple, on peut utiliser le wizard de Qt Creator pour créer un nouveau test. Comme pour l’application, l’instruction importante à ne pas oublier, c’est
include(../../../app.pri)
qui va chercher la configuration commune pour linker correctement avec la librairie.fichier
tst_class1.pro
include (../../../app.pri) QT += testlib QT -= gui CONFIG += qt console warn_on depend_includepath testcase CONFIG -= app_bundle TEMPLATE = app SOURCES += tst_class1.cpp
Utiliser Qt Creator pour compiler et exécuter l’application et les tests
Depuis Qt Creator, ouvrir le fichier racine project.pro. Dans une arborescence unique du projet, on va pouvoir accéder aussi bien aux fichiers de l’application, des librairies et des tests.- Lancer qmake pour générer les Makefile.
- Lancer la compilation globale du projet (Build All). Dans l’ordre c’est la librairie qui sera compilée en premier, l’application et les tests seront compilés par la suite (séquentiellement ou en parallèle).
- Exécuter l’application ou les tests (en choisissant le type de run).
Conclusion
Voici les grands principes pour tout avoir dans un seul et même projet Qt. Il existe surement d’autres méthodes, mais l’avantage de ce système c’est cette intégration dynamique de l’application et des tests unitaires autour d’une ou plusieurs librairies. Toute modification de la librairie entraînera une recompilation automatique de l’application et des tests, ce qui garantira l’intégrité du projet.Pour ce qui est de l’exécution des tests, il y a un nouveau système qui est apparu avec la version 4.3.0 de Qt Creator. Ce nouvel outil permet d’exécuter individuellement ou globalement tous les tests et fera l’objet d’un autre article.
Fin de l'article.