Umschalten zwischen GCC und Clang / LLVM mit CMake

269

Ich habe eine Reihe von Projekten mit CMake erstellt und möchte in der Lage sein, einfach zwischen GCC oder Clang / LLVM zu wechseln, um sie zu kompilieren. Ich glaube (bitte korrigieren Sie mich, wenn ich mich irre!), Dass ich für die Verwendung von Clang Folgendes einstellen muss:

    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")

Gibt es eine einfache Möglichkeit, zwischen diesen und den Standard-GCC-Variablen zu wechseln, vorzugsweise als systemweite Änderung und nicht als projektspezifisch (dh nicht nur zum Hinzufügen in die CMakeLists.txt eines Projekts)?

Ist es auch erforderlich, die llvm-*Programme anstelle der Systemstandards zu verwenden, wenn mit clang anstelle von gcc kompiliert wird? Was ist der Unterschied?

Rezzie
quelle

Antworten:

342

CMake berücksichtigt die Umgebungsvariablen CCund CXXerkennt beim Verwenden des C- und C ++ - Compilers Folgendes:

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

Die compilerspezifischen Flags können überschrieben werden, indem sie in eine systemweite CMake-Datei eingefügt und die CMAKE_USER_MAKE_RULES_OVERRIDEVariable darauf verweist . Erstellen Sie eine Datei ~/ClangOverrides.txtmit folgendem Inhalt:

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")

Das Suffix _INITveranlasst CMake, die entsprechende *_FLAGSVariable mit dem angegebenen Wert zu initialisieren . Rufen Sie dann cmakefolgendermaßen auf:

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

Um schließlich die Verwendung der LLVM-Binutils zu erzwingen, legen Sie die interne Variable fest _CMAKE_TOOLCHAIN_PREFIX. Diese Variable wird vom CMakeFindBinUtilsModul berücksichtigt:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Putting diese können Sie alle zusammen ein Shell - Wrapper schreiben , die die Umgebungsvariablen einrichtet CCund CXXund ruft dann cmakemit den genannten Variable überschreibt.

Sakra
quelle
1
Ich habe deine Antwort und alles außer den CMAKE_USER_MAKE_RULES_OVERRIDEArbeiten befolgt . Es scheint, dass die Datei ignoriert wird (dh obwohl CMAKE_C_FLAGS_RELEASEsie -O4in der Überschreibungsdatei festgelegt ist, wird der Standardwert -O3 -DNDEBUGin cmake angezeigt).
Rezzie
18
Beachten Sie, dass ein Großteil dieser Informationen in der Datei CMakeCache.txt in der obersten Ebene Ihres Build-Baums zwischengespeichert wird. Um zwischen gcc und clang zu wechseln, sollten Sie zwei vollständig getrennte Build-Bäume haben und einfach hin und her cd, um Compiler zu "wechseln". Sobald ein Build-Baum mit einem bestimmten Compiler generiert wurde, können Sie den Compiler für diesen Build-Baum nicht mehr wechseln.
DLRdave
@DLRdave Die Verwendung von zwei separaten Build-Bäumen ist eine sinnvolle Idee. eine, an die ich nicht gedacht hatte. Ups :) Selbst wenn Sie dies in einem neuen src/build-clangVerzeichnis tun, werden die Überschreibungen ignoriert.
Rezzie
1
@Rezzie Die Flags in ClangOverrides.txt müssen mit dem Suffix _INIT definiert werden. Siehe aktualisierte Antwort.
Sakra
8
Hinweis für die Leser. Wenn Sie Probleme haben mit CMake nicht die Ehrung CCund CXXVariablen, kann es sein , weil Sie zuerst alle Dateien aus dem Build - Verzeichnis löschen müssen. rm CMakeCache.txtist möglicherweise nicht ausreichend.
John Dibling
127

Systemweite C ++ - Änderung unter Ubuntu:

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

Wird so etwas drucken:

  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

Dann wählen Sie einfach clang ++.

Codierer
quelle
3
Danke, das wusste ich nicht! Obwohl ich denke, es hängt davon ab, wo cmake nach einem Compiler sucht, oder?
Ibrahim
1
@Ibrahim Diese Konfiguration setzt den "c ++" - Symlink auf den von Ihnen ausgewählten Compiler und cmake überprüft standardmäßig "c ++", nicht "g ++". Wenn die cmake-Konfiguration nicht sehr spezifisch ist, sollte dies gut funktionieren (und für mich auch).
Zoomulator
2
Ich erhalte die Antwort "Es gibt nur eine Alternative in der Linkgruppe c ++". Bitte erweitern Sie Ihre Antwort um das Hinzufügen von Clang zu dieser Liste
vedant
3
Seien Sie vorsichtig mit dieser Alternative, da dies zu Nebenwirkungen Ihres Systems führen kann. Hatte bereits Probleme mit Paketen wie dem nvidia-Treiber, der einige Kernelmodule während der Installation neu kompiliert und nicht mit clang kompatibel ist.
Falco
2
Wenn Sie clang-3.5, clang-3.6 usw. installieren möchten, verwenden Sie diese Option , um den Standard-Stackoverflow.com/a/30742451/1427533 festzulegen, da Sie sonstThere is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc
miguel.martin
23

