Moteurs de production pour C/C++

Vous n’êtes pas sans savoir qu’il existe plusieurs outils pour compiler un projet C/C++. Je ne parle pas des compilateurs, mais des moteurs de productions comme « GNU Make ». Ces outils sont en général assez rébarbatifs à utiliser, et deviennent de véritables plaies si les projets sont de tailles conséquentes. C’est pour cela que les « autotools » ont étés crées. Hélas, ces outils sont assez complexes. Microsoft a bien une alternative, mais elle est, comme la plupart des produits Microsoft, pas portable. J’ai donc cherché un outil permettant de compiler des projets C/C++. Cet outil pour moi, se devait d’être multi plate-forme, multi-compilateur, paramétrable, simple, et devait pouvoir être utilisé en ligne de commande .
J’ai donc essayé Cmake, Boost Build, Gradle (oui cet outil, bien connu des développeurs Java, prétend pouvoir compiler des projets C++) et Qmake .

Je ne sais pas si c’est moi, ou si c’est la documentation qui n’est pas assez claire, mais je n’ai pas réussi à créer d’exécutable viable avec Cmake et Gradle.

Dans le cas de Boost Build, l’outil n’existe pas, à priori, en version binaire, il faut donc le compiler (ce qui est tout sauf trivial). Une fois que c’est fait, on se rend compte que l’outil n’est pas vraiment intuitif à l’usage, que la documentation est des plus légères et qu’il est difficilement paramétrable. A titre d’exemple, je ne suis pas parvenu à lier statiquement des libraires à mon exécutable.

Il restait donc Qmake . A la base, je boycottais cet outil, pensant qu’il était utile si et seulement si on devait compiler un projet C++ avec Qt. Je trouvais en outre qu’a première vue, il était pas si différent que ça des fameux Makefile que je cherchais à éviter.
Une étude un peu plus approfondie m’a fait constater qu’on peut compiler, à priori, n’importe quel type de projet C/C++ avec Qmake, même si le projet n’utilise pas Qt. En étudiant un peu la syntaxe, j’ai constaté que Qmake nous faisait économiser pas mal de lignes de Makefile. En somme j’ai fini par me demander si Qmake n’était pas l’outil de construction idéal pour les projet C/C++.
J’ai donc fait un petit test, en créant une application parfaitement inutile. Cette application n’utilise pas Qt, ce choix est volontaire, car je me doute bien que Qmake prend correctement Qt en charge.
Le contexte est le suivant, je suis sur Windows 7 64bits, je compile avec une version native de GCC dérivée de MinGW (TDM GCC pour être exact). Je me refuse à utiliser Cygwin.

L’ arborescence du projet (intitulé ProjetTestCPP) est constituée comme suit :

ProjetTestCPP
  |__headers
  |    |__moduleA
  |    |    |__a.h
  |    |__moduleB
  |        |__b.h
  |__lib
  |    |__libmCtrl.a
  |__src
  |    |__main.cpp
  |    |__moduleA
  |    |    |__a.cpp
  |    |__moduleB
  |        |__b.cpp
  |__build.pro

La librairie « libmCtrl.a » ne sert à rien si ce n’est à tester la capacité de Qmake à lier statiquement une librairie à un exécutable.

Voici les sources de l’application :

build.pro

