Skip to content
Snippets Groups Projects
Commit c293cf77 authored by Steffen Müthing's avatar Steffen Müthing Committed by Steffen Müthing
Browse files

Merge branch 'feature/cmake-rewrite-c++-standard-detection' into 'master'


[CMake] Rewrite C++ standard detection

This is a complete rewrite of the existing C++ standard version test
that tries the various `-std=c++xy` flags and tries to figure out which
version of the standard to enable.

The existing version has a few problems:

- it is not really extensible to newer standards, those will just lead
to a deeper and deeper nesting of `if` statements.

- More importantly, there is no way to say: I want C++11, but not C++14
  (or, in the future, I want C++14, but not C++17). Being able to do so
  is important for testing and compatibility reason.

- There is no easy way for downstream modules to require a newer version
  of the standard than the core modules.

The new version of the test separates the logic from the data (versions,
flag names, compiler test sources) and just iterates over lists of that
data, in descending order to make sure the newest available standard
gets picked. The numerical value of that standard is stored in the
variable `CXX_MAX_SUPPORTED_STANDARD` and the test stops. If the test
fails to find a working combination, it issues a warning to the user and
only records support for C++03.

The test can be influenced by two CMake variables:

- `DISABLE_CXX_VERSION_CHECK` already existed in the old version of the
  test. It completely disables all testing and requires the user to
  manually set `CXX_MAX_SUPPORTED_STANDARD` to the correct value.
  Moreover, any required compiler command line switches have to be
  manually added to `CMAKE_CXX_FLAGS`.

- `CXX_MAX_STANDARD` defines the maximum version of the standard that
  the build system will try to enable. With this new switch, it becomes
  possible to limit the compiler to an older version of the standard
  than what it could theoretically support. For now, this defaults to
  C++14.

In order to allow module authors to easily state their minimum version
requirements, there is a new CMake function
`dune_require_cxx_standard()` that any Dune module can call to require
support for at least a given C++ standard:

```
dune_require_cxx_standard(MODULE "dune-functions" VERSION 14)
```

If the compiler doesn't meet the requirements, the function will report
the required and the actually available standard versions and abort with
a fatal error.

Moreover, it knows about CXX_MAX_STANDARD and will tell the user if the
value of that variable is below the requirements. This avoids desperate
users that have a shiny beta of GCC 6 with C++17 support wondering why
their own module using shiny C++17 concepts stubbornly fails to build...

This fixes #16, and a backport to 2.4 will also be a fix for #15.

See merge request !46
(cherry picked from commit 31252385)

