Basculer entre GCC et Clang / LLVM à l'aide de CMake

269

J'ai un certain nombre de projets construits en utilisant CMake et j'aimerais pouvoir basculer facilement entre l'utilisation de GCC ou Clang / LLVM pour les compiler. Je crois (veuillez me corriger si je me trompe!) Que pour utiliser Clang, je dois définir ce qui suit:

    SET (CMAKE_C_COMPILER             "/usr/bin/clang")
    SET (CMAKE_C_FLAGS                "-Wall -std=c99")
    SET (CMAKE_C_FLAGS_DEBUG          "-g")
    SET (CMAKE_C_FLAGS_MINSIZEREL     "-Os -DNDEBUG")
    SET (CMAKE_C_FLAGS_RELEASE        "-O4 -DNDEBUG")
    SET (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g")

    SET (CMAKE_CXX_COMPILER             "/usr/bin/clang++")
    SET (CMAKE_CXX_FLAGS                "-Wall")
    SET (CMAKE_CXX_FLAGS_DEBUG          "-g")
    SET (CMAKE_CXX_FLAGS_MINSIZEREL     "-Os -DNDEBUG")
    SET (CMAKE_CXX_FLAGS_RELEASE        "-O4 -DNDEBUG")
    SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")

    SET (CMAKE_AR      "/usr/bin/llvm-ar")
    SET (CMAKE_LINKER  "/usr/bin/llvm-ld")
    SET (CMAKE_NM      "/usr/bin/llvm-nm")
    SET (CMAKE_OBJDUMP "/usr/bin/llvm-objdump")
    SET (CMAKE_RANLIB  "/usr/bin/llvm-ranlib")

Existe-t-il un moyen facile de basculer entre ces variables et les variables GCC par défaut, de préférence en tant que modification à l'échelle du système plutôt que spécifique au projet (c'est-à-dire pas simplement en les ajoutant dans CMakeLists.txt d'un projet)?

De plus, est-il nécessaire d'utiliser les llvm-*programmes plutôt que les valeurs par défaut du système lors de la compilation en utilisant clang au lieu de gcc? Quelle est la différence?

Rezzie
la source

Réponses:

342

CMake honore les variables d'environnement CCet CXXlors de la détection du compilateur C et C ++ à utiliser:

$ export CC=/usr/bin/clang
$ export CXX=/usr/bin/clang++
$ cmake ..
-- The C compiler identification is Clang
-- The CXX compiler identification is Clang

Les indicateurs spécifiques au compilateur peuvent être remplacés en les plaçant dans un fichier CMake à l'échelle du système et en pointant la CMAKE_USER_MAKE_RULES_OVERRIDEvariable vers celui-ci. Créez un fichier ~/ClangOverrides.txtavec le contenu suivant:

SET (CMAKE_C_FLAGS_INIT                "-Wall -std=c99")
SET (CMAKE_C_FLAGS_DEBUG_INIT          "-g")
SET (CMAKE_C_FLAGS_MINSIZEREL_INIT     "-Os -DNDEBUG")
SET (CMAKE_C_FLAGS_RELEASE_INIT        "-O3 -DNDEBUG")
SET (CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "-O2 -g")

SET (CMAKE_CXX_FLAGS_INIT                "-Wall")
SET (CMAKE_CXX_FLAGS_DEBUG_INIT          "-g")
SET (CMAKE_CXX_FLAGS_MINSIZEREL_INIT     "-Os -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELEASE_INIT        "-O3 -DNDEBUG")
SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "-O2 -g")

Le suffixe _INITfera CMake initialiser la *_FLAGSvariable correspondante avec la valeur donnée. Appelez ensuite cmakede la manière suivante:

$ cmake -DCMAKE_USER_MAKE_RULES_OVERRIDE=~/ClangOverrides.txt ..

Enfin, pour forcer l'utilisation des binutils LLVM, définissez la variable interne _CMAKE_TOOLCHAIN_PREFIX. Cette variable est honorée par le CMakeFindBinUtilsmodule:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Mettre tout cela ensemble , vous pouvez écrire un emballage shell qui définit les variables d'environnement CCet CXXpuis Invoque cmakeavec les variables mentionnées overrides.

sakra
la source
1
J'ai suivi ta réponse et tout sauf les CMAKE_USER_MAKE_RULES_OVERRIDEtravaux. Il semble que le fichier soit ignoré (c'est-à-dire bien qu'il CMAKE_C_FLAGS_RELEASEsoit défini -O4dans le fichier de remplacement, il affiche la valeur par défaut de -O3 -DNDEBUGin cmake).
Rezzie
18
Notez qu'une grande partie de ces informations est mise en cache dans le fichier CMakeCache.txt au niveau supérieur de votre arborescence de génération. Pour basculer entre gcc et clang, vous devez avoir deux arborescences de construction complètement distinctes, et simplement faire des allers-retours pour "commuter" les compilateurs. Une fois qu'un arbre de génération est généré avec un compilateur donné, vous ne pouvez pas changer le compilateur pour cet arbre de génération.
DLRdave
@DLRdave L'utilisation de deux arbres de construction distincts est une idée sensée; celui que je n'avais pas envisagé. Oups :) Cependant, même en le faisant dans un nouveau src/build-clangrépertoire, les remplacements sont ignorés.
Rezzie
1
@Rezzie Les drapeaux dans ClangOverrides.txt doivent être définis avec le suffixe _INIT. Voir la réponse mise à jour.
sakra
8
Note aux lecteurs. Si vous rencontrez des problèmes avec CMake qui n'honore pas les variables CCet CXX, c'est peut-être parce que vous devez d'abord supprimer tous les fichiers du répertoire de génération. rm CMakeCache.txtpeut ne pas être indifférent.
John Dibling
128

