Przełączanie między GCC a Clang / LLVM za pomocą CMake

269

Mam wiele projektów zbudowanych przy użyciu CMake i chciałbym móc łatwo przełączać się między nimi za pomocą GCC lub Clang / LLVM w celu ich skompilowania. Wierzę (popraw mnie, jeśli się mylę!), Że aby użyć Clanga, muszę ustawić następujące ustawienia:

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

Czy istnieje prosty sposób przełączania między tymi a domyślnymi zmiennymi GCC, najlepiej jako zmiana ogólnosystemowa, a nie specyficzna dla projektu (tj. Nie tylko dodanie ich do pliku CMakeLists.txt projektu)?

Czy konieczne jest także używanie llvm-*programów zamiast domyślnych ustawień systemu podczas kompilacji przy użyciu clang zamiast gcc? Co za różnica?

Rezzie
źródło

Odpowiedzi:

342

CMake honoruje zmienne środowiskowe CCi CXXpo wykryciu kompilatora C i C ++ do użycia:

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

Flagi specyficzne dla kompilatora można zastąpić, umieszczając je w ogólnosystemowym pliku CMake i wskazując na niego CMAKE_USER_MAKE_RULES_OVERRIDEzmienną. Utwórz plik ~/ClangOverrides.txto następującej treści:

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

Przyrostek _INITspowoduje, że CMake zainicjuje odpowiednią *_FLAGSzmienną o podanej wartości. Następnie wywołaj cmakew następujący sposób:

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

Na koniec, aby wymusić użycie binutils LLVM, ustaw zmienną wewnętrzną _CMAKE_TOOLCHAIN_PREFIX. Ta zmienna jest honorowana przez CMakeFindBinUtilsmoduł:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Wyrażając to wszystko razem można napisać otoki shell który ustawia zmienne środowiskowe CCi CXXa następnie wywołuje cmakez wymienionych nadpisania zmiennych.

sakra
źródło
1
Śledziłem twoją odpowiedź i wszystko oprócz CMAKE_USER_MAKE_RULES_OVERRIDEprac. Wygląda na to, że plik jest ignorowany (tzn. Pomimo CMAKE_C_FLAGS_RELEASEustawienia -O4w pliku przesłonięć, pokazuje domyślną wartość -O3 -DNDEBUGw cmake).
Rezzie
18
Zauważ, że wiele z tych informacji znajduje się w pamięci podręcznej pliku CMakeCache.txt na najwyższym poziomie drzewa kompilacji. Aby przełączać się między gcc i clang, powinieneś mieć dwa całkowicie osobne drzewa kompilacji i po prostu cd do przodu i do tyłu, aby „przełączyć” kompilatory. Po wygenerowaniu drzewa kompilacji za pomocą danego kompilatora nie można przełączyć kompilatora dla tego kompilatora.
DLRdave,
@DLRdave Korzystanie z dwóch osobnych drzew kompilacji to rozsądny pomysł; jeden, którego nie wziąłem pod uwagę. Ups :) Jednak nawet w nowym src/build-clangkatalogu przesłonięcia są ignorowane.
Rezzie
1
@Rezzie Flagi w ClangOverrides.txt muszą być zdefiniowane z przyrostkiem _INIT. Zobacz zaktualizowaną odpowiedź.
sakra,
8
Uwaga dla czytelników. Jeśli masz problem z CMake, który nie honoruje zmiennych CCi CXX, przyczyną może być to, że musisz najpierw usunąć wszystkie pliki z katalogu kompilacji. rm CMakeCache.txtmoże nie być poważny.
John Dibling,
128

Systemowa zmiana C ++ w Ubuntu:

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

Wydrukuje coś takiego:

  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

Następnie wybierz clang ++.

Koder
źródło
3
Dzięki, nie wiedziałem o tym! Chociaż myślę, że zależy to od tego, gdzie cmake szuka kompilatora, prawda?
Ibrahim,
1
@Ibrahim Ta konfiguracja ustawia dowiązanie symboliczne „c ++” do wybranego kompilatora, a cmake domyślnie sprawdza „c ++”, a nie „g ++”. Więc jeśli konfiguracja cmake nie jest bardzo specyficzna, powinno to działać dobrze (i działa dla mnie).
Zoomulator,
2
Otrzymuję odpowiedź, że „Istnieje tylko jedna alternatywa w grupie linków c ++”. Rozwiń swoją odpowiedź, aby uwzględnić sposób dodawania clang do tej listy
wedanta
3
Uważaj na tę alternatywę, ponieważ może to prowadzić do skutków ubocznych w twoim systemie. Miałem już problemy z pakietami takimi jak sterownik nvidia, który rekompiluje niektóre moduły jądra podczas instalacji i nie był kompatybilny z clang.
Falco
2
Jeśli chcesz zainstalować clang-3.5, clang-3.6 itp., Użyj tego, aby ustawić domyślny stackoverflow.com/a/30742451/1427533, w przeciwnym razie dostanieszThere is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc
miguel.martin
23