Signed-off-by: default avatarSteffen Müthing <muething@dune-project.org>
parent 5efaf9f4
No related branches found
No related tags found
No related merge requests found
......@@ -13,45 +13,170 @@
# HAVE_NOEXCEPT_SPECIFIER True if nonexcept specifier is supported.
include(CMakePushCheckState)
cmake_push_check_state()
# test for C++11 flags
include(TestCXXAcceptsFlag)
if(NOT DISABLE_CXX11CHECK)
# try to use compiler flag -std=c++11
check_cxx_accepts_flag("-std=c++11" CXX_FLAG_CXX11)
endif(NOT DISABLE_CXX11CHECK)
if(CXX_FLAG_CXX11)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11 ")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -std=c++11 ")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11 ")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -std=c++11 ")
set(CXX_STD11_FLAGS "-std=c++11")
else()
if(NOT DISABLE_CXX11CHECK)
# try to use compiler flag -std=c++0x for older compilers
check_cxx_accepts_flag("-std=c++0x" CXX_FLAG_CXX0X)
if(NOT CXX_FLAG_CXX0X)
MESSAGE(FATAL_ERROR "Your compiler does not seem to support C++11. If it does, please add any required flags to your CXXFLAGS and run dunecontrol with --disable-cxx11check")
endif(NOT CXX_FLAG_CXX0X)
endif(NOT DISABLE_CXX11CHECK)
if(CXX_FLAG_CXX0X)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++0x" )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x ")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x ")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -std=c++0x ")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++0x ")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -std=c++0x ")
set(CXX_STD11_FLAGS "-std=c++0x")
endif(CXX_FLAG_CXX0X)
endif(CXX_FLAG_CXX11)
# perform tests
include(CheckCXXCompilerFlag)
include(CheckCXXSourceCompiles)
# C++ standard versions that this test knows about
set(CXX_VERSIONS 17 14 11)
# Compile tests for the different standard revisions; these test both the compiler
# and the associated library to avoid problems like using a C++14 user-installed
# compiler together with a non C++14-compliant stdlib from the system compiler.
# we need to escape semicolons in the tests to be able to stick them into a list
string(REPLACE ";" "\;" cxx_17_test
"
#include <type_traits>
// nested namespaces are a C++17 compiler feature
namespace A::B {
using T = int;
}
int main() {
// std::void_t is a C++17 library feature
return not std::is_same<void,std::void_t<A::B::T> >{};
}
")
string(REPLACE ";" "\;" cxx_14_test
"
#include <memory>
int main() {
// lambdas with auto parameters are C++14 - so this checks the compiler
auto l = [](auto x) { return x; };
// std::make_unique() is a C++14 library feature - this checks whether the
// compiler uses a C++14 compliant library.
auto v = std::make_unique<int>(l(0));
return *v;
}
")
string(REPLACE ";" "\;" cxx_11_test
"
#include <memory>
int main() {
// this checks both the compiler (by using auto) and the library (by using
// std::make_shared() for C++11 compliance at GCC 4.4 level.
auto v = std::make_shared<int>(0);
return *v;
}
")
# build a list out of the pre-escaped tests
set(CXX_VERSIONS_TEST "${cxx_17_test}" "${cxx_14_test}" "${cxx_11_test}")
# these are appended to "-std=c++" and tried in this order
# note the escaped semicolons; that's necessary to create a nested list
set(CXX_VERSIONS_FLAGS "17\;1z" "14\;1y" "11\;0x")
# by default, we enable C++14 for now, but not C++17
# The user can override this choice by explicitly setting this variable
set(CXX_MAX_STANDARD 14 CACHE STRING "highest version of the C++ standard to enable")
function(dune_require_cxx_standard)
include(CMakeParseArguments)
cmake_parse_arguments("" "" "MODULE;VERSION" "" ${ARGN})
if(_UNPARSED_ARGUMENTS)
message(WARNING "Unknown arguments in call to dune_require_cxx_standard(${ARGN})")
endif()
if(${_VERSION} GREATER ${CXX_MAX_SUPPORTED_STANDARD})
if(NOT _MODULE)
set(_MODULE "This module")
endif()
if(${_VERSION} GREATER ${CXX_MAX_STANDARD})
message(FATAL_ERROR "\
${_MODULE} requires compiler support for C++${_VERSION}, but the build system is currently \
set up to not allow newer language standards than C++${CXX_MAX_STANDARD}. Try setting the \
CMake variable CXX_MAX_STANDARD to at least ${_VERSION}."
)
else()
message(FATAL_ERROR "
${_MODULE} requires compiler support for C++${_VERSION}, but your compiler only supports \
C++${CXX_MAX_SUPPORTED_STANDARD}."
)
endif()
endif()
endfunction()
# try to enable all of the C++ standards that we know about, in descending order
if(NOT DISABLE_CXX_VERSION_CHECK)
foreach(version ${CXX_VERSIONS})
# skip versions that are newer than allowed
if(NOT(version GREATER CXX_MAX_STANDARD))
list(FIND CXX_VERSIONS ${version} version_index)
list(GET CXX_VERSIONS_FLAGS ${version_index} version_flags)
# First try whether the compiler accepts one of the command line flags for this standard
foreach(flag ${version_flags})
set(cxx_std_flag_works "cxx_std_flag_${flag}")
check_cxx_compiler_flag("-std=c++${flag}" ${cxx_std_flag_works})
if(${cxx_std_flag_works})
set(cxx_std_flag "-std=c++${flag}")
break()
endif()
endforeach()
# and if it did, run the compile test
if(cxx_std_flag)
list(GET CXX_VERSIONS_TEST ${version_index} version_test)
set(test_compiler_output "compiler_supports_cxx${version}")
cmake_push_check_state()
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${cxx_std_flag}")
check_cxx_source_compiles("${version_test}" ${test_compiler_output})
cmake_pop_check_state()
if(${test_compiler_output})
set(CXX_MAX_SUPPORTED_STANDARD ${version})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${cxx_std_flag} ")
break()
else()
# Wipe the variable, as this version of the standard doesn't seem to work
unset(cxx_std_flag)
endif()
endif()
endif()
endforeach()
if(NOT DEFINED CXX_MAX_SUPPORTED_STANDARD)
# Let's just assume every compiler at least claims C++03 compliance by now
message(WARNING "\
Unable to determine C++ standard support for your compiler, falling back to C++03. \
If you know that your compiler supports a newer version of the standard, please set the CMake \
variable DISABLE_CXX_VERSION_CHECK to true and the CMake variable CXX_MAX_SUPPORTED_STANDARD \
to the highest version of the standard supported by your compiler (e.g. 14). If your compiler \
needs custom flags to switch to that standard version, you have to manually add them to \
CMAKE_CXX_FLAGS."
)
set(CXX_MAX_SUPPORTED_STANDARD 3)
endif()
endif()
# make sure we have at least C++11
dune_require_cxx_standard(MODULE "DUNE" VERSION 11)
# perform tests
# nullptr
check_cxx_source_compiles("
int main(void)
......@@ -292,8 +417,6 @@ check_cxx_source_compiles("
)
cmake_pop_check_state()
# find the threading library
# Use a copy FindThreads from CMake 3.1 due to its support of pthread
if(NOT DEFINED THREADS_PREFER_PTHREAD_FLAG)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment