ARTICLE À TERMINER

Boîte à outils C++

Vous trouverez sur cette page des outils libres que j'utilise dans le cadre du développement en C++ sur GNU/Linux et que je vous recommande chaleureusement. Il se peut que ce ne soient pas les meilleurs et que vous en connaissiez d'autres plus aboutis, plus performants ou plus ergonomiques. S'ils sont libres, je vous invite à me les faire connaître et vous en remercie par avance.

Vous vous attendez certainement à ce que je vous vante ici les mérites de l'IDE Eclipse mais que nenni…

Certes, Eclipse s'est imposé comme l'outil ultime et incontournable pour le développement en Java. Mais il n'en va pas de même en C++ où son module dédié CDT arrive tout juste à maturité. Et si je ne nie pas les avantages des environnements de développement intégrés (j'ai utilisé avec bonheur Builder C++ sous MS-Windows jusqu'en 2000), je vous assure qu'aucun n'arrive à m'éloigner de l'éditeur GNU Emacs. Outre sa puissance en matière d'édition de code, Emacs a pour lui l'avantage d'être un éditeur à tout faire et il m'assure au quotidien et en regard des tâches très variées que j'ai à réaliser une bien meilleure productivité que n'importe quel IDE.

Et si Emacs vous semble inaccessible (certaines personnes n'ont que dix doigts) et qu'Eclipse vous demande trop d'investissement personnel (quoiqu'on en dise, une formation est nécessaire pour utiliser pleinement cet IDE), je vous invite à découvrir Geany, un éditeur orienté développement qui se prend en main très rapidement.

Les aficionados de Vim me reprocheront certainement de passer sous silence les mérites de leur éditeur préféré alors que je l'utilise moi-même de temps à autres mais, franchement, orienter vers Vim une personne traumatisée par Emacs serait une vacherie que je ne peux assumer ! ;-)

Il est d'usage de définir dans chaque projet des règles de mise en page et des règles de codage mais les développeurs ont tendance à les oublier. Heureusement, des outils aident à limiter les dégâts. Uncrustify et AStyle permettent de nettoyer et d'harmoniser la mise en page du code en appliquant les règles de mise en page du projet. De son côté, KWStyle permet de vérifier un certain nombre de règles de codage. Je vous invite à câbler ces outils dans votre gestionnaire de versions : le code sera ainsi reformaté à la volée avant d'être historisé par le gestionnaire ou rejeté s'il ne respecte pas les règles de codage.

Uncrustify requiert un fichier de configuration aux petits oignons. Je vous invite à consulter deux modèles du genre fournis par le projet ITK :

Plus récemment, le projet Clang nous a fourni clang-format, un outil de formatage qui semble plus intéressant que les précédents car, s'appuyant sur Clang, il est capable de traiter les syntaxes les plus alambiquées du C++ moderne. Mais je ne l'ai encore jamais utilisé sur une grosse base de code et je ne saurais donc être critique à son égard.

La compilation de code C ou C++ est en général confiée à GCC sur les systèmes Unix libres (frontal gcc pour le C, frontal g++ pour le C++). GCC est un compilateur libre (comme tous les outils proposés ici), éprouvé et en perpétuelle amélioration (respect des standards, diagnostic des anomalies, vitesse de compilation, performance du code généré). Je vous conseille vivement l'activation des options « -Wall -Wextra » ; GCC le laissera alors rien passer mais c'est pour votre bien. Au contraire, supprimer ces options pour cacher la misère sous le tapis est une très mauvaise idée…

J'en profite pour combattre ici une idée reçue : compiler un logiciel avec GCC n'impose pas la diffusion de ce logiciel sous licence GNU GPL. En effet, si GCC est bien publié sous cette licence diffusive et si le processus de génération de code (c'est ce qu'est la compilation : de la génération de code machine) provoque bien l'injection dans l'exécutable final de code en provenance du compilateur, une exception dite GCC Runtime Library Exception exonère explicitement l'utilisateur de cette obligation lorsque cette injection s'effectue dans le cadre normal d'utilisation de GCC. Une application propriétaire peut donc être compilée avec GCC. Et pour les plus sceptiques d'entre vous, la Free Software Foundation, qui détient l'entier copyright de GCC, l'écrit et le justifie noir sur blanc dans la foire aux questions de GCC.

