290

I have a number of projects built using CMake and I'd like to be able to easily switch between using GCC or Clang/LLVM to compile them. I believe (please correct me if I'm mistaken!) that to use Clang I need to set the following:

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

Is there an easy way of switching between these and the default GCC variables, preferably as a system-wide change rather than project specific (i.e. not just adding them into a project's CMakeLists.txt)?

Also, is it necessary to use the llvm-* programs rather than the system defaults when compiling using clang instead of gcc? What's the difference?

Rezzie
  • 4,295
  • 6
  • 22
  • 30

9 Answers9

370

CMake honors the environment variables CC and CXX upon detecting the C and C++ compiler to use:

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

The compiler specific flags can be overridden by putting them into a make override file and pointing the CMAKE_USER_MAKE_RULES_OVERRIDE variable to it. Create a file ~/ClangOverrides.txt with the following contents:

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

The suffix _INIT will make CMake initialize the corresponding *_FLAGS variable with the given value. Then invoke cmake in the following way:

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

Finally to force the use of the LLVM binutils, set the internal variable _CMAKE_TOOLCHAIN_PREFIX. This variable is honored by the CMakeFindBinUtils module:

$ cmake -D_CMAKE_TOOLCHAIN_PREFIX=llvm- ..

Putting this all together you can write a shell wrapper which sets up the environment variables CC and CXX and then invokes cmake with the mentioned variable overrides.

Also see this CMake FAQ on make override files.

sakra
  • 53,539
  • 13
  • 152
  • 136
  • 1
    I've followed your answer and everything except the `CMAKE_USER_MAKE_RULES_OVERRIDE` works. It seems the file is being ignored (i.e. despite `CMAKE_C_FLAGS_RELEASE` being set to `-O4` in the overrides file, it's showing the default value of `-O3 -DNDEBUG` in cmake). – Rezzie Aug 11 '11 at 20:43
  • 24
    Note that much of this information is cached in the file CMakeCache.txt in the top level of your build tree. To switch between gcc and clang, you should have two completely separate build trees, and simply cd back and forth to "switch" compilers. Once a build tree is generated with a given compiler, you cannot switch the compiler for that build tree. – DLRdave Aug 11 '11 at 22:07
  • @DLRdave Using two separate build trees is a sensible idea; one I hadn't considered. Oops :) However, even doing it in a new `src/build-clang` directory the overrides are being ignored. – Rezzie Aug 11 '11 at 23:00
  • 1
    @Rezzie The flags in ClangOverrides.txt have to be defined with the suffix _INIT. See updated answer. – sakra Aug 12 '11 at 06:42
  • 9
    Note to readers. If you are having trouble with CMake not honoring the `CC` and `CXX` variables, it may be because you need to delete all files from the build directory first. `rm CMakeCache.txt` may not be sifficient. – John Dibling Dec 05 '14 at 12:41
  • Never set global `CMAKE_xxxx` flags, instead use the flags described in the answer with `target_compile_options`. This is far more robust and ressembles a modern cmake approach. – Gabriel Feb 24 '18 at 10:11
  • @DLRdave instead of "cd back and forth" one can also stay in the same directory and use the (undocumented) `-B` option of cmake to specify the build directory. Another (undocumented) useful option in this context is the `-H` option which (if an argument is specified) will allow selecting the source directory instead of printing the `--help` output. – josch Dec 01 '18 at 08:34
  • It's more straightforward to set `CMAKE_C_COMPILER` instead. See https://stackoverflow.com/a/24380618/332798 – tmm1 Aug 13 '19 at 19:48
138

System wide C++ change on Ubuntu:

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

Will print something like this:

  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

Then just select clang++.

Stéphane
  • 17,613
  • 22
  • 82
  • 117
Coder
  • 1,511
  • 1
  • 10
  • 2
  • 3
    Thanks, I didn't know about this! Although I guess it depends on where cmake is looking for a compiler, right? – Ibrahim Dec 04 '12 at 05:55
  • 1
    @Ibrahim This configuration sets the "c++" symlink to the compiler you chose and cmake checks "c++" by default, not "g++". So unless the cmake configuration is very specific, this should work fine (and does for me). – Zoomulator Jan 12 '13 at 16:47
  • Right, I don't know what I meant by that now :/ Well, I guess one thing that changes things a little is if you define environment variables for CC and CXX yourself, then it could potentially be somewhere else. – Ibrahim Jan 14 '13 at 21:46
  • 2
    I get a reply that "There is only one alternative in link group c++". Please expand your answer to include how to add clang to this list – vedant Dec 10 '13 at 06:51
  • @vedant1811: I think you just have to install clang and it will appear in the list. – thilinarmtb May 21 '14 at 22:05
  • 3
    Be careful with this alternative as it can lead to side effects with your system. Already had trouble with packages such as nvidia driver that recompiles some kernel modules during install and was not compatible with clang. – Falco Jan 29 '16 at 14:12
  • 2
    If you want to install clang-3.5, clang-3.6, etc. use this to set the default http://stackoverflow.com/a/30742451/1427533 as otherwise you'll get `There is only one alternative in link group cc (providing /usr/bin/cc): /usr/bin/gcc` – miguel.martin Feb 18 '16 at 00:17
