Journal CMake mon amour

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
23
9
fév.
2016

CMake c'est comme Marmite(*), on l'aime ou on le hait. Pour moi, cela est fortement sujet a mon état du moment.

Aujourd'hui je suis en complète admiration. Car je viens de découvrir deux astuces très intéressantes pour simplifier mes CMakeLists.txt

Compilation

Plutôt que de forcer --std=c++11 dans la liste de ces flags, ces deux petites lignes suffisent pour utiliser la version c++11 du standard

# require C++11 standard by default
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

La second ligne est nécessaire pour forcer une erreur si le compilateur ne le permet pas, autrement une version antérieur sera quand même tente parce qu'on se sait jamais.

Il est aussi possible de choisir le standard cible par cible

add_library(foo foo.cpp)
set_property(TARGET foo PROPERTY CXX_STANDARD 11)

Encore mieux ! Parfois seulement une partie du standard est nécessaire. Et on peux choisir features par features :

add_library(mylib requires_constexpr.cpp)
target_compile_features(mylib PUBLIC cxx_constexpr)

PUBLIC impose que toutes les cibles dépendantes de cette bibliothèques (internes au projet) seront aussi compilés avec les flags nécessaires.

Les exemples sont tirés de la documentation officielles. Il y a bien plus de possibilités !

La même interface est bien sur disponible pour des projets en C uniquement. (avec les variables/propriétés C au lieu de CXX)

Répertoire d'installation

Plutôt que d'essayer de chercher si il faut installer dans lib,lib32,lib64 ou autre, on peut laisser faire le module GNUInstallDirs. Ce module va définir les variables CMAKE_INSTALL_<dir> and CMAKE_INSTALL_FULL_<dir> pour différents <dir> (pour les exécutables, les bibliothèques, les données, …)

include(GNUInstallDirs)

# the libraries install dir
set( LIBRARY_INSTALL_DIR
    ${CMAKE_INSTALL_FULL_LIBDIR}
    CACHE PATH "Installation directory for libraries"
)
set( BIN_INSTALL_DIR
     ${CMAKE_INSTALL_FULL_BINDIR}
     CACHE PATH "Installation directory for executables"
)

It's magic ! La documentation officielle donne tous les détails nécessaires.

La morale de cette histoire :

Même si c'est pénible, il me semble que chercher internet pour des solutions concernant CMake vous donnera forcément une mauvaise solution. Se pencher dans la documentation officielle (qui laisse parfois a désirer) est la seule façon d'obtenir un CMakeLists.txt a peu prés correct.

La prochaine fois, (si jamais, juste pour me contredire) j'expliquerais comment j'ai réussi a intégrer les 'sanitizers' et la compilation utilisant la PGO.

