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


78

是否有一种方法可以让CMake自动检测是否编译器支持C ++ 11?

最好在CMake运行期间通知用户代码将不会编译,因为编译器不支持C ++ 11。目前,我设置了C ++ 11标志。但是,如果编译器不支持它,则用户会在CMake运行期间收到编译错误,而不是错误。

完美的东西就像find_package()。但是,我没有找到任何提供所需功能的模块或功能。

此外,最好具有检测编译器是否需要标志std=c++0x或的功能std=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

Answers:


104

如果您具有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目标设置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
$

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

例如,此文件名为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 2015年

5
似乎CXX_STANDARD 11不适用于GLOBAL属性范围,这完全糟透了。
abergmeier

9
@MarcGlisse @abergmeier要在当前CMake范围内定义它,您可以set(CMAKE_CXX_STANDARD 11)
typ1232 2015年

6
由于此方法添加-std=gnu++11并启用了非标准扩展,因此我仍然遇到一些问题。std=c++11如果我尝试使用这些非标准扩展程序之一(可移植性优先),我真的很想得到一个错误。
user1942027

8
@RaphaelMiedl我问了这个问题并得到了答案。见链接。基本上,您使用SET(CMAKE_CXX_EXTENSIONS OFF)。
doc07b5'7

42

在这一点上,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 ()

距您的答案已经六个月多了,您是否知道任何更新?这个问题尚未解决似乎很奇怪。
杰克·普尔森

2
不幸的是,我仍然没有更好的解决方案。也许有助于在CMake邮件列表上引起这种担忧。
mavam

4
嗨,杰克(Jack)和马蒂亚斯(Matthias),我知道这条评论很晚了,但是看起来终于对CMake添加了一些C ++ 11支持:public.kitware.com/Bug/view.php?id=13842我的理解是,这将是CMake 3.1的一部分,该版本计划于11月初发布。
ryan

1
太酷了,一旦这成为主流,我将更新答案。看来现在可以通过关联C ++版本和CMake目标set_property(TARGET tgt PROPERTY CXX_STANDARD 11)
mavam 2014年

@LucasB,是的,但是Eric已经提供了一个更好的答案,OP在这一点上可能应该接受。
mavam

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的更多信息,请访问: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)并进行了很小的更改。


2
你好!不幸的是,您的解决方案仅适用于支持用于设置C ++标准版本的指定标志的编译器。如果编译器支持C ++ 11但不支持这些标志,则您的解决方案将给出错误的否定结果。例如,MS VS 2015将不会通过。
Innokentiy Alaytsev '18

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文档确实对此进行了记录,因此它应该可以工作。


1

我们已经编写了一个用于检测和启用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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.