Możesz użyć polecenia Option:

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

a następnie zawiń ustawienia kompilatora clang w if () s:

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

W ten sposób jest wyświetlany jako opcja cmake w narzędziach do konfiguracji GUI.

Aby uczynić go ogólnosystemowym, możesz oczywiście użyć zmiennej środowiskowej jako wartości domyślnej lub pozostać przy odpowiedzi Ferruccio.

Tobias Schlegel
źródło
Tak właśnie go skonfigurowałem, ale oczywiście trzeba to zrobić dla każdego projektu. Miałem nadzieję, że będzie takie polecenie cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake.
Rezzie
1
Teraz rozumiem, co próbujesz osiągnąć. Nie wiem, czy takie zachowanie zapewnia cmake, ale możesz wypróbować opcję -C, która wydaje się ładować skrypt przed uruchomieniem CMakeLists.txt. Jednak nie próbowałem tego.
Tobias Schlegel,
18

Systemowa zmiana C w Ubuntu:

sudo update-alternatives --config cc

Systemowa zmiana C ++ w Ubuntu:

sudo update-alternatives --config c++

Dla każdego z powyższych naciśnij Numer wyboru (1) i Enter, aby wybrać 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
źródło
7
Jeśli zainstalowałeś swój clang ręcznie i umieściłeś go w niestandardowym miejscu, może się nie wyświetlać z opcją --config. Na przykład, jeśli jest /opt/clang-llvm-3.5/sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30
włączony,
16

W tym celu można użyć mechanizmu pliku cmake w pliku toolchain, patrz np . Tutaj . Piszesz plik łańcucha narzędzi dla każdego kompilatora zawierający odpowiednie definicje. W czasie konfiguracji uruchamiasz np

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

a wszystkie informacje kompilatora zostaną ustawione podczas wywołania projektu () z pliku toolchain. Chociaż w dokumentacji wspomniano tylko w kontekście kompilacji krzyżowej, działa ona również w przypadku różnych kompilatorów w tym samym systemie.

j_fu
źródło
4
Nadal jestem zdumiony, jak prawidłowe odpowiedzi można znaleźć tylko na dole wątku tutaj w SO.
Slava
10

Na pewno nie musisz używać różnych programów llvm-ar itp .:

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

Są one przystosowane do pracy w wewnętrznym formacie llvm i jako takie nie są przydatne do budowania aplikacji.

Uwaga: -O4 wywoła LTO w twoim programie, którego możesz nie chcieć (znacznie wydłuży to czas kompilacji) i powróci domyślnie do trybu c99, aby flaga również nie była potrzebna.

echristo
źródło
7

Jeśli domyślnym kompilatorem wybranym przez cmakejest gcci masz zainstalowany clang, możesz użyć łatwego sposobu na kompilację swojego projektu z clang:

$ mkdir build && cd build
$ CXX=clang++ CC=clang cmake ..
$ make -j2
tak
źródło
5

Według pomocy 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.

Umożliwisz tworzenie plików podobnych gcc_compiler.txti clang_compiler.txtobejmujących całą konfigurację względną w składni CMake.

Przykład Clang (clang_compiler.txt):

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

Następnie uruchom go jako

GCC:

cmake -C gcc_compiler.txt XXXXXXXX

Szczęk:

cmake -C clang_compiler.txt XXXXXXXX
Enze Chi
źródło
3

Możesz użyć składni: $ENV{environment-variable}w swoim, CMakeLists.txtaby uzyskać dostęp do zmiennych środowiskowych. Możesz tworzyć skrypty, które odpowiednio inicjują zestaw zmiennych środowiskowych i po prostu zawierają odniesienia do tych zmiennych w twoich CMakeLists.txtplikach.

Ferruccio
źródło
Czy mógłbyś rozwinąć nieco dalej? Czy masz na myśli skrypt powłoki, aby wyeksportować zmienne środowiskowe przed uruchomieniem cmake? Jakie zmienne należałoby ustawić? Czy masz na myśli skrypt / alias, który po prostu wywołuje cmake z -DCMAKE_C_COMPILER ...etc?
Rezzie
Mam na myśli skrypt, który po prostu eksportuje odpowiednie zmienne środowiskowe. Tworzysz własne zmienne środowiskowe i odwołujesz się do nich w pliku CMakeLists.txt.
Ferruccio,
Ahh; Rozumiem, co masz na myśli. Jedyne, co wymagałoby przejrzenia CMakeLists.txtkażdego projektu i wysłania zapytania o nowe zmienne. Miałem nadzieję, że będzie to wygodny sposób, aby zrobić to w całym systemie, bez konieczności modyfikowania plików projektu. Podobne do zaliczania a CMAKE_TOOLCHAIN_FILE.
Rezzie