Alternando entre GCC e Clang / LLVM usando CMake

269

Eu tenho vários projetos criados usando o CMake e gostaria de poder alternar facilmente entre usar o GCC ou o Clang / LLVM para compilá-los. Acredito (por favor, corrija-me se estiver enganado!) Que, para usar o Clang, preciso definir o seguinte:

    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 uma maneira fácil de alternar entre essas e as variáveis ​​padrão do GCC, preferencialmente como uma alteração em todo o sistema, em vez de específica do projeto (ou seja, não apenas adicioná-las ao CMakeLists.txt do projeto)?

Além disso, é necessário usar os llvm-*programas em vez dos padrões do sistema ao compilar usando clang em vez de gcc? Qual é a diferença?

Rezzie
fonte

Respostas:

342

O CMake respeita as variáveis ​​de ambiente CCe CXXao detectar o compilador C e C ++ para usar:

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

Os sinalizadores específicos do compilador podem ser substituídos colocando-os em um arquivo CMake em todo o sistema e apontando a CMAKE_USER_MAKE_RULES_OVERRIDEvariável para ele. Crie um arquivo ~/ClangOverrides.txtcom o seguinte conteúdo:

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

O sufixo _INITfará com que o CMake inicialize a *_FLAGSvariável correspondente com o valor fornecido. Em seguida, invoque cmakeda seguinte maneira:

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

Finalmente, para forçar o uso dos binutils do LLVM, defina a variável interna _CMAKE_TOOLCHAIN_PREFIX. Esta variável é respeitada pelo CMakeFindBinUtilsmódulo:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Colocando tudo isso junto, você pode escrever um wrapper shell que define as variáveis de ambiente CCe CXXe chama cmakecom as substituições de variáveis mencionadas.

sakra
fonte
1
Eu segui sua resposta e tudo, exceto as CMAKE_USER_MAKE_RULES_OVERRIDEobras. Parece que o arquivo está sendo ignorado (ou seja, apesar de CMAKE_C_FLAGS_RELEASEestar definido -O4no arquivo de substituição, está mostrando o valor padrão -O3 -DNDEBUGem cmake).
Rezzie
18
Observe que muitas dessas informações são armazenadas em cache no arquivo CMakeCache.txt no nível superior da sua árvore de construção. Para alternar entre o gcc e o clang, você deve ter duas árvores de compilação completamente separadas e simplesmente fazer um CD para alternar entre compiladores. Depois que uma árvore de construção é gerada com um determinado compilador, você não pode alternar o compilador para essa árvore de construção.
precisa saber é o seguinte
@DLRdave Usar duas árvores de construção separadas é uma ideia sensata; um que eu não tinha considerado. Ops :) No entanto, mesmo fazendo isso em um novo src/build-clangdiretório, as substituições estão sendo ignoradas.
Rezzie
1
@Rezzie Os sinalizadores no ClangOverrides.txt devem ser definidos com o sufixo _INIT. Veja a resposta atualizada.
Sakra
8
Nota para os leitores. Se você está tendo problemas com o CMake, não honrando as variáveis CCe CXX, pode ser porque você precisa excluir todos os arquivos do diretório de compilação primeiro. rm CMakeCache.txtpode não ser suficiente.
John Dibling
128

Alteração de C ++ em todo o sistema no Ubuntu:

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

Irá imprimir algo como isto:

  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

Depois, selecione clang ++.

Codificador
fonte
3
Obrigado, eu não sabia disso! Embora eu acho que depende de onde o cmake está procurando um compilador, certo?
Ibrahim
1
@Ibrahim Essa configuração define o link simbólico "c ++" para o compilador escolhido e o cmake verifica "c ++" por padrão, não "g ++". Portanto, a menos que a configuração do cmake seja muito específica, isso deve funcionar bem (e funciona para mim).
Zoomulator
2
Eu recebo uma resposta que "Existe apenas uma alternativa no grupo de links c ++". Expanda sua resposta para incluir como adicionar clang a esta lista
vedant
3
Tenha cuidado com essa alternativa, pois ela pode causar efeitos colaterais no seu sistema. Já teve problemas com pacotes como o driver nvidia que recompila alguns módulos do kernel durante a instalação e não é compatível com o clang.
Falco
2
Se você deseja instalar clang-3.5, clang-3.6, etc. usar isso para definir o padrão stackoverflow.com/a/30742451/1427533 , caso contrário você vai terThere is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc
miguel.martin
23