23

You can use the option command:

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

and then wrap the clang-compiler settings in if()s:

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

This way it is displayed as an cmake option in the gui-configuration tools.

To make it systemwide you can of course use an environment variable as the default value or stay with Ferruccio's answer.

Tobias Schlegel
  • 3,920
  • 15
  • 22
  • That is how I currently have it set up, but obviously it needs doing on a per-project basis. I was hoping there'd be a command like `cmake -DCMAKE_COMPILER_DEFINITIONS=MyLlvmDefinitions.cmake`. – Rezzie Aug 11 '11 at 19:40
  • 1
    Now I understand what you're trying to accomplish. I don't know if that behavior is provided by cmake, but you could try the -C option that seems to load a script before starting to run the CMakeLists.txt. Haven't tried it though. – Tobias Schlegel Aug 11 '11 at 19:52
21

You can use the toolchain file mechanism of cmake for this purpose, see e.g. here. You write a toolchain file for each compiler containing the corresponding definitions. At config time, you run e.g

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

and all the compiler information will be set during the project() call from the toolchain file. Though in the documentation is mentionend only in the context of cross-compiling, it works as well for different compilers on the same system.

j_fu
  • 221
  • 2
  • 5
  • 5
    I am still amazed how the right answers can only be found at the bottom of a thread here at SO. – Slava Apr 17 '19 at 11:45
19

System wide C change on Ubuntu:

sudo update-alternatives --config cc

System wide C++ change on Ubuntu:

sudo update-alternatives --config c++

For each of the above, press Selection number (1) and Enter to select 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
  • 8,914
  • 22
  • 77
  • 122
  • 8
    If you've installed your clang manually and put it in a non standard place it may not show up with --config. For example if it's in `/opt/clang-llvm-3.5/` then first install a new alternative: `sudo update-alternatives --install /usr/bin/c++ c++ /opt/clang-llvm-3.5/bin/clang++ 30` – CodeKid Oct 14 '14 at 22:16
10

You definitely don't need to use the various different llvm-ar etc programs:

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

These are made to work on the llvm internal format and as such aren't useful to the build of your application.

As a note -O4 will invoke LTO on your program which you may not want (it will increase compile time greatly) and clang defaults to c99 mode so that flag isn't necessarily needed either.

echristo
  • 1,632
  • 10
  • 8
9

If the default compiler chosen by cmake is gcc and you have installed clang, you can use the easy way to compile your project with clang:

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

According to the help of 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.

You make be able to create files like gcc_compiler.txt and clang_compiler.txt to includes all relative configuration in CMake syntax.

Clang Example (clang_compiler.txt):

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

Then run it as

GCC:

cmake -C gcc_compiler.txt XXXXXXXX

Clang:

cmake -C clang_compiler.txt XXXXXXXX
Enze Chi
  • 1,595
  • 13
  • 23
3

You can use the syntax: $ENV{environment-variable} in your CMakeLists.txt to access environment variables. You could create scripts which initialize a set of environment variables appropriately and just have references to those variables in your CMakeLists.txt files.

Ferruccio
  • 93,779
  • 37
  • 217
  • 294
  • Please could you elaborate a little further? Do you mean a shell script to export environment variables before launching cmake? Which variables would need to be set? Or do you mean a script/alias which just calls cmake with `-DCMAKE_C_COMPILER ...` etc? – Rezzie Aug 11 '11 at 19:16
  • I mean a script that just exports the appropriate environment variables. You would make up your own environment variables and reference them in the CMakeLists.txt file. – Ferruccio Aug 11 '11 at 19:20
  • Ahh; I see what you mean. The only thing is that'd require going through the `CMakeLists.txt` of every project and have it query the new variables. I was hoping there'd be a convenient way of doing it system-wide, without having to modify any project files. Similar to passing a `CMAKE_TOOLCHAIN_FILE`. – Rezzie Aug 11 '11 at 19:37