Changement C ++ à l'échelle du système sur Ubuntu:

sudo apt-get install clang
sudo update-alternatives --config c++

Imprime quelque chose comme ceci:

  Selection    Path              Priority   Status
------------------------------------------------------------
* 0            /usr/bin/g++       20        auto mode
  1            /usr/bin/clang++   10        manual mode
  2            /usr/bin/g++       20        manual mode

Sélectionnez ensuite clang ++.

Codeur
la source
3
Merci, je n'en savais rien! Bien que je suppose que cela dépend de l'endroit où cmake recherche un compilateur, non?
Ibrahim
1
@Ibrahim Cette configuration définit le lien symbolique "c ++" vers le compilateur que vous avez choisi et cmake vérifie "c ++" par défaut, pas "g ++". Donc, à moins que la configuration de cmake soit très spécifique, cela devrait fonctionner correctement (et cela fonctionne pour moi).
Zoomulator
2
Je reçois une réponse "Il n'y a qu'une seule alternative dans le groupe de liens c ++". Veuillez développer votre réponse pour inclure comment ajouter un clang à cette liste
vedant
3
Soyez prudent avec cette alternative car elle peut entraîner des effets secondaires avec votre système. Vous avez déjà eu des problèmes avec des packages tels que le pilote nvidia qui recompile certains modules du noyau lors de l'installation et n'était pas compatible avec clang.
Falco
2
Si vous souhaitez installer clang-3.5, clang-3.6, etc., utilisez-le pour définir le stackoverflow.com/a/30742451/1427533 par défaut , sinon vous obtiendrezThere is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc
miguel.martin
23

Vous pouvez utiliser la commande d'option:

option(USE_CLANG "build application with clang" OFF) # OFF is the default

puis envelopper les paramètres du compilateur clang dans if () s:

if(USE_CLANG)
    SET (...)
    ....
endif(USE_CLANG)

De cette façon, il est affiché comme une option cmake dans les outils de configuration de l'interface graphique.

Pour le faire à l'échelle du système, vous pouvez bien sûr utiliser une variable d'environnement comme valeur par défaut ou rester avec la réponse de Ferruccio.

Tobias Schlegel
la source
C'est ainsi que je l'ai actuellement mis en place, mais il est évident que cela doit être fait par projet. J'espérais qu'il y aurait une commande comme cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake.
Rezzie
1
Je comprends maintenant ce que vous essayez d'accomplir. Je ne sais pas si ce comportement est fourni par cmake, mais vous pouvez essayer l'option -C qui semble charger un script avant de commencer à exécuter CMakeLists.txt. Je n'ai pas encore essayé.
Tobias Schlegel
18

Changement C à l'échelle du système sur Ubuntu:

sudo update-alternatives --config cc

Changement C ++ à l'échelle du système sur Ubuntu:

sudo update-alternatives --config c++

Pour chacun des éléments ci-dessus, appuyez sur Numéro de sélection (1) et Entrée pour sélectionner Clang:

  Selection    Path            Priority   Status
------------------------------------------------------------
* 0            /usr/bin/gcc     20        auto mode
  1            /usr/bin/clang   10        manual mode
  2            /usr/bin/gcc     20        manual mode
