如何使用CMake检测编译器对C++11的支持情况

88
有没有一种方法可以让CMake自动检测编译器是否支持C++11?
在CMake运行期间通知用户代码无法编译,因为编译器不支持C++11将非常有用。目前,我设置了C++11标志。但是,如果编译器不支持它,则用户会得到编译错误,而不是在CMake运行期间得到错误。
完美的解决方法类似于find_package()。但是,我没有找到提供所需功能的任何模块或函数。
另外,能够检测编译器是否需要std=c++0xstd=c++11标志也是很好的。
是否有可用的内容,还是需要自己开发?
以下是我迄今为止使用的一些代码,但它仅适用于GNU'c GCC编译器。如果有更普遍的解决方案,那就太好了。
if(CMAKE_COMPILER_IS_GNUCXX)
   execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
   if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
        message(STATUS "C++11 activated.")
        add_definitions("-std=gnu++11")
   elseif(GCC_VERSION VERSION_GREATER 4.3 OR GCC_VERSION VERSION_EQUAL 4.3)
        message(WARNING "C++0x activated. If you get any errors update to a compiler which fully supports C++11")
        add_definitions("-std=gnu++0x")
   else ()
        message(FATAL_ERROR "C++11 needed. Therefore a gcc compiler with a version higher than 4.3 is needed.")   
   endif()
else(CMAKE_COMPILER_IS_GNUCXX)
   add_definitions("-std=c++0x") 
endif(CMAKE_COMPILER_IS_GNUCXX)

为什么要使用add_definitions命令而不是设置CMAKE_CXX_FLAGS来设置编译器选项? - Li Dong
7个回答

110
如果您使用的是CMake 3.1.0或更高版本,您可以检测您的C++编译器支持哪些C++特性。
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(foobar CXX)
message("Your C++ compiler supports these C++ features:")
foreach(i ${CMAKE_CXX_COMPILE_FEATURES})
  message("${i}")
endforeach()

通常情况下,在CMake脚本中不需要使用CMake变量CMAKE_CXX_COMPILE_FEATURES。取而代之,有两种方法告诉CMake在哪个C++标准下编译您的C++文件:一种是明确指定C++标准,另一种是指定所需的C++功能,让CMake引导C++标准。CMake将确保使用正确的命令行标志(例如-std=c++11)调用C++编译器。

  1. 显式指定C++标准

您可以通过设置CMake目标的属性CXX_STANDARDCXX_STANDARD_REQUIRED来明确指定C++标准。

$ cat /tmp/src/CMakeLists.txt
project(foobar CXX)
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
add_executable(prog main.cc)
set_property(TARGET prog PROPERTY CXX_STANDARD 11)
set_property(TARGET prog PROPERTY CXX_STANDARD_REQUIRED ON)
$ cat /tmp/src/main.cc
int main() {
  return 0;
}
$ mkdir /tmp/build
$ cd /tmp/build
$ cmake /tmp/src
-- The CXX compiler identification is GNU 4.8.2
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- 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: /tmp/build
$ make VERBOSE=1 | grep main.cc | grep -- "-c"
/usr/bin/c++    -std=gnu++11 -o CMakeFiles/prog.dir/main.cc.o -c /tmp/src/main.cc
$
  1. 使用CMake命令target_compile_features指定所需的C++功能。从列表中,CMake将推断要使用的C++标准。CMake全局属性CMAKE_CXX_KNOWN_FEATURES列出了可供选择的C++功能。

cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
message("Your CMake version supports these C++ features:")
get_property(known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
foreach(i ${known_features})
  message("${i}")
endforeach()

例如,这个文件名为main.cc的C++程序使用了C++11的特性: cxx_strong_enumscxx_constexprcxx_auto_type

#include <cstdlib>

int main(int argc, char *argv[]) {
  enum class Color { Red, Orange, Yellow, Green, Blue, Violet };
  constexpr float a = 3.1415f;
  auto b = a;
  return EXIT_SUCCESS;
}

这个 CMakeLists.txt 文件将会构建它

cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(foobar CXX)
add_executable(foobar main.cc)                                                                                                                                                                                                                                                     
set(needed_features
    cxx_strong_enums
    cxx_constexpr
    cxx_auto_type)
target_compile_features(foobar PRIVATE ${needed_features})
              

12
这必须按照目标进行操作有点麻烦。如果能够方便地说“在所有地方都使用C++11”,那就好了。 - Marc Glisse
5
似乎 CXX_STANDARD 11GLOBAL 属性范围不兼容,这真的很糟糕。 - abergmeier
12
为了在当前的CMake范围内定义它,你可以使用set(CMAKE_CXX_STANDARD 11) - typ1232
6
我仍然对这种方法有一些问题,因为它添加了“-std=gnu++11”并启用了非标准扩展。我只想要“std=c++11”,如果我试图使用这些非标准扩展中的任何一个,就会出现错误,以便首先考虑可移植性。 - AliciaBytes
9
@RaphaelMiedl 我问了这个问题并得到了答案。请查看链接。基本上,您需要使用SET(CMAKE_CXX_EXTENSIONS OFF)。 - doc07b5
显示剩余6条评论

40

目前,CMake没有方便的形式来支持C++11。理想情况下,您可以像这样指定C++11项目:

project(foo CXX11)

在你的CMakeLists.txt开头加入以下内容。但是CXX11项目类型目前还不存在。在那之前,你可以使用两阶段的技术:

  1. 确定编译器类型和版本。
  2. 相应地调整构建标志。

例如,这是我用来支持Clang和GCC的C++11的方法:

# Initialize CXXFLAGS.
set(CMAKE_CXX_FLAGS                "-Wall -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG          "-O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL     "-Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE        "-O4 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")

# Compiler-specific C++11 activation.
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
    execute_process(
        COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
    if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7))
        message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.")
    endif ()
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
else ()
    message(FATAL_ERROR "Your C++ compiler does not support C++11.")
endif ()

现在距离您的回答已经过去了六个多月,您知道有任何更新吗?这个问题还没有解决似乎很奇怪。 - Jack Poulson
2
很遗憾,我仍然不知道任何更好的解决方案。也许在CMake邮件列表上提出这个问题会有所帮助。 - mavam
4
你好Jack和Matthias, 我知道这条留言有些晚了,但是看起来CMake终于添加了对C++11的支持: http://public.kitware.com/Bug/view.php?id=13842 我的理解是这将成为CMake 3.1的一部分,计划在11月初发布。 - ryan
1
很酷,一旦这变得更加流行,我会更新答案。看起来现在可以通过 set_property(TARGET tgt PROPERTY CXX_STANDARD 11) 将C++版本与CMake目标关联起来。 - mavam
@LucasB,没错,但是Eric已经提供了一个更好的答案,OP现在可能应该接受它。 - mavam
说实话,现在依赖于CMake 3.1可能还不是一个好主意。例如,Ubuntu 14.04 LTS并没有提供CMake 3.x版本。 - ypnos

9

在撰写本文(GCC 4.8之前)时,检测C++11标志并添加它们可能不是一个好主意。这是因为更改标准(至少对于GCC来说)会破坏ABI兼容性,可能导致链接错误。

因此,在项目的初始CMake配置期间应该明确指定使用C++11标准的编译器设置,例如:

CXX='g++ -std=c++11' cmake /path/to/source

也就是说,使用-std=c++11应该像单独的编译器一样处理,不应在项目中混合或更改。


现在我们已经过了GCC 4.8,情况还是一样吗? - Jack Poulson
基本上是这样的。或者需要添加编译器定义等。有关GCC 5的更多信息,请参见此处:https://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/ - Matt McCormick

8

使用:

include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
    message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER} has no C++11 support.")
endif()

这段内容来自于 在 CMake 中启用 C++11(C++0x) 的文章,稍作修改后得到了现在的版本。

4
你好!很遗憾,你的解决方案只适用于支持指定标志以设置C++标准版本的编译器。对于支持C++11但不支持这些标志的编译器,你的解决方案将会给出错误的结果。例如,MS VS 2015将无法通过。 - Innokentiy Alaytsev

6

如果您使用的是CMake 3.8或更高版本,您可以使用cxx_std_11功能,该功能要求使用C++11或更高版本

target_compile_features(Hello PUBLIC cxx_std_11)

在CMake 3.1及更高版本中,完成此操作的正确简单方法是为给定目标使用 CXX_STANDARD 属性。例如,对于使用 auto 的简单示例(命名为 main.cpp):

#include <iostream>

int main() {
    auto num = 10;
    std::cout << num << std::endl;
    return 0;
}

以下的 CMakeLists.txt 将启用 C++11 支持:
cmake_minimum_required(VERSION 3.3)
project(Hello CXX)

set(SOURCE_FILES main.cpp)
add_executable(Hello ${SOURCE_FILES})

set_property(TARGET Hello PROPERTY
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED ON
)

这将添加任何必要的标志,比如-std=c++11。注意CXX_STANDARD_REQUIRED属性将防止标准降级到较早版本。


另一种适当但不太简单的方法是指定你使用的CMAKE_CXX_KNOWN_FEATURES,如cxx_auto_type

cmake_minimum_required(VERSION 3.3)
project(Hello CXX)

set(SOURCE_FILES main.cpp)
add_executable(Hello ${SOURCE_FILES})
target_compile_features(Hello PRIVATE cxx_auto_type)

* 我尚未在CMake 3.1上尝试过这个方法,但已经验证了它可以在CMake 3.3上工作。根据3.1版本的文档,这应该是有效的。


0

Erik Sjölund答案原则更好地应用:

对于CMake 3.3及更高版本,请使用:

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
project(foo CXX)
if("cxx_std_11" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
  # Do something assuming C++11 can be used
endif()

不需要使用 foreach 等语法。

注意:自然地,您可以使用 C++17、C++20 和非标准版本的特性来完成此操作。


0

我们编写了一个CMake模块,用于检测和启用C++11支持,您可以在此处找到:
https://github.com/NitroShare/CXX11-CMake-Macros

它仍然是一个正在进行中的工作,但我们正在将其用于针对Windows/Linux/Mac的许多Qt项目。目前仅支持MSVC ++,GCC和Clang。

示例:

include(CXX11)

check_for_cxx11_compiler(CXX11_COMPILER)

# If a C++11 compiler is available, then set the appropriate flags
if(CXX11_COMPILER)
    enable_cxx11()
endif()

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