CONFIG = console
SOURCES += src/*.cpp
SOURCES += src/moduleA/*.cpp
SOURCES += src/moduleB/*.cpp

QMAKE_CXXFLAGS += -m32
QMAKE_LFLAGS += -m32
INCLUDEPATH  += headers
INCLUDEPATH  += mCtrl/include
LIBS += -Llib -lmCtrl

TARGET = project

main.cpp

#include "moduleB/b.h"
#include "moduleA/a.h"
#include
#include


int main(){
    int entierA = functionA();
    int entierB = functionB();
    printf("valeur : %d\n",entierB +entierA);
    return EXIT_SUCCESS;
}

a.cpp

#include "moduleA/a.h"

int functionA(){
    int i = 0;
    i++;
    return i;
}

b.cpp

#include "moduleB/b.h"

int functionB(){
    int i = 0;
    i++;
    return i;
}

a.h

#ifndef A_H
#define A_H

extern int functionA();

#endif

b.h

#ifndef B_H
#define B_H

extern int functionB();

#endif

Comme vous l’avez constaté, le projet n’est pas très compliqué. Attardons nous maintenant sur le fichier « build.pro » c’est lui qui va être analysé par Qmake. Je vous conseille de lire attentivement la documentation de Qmake (qui est plutôt bien faite par ailleurs).
La première ligne précise que le projet générera un exécutable qui s’exécutera exclusivement en ligne de commande. Cette ligne précise en outre que le projet n’a aucune dépendance envers la librairie Qt.

Les trois lignes suivantes, permettent de préciser quels fichiers sources sont pris en compte par le projet. Remarquez l’utilisation d’expressions régulières, grâce à elles, je ne suis pas obligé de mentionner chaque fichier source. Néanmoins, ces expressions régulières ne scannent pas les sous-répertoire, du coup, à chaque fois qu’il y a un répertoire, je dois compléter la variable « SOURCES ». Il se trouve que dans ce mini projet, je n’ai que trois répertoires, donc la variable « SOURCES » a été modifiée trois fois. Si j’avais quinze répertoires, j’aurais du la modifier quinze fois. En revanche, je peux très bien rajouter dix milles fichiers sources par répertoire, sans avoir besoin de modifier le fichier projet (« build.pro »).

Les deux lignes suivantes, me permettent de rajouter des flags de compilations et de linkage. Pour la petite histoire, je compile avec un compilateur 64 bits (GCC), je précise donc dans les flags de compilation et linkage que je compte générer un exécutable 32 bits via le flag « -m32 » (qui ne fonctionne que sous GCC).

Les deux lignes suivantes permettent de référencer les répertoires contenant les fichiers d’en-tête (headers). La encore nul besoin de référencer les fichiers un à un.

La ligne suivante me permet de lier statiquement une librairie à l’exécutable. Vous constaterez que la valeur associé à la variable « LIBS » est en fait des paramètres de linkage spécifique à GCC. Il est bien entendu possible de faire la même chose pour d’autres compilateurs (MSVC etc.).

La dernière ligne permet de préciser le nom de l’exécutable. Il convient de noter que sous Windows, ce nom sera suffixé par « .exe ».

Comme vous pouvez le constater, le fichier est clair et concis. De surcroît, je ne suis pas obligé de le modifier à chaque fois que je rajoute ou que j’enlève un fichier source. Seul les ajouts de répertoire (contenant des sources) et de librairies m’obligeront à modifier le fichier projet (« build.pro »).

Comment utiliser ce fichier ? Dans mon cas c’est simple, je lance un shell comme « cmd » ou « Windows power shell » je me rend dans le répertoire du projet. Je lance la commande suivante : « qmake -o Makefile build.pro ». Cette commande va me générer un Makefile à partir de mon fichier « build.pro ». Bien entendu, je n’ai pas à modifier le Makefile, je n’ai même pas besoin d’en consulter le contenu. Dans mon cas pour compiler l’application je n’ai plus qu’a lancer un « mingw32-make » (une version Windows de « Make » ) dans le répertoire du projet et mon exécutable est généré.

Pour résumer, je suis agréablement surpris par Qmake. Il y a d’autres outils sur lesquels je n’ai pas encore eu l’occasion de me pencher sérieusement comme « Scons » ou « Ant » (oui, il semblerait qu’on puisse compiler des projets C/C++ avec Ant), mais je doute qu’ils soient meilleur que Qmake.

Par la suite, j’ai entendu parlé de Qbs. Là encore, il s’agit d’un outil fait par me semble t’il, l’équipe qui développe Qt. Cet outil est présenté par certain comme le remplaçant de Qmake, d’autres préfèrent en parler comme un excellent substitut à ce dernier. J’ai donc voulu tester cet outil en utilisant le même projet de test : ProjetTestCPP.
Pour utiliser Qbs, il faut tout d’abord l’installer, ce qui n’est pas aussi simple que ça en à l’air . Certes, il y a une archive qu’il faut décompresser et dont il faut référencer le répertoire bin dans le PATH, mais ce n’est pas tout ! Il faut configurer l’outil, cela ne se fait pas comme on pourrait le penser en modifiant un fichier de configuration, mais en exécutant une série de lignes de commandes qui permettent à Qbs de connaître l’ensemble des compilateurs disponibles sur votre machine et de s’adapter en fonction de ça en créant des « profils » (notion spécifique à Qbs). Je vous conseille vivement de lire la documentation (plutôt claire) afin d’en savoir plus.

J’ai ensuite créé un fichier  « build.qbs » à la racine du projet, voici son contenu :

import qbs
CppApplication {
    name: "Project-Test"
    cpp.architecture : "x86"
    files: ["src/*.cpp","src/*/*.cpp"]
    consoleApplication: true
    cpp.includePaths: [ 'headers', 'mCtrl/include']
    cpp.linkerFlags : [ '-Llib']
    cpp.staticLibraries : [ 'mCtrl']
}

