88

Is there a way to let CMake detect automatically if a compiler supports C++11 or not?

As it would be nice to inform the users during the CMake run that the code will not compile as the compiler does not support C++11. At the moment I set the C++11 flags. However, if a compiler does not support it the user gets compile errors instead of an error during the CMake run.

Perfect would be something that works like find_package(). However, I have not found any module or function which provides the functionality needed.

Additional it would be nice to have the feature to detect if the compiler needs the flags std=c++0x or std=c++11.

Is there something available or do I need to develop this on my own?

Below is some code I use so far, however it works only with GNU'c GCC compilers. It would be nice if there would be a more general solution.

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)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tune2fs
  • 7,605
  • 5
  • 41
  • 57
  • Why use `add_definitions` command instead of setting `CMAKE_CXX_FLAGS` to set compiler options? – Li Dong Mar 04 '14 at 03:20

7 Answers7

110

If you have CMake version 3.1.0 or later you can detect what C++ features your C++ compiler supports

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

But normally you don't need to use the CMake variable CMAKE_CXX_COMPILE_FEATURES in your CMake scripts. Instead there are two ways of how to tell CMake under which C++ standard your C++ files should be compiled, either by specifying the C++ standard explicitly or by specifying the required C++ features and let CMake induce the C++ standard. CMake will make sure the C++ compiler is invoked with the correct command line flags (e.g. -std=c++11).

  1. Specifying the C++ standard explicitly

You could specify the C++ standard explicitly, by setting the CMake properties CXX_STANDARD and CXX_STANDARD_REQUIRED for your CMake target.

$ 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. Specifying the required C++ features and let CMake induce the C++ standard

You could use the CMake command target_compile_features to specify the C++ features that are made use of in your CMake target. From this list CMake will induce the C++ standard to be used. The CMake global property CMAKE_CXX_KNOWN_FEATURES lists the C++ features you can choose from.

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

For example, this C++ program with the filename main.cc makes use of the C++11 features: cxx_strong_enums, cxx_constexpr, cxx_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;
}

This CMakeLists.txt file would build it

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})
              
starball
  • 20,030
  • 7
  • 43
  • 238
Erik Sjölund
  • 10,690
  • 7
  • 46
  • 74
  • Thanks @MarcGlisse, I have now updated my answer. Instead of **FindCXXFeatures** it now makes use of **target_compile_features**. – Erik Sjölund Apr 12 '14 at 05:50
  • 12
    It is a bit painful that this has to be done per target. It would be convenient to say "just use C++11 everywhere" and be done with it. – Marc Glisse Jun 10 '15 at 12:49
  • It seems to me that the first way is always better since you don't have to remember and look up the cxx11 feature you used in your program. – Nothing More Jun 11 '15 at 09:38
  • 5
    Seems like `CXX_STANDARD 11` does not work with `GLOBAL` property scope, which totally sucks. – abergmeier Aug 20 '15 at 13:28
  • 12
    @MarcGlisse @abergmeier To define it in the current CMake scope, you can `set(CMAKE_CXX_STANDARD 11)` – typ1232 Sep 23 '15 at 23:06
  • I don't know why, but `CMAKE_CXX_STANDARD` overwrites `CXX_STANDARD`. I have to set `set(CMAKE_CXX_STANDARD 14)` directly (as @typ1232 already mentioned). Why is it? – patryk.beza Oct 15 '15 at 22:53
  • 6
    I still got some problems with this approach since it adds `-std=gnu++11` and enables non-standard extensions. I'd really only like `std=c++11` and get an error if I try to use one of these non standard extensions, portability first. – AliciaBytes Jan 27 '16 at 11:55
  • 2
    How to make a feature set default for all further targets? – Sergei Krivonos Jan 30 '16 at 16:58
  • 9
    @RaphaelMiedl I asked this question and got an answer. see [link](http://stackoverflow.com/q/38132793/1695375). Basically you use SET( CMAKE_CXX_EXTENSIONS OFF ). – doc07b5 Jul 01 '16 at 02:21
  • For others coming across this answer, there's a full article that goes through this whole area [here](https://crascit.com/2015/03/28/enabling-cxx11-in-cmake/), including discussion of choosing between the two approaches outlined in the above answer and handling of compiler extensions. Disclaimer: I wrote the linked article. – Craig Scott Oct 30 '16 at 06:23
  • Be aware that `CXX_STANDARD` does **not** work on MSVC, so basically you have to fall back to `target_compile_features` if you want something that works cross-platform. – Ela782 Dec 09 '16 at 20:20
40

At this point, CMake does not have a convenient form to support C++11. Ideally, you would specify a C++11 project like this:

project(foo CXX11)

at the beginning of your CMakeLists.txt. But the CXX11 project type does not exist (yet). Until then, you may use a two-staged technique:

  1. Determine the compiler type and version
  2. Adjust build flags accordingly.

For example, this is what I use to support C++11 with Clang and GCC:

# 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 ()
mavam
  • 12,242
  • 10
  • 53
  • 87
  • It has now been more than six months since your answer, do you know of any updates? It seems strange that this issue isn't resolved yet. – Jack Poulson Jan 31 '13 at 18:07
  • 2
    Unfortunately I am still not aware of any better solution. Maybe it helps to raise this concern on the CMake mailing list. – mavam Feb 01 '13 at 02:31
  • 4
    Hi Jack and Matthias, I know this comment is very late, but it looks like some C++11 support was finally added to CMake: http://public.kitware.com/Bug/view.php?id=13842 My understanding is that this will be a part of CMake 3.1, which is scheduled for release in the beginning of November. – ryan Oct 13 '14 at 15:09
  • 1
    Cool, once this becomes more mainstream I'll update the answer. It looks like one can now associate the C++ version with a CMake target via `set_property(TARGET tgt PROPERTY CXX_STANDARD 11)`. – mavam Oct 17 '14 at 21:27
  • @LucasB, true that, but Eric already provides a much nicer answer, which the OP should probably accept at this point. – mavam Nov 01 '15 at 16:26
  • Honestly, depending on CMake 3.1 might not be a good idea yet. For example, Ubuntu 14.04 LTS does not provide CMake 3.x for example. – ypnos Feb 20 '16 at 23:23
9

At the time of this writing (pre-GCC 4.8), it may not be a good idea to detect C++11 flags and add them. This is because changing the standard (at least for GCC) breaks ABI compatibility, which can result in link errors.

Therefore, the use of the C++11 standard should explicitly specified with the compiler setting during the initial CMake configuration of the project, e.g.

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

That is, the use of -std=c++11 should be treated like a separate compiler, which should not be mixed or changed in a project.

Community
  • 1
  • 1
Matt McCormick
  • 314
  • 2
  • 3
8

Use:

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

This is from Enabling C++11 (C++0x) in CMake with minor changes.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nkout
  • 491
  • 4
  • 11
  • 4
    Hello! Unfortunately, your solution only works for compilers that support specified flags for setting C++ standard version. In case of a compiler that supports C++11 but does not support those flags, your solution will give a false negative. For example, MS VS 2015 will not pass. – Innokentiy Alaytsev Feb 13 '18 at 22:29
6

If you are using CMake 3.8 or newer, you can use the feature cxx_std_11, which requests C++11 or above:

target_compile_features(Hello PUBLIC cxx_std_11)

In CMake 3.1* and newer, the proper, simple way to do this is to use the CXX_STANDARD property for a given target. For example, given this simple example using auto (named main.cpp):

#include <iostream>

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

The following CMakeLists.txt will enable C++11 support:

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
)

This will add any flags necessary such as -std=c++11. Note that the CXX_STANDARD_REQUIRED property will prevent the standard from decaying to an earlier version.


Another proper but not as simple way to specify the CMAKE_CXX_KNOWN_FEATURES that you use, such as 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)

* I have not tried this on CMake 3.1, but have verified it works in CMake 3.3. The documentation for 3.1 does document this so it should work.

Levi Morrison
  • 19,116
  • 7
  • 65
  • 85
0

We've written a CMake module for detecting and enabling C++11 support which you can find here:
https://github.com/NitroShare/CXX11-CMake-Macros

It's still a work-in-progress, but we are using it for a number of Qt projects that target Windows/Linux/Mac. Currently only MSVC++, GCC, and Clang are supported.

Example:

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()
Nathan Osman
  • 71,149
  • 71
  • 256
  • 361
0

Putting the principle of Erik Sjölund's answer to better effect:

For CMake versions 3.3 and later, use:

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

No need to use foreach and such.

Note: Naturally, you can do this with C++17, C++20 and non-standard-version features.

einpoklum
  • 118,144
  • 57
  • 340
  • 684