GCC ne manque pas de qualités mais la complexité de son code et son design monolithique rendent quasiment impossible l'accès aux représentations internes et intermédiaires du code compilé. Or, cet accès est nécessaire à l'instrumentation de la chaine de compilation et à la réalisation d'outils tiers susceptibles exploiter ces formes intermédiaires du code. Ce défaut et la licence de GCC dont les éditeurs n'apprécient pas le caractère diffusif, ont favorisé l'émergence d'un compilateur alternatif nommé Clang. Bénéficiant d'un design moderne, propre et modulaire, Clang a rapidement gagné en maturité et des outils qui faisaient jusque-là défaut dans le monde libre apparaissent dans son sillage (clang-format, clang-tidy, iwyu, …). GCC qui régnait jusque-là en maitre se voit défié et la compétition qui s'engage est bénéfique tant aux outils qu'à leurs utilisateurs. De mon côté, j'utilise de plus en plus Clang et si son éco-système continue ainsi à s'étoffer, il ne tardera pas à devenir mon compilateur de prédilection.

On ne présente plus l'utilitaire Make, certainement porté sur tous les systèmes d'exploitation actuels. Même lorsque vous utilisez un environnement de développement intégré entièrement graphique, il n'est pas rare qu'en coulisses, ce soit Make qui orchestre la compilation. GNU Make, qui est comme son nom l'indique l'implantation de Make réalisée dans le cadre du projet GNU, offre une syntaxe et des fonctions enrichies qui le rendent vite indispensable : avez-vous déjà eu la frustration de disposer d'un machine dotée de 24 cœurs et d'une vieille version propriétaire de Make, incapable de paralléliser les compilations ? Moi oui… Et lorsque j'ai enfin pu disposer de GNU Make sur cette machine, le temps de compilation de mon logiciel est passé de 10 minutes à 32 secondes. :-P Ces dernières années, un outil concurrent à GNU Make a émergé, il se nomme Ninja. Ninja s'avère un tantinet plus rapide que GNU Make.

Je viens de vous dire beaucoup de bien de GNU Make mais je dois bien avouer qu'un script Make devient vite complexe à maintenir, surtout si vous devez gérer des compilations optionnelles et rendre votre script multiplateformes. Dans ce cas, mieux vaut confier à un outil spécialisé la détection de l'environnement et la génération in situ de fichiers Makefile adaptés. Cette tâche a longtemps été confiée dans le libre aux Autotools, collection d'outils que l'on désigne aussi par le nom GNU build system. Malheureusement, si ces outils remplissent bien leur rôle, ils apportent leur propre lot de problèmes, à commencer par des syntaxes absconses (oui, « des syntaxes » car il y en a trois à connaître). Parmi tous les projets initiés pour offrir une alternative simple aux Autotools, CMake s'est largement démarqué. Pour faire court, oubliez le reste et plongez dans CMake ! Cet outil est puissant, souple et extensible à l'envi, sa syntaxe est limpide et les paramètres de compilation peuvent être configurés via des interfaces semi-graphique (curses) ou graphique (Qt). Un script CMake est bien plus simple à écrire que les scripts GNU Make ou Autotools équivalents.

CMake intègre en outre :

À vrai dire, la seule chose à laquelle je n'arrive pas à me résigner, c'est qu'un script ait pour extension « .txt » (les scripts CMake se nomment CMakeLists.txt). Il paraît que c'est pour faciliter l'édition de ces scripts sur MS-Windows… Voilà à quoi nous réduisent ceux qui veulent développer sur une plateforme bureautique sans prendre la peine d'utiliser des outils décents. Il existe pourtant d'excellents éditeurs libres et orientés développement sur MS-Windows tel Notepad++ !

Ah, le débogage, activité dont se passerait bien tout développeur mais à laquelle aucun n'échappe ! Sur GNU/Linux, GDB est l'outil d'exécution en mode pas à pas incontournable mais son interface spartiate a de quoi refroidir, au point que certains lui préfèrent le bon vieux printf(). Au lieu de faire pareille bêtise, essayez plutôt DDD, un frontal graphique dont l'interface en Motif accuse son âge mais qui recèle des fonctions bien utiles telle la visualisation des chaînages de pointeurs. Et si DDD ne vous convient pas, optez pour Nemiver dont l'interface graphique en GTK est un peu plus jolie. Pour ma part, ceux qui me connaissent l'auront deviné, je préfère le mode de débogage multi-fenêtré d'Emacs (mode gdb-ui avec option gdb-many-windows), seul mode dans lequel j'active la barre d'outil d'Emacs (M-x tool-bar-mode).

