Переключение между GCC и Clang / LLVM с использованием CMake

269

У меня есть несколько проектов, созданных с использованием CMake, и я хотел бы иметь возможность легко переключаться между использованием GCC или Clang / LLVM для их компиляции. Я считаю (пожалуйста, поправьте меня, если я ошибаюсь!), Что для использования Clang мне нужно установить следующее:

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

Существует ли простой способ переключения между этими переменными и переменными GCC по умолчанию, предпочтительно в виде общесистемного изменения, а не конкретного проекта (т. Е. Не просто добавления их в CMakeLists.txt проекта)?

Кроме того, необходимо ли использовать llvm-*программы, а не системные значения по умолчанию при компиляции с использованием clang вместо gcc? Какая разница?

Rezzie
источник

Ответы:

342

CMake учитывает переменные среды CCи CXXпри обнаружении компилятора C и C ++ использовать:

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

Флаги, специфичные для компилятора, можно переопределить, поместив их в системный файл CMake и указав на него CMAKE_USER_MAKE_RULES_OVERRIDEпеременную. Создайте файл ~/ClangOverrides.txtсо следующим содержимым:

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

Суффикс _INITзаставит CMake инициализировать соответствующую *_FLAGSпеременную с заданным значением. Затем вызовите cmakeследующим образом:

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

Наконец, чтобы принудительно использовать binutils LLVM, установите внутреннюю переменную _CMAKE_TOOLCHAIN_PREFIX. Эта переменная учитывается CMakeFindBinUtilsмодулем:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Сведя все это вместе , вы можете написать обертку оболочки , которая устанавливает переменные среды CCи , CXXа затем вызывает cmakeс указанным переменным переопределением.

Sakra
источник
1
Я следил за твоим ответом и всем, кроме CMAKE_USER_MAKE_RULES_OVERRIDEработ. Кажется, что файл игнорируется (то есть, несмотря на то, CMAKE_C_FLAGS_RELEASEчто установлен -O4в файле переопределений, он показывает значение по умолчанию-O3 -DNDEBUG в cmake).
Реззи
18
Обратите внимание, что большая часть этой информации кэшируется в файле CMakeCache.txt на верхнем уровне дерева сборки. Чтобы переключаться между gcc и clang, у вас должно быть два совершенно разных дерева сборки и просто cd туда и обратно, чтобы «переключать» компиляторы. Как только дерево сборки сгенерировано с помощью данного компилятора, вы не можете переключить компилятор для этого дерева сборки.
DLRdave
@DLRdave Использование двух отдельных деревьев сборки - разумная идея; один я не учел. Ой :) Однако, даже делая это в новом src/build-clangкаталоге, переопределения игнорируются.
Реззи
1
@Rezzie Флаги в ClangOverrides.txt должны быть определены с суффиксом _INIT. Смотрите обновленный ответ.
Сакра
8
Примечание для читателей. Если у вас возникли проблемы с CMake не почитания CCи CXXпеременными, это может быть потому , что вам нужно удалить все файлы из каталога сборки первыми. rm CMakeCache.txtне может быть существенным.
Джон Диблинг
128

Общесистемные изменения C ++ в Ubuntu:

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

Напечатает что-то вроде этого:

  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

Затем просто выберите Clang ++.

кодировщик
источник
3
Спасибо, я не знал об этом! Хотя, думаю, это зависит от того, где cmake ищет компилятор, верно?
Ибрагим
1
@Ibrahim Эта конфигурация устанавливает символическую ссылку «c ++» на выбранный вами компилятор и cmake проверяет «c ++» по умолчанию, а не «g ++». Так что, если конфигурация cmake не очень специфична, это должно работать нормально (и делает для меня).
Zoomulator
2
Я получаю ответ: «В группе ссылок C ++ есть только одна альтернатива».
Расширьте,
3
Будьте осторожны с этой альтернативой, так как она может привести к побочным эффектам в вашей системе. Уже были проблемы с такими пакетами, как драйвер nvidia, который перекомпилирует некоторые модули ядра во время установки и не был совместим с clang.
Фалько
2
Если вы хотите установить clang-3.5, clang-3.6 и т. Д., Используйте эту настройку для установки по умолчанию stackoverflow.com/a/30742451/1427533, иначе вы получитеThere is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc
miguel.martin
23