La première ligne déclare le module (notion propre à Qbs) Qbs, c’est le module de base.

La deuxième précise qu’on utilise en plus le module C++.

La troisième est triviale, elle précise le nom du projet. Ce nom sera utilisé pour générer un exécutable. Il sera bien entendu suffixé sous Windows de l’extension « .exe ».

La quatrième précise l’architecture cible, içi x86, donc 32 bits. Cela m’évite de rajouter des « -m32 » dans les flags de compilations et de linkage. C’est un plus pour la portabilité inter compilateur.

La cinquième est une liste (au sens Qbs du terme) précisant les fichiers sources pris en compte dans le projet. Là encore, nous utilisons des expressions régulières. Là encore les sous répertoires ne sont pas pris en compte. Mais ici, c’est un tout petit peu différent. Je ne suis pas obligé de rajouter un élément à la liste « files » chaque fois que je veux prendre en compte un répertoire contenant des fichiers sources. Je rajoute un élément à chaque fois que la profondeur de l’ arborescence des fichiers sources augmente. Si je n’avais qu’un seul niveau, je n’aurais mis qu’un seul élément dans la liste à savoir « src/*.cpp » (prenant en compte exclusivement, les sources présente dans le répertoire src) . Dans mon cas j’ai deux niveau, j’ai donc mis deux éléments dans la liste.

La sixième ligne précise que l’on génère une application vouée à être exécutée exclusivement en ligne de commande.

La septième ligne est une liste précisant l’ensemble des répertoires contenant des fichiers d’en-tête.

La huitième ligne me permet de rajouter un flag de linkage. En l’occurrence le flag « -Llib » spécifique à GCC, me permet de préciser le répertoire dans lequel il faudra chercher les libraires statiques.

La neuvième ligne est une liste qu me permet de préciser le nom des libraires statiques que je veux utiliser dans mon projet. En l’occurrence, ici, cette liste n’a qu’un seul élément.

La dixième ligne n’est qu’une accolade fermante.

Je compile mon projet en lançant la commande suivante dans le répertoire racine du projet « qbs build profile:x86_64-w64-mingw32 ». Je retrouve mon exécutable dans le répertoire x86_64-w64-mingw32_debug. Le profil utilisé içi (x86_64-w64-mingw32 ) est spécifique au compilateur que j’utilise. Il est fort probable que vous ayez un profil différent, ne soyez pas surpris. Je pense que par défaut, Qbs compile en mode debug, ce qui explique le suffixe « _debug » dans le répertoire généré par Qbs.

Au final, Qbs semble être effectivement un brin meilleur que Qmake. Qbs étant assez récent, certains préféreront, pour éviter les mauvaises surprises, utiliser dans leurs projet un outil comme Qmake, bien plus mature à leur yeux. Ce choix, comme a pu le voir sur l’ exemple «  ProjetTestCPP » , ne semble pas trop impactant. Cela dit dans des projets plus complexes, avec plusieurs compilateurs différents pris en compte Qbs peut s’avérer plus simple, car plus générique.

Laisser un commentaire