Você pode usar o comando option:

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

e, em seguida, agrupe as configurações do clang-compiler em if () s:

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

Dessa forma, ele é exibido como uma opção cmake nas ferramentas de configuração da GUI.

Para torná-lo abrangente, é claro que você pode usar uma variável de ambiente como valor padrão ou ficar com a resposta de Ferruccio.

Tobias Schlegel
fonte
É assim que eu o configurei atualmente, mas obviamente ele precisa ser feito por projeto. Eu esperava que houvesse um comando como cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake.
Rezzie
1
Agora eu entendo o que você está tentando realizar. Não sei se esse comportamento é fornecido pelo cmake, mas você pode tentar a opção -C que parece carregar um script antes de começar a executar o CMakeLists.txt. Ainda não tentei.
Tobias Schlegel
18

Mudança C em todo o sistema no Ubuntu:

sudo update-alternatives --config cc

Alteração de C ++ em todo o sistema no Ubuntu:

sudo update-alternatives --config c++

Para cada uma das opções acima, pressione Número da seleção (1) e Enter para selecionar 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
fonte
7
Se você instalou seu clang manualmente e o colocou em um local não padrão, ele pode não aparecer com --config. Por exemplo, se estiver /opt/clang-llvm-3.5/instalado, primeiro instale uma nova alternativa: sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30
CodeKid 14/10
16

Você pode usar o mecanismo de arquivo da cadeia de ferramentas do cmake para esse fim, veja, por exemplo, aqui . Você escreve um arquivo de cadeia de ferramentas para cada compilador que contém as definições correspondentes. No momento da configuração, você executa, por exemplo

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

e todas as informações do compilador serão definidas durante a chamada project () do arquivo da cadeia de ferramentas. Embora a documentação seja mencionada apenas no contexto da compilação cruzada, ela também funciona para diferentes compiladores no mesmo sistema.

j_fu
fonte
4
Ainda estou surpreso como as respostas certas só podem ser encontradas na parte inferior de um tópico aqui na SO.
Slava
10

Você definitivamente não precisa usar os vários programas diferentes do 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")

Eles foram criados para funcionar no formato interno llvm e, como tal, não são úteis para a construção do seu aplicativo.

Como nota, -O4 invocará LTO no seu programa que você pode não querer (aumentará bastante o tempo de compilação) e retornará os padrões para o modo c99, de modo que o sinalizador não seja necessariamente necessário.

echristo
fonte
7

Se o compilador padrão escolhido por cmakeé gcce você instalou clang, você pode usar a maneira fácil de compilar seu projeto com clang:

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

De acordo com a ajuda 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.

Você pode criar arquivos como gcc_compiler.txte clang_compiler.txtincluir toda a configuração relativa na sintaxe do CMake.

Exemplo de clang (clang_compiler.txt):

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

Em seguida, execute-o como

GCC:

cmake -C gcc_compiler.txt XXXXXXXX

Clang:

cmake -C clang_compiler.txt XXXXXXXX
Enze Chi
fonte
3

Você pode usar a sintaxe: $ENV{environment-variable}em suas CMakeLists.txtvariáveis de ambiente acesso. Você pode criar scripts que inicializem um conjunto de variáveis ​​de ambiente adequadamente e apenas tenham referências a essas variáveis ​​em seus CMakeLists.txtarquivos.

Ferruccio
fonte
Por favor, você poderia elaborar um pouco mais? Você quer dizer um script de shell para exportar variáveis ​​de ambiente antes de iniciar o cmake? Quais variáveis ​​precisariam ser definidas? Ou você quer dizer um script / alias que apenas chama cmake com -DCMAKE_C_COMPILER ...etc?
Rezzie
Quero dizer um script que apenas exporta as variáveis ​​de ambiente apropriadas. Você criaria suas próprias variáveis ​​de ambiente e as referenciaria no arquivo CMakeLists.txt.
Ferruccio
Ahh; Eu vejo o que você quer dizer. A única coisa é que seria necessário passar por CMakeLists.txttodos os projetos e fazer com que ele consultasse as novas variáveis. Eu esperava que houvesse uma maneira conveniente de fazer isso em todo o sistema, sem precisar modificar nenhum arquivo de projeto. Semelhante a passar a CMAKE_TOOLCHAIN_FILE.
Rezzie