(*) oui bon…don't judge my references, I've never find anything like BBC4 yet… https://www.youtube.com/watch?v=FWQqgfE1YaM , if you ever need some explanation…

  • # Modern CMake

    Posté par  (Mastodon) . Évalué à 8.

    Ça s'appelle du Modern CMake mais malheureusement, c'est très mal documenté :

    Et ça nécessite d'avoir un CMake en version 3.1 pour avoir les compiles features (donc pas sur une Debian stable par exemple, qui est en 3.0.2).

    • [^] # Re: Modern CMake

      Posté par  (site web personnel) . Évalué à 3.

      Merci :)

      Le "très mal documenté" est un euphémisme….

      Le plus gros manque de la doc est la date d'apparition des nouvelles features, du coup je ne savais pas pour Debian.

      C'est en effet dommage pour Debian stable, mais en même temps, c++11 sur debian stable c'est pas forcement le meilleur choix… Avant GCC 4.9 il y a toujours des petits ennuis. (Même si je me souviens que GCC 4.9.2 est installable sur debian stable, mais n'est pas le défaut).

      • [^] # Re: Modern CMake

        Posté par  . Évalué à 3.

        Même si je me souviens que GCC 4.9.2 est installable sur debian stable, mais n'est pas le défaut

        Tu as une source pour ça ? Parce que ce n'est pas ce qu'indique le package

        « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

        • [^] # Re: Modern CMake

          Posté par  (site web personnel) . Évalué à 1.

          Ma source, c'est mon expérience, mais j'avoue qu'elle est minime.

          C'est le docker file que j'ai utilisé pendant un moment. Si je ne forçais pas gcc-4.9 et g++-4.9 je me retrouvais avec un "vieux" gcc. (a noter que selon le commentaire plus haut, il ne devrait plus fonctionner…). Mais peut-être que c'est moi qui est mal compris comment cela fonctionne ? Ou ils ont mis a jour ce paquet ? Il y a toujours des bugs un peu étrange dans 4.7 et 4.8 (par example https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41933 )

        • [^] # Re: Modern CMake

          Posté par  . Évalué à 1.

          Tu as une source pour ça ? Parce que ce n'est pas ce qu'indique le package

          Et pourtant…

  • # GNUInstallDirs ... portabilité?

    Posté par  . Évalué à 3.

    Bon, rewind à déjà explicité le fait que ce n'est pas disponible sous Debian stable, mais, quand je vois un truc qui à GNU dans le nom, je me demande rapidement 2 choses:

    • est-ce utilisable sous windows?
    • est-ce utilisable sous *BSD?
    • est-ce utilisable dans un projet non GPL?

    Oups, ça fait 3… en tout cas, l'intérêt majeur de cmake est vraiment son côté portable, alors tout module qui ne l'est pas, ou est trop restrictif sur sa licence (et les licences GNU sont devenues tellement complexes et illisibles qu'elles en sont devenues trop complexes pour mon esprit, qui assimile généralement la sur-complexité à de l'obfuscation, qui est une forme de restriction) pour moi, c'est mort.

    Donc, qu'en est-il de ces 3 points?

    • [^] # Re: GNUInstallDirs ... portabilité?

      Posté par  (site web personnel) . Évalué à 2.

      Cela n'a aucune importance je dirais. Le "GNU" dans "GNUInstallDirs" veut dire que l'on va respecter les standards GNU. A mon avis c'est ce qu'autotools utilise aussi par defaut. Donc ça devrait marchait dans 99.99% des cas sur une plateforme POSIX. Pour windows chaque logiciel est installé dans son propre dossier, donc on peut choisir sa propre hiérarchie locale (? il me semble ?).
      Et j'utilise une variable de cache, donc l'utilisateur mécontent, i.e. le packageur, peut indiquer ses propres chemins si il le souhaite.

      Et on parle juste de ou mettre tout son bazar une fois la compilation terminée. Les licences n'ont absolument rien a voir dans tout ça.

      • [^] # Re: GNUInstallDirs ... portabilité?

        Posté par  . Évalué à 1. Dernière modification le 10 février 2016 à 00:37.

        Le "GNU" dans "GNUInstallDirs" veut dire que l'on va respecter les standards GNU.

        Je me suis ajouté au groupe staff sur ma debian, quand je me suis aperçu que ce groupe à le droit d'écriture sur /usr/local. Ça me permets de faire des make install sans stresser d'avoir à nouveau des problèmes de trucs qui s'installent bizarrement parce que j'ai osé faire un make install en étant root. J'ai eu le cas. J'ai galéré à retaper le système. Je ne jurerai pas que c'était suite à un triptyque "./configure;make;sudo make install" mais… c'est très possible. Que celui qui lit les +5kLoC de code (de code shell/make, en plus) des fichiers générés par les autotools me contredise.
        Et autotools sont, en effet, des outils GNU.

        Et on parle juste de ou mettre tout son bazar une fois la compilation terminée. Les licences n'ont absolument rien a voir dans tout ça.

        Vrai, ce n'est que le système de build. Mais, si un système de build sous licence (voire L)GPL intègre, pour une raison ou une autre, la génération d'un header, qui contiens des données liées statiquement au binaire, la viralité de la GPL ne vient-elle pas, de fil en aiguille, toucher l'ensemble du système de build? (vraie question)

        • [^] # Re: GNUInstallDirs ... portabilité?

          Posté par  (site web personnel) . Évalué à 4.

          1) Un système de build se doit être par définition très flexible, i.e. on peut faire n'importe quoi avec…. c'est pourquoi le packaging est une tache si difficile… L'idee de GNUInstallDIrs, c'est juste de fournir les noms standards, les exécutables vont dans bin, les libraries dans libxx, les données dans share. Avant je le faisait a la main, maintenant, j'ai un module qui le fait pour moi. C'est plus robuste et ça juste marche, c'est tout. Le fait que je puisse faire n'importe quoi serait vrai avec les autotools, SCons ou mon propre makefile.

          Regarde juste ce commit. Pas de grosse différence. Mais la version utilisant GNUInstallDirs est juste plus simple et robuste. Un gnu, cela peut aussi servir parfois :)

          2) Si c’était a ce point dépendant, je pense qu'il serait temps d’étriper une bonne dizaine de juristes en place publiques, juste pour l'exemple. C'est un système de building, il ne fait rien de plus qu'un shell pourrait. Une fois son travail terminée, c'est comme si il n'avait jamais existé. Si le système de build introduisait des dépendances fortes a son runtime dans les binaires qu'ils génère, ce serait un système complètement foireux.

          • [^] # Re: GNUInstallDirs ... portabilité?

            Posté par  . Évalué à 7.

            Un système de build se doit être par définition très flexible, i.e. on peut faire n'importe quoi avec….

            Oui… et non… Sincèrement on peut couvrir presque tous les usages avec des outils qui suivent un paradigme. J'en veux pour preuve que dans la réalité ce qui se passe, c'est que tu prend un outil extrêmement puissant et tu touche le minimum possible pour rester au maximum sur sa configuration par défaut et ne pas mettre une demi journée pour pouvoir poser une question quand tu en a une.

            Faire des bidouilles au build, par définition c'est de la bidouille :) Ça rend le build de ton projet hyper complexe, ça peut potentiellement empêcher tout bonnement la reproductibilité de build, c'est très très très fragile (vas-y maintiens-le sur linux (Arch, Debian Stable et RHEL), OpenBSD, OSX et Windows 7&10,…),… Tout ça parce que tu veut changer la date dans un fichier à la noix que personne ne lis jamais.

            Je sais qu'on va me rentrer dedans, mais si vous la valeur de votre travail consiste à maintenir des toolchains complexes pour le plaisir de se dire qu'on est turing-complet à tous les étages, je vous laisse ce travail bien volontiers.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: GNUInstallDirs ... portabilité?

              Posté par  (site web personnel) . Évalué à 1.

              Je suis totalement d'accord. C'est bien mon point dans ce journal. J'ai découvert des features de CMake qui font ce que je faisais, mais bien mieux. Du coup hop, je change. Il y a moins de bidouille au final.

              Mais j'ai toujours besoin de flexibilité. J'ai quelque besoin particulier. Par exemple, voici ce que j'utilise pour la pgo ou pour les tests. C'est rien de bien méchant. Mais pour pouvoir faire ça, cela veut aussi dire qu'on peut faire n'importe quoi. C'est au développeur de faire attention.

              • [^] # Catch et sa compilation

                Posté par  (site web personnel) . Évalué à 3.

                Par exemple, voici ce que j'utilise […] pour les tests.

                J’utilise également catch, par contre je ne compile pas du tout de la même façon.
                Je définis un seul target, qui compile tous mes tests/*.cpp, et un seul de ces fichiers contient le CATCH_CONFIG_MAIN.

                Ça permet de ne pas avoir à compiler plein de fois le main de catch (ce qui prend chez moi 4.3 secondes, pour un fichier « vide » c’est un peu long), et ça me donne un seul binaire sur lequel je peux lancer les tests que je veux juste en passant des arguments.
                J’ai fait un peu le même constat avec boost.test : c’est le « main » du framework qui prend (de loin) le plus de temps à compiler. J’ai donc décidé de toujours compiler tous mes tests en un seul binaire, et de mettre la partie « main du framework » dans un fichier rien que pour lui, pour ne jamais avoir à le recompiler quand je modifie simplement un de mes tests.

                (Et puis en plus, ça me parait plus court à écrire : https://git.poez.io/biboumi/tree/CMakeLists.txt#n159 )

                Quels sont les avantages de ta méthode, à ton avis ?

                • [^] # Re: Catch et sa compilation

                  Posté par  (site web personnel) . Évalué à 4. Dernière modification le 11 février 2016 à 15:10.

                  Il y a plusieurs raisons (pas forcément excellentes ou bonnes, juste raisons…)

                  • Ce n'est pas un programme mais un ensemble de bibliothèques, dépendantes les unes des autres. J'ai en gros un test pour chaque unité logique. ('misc', base de données, solveur chimique, ….). Si je casse quelque chose dans une des bibliothèques, cela me permet de réparer les autres unes a unes. Si je n'avais qu'un seul test cela nécessiterait que tout compile avant que je puisse lancer les tests et vérifier que cela fonctionne comme je le veux.

                  • L'ensemble (bibliothèques, exécutables, exemples, tests) prend environ 5 minutes a compiler. Je ne suis pas a 10s prés (hourra pour la pause café…).

                  • Catch n'est pas ce qui prend le plus de temps a compiler…. J'utilise Eigen, et je rajoute une couche de templates… c'est pas toujours utile mais c'est marrant (example ). Le temps de compilation de ce genre de trucs est juste affreux, mais ça fonctionne plutôt bien.

                  • je suis un doctorant en science des matériaux, je n'ai jamais eu de "vrai" cours de programmation et je teste et réalise les choses au fur et a mesure… beaucoup de choses laissent a désirer j'en suis conscient, mais globalement ça marche et je ne sais pas forcement comment faire mieux. Je n'ai jamais trouve de ressources expliquant comment organiser son projet, je récupéré juste des idées ici ou la… Si vous avez des références, n’hésitez pas a me les faire connaître.

                  • [^] # Re: Catch et sa compilation

                    Posté par  (site web personnel) . Évalué à 1.

                    beaucoup de choses laissent a désirer j'en suis conscient, mais globalement ça marche et je ne sais pas forcement comment faire mieux. Je n'ai jamais trouve de ressources expliquant comment organiser son projet, je récupéré juste des idées ici ou la… Si vous avez des références, n’hésitez pas a me les faire connaître.

                    Ouais, ben comme moi du coup. Et c’est pour ça que je te demandais, je me renseigne pour voir si je passe à côté de méthodes formidables.

                    Mais donc c’est bien « parce que ça marche et que ça me convient », les mêmes raisons que moi à peu près.

          • [^] # Re: GNUInstallDirs ... portabilité?

            Posté par  . Évalué à 0.

            je t'ai plussé, juste pour l'exemple.

        • [^] # Re: GNUInstallDirs ... portabilité?

          Posté par  . Évalué à 3.

          J'ai galéré à retaper le système. Je ne jurerai pas que c'était suite à un triptyque "./configure;make;sudo make install" mais… c'est très possible.

          Ça m'est déjà arrivé aussi. C'est pour ça que j'utilise checkinstall, ça permet de désinstaller proprement les logiciels compilés depuis les sources.

          Envoyé depuis ma Debian avec Firefox

    • [^] # Re: GNUInstallDirs ... portabilité?

      Posté par  (site web personnel, Mastodon) . Évalué à 5.

      Le nom vient du fait que ces dossiers d'installations sont définis par les guidelines du projet GNU, et utilisés également (par défaut) par les autotools (automake, autoconf, etc).
      Le fait d'utiliser cette fonctionalité de cmake permet justement d'avoir un comportement qui marche sur toutes les plateformes, de Linux qui utilise /usr/include à Windows qui utilise un truc dans C:\Program Files, en passant par Haiku qui utilise /system/develop/headers. GNUInstallDirs tient compte de toutes ces particularités et fait tout bien comme il faut.

      D'autres trucs cools dans cmake: l'intégration avec CTest pour les tests unitaires, et avec CPack pour générer des paquets deb, rpm, et même des installeurs NSIS ou Wix pour Windows, le tout avec une seule déclaration de ce qu'il faut mettre dans le paquet.

  • # CMake <3

    Posté par  (site web personnel) . Évalué à 2.

    Aaah CMake <3 toujours aussi fan :)

    /me est tellement content d'avoir changé le build-system vers CMake à $JOB

    git is great because linus did it, mercurial is better because he didn't

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.