In Cmake, How to Test If the Compiler Is Clang

In CMake, how can I test if the compiler is Clang?

A reliable check is to use the CMAKE_<LANG>_COMPILER_ID variables. E.g., to check the C++ compiler:

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# using Clang
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# using GCC
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
# using Intel C++
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# using Visual Studio C++
endif()

These also work correctly if a compiler wrapper like ccache is used.

As of CMake 3.0.0 the CMAKE_<LANG>_COMPILER_ID value for Apple-provided Clang is now AppleClang. To test for both the Apple-provided Clang and the regular Clang use the following if condition:

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# using regular Clang or AppleClang
endif()

Also see the AppleClang policy description.

CMake 3.15 has added support for both the clang-cl and the regular clang front end. You can determine the front end variant by inspecting the variable CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT:

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
# using clang with clang-cl front end
elseif (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
# using clang with regular front end
endif()
endif()

CMAKE clang variables

In the end, I have done it with this snipped of code.
FIND_PACKAGE is not needed.

IF (NOT ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
MESSAGE(FATAL_ERROR "Set clang as C compiler")
ENDIF()

How to check what compiler cmake is using?

You can see what variables are available in your CMake's binary output directory CMakeFiles/[your CMake's version]/CMakeCXXCompiler.cmake.

If you just want to print it, your are looking for CMAKE_CXX_COMPILER_ID and CMAKE_CXX_COMPILER_VERSION. Those are available cross-platform.

Here are two examples of what CMake detects and generates from my projects:

set(CMAKE_CXX_COMPILER_ID "GNU")
set(CMAKE_CXX_COMPILER_VERSION "4.6.3")

Or

set(CMAKE_CXX_COMPILER_ID "MSVC")
set(CMAKE_CXX_COMPILER_VERSION "19.0.24215.1")

Other kind of variables are there to check for platforms/toolchains like CMAKE_COMPILER_IS_GNUCXX.

cmake detects clang-cl as clang

The proper way to handle this case is checking for

(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" AND "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC"))

How do I tell CMake to use Clang on Windows?

You also need - in addition to the Clang compilers itself - an build/link environment for Windows.

The latest CMake 3.6 builds do have several integrated supported Clang build environments on Windows (e.g. Visual Studio, Cygwin; see Release Notes).

I've just run a successful test with

  • LLVM-3.9.0-r273898-win32.exe from http://llvm.org/builds/
  • cmake-3.6.0-rc4-win64-x64.msi from https://cmake.org/download/
  • Microsoft VS2015 Community Edition Version 14.0.23107.0

All installed to their standard paths with their bin directories in the global PATH environment.

The part you need to know is setting the right toolset with the CMake -T"LLVM-vs2014" command line option. During the configuration process CMake will let you know which compiler it has found/taken.

CMakeLists.txt

cmake_minimum_required(VERSION 3.6)

project(HelloWorld)

file(
WRITE main.cpp
"#include <iostream>\n"
"int main() { std::cout << \"Hello World!\" << std::endl; return 0; }"
)
add_executable(${PROJECT_NAME} main.cpp)

Windows Console

...> mkdir VS2015
...> cd VS2015
...\VS2015> cmake -G"Visual Studio 14 2015" -T"LLVM-vs2014" ..
-- The C compiler identification is Clang 3.9.0
-- The CXX compiler identification is Clang 3.9.0
-- Check for working C compiler: C:/Program Files (x86)/LLVM/msbuild-bin/cl.exe
-- Check for working C compiler: C:/Program Files (x86)/LLVM/msbuild-bin/cl.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files (x86)/LLVM/msbuild-bin/cl.exe
-- Check for working CXX compiler: C:/Program Files (x86)/LLVM/msbuild-bin/cl.exe -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: .../VS2015
...\VS2015> cmake --build .
Microsoft (R)-Buildmodul, Version 14.0.23107.0
[...]
...\VS2015> Debug\HelloWorld.exe
Hello World!

Installation Hints

Please note that I have added LLVM to my search paths during setup:

LLVM Installation with Add to PATH

And you can crosscheck the available "Platform Toolsets" in any VS project's property page:

VS Project Properties - Platform Toolsets

References

  • What is the -D define to tell Cmake where to find nmake?
  • Linker for Clang?
  • Switching between GCC and Clang/LLVM using CMake

Clang based on platform in CMake

You (the project author) don't get to choose the compiler, the user that invokes CMake chooses the compiler.

The only thing you can do from within the CMakeLists is abort with an error if you are not happy with the choice that the user made.

There are a couple of ways how the user can specify to CMake which compiler to use. Popular examples include setting the respective environment variables, passing it via -DCMAKE_CXX_COMPILER on the first CMake run (and only on the first run; you cannot change this later without re-configuring from scratch!), or by using a toolchain file.

On Visual Studio, you can also use the -T option to select a build toolchain. This allows you to build with clang from Visual Studio.

You should document in your project's Readme which compiler you expect your users to configure for CMake and maybe give them a small example how to do this via one of the methods above.

How can I achieve native-level optimizations when cross-compiling with Clang?

TL;DR: for the x86 targets, frame-pointers are enabled by default when the OS is unknown. You can manually disable them using -fomit-frame-pointer. For ARM platforms, you certainly need to provide more information so that the backend can deduce the target ABI. Use -emit-llvm so to check which part of Clang/LLVM generate an inefficient code.


The Application Binary Interface (ABI) can change from one target to another. There is no standard ABI in C. The chosen one is dependent of several parameters including the architecture, its version, the vendor, the OS, the environment, etc.

The use of the -target parameter help the compiler to select a ABI. The thing is x86_64-none-elf is not complete enough so the backend can actually generate a fast code. In fact, I think this is actually not a valid target since there is a warning from Clang in this case and the same warning appear with wrong random targets. Surprisingly, the compiler still succeed to generate a generic binary with the provided information. Targets like x86_64-windows and x86_64-linux works as well as x86_64-unknown-windows-cygnus (for Cygwin in Windows). You can get the list of the Clang supported platforms, OS, environment, etc. in the code.

One particular aspect of the ABI is the calling conventions. They are different between operating systems. For example, x86-64 Linux platforms uses the calling convention from the System V AMD64 ABI while recent x86-64 Windows platforms uses the vectorcall calling convention based on the older Microsoft x64 one. The situation is more complex for old x86 architectures. For more information about this please read this and this.

In your case, without information about the OS, the compiler might select its own generic ABI resulting in the old push/pop instruction being used. That being said, the compiler assumes that edi contains the argument passed to the function meaning that the chosen ABI is the System V AMD64 (or a derived version). The environment can play an important role in stack optimizations like the access of the stack from outside the function (eg. the callee functions).

My initial guess was that the assembly backend disabled some optimizations due to the lack of information regarding the specified target, but this is not the case for x86-64 ELFs as the same backend is selected. Note that target is parsed by architecture-specific backend (see here for example).

In fact, the issue comes from Clang emitting an IR code with frame-pointer flag set to all. You can check the IR code with the flag -emit-llvm. You can solve this problem using -fomit-frame-pointer.

On ARM, the problem can be different and come from the assembly backend. You should certainly specify the target OS or at least more information like the sub-architecture type (mainly for ARM), the vendor and the environment.

Overall, note that it is reasonable to think that a more generic targets produces a less efficient code due to the lack of information. If you want more information about this, please fill an issue on the Clang or LLVM bug tracker so to track the reason why this happens or/and let developers fix this.

Related posts/links:

  • clang: how to list supported target architectures?
  • https://clang.llvm.org/docs/CrossCompilation.html


Related Topics



Leave a reply



Submit