Вы можете использовать команду option:

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

а затем оберните настройки компилятора clang в if () s:

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

Таким образом, он отображается как опция cmake в инструментах настройки GUI.

Чтобы сделать его общесистемным, вы, конечно, можете использовать переменную окружения в качестве значения по умолчанию или остаться с ответом Ферруччо.

Тобиас Шлегель
источник
Это то, как я сейчас настроил его, но, очевидно, это нужно делать для каждого проекта. Я надеялся, что будет такая команда cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake.
Реззи
1
Теперь я понимаю, чего вы пытаетесь достичь. Я не знаю, обеспечивается ли такое поведение cmake, но вы можете попробовать опцию -C, которая, кажется, загружает скрипт перед началом запуска CMakeLists.txt. Хотя не пробовал.
Тобиас Шлегель
18

Изменение всей системы C в Ubuntu:

sudo update-alternatives --config cc

Общесистемные изменения C ++ в Ubuntu:

sudo update-alternatives --config c++

Для каждого из вышеперечисленных нажмите Номер выбора (1) и Enter, чтобы выбрать 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:
Виктор Любославский
источник
7
Если вы установили свой clang вручную и поместили его в нестандартное место, он может не отображаться с --config. Например, если он /opt/clang-llvm-3.5/sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30
включен,
16

Для этого вы можете использовать механизм файлов цепочки инструментов cmake, см., Например, здесь . Вы пишете файл цепочки инструментов для каждого компилятора, содержащий соответствующие определения. Во время настройки вы запускаете, например,

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

и вся информация компилятора будет установлена ​​во время вызова project () из файла toolchain. Хотя в документации упоминается только в контексте кросс-компиляции, он работает также для разных компиляторов в одной системе.

j_fu
источник
4
Я до сих пор поражаюсь, как правильные ответы можно найти только в нижней части темы здесь, в SO.
Слава
10

Вам определенно не нужно использовать различные программы llvm-ar и т.д:

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

Они предназначены для работы во внутреннем формате llvm и, как таковые, бесполезны для сборки вашего приложения.

В качестве примечания -O4 вызовет LTO в вашей программе, который вам может не понадобиться (это значительно увеличит время компиляции), и по умолчанию clang перейдет в режим c99, так что флаг также не обязательно нужен.

echristo
источник
7

Если по умолчанию выбран компилятор cmakeis gccи вы установили его clang, вы можете использовать простой способ компиляции проекта с помощью clang:

$ mkdir build && cd build
$ CXX=clang++ CC=clang cmake ..
$ make -j2
Йи
источник
5

Согласно помощи 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.

Вы сможете создавать файлы, как gcc_compiler.txtиclang_compiler.txt включать все относительные конфигурации в синтаксис CMake.

Пример Clang (clang_compiler.txt):

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

Затем запустите его как

GCC:

cmake -C gcc_compiler.txt XXXXXXXX

Clang:

cmake -C clang_compiler.txt XXXXXXXX
Энце Чи
источник
3

Вы можете использовать следующий синтаксис: $ENV{environment-variable}в ваших CMakeLists.txtк переменным окружения доступа. Вы можете создавать сценарии, которые соответствующим образом инициализируют набор переменных среды и просто содержат ссылки на эти переменные в ваших CMakeLists.txtфайлах.

Ферруччо
источник
Пожалуйста, не могли бы вы уточнить немного дальше? Вы имеете в виду скрипт оболочки для экспорта переменных среды перед запуском cmake? Какие переменные нужно будет установить? Или вы имеете в виду скрипт / псевдоним, который просто вызывает cmake с помощью -DCMAKE_C_COMPILER ...etc?
Реззи
Я имею в виду скрипт, который просто экспортирует соответствующие переменные среды. Вы должны создать свои собственные переменные окружения и ссылаться на них в файле CMakeLists.txt.
Ферруччо
Ааа; Я понимаю что ты имеешь ввиду. Единственное, что требует прохождения CMakeLists.txtкаждого проекта и запроса новых переменных. Я надеялся, что будет удобный способ сделать это в масштабе всей системы, без необходимости изменять какие-либо файлы проекта. Похоже на прохождение CMAKE_TOOLCHAIN_FILE.
Реззи