Les outils comme GDB sont extrêmement précieux mais leur concept même (exécution pas à pas laissant le soin de l'interprétation et de l'analyse au développeur) les empêche de détecter les erreurs de programmation qui ne provoquent pas d'erreur d'exécution telles que les fuites de mémoire. Et même lorsqu'une erreur d'exécution se produit, un outil comme GDB montre la cause immédiate de l'erreur (l'instruction qui l'a provoquée) mais pas sa cause originelle qui se situe parfois bien loin de la ligne incriminée. Il est par exemple tout aussi intéressant de savoir à quel moment un pointeur a été invalidé que de savoir quelle instruction a utilisé ce pointeur invalide. Pour avoir cette information, il faut se tourner vers des outils tels que Valgrind qui exécutent le programme demandé en instrumentant à la volée le code binaire et en enregistrant où et quand ont lieu les opérations en rapport avec le type d'anomalie recherché. Par exemple, le module Memcheck de Valgrind (module par défaut) surveille les allocations et libérations de mémoire dynamique ainsi que les opérations sur les pointeurs manipulant cette mémoire. Lorsque le programme se termine, Memcheck signale tout oubli de libération de mémoire dynamique. Et si une erreur de segmentation mémoire survient en cours d'exécution, outre la ligne fautive et la pile d'appel que montrent aussi GDB, Valgrind indique à quel endroit la mémoire a été allouée, voire désallouée, et à quel moment le pointeur fautif a été manipulé.

L'apport de Valgrind en matière de fiabilisation de code est tel que je ne sais plus me passer de lui. Et comme il n'exige pas de compilation particulière (le mode debug étant toutefois plus que recommandé) et exécute le programme sans interaction spécifique (du point de vue de l'utilisateur, le programme s'exécute bien plus lentement que d'ordinaire mais normalement), j'utilise Valgrind au fil de l'eau et très fréquemment afin de m'assurer de l'absence de bogues latents. Désormais, je ne recours à GDB que lorsque je ne comprends pas l'enchaînement des opérations que me montre Valgrind et que j'espère qu'une exécution pas à pas me permettra d'y voir plus clair.

Pour finir, sachez que Memcheck n'est que l'un des modules de Valgrind. Helgrind est un autre module utile au débogage qui s'attache plutôt à découvrir les problèmes de concurrence d'accès entre fils d'exécution (threads).

L'automatisation des tests est réputée coûteuse et cet argument conduit beaucoup de développeurs (y compris dans un contexte professionnel) à s'en passer. C'est une erreur fondamentale qui se paie sur le long terme car les tests manuels ne peuvent remplacer en reproductibilité, rapidité et facilité d'exécution les tests automatisés. Le test manuel étant fastidieux et ingrat, les projets reposant sur des tests manuels se transforment rapidement en projets non testés et en recettes refusées.

Si l'automatisation a effectivement un coût - largement amorti sur la durée - le recours à des canevas spécialisés le réduit sensiblement et libère les développeurs des aspects les plus répétitifs et insipides de la construction d'un bon jeu de tests. Pour ce faire, j'utilisais autrefois CppUnit mais des collègues m'ont chaleureusement recommandé CxxTest, Boost::Test et Google Test. J'ai essayé Boost::Test et l'essai a été concluant ; comme j'utilise d'autres bibliothèques de la collection Boost, je ne vais pas chercher plus loin dans un avenir proche même si voir de plus en plus de projets utiliser Catch me donne envie de l'évaluer.

Construire et exécuter les tests est une chose, interpréter leurs résultats pour produire et diffuser des rapports circonstanciés et faciles à exploiter en est une autre. À ce titre, je conseille vivement l'utilisation combinée de CTest et de CDash. Le premier déroule les tests et met en forme les résultats qui sont transmis au second pour historisation et consultation via une interface web. Ces outils sont par exemple massivement utilisés dans le cadre du projet Orfeo Toolbox. Outre les tests que les développeurs exécutent sur leur machine au fil de l'eau, une batterie de plus de 2700 tests unitaires est jouée chaque nuit sur une dizaine de plateformes et les résultats sont publiés dans un tableau de bord que les membres de l'équipe consultent tous les matins en arrivant.

FIXME: Parler d'intégration continue et de Jenkins

  • CPack ⇒ Expliquer pourquoi je ne l'utilise pas.
  • Paquets DEB pour Debian (format utilisé aussi par Ubuntu)
  • Paquets RPM pour Fedora (format utilisé aussi par Red Hat, CentOS, OpenSuse et Mandriva)
  • Dire un mot de QtCreator.
  • Ajouter une section sur l'intégration continue (Jenkins, Buildbot, Gitlab CI)
  • Ajouter une section sur l'amélioration des temps de compilation avec les systèmes de cache (ccache) et la distribution de la compilation sur plusieurs machines (distcc).
  • Ajouter une section sur la documentation à base de fichiers textuels à balisage léger (Markdown, reStructuredText) sublimés par les outils produisant de superbes documentations navigables (Sphinx, Read the Docs)
  • Ajouter une section sur les forges (Redmine, Phabricator, Gitlab)