Cambiar entre GCC y Clang / LLVM usando CMake

269

Tengo una serie de proyectos creados con CMake y me gustaría poder cambiar fácilmente entre usar GCC o Clang / LLVM para compilarlos. Creo (¡corríjame si me equivoco!) Que para usar Clang necesito configurar lo siguiente:

    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 una manera fácil de cambiar entre estas y las variables GCC predeterminadas, preferiblemente como un cambio en todo el sistema en lugar de un proyecto específico (es decir, no solo agregarlas a CMakeLists.txt de un proyecto)?

Además, ¿es necesario usar los llvm-*programas en lugar de los valores predeterminados del sistema al compilar usando clang en lugar de gcc? ¿Cual es la diferencia?

Rezzie
fuente

Respuestas:

342

CMake respeta las variables de entorno CCy CXXal detectar el compilador C y 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

Los indicadores específicos del compilador pueden anularse colocándolos en un archivo CMake de todo el sistema y apuntando la CMAKE_USER_MAKE_RULES_OVERRIDEvariable a él. Cree un archivo ~/ClangOverrides.txtcon los siguientes contenidos:

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

El sufijo _INIThará que CMake inicialice la *_FLAGSvariable correspondiente con el valor dado. Luego invoque cmakede la siguiente manera:

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

Finalmente para forzar el uso de los binutils LLVM, establezca la variable interna _CMAKE_TOOLCHAIN_PREFIX. Esta variable es respetada por el CMakeFindBinUtilsmódulo:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Poniendo todo esto junto se puede escribir una envoltura del shell que establece las variables de entorno CCy CXXe invoca cmakecon las sustituciones variables mencionadas.

sakra
fuente
1
He seguido tu respuesta y todo excepto las CMAKE_USER_MAKE_RULES_OVERRIDEobras. Parece que el archivo se está ignorando (es decir, a pesar de CMAKE_C_FLAGS_RELEASEestar configurado -O4en el archivo de anulaciones, muestra el valor predeterminado de -O3 -DNDEBUGin cmake).
Rezzie
18
Tenga en cuenta que gran parte de esta información se almacena en caché en el archivo CMakeCache.txt en el nivel superior de su árbol de compilación. Para cambiar entre gcc y clang, debe tener dos árboles de compilación completamente separados, y simplemente cd de ida y vuelta para "cambiar" los compiladores. Una vez que se genera un árbol de compilación con un compilador determinado, no puede cambiar el compilador para ese árbol de compilación.
DLRdave
@DLRdave Usar dos árboles de construcción separados es una idea sensata; uno que no había considerado. Vaya :) Sin embargo, incluso hacerlo en un nuevo src/build-clangdirectorio se ignoran las anulaciones.
Rezzie
1
@Rezzie Las banderas en ClangOverrides.txt deben definirse con el sufijo _INIT. Ver respuesta actualizada.
sakra
8
Nota para los lectores. Si tiene problemas con CMake que no respeta las variables CCy CXX, puede ser porque primero debe eliminar todos los archivos del directorio de compilación. rm CMakeCache.txtPuede que no sea eficiente.
John Dibling
128

Cambio de C ++ en todo el sistema en Ubuntu:

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

Imprimirá algo como esto:

  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

Luego solo selecciona clang ++.

Descifrador
fuente
3
¡Gracias, no sabía sobre esto! Aunque supongo que depende de dónde cmake esté buscando un compilador, ¿verdad?
Ibrahim
1
@Ibrahim Esta configuración establece el enlace simbólico "c ++" en el compilador que eligió y cmake marca "c ++" de forma predeterminada, no "g ++". Entonces, a menos que la configuración de cmake sea muy específica, esto debería funcionar bien (y lo hace para mí).
Zoomulator
2
Recibo una respuesta que dice "Solo hay una alternativa en el grupo de enlaces c ++". Expanda su respuesta para incluir cómo agregar el
sonido metálico
3
Tenga cuidado con esta alternativa, ya que puede provocar efectos secundarios con su sistema. Ya tuve problemas con paquetes como el controlador nvidia que vuelve a compilar algunos módulos del kernel durante la instalación y no era compatible con clang.
Falco
2
Si desea instalar clang-3.5, clang-3.6, etc., use esto para establecer el stackoverflow predeterminado / a / 30742451/1427533 , de lo contrario obtendráThere is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc
miguel.martin
23

