Specifying a minimum required C++ standard - cmake

According to the documentation, setting CXX_STANDARD_REQUIRED to ON prohibits the build system from using older C++ standards then the one requested by CXX_STANDARD.
But this leads to "interesting" problems when combined with the WriteCompilerDetectionHeader module. e.g. when offering some form of "forward compatibility":
set(CMAKE_CXX_STANDARD 11) and testing for cxx_relaxed_constexpr, then conditionally defining a macro that expands to constexpr leads to compilation errors on GCC 6.3.0 as the requirements of CXX_STANDARD and the usage of the macro are conflicting (the feature header using the actual feature set of the compiler, not the one offered by the compiler when requiring C++11).
Is there any way to specifying a required minimum C++ standard, without prohibiting more recent standards (e.g. C++ 11 or later)? The only workaround I can currently think about would be to not set CXX_STANDARD and CXX_STANDARD_REQUIRED at all and just "informally" require a minimum version of C++...

No, there is no way to do this easily. While you could extend CMake to continuously compile objects up-to (or down-to) a specific standard, this would take quite a lot of effort. There ways to possibly do what you want depending on your circumstances.
Use non-conflicting macros. Using your example, use #define MYCONSTEXPR ... instead of #define constexpr ...
Use larger conditional blocks based on the standard
Is your project mostly one standard, but have a few files that need a different one? use CMake's set_source_files_properties() to set a new CXX_STANDARD or COMPILE_FLAGS.

Related

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

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.

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?

How to get cmake to find size of type in third-party header mpi.h?

I am working on a free-software project which involves high performance computing and the MPI library.
In my code, I need to know the size of the MPI_Offset type, which is defined in mpi.h.
Normally such projects would be build using autotools and this problem would be easily solved. But for my sins, I am working with a CMake build and I can't find any way to perform this simple task. But there must be a way to do - it is commonly done on autotools projects, so I assume it is also possible in CMake.
When I use:
check_type_size("MPI_Offset" SIZEOF_MPI_OFFSET)
It fails, because mpi.h is not included in the generated C code.
Is there a way to tell check_type_size() to include mpi.h?
This is done via CMAKE_EXTRA_INCLUDE_FILES:
INCLUDE (CheckTypeSize)
find_package(MPI)
include_directories(SYSTEM ${MPI_INCLUDE_PATH})
SET(CMAKE_EXTRA_INCLUDE_FILES "mpi.h")
check_type_size("MPI_Offset" SIZEOF_MPI_OFFSET)
SET(CMAKE_EXTRA_INCLUDE_FILES)
It may be more common to write platform checks with autotools, so here is some more information on how to write platform checks with CMake.
On a personal note, while CMake is certainly not the most pleasant exercise, for me autotools is reserved for the capital sins. It is really hard to me to defend CMake, but in this instance, it is even documented. Naturally, setting a separate "variable" that you even have to reset after the fact, instead of just passing it as a parameter, is clearly conforming to the surprising "design principles" of CMake.

cmake LibFindMacros set library minimum version number

In Cmake, when using LibFindMacros, is there a way to tell cmake that a library needs to have a minimum version number? The information I've found on http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries doesn't say anything about that.
No... No integrated version support in finding libraries with cmake.
My guess is that in most cases each library has its own way of defining its version and therefore it makes it a daunting task to find out what that version really is. On a Linux system, you could try to query the system with a tool such as dpkg or rpm, but those would only give you the version of the installed library, not the one you're about to compile against.
There is an issue talking about that here:
http://public.kitware.com/Bug/view.php?id=8396
Now, the macros you mentioned have a function to extract a version assuming there is #define <version> <#.#.#> or something of the sort in a header file for that library.
https://github.com/Tronic/cmake-modules/blob/master/LibFindMacros.cmake
# Extracts a version #define from a version.h file, output stored to <PREFIX>_VERSION.
# Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR)
# Fourth argument "QUIET" may be used for silently testing different define names.
# This function does nothing if the version variable is already defined.
function (libfind_version_header PREFIX VERSION_H DEFINE_NAME)
...
So you use this macro, retrieve a #define from a .h file and then compare that value saved in ${PREFIX}_VERSION in your own way. In my libraries, I often have a set of 3 or 4 #define with the major, minor, and release version numbers. That makes it easy to compare. When you get all those numbers together, such as 3.2.4.1, it makes it harder to compare without specialized code... (again, that makes it really complicated for cmake to support a version comparison scheme.)
Note that the macro expects ${PREFIX}_INCLUDE_DIR to be set. It looks like libfind_pkg_detect() sets that variable so you need to first detect the location of the library (headers) and then you can gather the version in a header.
Update:
Note that there are specialized compare operators you can use to compare two version strings against each other:
if(${PREFIX}_VERSION VERSION_EQUAL 1.2.9)
The only problem is that it's going to be a cmake version comparison algorithm which may not match the corresponding project algorithm. That means it's limited to just numbers separated by periods. I'm not too sure when these operators were added to cmake.

