【问题标题】:How to detect C++11 support of a compiler with CMake如何使用 CMake 检测编译器对 C++11 的支持
【发布时间】:2012-06-14 14:44:22
【问题描述】:

有没有办法让 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 来设置编译器选项?

标签: c++ c++11 cmake


【解决方案1】:

如果您有 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 属性显式指定 C++ 标准 CXX_STANDARDCXX_STANDARD_REQUIRED 用于您的 CMake 目标。

$ 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
$

2。指定所需的 C++ 特性,让 CMake 引入 C++ 标准

您可以使用 CMake 命令target_compile_features 指定在您的 CMake 目标中使用的 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()

例如,这个文件名为 ma​​in.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})

【讨论】:

  • 每个目标都必须这样做有点痛苦。说“在所有地方都使用 C++11”会很方便。
  • 似乎CXX_STANDARD 11 不适用于GLOBAL 属性范围,这太糟糕了。
  • @MarcGlisse @abergmeier 要在当前CMake范围内定义,可以set(CMAKE_CXX_STANDARD 11)
  • 这种方法仍然存在一些问题,因为它添加了-std=gnu++11 并启用了非标准扩展。我真的只喜欢std=c++11,如果我尝试使用这些非标准扩展中的一个,首先会出现可移植性错误。
  • @RaphaelMiedl 我问了这个问题并得到了答案。见link。基本上你使用 SET(CMAKE_CXX_EXTENSIONS OFF)。
【解决方案2】:

此时,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 ()

【讨论】:

  • 距离您的回答已经过去了六个月,您知道有什么更新吗?奇怪的是这个问题还没有解决。
  • 不幸的是,我仍然不知道有什么更好的解决方案。也许它有助于在 CMake 邮件列表中提出这个问题。
  • 嗨 Jack 和 Matthias,我知道这条评论很晚了,但看起来 C++ 11 支持终于添加到了 CMake:public.kitware.com/Bug/view.php?id=13842 我的理解是这将成为CMake 3.1,计划于 11 月初发布。
  • 酷,一旦这成为主流,我会更新答案。看起来现在可以通过set_property(TARGET tgt PROPERTY CXX_STANDARD 11) 将 C++ 版本与 CMake 目标相关联。
  • @LucasB,确实如此,但 Eric 已经提供了一个更好的答案,此时 OP 可能应该接受。
【解决方案3】:

在撰写本文时(pre-GCC 4.8),检测 C++11 标志并添加它们可能不是一个好主意。这是因为更改标准(至少对于 GCC)breaks ABI compatibility,这可能会导致链接错误。

因此,在initial CMake configuration of the project期间应使用编译器设置明确指定C++11标准的使用,例如

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

即-std=c++11的使用应该被当作一个单独的编译器来对待,不应该在一个项目中混用或改变。

【讨论】:

【解决方案4】:

用途:

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

这是来自 Enabling C++11 (C++0x) in CMake 的小改动。

【讨论】:

  • 您好!不幸的是,您的解决方案仅适用于支持指定标志以设置 C++ 标准版本的编译器。如果编译器支持 C++11 但不支持这些标志,您的解决方案将给出假阴性。例如,MS VS 2015 不会通过。
【解决方案5】:

如果您使用的是 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 中有效。 documentation for 3.1 确实记录了这一点,因此它应该可以工作。

【讨论】:

    【解决方案6】:

    我们编写了一个用于检测和启用 C++11 支持的 CMake 模块,您可以在此处找到该模块:
    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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-04
      • 2017-12-15
      • 2014-10-22
      • 2012-12-23
      • 2017-02-24
      • 2013-08-13
      相关资源
      最近更新 更多