How to error out cleanly in CMake if CMAKE_CXX_STANDARD cannot be met? - cmake

CMake provides CMAKE_CXX_STANDARD for specifying the required C++ Standard.
set (CMAKE_CXX_STANDARD 17)
However, if your compiler is old... say gcc4... it will still attempt to compile the sources, and it will of course fail with bizarre error messages, due to missing compiler features.
Is there a clean way to make CMake detect this missing support, and fail in a more obvious way?

From https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD.html :
See the cmake-compile-features(7) manual for information on compile features and a list of supported compilers.
From https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html#manual:cmake-compile-features(7) :
The CMAKE_C_KNOWN_FEATURES, CMAKE_CUDA_KNOWN_FEATURES, and CMAKE_CXX_KNOWN_FEATURES global properties contain all the features known to CMake, regardless of compiler support for the feature. The CMAKE_C_COMPILE_FEATURES, CMAKE_CUDA_COMPILE_FEATURES , and CMAKE_CXX_COMPILE_FEATURES variables contain all features CMake knows are known to the compiler, regardless of language standard or compile flags needed to use them.
From https://cmake.org/cmake/help/latest/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html#prop_gbl:CMAKE_CXX_KNOWN_FEATURES :
cxx_std_17
Compiler mode is at least C++ 17.
Check:
if ("cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES)

Using target_compile_features for more control
As KamilCuk mentioned in their answer, you can check for cxx_std_17.
Additionally, you can set the requirement on a target with:
target_compile_features(my_example_library PUBLIC cxx_std_17)
Source https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html#requiring-language-standards
This is more verbose, but it allows you to specify that only some targets need certain standards. The same syntax can also be used to require only specific features, full list found in CMAKE_CXX_KNOWN_FEATURES.
Simpler option for CMAKE_CXX_STANDARD, set CXX_STANDARD_REQUIRED
However, if you're using CMAKE_CXX_STANDARD, it's much easier to just set CMAKE_CXX_STANDARD_REQUIRED, e.g.:
# will affect any library/target defined after this
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# also recommended, uses -std=c++17 instead of -std=gnu++17
# if you code is compatible with non-GCC compilers too
# set(CMAKE_CXX_EXTENSIONS OFF)
You'll get an error that looks something like:
CMake Error in CMakeLists.txt:
Target "my_example_library" requires the language dialect "C17" (with
compiler extensions). But the current compiler "GNU" does not support
this, or CMake does not know the flags to enable it.

Related

How to detect C++11-... support of a compiler with CMake and react without failing generation

I did read this question: How to detect C++11 support in CMake. It basically tells me that CMake's support for detecting C++ features is very sophisticated when you want to fail generation if a certain compiler does NOT support the feature you are using in your code but also extremely limited when you want to react on this at generation time without failing.
What I would like to be able to do is not to fail but e.g. to define a different set of source files if certain features are not available in order to allow compilation of old c++03 code if needed and use the newer c++11-... ones if possible. Something like this would be great:
set(TARGET MyTarget)
if(CPP_STANDARD_VERSION GREATER_EQUAL 11)
set(SOURCE myModernFile.cpp)
else()
set(SOURCE myLegacyFile.cpp)
endif()
add_executable(${TARGET} ${SOURCE })
if(CPP_STANDARD_VERSION GREATER_EQUAL 11)
# Here failing would be fine if lambdas are not supported!
target_compile_features(${TARGET} PRIVATE cxx_lambdas)
endif()
When following this approach as recommended in the linked question:
foreach(i ${CMAKE_CXX_COMPILE_FEATURES})
message("${i}")
endforeach()
e.g. with Visual Studio 2013 with CMake 3.13.2 I am getting the following output:
cxx_std_98
cxx_std_11
cxx_std_14
cxx_std_17
cxx_std_20
cxx_alias_templates
cxx_auto_type
cxx_contextual_conversions
cxx_decltype
cxx_default_function_template_args
cxx_defaulted_functions
cxx_delegating_constructors
cxx_enum_forward_declarations
cxx_explicit_conversions
cxx_extended_friend_declarations
cxx_extern_templates
cxx_final
cxx_generalized_initializers
cxx_lambdas
cxx_local_type_template_args
cxx_long_long_type
cxx_nullptr
cxx_override
cxx_range_for
cxx_raw_string_literals
cxx_right_angle_brackets
cxx_rvalue_references
cxx_static_assert
cxx_strong_enums
cxx_template_template_parameters
cxx_trailing_return_types
cxx_uniform_initialization
cxx_variadic_macros
cxx_variadic_templates
To me the list makes sense for the specific features, but the general ones with the supported C++ standards apparently cannot be right as VS 2013 does not support C++20. Am I missing something?
Right now I ended up with more or less the same approach as described in the linked question: I check various compiler versions manually and then depending on that I set my CPP_STANDARD_VERSION property, but this seems very fragile to me. Is there really no other way?

"Rosetta" for switching to native CMake CUDA support

I want to switch a CMake-based project of mine to using CMake 3.8 and up's native support for CUDA. I know that, basically, I enable_language(CUDA), then use the regular add_target and add_library instead of the cuda_xxx variants of the same.
However, I'm not sure how to rewrite my code exactly. How do I...:
Set of global CUDA-related CMake variables , e.g. separable compilation, propagation of host flags?
Invoke the CUDA equivalent of CMAKE_CXX_STANDARDand friends?
Add dependencies on CUDA libraries which are not required by default (e.g. NVTX)
... and everything else?
Is there some uniform method to all this, or do I just have to memorize a bunch of new commands?
CMake's native CUDA support works very much in the same way as it does for C and C++ projects. Using CUDA instead of C++ is a similar process to using C instead of C++.
First of all, you should enable CUDA support in your project by adding:
project(myproject LANGUAGES CUDA)
This way, you shouldn't need to run enable_language, and your CMake cache will not be cluttered with the C and C++ compiler configuration generated by the default implied project(myproject LANGUAGES C CXX) you get when you write just project(myproject).
The equivalent of CMAKE_CXX_STANDARD for CUDA is CMAKE_CUDA_STANDARD, much like how there is also CMAKE_C_STANDARD. Additionally, there is CMAKE_CUDA_EXTENSIONS which can be configured to enable / disable compiler specific extensions (i.e. whether to choose -std=c++14 or -std=gnu++14). It's generally the case that if you see CMake variables of the form CMAKE_CXX_..... or CMAKE_C_....., there is an equivalent CMAKE_CUDA_...... And in general, where you see CXX or C in variables and properties, there may be a CUDA equivalent.
set(CMAKE_CUDA_STANDARD 11)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
set(CMAKE_CUDA_EXTENSIONS OFF)
One thing to note however, is that these particular variables have target-specific equivalents in the form of target properties (both for C++ and CUDA):
set_target_properties(
myprojectlibrary PROPERTIES
CUDA_STANDARD 11
CUDA_STANDARD_REQUIRED ON
CUDA_EXTENSIONS OFF)
As for CUDA-specific features like in the old FindCUDA, you should find these as more target properties just like those in the previous example:
set_target_properties(
myprojectlibrary PROPERTIES
CUDA_SEPARABLE_COMPILATION ON
CUDA_RESOLVE_DEVICE_SYMBOLS ON)
Information about these target properties can be found here, searching on the page for "CUDA".
As for linking to third-party libraries, this can be done the same way as it is done for regular C++ projects:
target_link_libraries(myprojectlib nvtx)
Or if your library isn't defined as a first-class CMake target:
target_link_libraries(myprojectlib ${NVTX_LIBRARY})
target_include_directories(myprojectlib PRIVATE ${NVTX_INCLUDE_DIR})
target_compile_definitions(myprojectlib PRIVATE ${NVTX_DEFINES})
Lastly, if there are any specific nvcc flags required to build your project, these can be set as follows:
target_compile_options(myprojectlib PRIVATE --default-stream=per-thread)
Note that the global user-configurable nvcc flags are also available as the variable CMAKE_CUDA_FLAGS.
Also note that you may have to change PRIVATE to PUBLIC in places where you want particular flags / include directories / compile definitions to also be propagated to targets linking to your library, in the case that myprojectlib is a library and not an executable, which may or may not be desired depending on how your project is structured.

What does CMAKE_BUILD_TYPE affect, other than the compiler flag selection?

I know that if if we set -DCMAKE_BUILD_TYPE=Release (or Debug etc.), then the values of CMAKE_C_FLAGS_RELEASE and CMAKE_CXX_FLAGS_RELEASE will be appended to CMAKE_C_FLAGS and CMAKE_C_FLAGS respectively.
But is this the only effect of setting the build type? If not, what are the other effects?
Actually, build type affects on many things. Among them:
generator expressions:
Expression $<$<CONFIG:DEBUG>:XXX> will be expanded to XXX with CMAKE_BUILD_TYPE set to Debug and to nothing otherwise.
Because generator expressions can be used in a number of commands, setting build type affects all commands which uses expressions dependent on build type.
libraries added by target_link_libraries with debug keyword take an effect only in Debug build type.
Similar to optimized keyword.
(Implicitely, this uses generator expressions described above).
Some properies of IMPORTED libraries.
Properties like IMPORTED_LOCATION have config-specific variants, which are choosen dependent on configuration type.
Often IMPORTED libraries are created as a result of find_package() call, so your project may be linked with 3d-party project in configuration-dependent manner.
CONFIGURATION-specific part of install command.
Only those CONFIGURATION <conf> part are applies, which corresponds to active configuration.
Multi-configuration tools doesn't use CMAKE_BUILD_TYPE variable, but they still have a notion of the "build type". That build type is NOT known at configuration stage, when CMake parses CMakeLists.txt, it is set only when performing a build of the project. Nevertheless, this build type "retroactively" affects on all properties described above.
Also, with multi-configuration build tools selected build type is appended to the location of output artifacts, like executables and libraries (see e.g. description of RUNTIME_OUTPUT_DIRECTORY target's property).

Documentation for Cmake -std parameters

I am using Clion which uses Cmake which uses a CMakeLists.txt that looks like this:
cmake_minimum_required(VERSION 3.3)
project(Thesis)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c99")
set(SOURCE_FILES main.cpp graph.h graph.c shared.h shared.c)
add_executable(Thesis ${SOURCE_FILES})
I have been searching and searching for an explanation on -std=c99. The default was -std=c++11 and I changed it because I am writting a C program, by intuition, I didn't see that keyword anywhere, I just guessed it. However, I don't fully understand what this parameter does, and I want to. What other parameters can it take ?
Where is that documentation exactly ?
First of all. If you are writing a C program, name your files .c - not .cpp to be sure that CMake identifies them as C code. This is the reason you see -std=c++11 by default.
You can then use SET(CMAKE_C_FLAGS ...) to add compiler flags even though this isn't the recommended way. You shall never assume that GCC will be called.
As #zaufi said. You can use:
set_property(TARGET Thesis PROPERTY C_STANDARD 99)
https://cmake.org/cmake/help/v3.1/prop_tgt/C_STANDARD.html#prop_tgt:C_STANDARD
The -std flag is a GCC compiler flag that determine which language features GCC shall enable. Read more here:
http://linux.die.net/man/1/gcc
-std=
Determine the language standard.
This option is currently only supported when compiling C or C ++ .
The compiler can accept several base standards, such as c89 or c++98,
and GNU dialects of those standards, such as gnu89 or gnu++98. By
specifying a base standard, the compiler will accept all programs
following that standard and those using GNU extensions that do not
contradict it. For example, -std=c89 turns off certain features of
GCC that are incompatible with ISO C90, such as the "asm" and
"typeof" keywords, but not other GNU extensions that do not have a
meaning in ISO C90, such as omitting the middle term of a "?:"
expression. On the other hand, by specifying a GNU dialect of a
standard, all features the compiler support are enabled, even when
those features change the meaning of the base standard and some
strict-conforming programs may be rejected.

CMake Fortran compiler-dependent flags

I'm using CMake for a moderate-sized Fortran project; sometimes I build it with gfortran, other times with ifort. When I want to do a debug build, the compiler flags are different; I'd like to have CMake automatically check which compiler is being used and set the flags accordingly.
It looks like this answer shows how to do the same thing for different C++ compilers. There's an example of how to check compilers with Fortran, using
if (Fortran_COMPILER_NAME MATCHES "gfortran.*")
However, this fails to invoke the conditional, because CMake has decided to use f95. Of course, f95 happens to alias to gfortran, but CMake doesn't detect that.
What's the right way to do this?
You can use also use CMAKE_Fortran_COMPILER_ID:
if ("${CMAKE_Fortran_COMPILER_ID}" MATCHES "Intel")
# something
elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
# something else
endif
Best way is to read file CMakeDetermineFortranCompiler.cmake and related files referenced from it.
Rather than trying to special case for different compilers you should actually test that your compiler supports the flags you want to set using check_fortran_compiler_flag like so:
include(CheckFortranCompilerFlag)
check_fortran_compiler_flag("-my-flag" _my_flag)
if(_my_flag)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -my-flag")
endif()
This is both safer and (I think) simpler because you don't need the implied knowledge of which compiler (and version) supports which flag.
It's simple. Only indicate the full path of compiler installed e.g.gfortran. the code: cmake -DCMAKE_fortran_PATH=/usr/bin/gfortran