Does Ada have a preprocessor?

To support multiple platforms in C/C++, one would use the preprocessor to enable conditional compiles. E.g.,
#ifdef _WIN32
#include <windows.h>
#endif
How can you do this in Ada? Does Ada have a preprocessor?
The answer to your question is no, Ada does not have a pre-processor that is built into the language. That means each compiler may or may not have one and there is not "uniform" syntax for pre-processing and things like conditional compilation. This was intentional: it's considered "harmful" to the Ada ethos.
There are almost always ways around a lack of a preprocessor but often times the solution can be a little cumbersome. For example, you can declare the platform specific functions as 'separate' and then use build-tools to compile the correct one (either a project system, using pragma body replacement, or a very simple directory system... put all the windows files in /windows/ and all the linux files in /linux/ and include the appropriate directory for the platform).
All that being said, GNAT realized that sometimes you need a preprocessor and has created gnatprep. It should work regardless of the compiler (but you will need to insert it into your build process). Similarly, for simple things (like conditional compilation) you can probably just use the c pre-processor or even roll your own very simple one.
AdaCore provides the gnatprep preprocessor, which is specialized for Ada. They state that gnatprep "does not depend on any special GNAT features", so it sounds as though it should work with non-GNAT Ada compilers. Their User Guide also provides some conditional compilation advice.
I have been on a project where m4 was used as well, with the Ada spec and body files suffixed as ".m4s" and ".m4b", respectively.
My preference is really to avoid preprocessing altogether, and just use specialized bodies, setting up CM and the build process to manage them.
No but the CPP preprocessor or m4 can be called on any file on the command line or using a building tool like make or ant. I suggest calling your .ada file something else. I have done this for some time on java files. I call the java file .m4 and use a make rule to create the .java and then build it in the normal way.
I hope that helps.
Yes, it has.
If you are using GNAT compiler, you can use gnatprep for doing the preprocessing, or if you use GNAT Programming Studio you can configure your project file to define some conditional compilation switches like
#if SOMESWITCH then
-- Your code here is executed only if the switch SOMESWITCH is active in your build configuration
#end if;
In this case you can use gnatmake or gprbuild so you don't have to run gnatprep by hand.
That's very useful, for example, when you need to compile the same code for several different OS's using even different cross-compilers.
Some old Ada1983-era compilers have a package called a.app that utilized a #-prefixed subset of Ada (interpreted at build-time) as a preprocessing language for generating Ada (to be then translated to machine code at compile-time). Rational's Verdix Ada Development System (VADS) appears to be the progenitor of a.app among several Ada compilers. Sun Microsystems, for example, derived the Ada SPARCompiler from VADS and thus also had a.app. This is not unlike the use of PL/I as the preprocessor of PL/I, which IBM did.
Chapter 2 is some documentation of what a.app looks like: http://dlc.sun.com/pdf/802-3641/802-3641.pdf
No, it does not.
If you really want one, there are ways to get one (Use C's, use a stand-alone one, etc.) However I'd argue against it. It was a purposeful design decision to not have one. The whole idea of a preprocessor is very un-Ada.
Most of what C's preprocessor is used for can be accomplished in Ada in other more reliable ways. The only major exception is in making minor changes to a source file for cross-platform support. Given how much this gets abused in a typical cross-platform C program, I'm still happy there's no support for it in Ada. Very few C/C++ developers can control themselves enough to keep the changes "minor". The result may work, but is often nearly impossible for a human to read.
The typical Ada way to accomplish this would be to put the different code in different files and use your build system to somehow choose between them at compile time. Make is plenty powerful enough to help you do this.