Sie können den Optionsbefehl verwenden:

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

und verpacken Sie dann die Clang-Compiler-Einstellungen in if () s:

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

Auf diese Weise wird es in den GUI-Konfigurationstools als cmake-Option angezeigt.

Um es systemweit zu machen, können Sie natürlich eine Umgebungsvariable als Standardwert verwenden oder bei Ferruccios Antwort bleiben.

Tobias Schlegel
quelle
So habe ich es derzeit eingerichtet, aber es muss natürlich pro Projekt durchgeführt werden. Ich hatte gehofft, dass es einen Befehl wie diesen geben würde cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake.
Rezzie
1
Jetzt verstehe ich, was Sie erreichen wollen. Ich weiß nicht, ob dieses Verhalten von cmake bereitgestellt wird, aber Sie können die Option -C ausprobieren, mit der ein Skript geladen zu werden scheint, bevor Sie mit der Ausführung von CMakeLists.txt beginnen. Ich habe es aber nicht versucht.
Tobias Schlegel
18

Systemweite C-Änderung unter Ubuntu:

sudo update-alternatives --config cc

Systemweite C ++ - Änderung unter Ubuntu:

sudo update-alternatives --config c++

Drücken Sie für jede der oben genannten Optionen die Auswahlnummer (1) und die Eingabetaste, um Clang auszuwählen:

  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
quelle
7
Wenn Sie Ihren Clang manuell installiert und an einem nicht standardmäßigen Ort abgelegt haben, wird er möglicherweise nicht mit --config angezeigt. Wenn es zum Beispiel drin ist, /opt/clang-llvm-3.5/installieren Sie zuerst eine neue Alternative: sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30
CodeKid
16

Zu diesem Zweck können Sie den Toolchain-Dateimechanismus von cmake verwenden, siehe z . B. hier . Sie schreiben für jeden Compiler eine Toolchain-Datei mit den entsprechenden Definitionen. Zur Konfigurationszeit führen Sie z

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

Alle Compilerinformationen werden während des Aufrufs von project () aus der Toolchain-Datei festgelegt. Obwohl in der Dokumentation nur im Zusammenhang mit dem Cross-Compilieren erwähnt wird, funktioniert es auch für verschiedene Compiler auf demselben System.

j_fu
quelle
4
Ich bin immer noch erstaunt, wie die richtigen Antworten hier bei SO nur am Ende eines Threads zu finden sind.
Slava
10

Sie müssen definitiv nicht die verschiedenen llvm-ar etc Programme verwenden:

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")

Diese funktionieren im internen llvm-Format und sind daher für die Erstellung Ihrer Anwendung nicht hilfreich.

Als Hinweis ruft -O4 LTO in Ihrem Programm auf, was Sie möglicherweise nicht möchten (dies erhöht die Kompilierungszeit erheblich) und klingelt standardmäßig auf den c99-Modus, sodass das Flag auch nicht unbedingt benötigt wird.

echristo
quelle
7

Wenn der von ausgewählte Standard-Compiler cmakeist gccund Sie installiert haben clang, können Sie Ihr Projekt auf einfache Weise kompilieren mit clang:

$ mkdir build && cd build
$ CXX=clang++ CC=clang cmake ..
$ make -j2
yee
quelle
5

Mit Hilfe von 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.

Sie können Dateien wie gcc_compiler.txtund erstellen, um clang_compiler.txtalle relativen Konfigurationen in die CMake-Syntax aufzunehmen.

Clang-Beispiel (clang_compiler.txt):

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

Dann führen Sie es als

GCC:

cmake -C gcc_compiler.txt XXXXXXXX

Clang:

cmake -C clang_compiler.txt XXXXXXXX
Enze Chi
quelle
3

Sie können die Syntax verwenden: $ENV{environment-variable}in Ihrem, CMakeLists.txtum auf Umgebungsvariablen zuzugreifen. Sie können Skripte erstellen, die eine Reihe von Umgebungsvariablen entsprechend initialisieren und nur Verweise auf diese Variablen in Ihren CMakeLists.txtDateien enthalten.

Ferruccio
quelle
Könnten Sie bitte etwas näher darauf eingehen? Meinen Sie ein Shell-Skript zum Exportieren von Umgebungsvariablen vor dem Starten von cmake? Welche Variablen müssten gesetzt werden? Oder meinst du ein Skript / Alias, das nur cmake mit -DCMAKE_C_COMPILER ...etc aufruft ?
Rezzie
Ich meine ein Skript, das nur die entsprechenden Umgebungsvariablen exportiert. Sie würden Ihre eigenen Umgebungsvariablen erstellen und diese in der Datei CMakeLists.txt referenzieren.
Ferruccio
Ahh; Ich verstehe was du meinst. Das Einzige ist, dass Sie CMakeLists.txtjedes Projekt durchgehen und die neuen Variablen abfragen müssen. Ich hatte gehofft, dass es eine bequeme Möglichkeit geben würde, dies systemweit zu tun, ohne Projektdateien ändern zu müssen. Ähnlich wie beim Bestehen eines CMAKE_TOOLCHAIN_FILE.
Rezzie