在CMake中,我该如何测试编译器是否为Clang?

209

我们有一组跨平台的 CMake构建脚本,并支持使用 Visual C++GCC 进行构建。

我们正在尝试使用 Clang,但我不知道如何在我们的CMake脚本中测试编译器是否为Clang。

应该测试什么来确定编译器是否为Clang?目前,我们正在使用 MSVCCMAKE_COMPILER_IS_GNU<LANG> 来分别测试Visual C++和GCC。


1
你可以通过将CMAKE_C_COMPILER和CMAKE_CXX_COMPILER设置为clang或clang++的路径来设置编译器。+1支持clang。 - Zaffy
为什么你应该关心呢?Clang在接受编译器选项方面非常类似于GCC... - Basile Starynkevitch
3
@BasileStarynkevitch 自从我们支持了MSVC,我们就需要检测Clang,以便知道是否打开类似于GCC的选项或类似于MSVC的选项。对我来说已经太久了,我记不清了,但我们也有可能使用Clang不支持的选项。 - leedm777
2
@BasileStarynkevitch - Clang假装是__GNUC___MSC_VER,但它不能像任何一个编译器一样处理相同的程序。检测LLVM Clang和Apple Clang对于确保代码编译和执行符合预期至关重要。我已经厌倦了处理Clang的问题,我们只是在Windows上中断编译。我们已经采取了让用户向LLVM抱怨以便Clang开发人员改变其行为的政策。另请参见如何告诉Clang停止假装成其他编译器? - jww
5个回答

333

可以通过使用CMAKE_<LANG>_COMPILER_ID变量进行可靠的检查。例如,要检查C++编译器:

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

如果使用编译器包装器(例如ccache),这些也可以正确地工作。

从CMake 3.0.0开始,Apple提供的Clang的CMAKE_<LANG>_COMPILER_ID值现在是AppleClang。要同时测试Apple提供的Clang和常规Clang,请使用以下if条件:

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

另请参阅AppleClang策略描述

CMake 3.15已经支持了 clang-cl 和常规的clang前端。您可以通过检查变量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()

18
从 CMake 2.8.10 版本开始,该变量(终于!)被记录在文档中。请参见:http://www.cmake.org/cmake/help/v2.8.10/cmake.html#variable:CMAKE_LANG_COMPILER_ID - Nick Hutchinson
+1,这也是我们在这里使用了5-6年的东西:https://projects.kde.org/projects/playground/games/gluon/repository/revisions/master/entry/CMakeLists.txt#L41 - László Papp
14
请注意,CMAKE_CXX_COMPILER_ID 变量只能在 project(Foo CXX) 命令执行之后使用。 - waldyrious
6
对于gcc和clang都支持的标志,我使用if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") ... endif() - Fred Schoen
5
如果您不明白为什么无法特别检测“AppleClang”,@sakra指出cmake 3.0.0发布了“AppleClang”。然而,仅仅因为cmake --version报告相等或更高并不足够--您必须使用cmake_minimum_required(VERSION 3.0.0),以便使用“3.0.0”标准! - svenevs
显示剩余4条评论

8

这是一个更详细的CMake新手答案,修改自sakra的答案。最低版本3.1似乎很重要,因为它改变了CMake处理引号“MSVC”字符串的方式(根据政策CMP0054)。

cmake_minimum_required(VERSION 3.1)
project(MyProject CXX)

if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
  MESSAGE("Clang")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
  MESSAGE("GNU")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
  MESSAGE("Intel")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
  MESSAGE("MSVC")
endif()

3
如果您的cmake_minimum_required版本低于3.1,则必须使用带引号的变量来确定编译器,如果与STREQUAL命令一起使用,即:
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
  MESSAGE("MSVC")
endif()

如果你不喜欢引用的内容,你可以使用MATCHES命令:

if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
  MESSAGE("MSVC")
endif()

如果您指定cmake_minimum_required版本>=3.1,那么您可以在不使用引号的情况下愉快地使用STREQUAL

if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
  MESSAGE("MSVC")
endif()

关于cmake 3.1版本的问题,可以在这里查看文档:https://cmake.org/cmake/help/latest/policy/CMP0054.html


2

为避免任何拼写错误问题,我使用不区分大小写的比较,例如:

string( TOLOWER "${CMAKE_CXX_COMPILER_ID}" COMPILER_ID )
if (COMPILER_ID STREQUAL "clang")
    set(IS_CLANG_BUILD true)
else ()
    set(IS_CLANG_BUILD false)
endif ()

想要让 MATCHES 的正则表达式不区分大小写,我已经尝试了这里的所有方法,但都没有成功(似乎在CMake中不支持)。


事实上,就目前而言,不可能进行大小写不敏感的匹配。https://cmake.org/pipermail/cmake/2017-May/065432.html - fferri

2
你可以这样测试Clang及其前端:

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
  if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") # clang-cl
    # ...
  elseif (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU") # clang native
    # ...
  endif()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") # both
    # ...
endif()

CMAKE_CXX_COMPILER_FRONTEND_VARIANT需要CMake v3.14+。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接