ChatGPT解决这个技术问题 Extra ChatGPT

Switching between GCC and Clang/LLVM using CMake

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?


s
sakra

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.


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).
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 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 The flags in ClangOverrides.txt have to be defined with the suffix _INIT. See updated answer.
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.
S
Stéphane

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++.


Thanks, I didn't know about this! Although I guess it depends on where cmake is looking for a compiler, right?
@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).
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
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.
If you want to install clang-3.5, clang-3.6, etc. use this to set the default 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
j
j_fu

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.


T
Tobias Schlegel

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.


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.
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.
V
Victor Lyuboslavsky

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:

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
y
yee

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

or specify the compiler for cmake:

$ cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang ..

e
echristo

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.


E
Enze Chi

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

F
Ferruccio

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.


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?
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.
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.
S
SuperNoob

Simply add this at the end of your ~/.bashrc

export CC=/usr/bin/clang
export CXX=/usr/bin/clang++

Relaunch the terminal and from now on, cmake would use clang to build every projects. Edit the ~/.bashrc to switch back to gcc.


b
blackice

It is best not to specify a compiler in CMakelists.txt.

Just add the following command

-D CMAKE_CXX_COMPILER="xx" -D CMAKE_C_COMPILER="xx"

Example:

cmake -D CMAKE_CXX_COMPILER="/bin/clang++-xx" -D CMAKE_C_COMPILER="/bin/clang-xx"

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now