Puede usar el comando de opción:

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

y luego envuelva la configuración del compilador clang en if () s:

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

De esta manera, se muestra como una opción de cmake en las herramientas de configuración de GUI.

Para hacerlo en todo el sistema, por supuesto, puede usar una variable de entorno como valor predeterminado o quedarse con la respuesta de Ferruccio.

Tobias Schlegel
fuente
Así es como lo tengo configurado actualmente, pero obviamente necesita hacerlo por proyecto. Esperaba que hubiera una orden como cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake.
Rezzie
1
Ahora entiendo lo que estás tratando de lograr. No sé si cmake proporciona ese comportamiento, pero podría probar la opción -C que parece cargar un script antes de comenzar a ejecutar CMakeLists.txt. Aunque no lo he probado.
Tobias Schlegel
18

Cambio de C en todo el sistema en Ubuntu:

sudo update-alternatives --config cc

Cambio de C ++ en todo el sistema en Ubuntu:

sudo update-alternatives --config c++

Para cada uno de los anteriores, presione el número de selección (1) e Intro para seleccionar 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
fuente
77
Si instaló su clang manualmente y lo colocó en un lugar no estándar, es posible que no aparezca con --config. Por ejemplo, si está /opt/clang-llvm-3.5/instalado, primero instale una nueva alternativa: sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30
CodeKid,
16

Puede utilizar el mecanismo de archivo de cadena de herramientas de cmake para este propósito, consulte, por ejemplo, aquí . Usted escribe un archivo de cadena de herramientas para cada compilador que contiene las definiciones correspondientes. En el momento de la configuración, ejecuta, por ejemplo

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

y toda la información del compilador se establecerá durante la llamada del proyecto () desde el archivo de cadena de herramientas. Aunque en la documentación se menciona solo en el contexto de la compilación cruzada, también funciona para diferentes compiladores en el mismo sistema.

j_fu
fuente
44
Todavía estoy sorprendido de cómo las respuestas correctas solo se pueden encontrar en la parte inferior de un hilo aquí en SO.
Slava
10

Definitivamente no necesita usar los diferentes programas 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")

Estos están diseñados para funcionar en el formato interno llvm y, como tales, no son útiles para la construcción de su aplicación.

Como nota, -O4 invocará LTO en su programa, lo que puede que no desee (aumentará enormemente el tiempo de compilación) y el sonido predeterminado de clang al modo c99, por lo que esa bandera tampoco es necesariamente necesaria.

Echristo
fuente
7

Si el compilador predeterminado elegido por cmakees gccy ha instalado clang, puede usar la manera fácil de compilar su proyecto con clang:

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

De acuerdo con la ayuda 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.

Puede crear archivos como gcc_compiler.txte clang_compiler.txtincluye toda la configuración relativa en la sintaxis de CMake.

Ejemplo de Clang (clang_compiler.txt):

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

Luego ejecútalo como

CCG:

cmake -C gcc_compiler.txt XXXXXXXX

Sonido metálico:

cmake -C clang_compiler.txt XXXXXXXX
Enze Chi
fuente
3

Puede usar la sintaxis: $ENV{environment-variable}en su CMakeLists.txtpara acceder a las variables de entorno. Puede crear scripts que inicialicen un conjunto de variables de entorno de manera adecuada y solo tengan referencias a esas variables en sus CMakeLists.txtarchivos.

Ferruccio
fuente
Por favor, ¿podría elaborar un poco más? ¿Te refieres a un script de shell para exportar variables de entorno antes de lanzar cmake? ¿Qué variables necesitarían establecerse? ¿O te refieres a un script / alias que solo llama a cmake con -DCMAKE_C_COMPILER ...etc?
Rezzie
Me refiero a un script que solo exporta las variables de entorno apropiadas. Crearía sus propias variables de entorno y las haría referencia en el archivo CMakeLists.txt.
Ferruccio
Ahh Veo a que te refieres. Lo único es que requeriría revisar CMakeLists.txtcada proyecto y hacer que consulte las nuevas variables. Esperaba que hubiera una forma conveniente de hacerlo en todo el sistema, sin tener que modificar ningún archivo de proyecto. Similar a pasar a CMAKE_TOOLCHAIN_FILE.
Rezzie