Press enter to keep the current choice[*], or type selection number:
Victor Lyuboslavsky
la source
7
Si vous avez installé votre clang manuellement et que vous l'avez placé dans un endroit non standard, il peut ne pas apparaître avec --config. Par exemple, si c'est le cas, /opt/clang-llvm-3.5/installez d'abord une nouvelle alternative: sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30
CodeKid
16

Vous pouvez utiliser le mécanisme de fichier de la chaîne d'outils de cmake à cet effet, voir par exemple ici . Vous écrivez un fichier de chaîne d'outils pour chaque compilateur contenant les définitions correspondantes. Au moment de la configuration, vous exécutez par exemple

 cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/clang-toolchain.cmake ..

et toutes les informations du compilateur seront définies lors de l'appel à project () à partir du fichier de la chaîne d'outils. Bien que la documentation ne soit mentionnée que dans le contexte de la compilation croisée, elle fonctionne également pour différents compilateurs sur le même système.

j_fu
la source
4
Je suis toujours étonné de voir que les bonnes réponses ne peuvent être trouvées qu'au bas d'un fil ici à SO.
Slava
10

Vous n'avez certainement pas besoin d'utiliser les différents programmes llvm-ar etc:

SET (CMAKE_AR      "/usr/bin/llvm-ar")
SET (CMAKE_LINKER  "/usr/bin/llvm-ld")
SET (CMAKE_NM      "/usr/bin/llvm-nm")
SET (CMAKE_OBJDUMP "/usr/bin/llvm-objdump")
SET (CMAKE_RANLIB  "/usr/bin/llvm-ranlib")

Celles-ci sont conçues pour fonctionner sur le format interne llvm et en tant que telles ne sont pas utiles à la construction de votre application.

Remarque: -O4 invoquera LTO sur votre programme, ce que vous ne voudrez peut-être pas (cela augmentera considérablement le temps de compilation) et basculera par défaut en mode c99 afin que l'indicateur ne soit pas nécessairement nécessaire non plus.

échristo
la source
7

Si le compilateur par défaut choisi par cmakeest gccet que vous avez installé clang, vous pouvez utiliser la méthode simple pour compiler votre projet avec clang:

$ mkdir build && cd build
$ CXX=clang++ CC=clang cmake ..
$ make -j2
oui
la source
5

Selon l'aide de cmake:

-C <initial-cache>
     Pre-load a script to populate the cache.

     When cmake is first run in an empty build tree, it creates a CMakeCache.txt file and populates it with customizable settings for the project.  This option may be used to specify a  file  from
     which to load cache entries before the first pass through the project's cmake listfiles.  The loaded entries take priority over the project's default values.  The given file should be a CMake
     script containing SET commands that use the CACHE option, not a cache-format file.

Vous pouvez créer des fichiers comme gcc_compiler.txtetclang_compiler.txt inclure toute la configuration relative dans la syntaxe CMake.

Exemple de Clang (clang_compiler.txt):

 set(CMAKE_C_COMPILER "/usr/bin/clang" CACHE string "clang compiler" FORCE)

Ensuite, exécutez-le en tant que

GCC:

cmake -C gcc_compiler.txt XXXXXXXX

Bruit:

cmake -C clang_compiler.txt XXXXXXXX
Enze Chi
la source
3

Vous pouvez utiliser la syntaxe: $ENV{environment-variable}dans votre CMakeLists.txtpour accéder aux variables d'environnement. Vous pouvez créer des scripts qui initialisent un ensemble de variables d'environnement de manière appropriée et ont juste des références à ces variables dans vos CMakeLists.txtfichiers.

Ferruccio
la source
Pourriez-vous nous en dire un peu plus? Voulez-vous dire un script shell pour exporter les variables d'environnement avant de lancer cmake? Quelles variables devraient être définies? Ou voulez-vous dire un script / alias qui appelle simplement cmake avec -DCMAKE_C_COMPILER ...etc?
Rezzie
Je veux dire un script qui exporte simplement les variables d'environnement appropriées. Vous devez créer vos propres variables d'environnement et les référencer dans le fichier CMakeLists.txt.
Ferruccio
Ahh; Je vois ce que tu veux dire. La seule chose est que cela nécessiterait de parcourir CMakeLists.txtchaque projet et de lui demander les nouvelles variables. J'espérais qu'il y aurait un moyen pratique de le faire à l'échelle du système, sans avoir à modifier les fichiers de projet. Semblable à passer a CMAKE_TOOLCHAIN_FILE.
Rezzie