How to bump the C++ standard from the CMake command line? - c++

Currently I have a project that needs C++17, therefore in the CMakeLists.txt I have this line pretty early on:
set(CMAKE_CXX_STANDARD 17)
From the command line (cmake) once in a while I want to test that the project also compiles with C++20. (to avoid surprises).
How can I choose to compile with C++20 from command line?
If I do cmake -DCMAKE_CXX_STANDARD=20 then it is later overwritten by the configuration instead of 17 being interpreted as just a minimum requirement.
I could check if the variable is predefined to avoid overwritting but I was looking for a more declative way to specify this.
(I am using cmake around 3.18.)

The solution is to remove that set command and use target properties instead:
# set(CMAKE_CXX_STANDARD 17)
target_compile_features(myexecutable PUBLIC cxx_std_17)
Then, setting -DCMAKE_CXX_STANDARD=20 on the terminal should work again.

Make CMAKE_CXX_STANDARD a cache variable. This allows you to easily specify a default that can be overwritten via the command line. Note that you need to make sure no "normal" CMAKE_CXX_STANDARD variable is available, since this value would take precedence over the cache variable.
set(CMAKE_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for this project")
add_library(...)
Using cmake -D CMAKE_CXX_STANDARD=20 binary_dir should allow you to update the standard now, cmake -U CMAKE_CXX_STANDARD binary_dir should revert back to the default.
Alternatively you could create a custom cache variable. This would make a reuse easier, since it would allow you to overwrite the property, even if someone else sets CMAKE_CXX_STANDARD before using add_subdirectory to add your project.
set(MYPROJECT_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for myproject")
# overwrite possibly preexisting value for this directory and subdirectories
set(CMAKE_CXX_STANDARD ${MYPROJECT_CXX_STANDARD})
add_library(...)

Related

Appending CXX_FLAGS in cmake invocation [duplicate]

This question already has answers here:
Passing compiler options in CMake
(6 answers)
Closed 5 years ago.
I am wondering if it is possible to append to inferred variables when calling cmake. Due either the compiler or the app supplied CMakeLists.txt C++11 support isn't detected by cmake even though it is supported and the application requires it. If I do:
cmake-DCMAKE_CXX_FLAGS='-std=c++11' ../source_dir
The flags get overwritten and I wouldn't like to lose the inferred flags. I could manually run without overwriting and just copy the flags and append c++11 but I think there must be a better solution.
I have found a number of posts on adding c++11 support to CMakeLists.txt but not in cmake call, so I am wondering if this is possible at all. Please let me know.
Yes, this is possible. First, create a variable called MY_FLAGS in the command line with the flags you want.
cmake -DMY_FLAGS='-std=c++11' ../source_dir
Then, in your CMakeLists.txt, do the following:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_FLAGS}")
This will effectively amend your flags to the flags that CMake generates. However, if you want to set the C++ standard to C++11, you can just do the following without directly dealing with flags:
cmake -DCMAKE_CXX_STANDARD=11 -DCMAKE_CXX_STANDARD_REQUIRED=ON ../source_dir
On a side note, make sure that the ../source_dir is after the CMake options, since otherwise CMAKE_CXX_STANDARD_REQUIRED would not work. (Read)
I recently did this, when building a dependency for one of the applications I work to.
There are 2 CMake variables that you can make use of, namely:
-DCMAKE_BUILD_TYPE=Debug/Release
-DCMAKE_CXX_FLAGS_[DEBUG/RELEASE]
When specifying a build type, cmake will add to the compiler flags the flags that are specified in that second variable. So for me, using:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS_RELEASE=-fPIC
did the thing.
In your case, you can try the following approaches:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS_RELEASE=-std=c++11
or
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS_DEBUG=-std=c++11
Of course, this will work if the variables are not overwritten in the script. Try both approaches.
You are probably looking for
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
Or via command line:
cmake -DCMAKE_CXX_STANDARD=11 -DCMAKE_CXX_STANDARD_REQUIRED=ON
That being said, CMAKE_CXX_FLAGS is not overridden by cmake, so your CMakeLists.txt must be doing it somewhere.
The polite way of setting the flags from within a script is to append to the variable, that is:
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} stuff")

CMake: Can subdirectories inherit compile features?

I have a C++ project that consists of a main program (main.cpp), a header that defines an abstract class (algorithm.hpp), and a subdirectory (algorithms/) full of classes that implement the abstract class. I've configured CMake to build the subdirectory as an object library:
# algorithms/CMakeLists.txt
add_library(algorithms_lib OBJECT
algo1.cpp
algo2.cpp
# etc.
)
In my project's root directory, I use this object library as one of the application's sources:
# Top-level CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(MyApp CXX)
add_subdirectory(algorithms)
add_executable(my-app
main.cpp
$<TARGET_OBJECTS:algorithms_lib>
)
My application is written in C++14, so I need to tell CMake to use the correct compile options for that. I don't want to require CMake version 3.8 (since it's not packaged in some recent Linux distros that I want to support), so I can't use the cxx_std_14 compile feature; instead, I've listed a bunch of individual language features that I use:
# Top-level CMakeLists.txt (cont.)
target_compile_features(my-app PUBLIC
cxx_auto_type
cxx_constexpr
cxx_defaulted_functions
# ...14 more lines...
)
The problem is, these features only apply to the top-level my-app target, not the algorithms_lib target, so the sources in the subdirectory don't get compiled as C++14.
I know I could copy the whole big target_compile_features block into algorithms/CMakeLists.txt, but I'd rather not do that — especially since this example is simplified and I actually have nine such subdirectories, each building its own object library. That'd be a lot of duplication of boilerplate code.
Is there a way to set compile features globally for all C++ targets in the project, including subdirectories? Or would it be better to get rid of add_subdirectory and the object library, and just list all the individual subdirectory files in the top-level add_executable command? I'm new to CMake, so I don't know what the "best practices" are for this sort of thing.
It sounds like you want to put the following at the top of your CMakeLists.txt file:
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
These set defaults for the same-named target properties without the leading CMAKE_. The equivalent target properties do the following:
CXX_STANDARD yy sets the desired minimum C++ standard your code wants to use.
CXX_STANDARD_REQUIRED bb says whether CXX_STANDARD is a requirement (bb is YES or some other boolean equivalent) or whether it is just desirable (bb is NO or equivalent).
CXX_EXTENSIONS bb enables (bb = YES) or disables (bb = NO) compiler extensions.
You don't need to specify individual compiler features to achieve what you're ultimately trying to do (enable C++14 for your targets). You can find a full discussion of this whole area here.

How to create a CMake configuration type that inherits from Release

This answer describes how to create a custom configuration type from scratch. How can I make a configuration type that exactly matches the builtin Release, only with some added flags? I'm using this right now:
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;ReleaseWithAssertions" CACHE STRING
"Available build-types: Debug, Release and ReleaseWithAssertions")
set(CMAKE_CXX_FLAGS_RELEASEWITHASSERTIONS "${CMAKE_CXX_FLAGS_RELEASE}
-DENABLE_ASSERTIONS=1")
This seems to do what I want, but I'm only copying the value of CMAKE_CXX_FLAGS_RELEASE, so I'm wondering if there is anything I'm missing that users might expect?
No, not in respect of adding a custom "configuration type that exactly matches the builtin Release". That's more like a feature request for CMake.
Edit: Just have seen that there actually is a "Creating new configurations for MSVC" feature request you could give support.
Here is some background information what's possible and what's not:
There are potentially many configuration specific variables. You could copy those with a script:
get_directory_property(_vars VARIABLES)
foreach(_var IN LISTS _vars)
if (_var MATCHES "_RELEASE$")
string(REPLACE "_RELEASE" "_RELEASEWITHASSERTIONS" _var_new "${_var}")
set(${_var_new} "${${_var}}")
endif()
endforeach()
You need to map imported targets to for your new configuration with setting CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>:
list(APPEND CMAKE_MAP_IMPORTED_CONFIG_RELEASEWITHASSERTIONS "Release" "")
You can't do anything about $<CONFIG:cfg> type generator expressions checking for specific configuration names
You have to check directory/target/source file and configuration specific changes in properties
References
Is Cmake set variable recursive?
What's the CMake syntax to set and use variables?
The only other one you might want would be CMAKE_C_FLAGS_RELEASE in case you're compiling any C files.
See cmake's documentation:
None (CMAKE_C_FLAGS or CMAKE_CXX_FLAGS used)
Debug (CMAKE_C_FLAGS_DEBUG or CMAKE_CXX_FLAGS_DEBUG)
Release (CMAKE_C_FLAGS_RELEASE or CMAKE_CXX_FLAGS_RELEASE)
RelWithDebInfo (CMAKE_C_FLAGS_RELWITHDEBINFO or CMAKE_CXX_FLAGS_RELWITHDEBINFO
MinSizeRel (CMAKE_C_FLAGS_MINSIZEREL or CMAKE_CXX_FLAGS_MINSIZEREL)
From https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#how-can-i-extend-the-build-modes-with-a-custom-made-one-
It seems you would want to do the same for all of these.
CMAKE_CXX_FLAGS_RELEASEWITHASSERTIONS
CMAKE_C_FLAGS_RELEASEWITHASSERTIONS
CMAKE_EXE_LINKER_FLAGS_RELEASEWITHASSERTIONS
CMAKE_SHARED_LINKER_FLAGS_RELEASEWITHASSERTIONS

Forcing C99 in CMake (to use 'for' loop initial declaration)

I've been searching a portable way to force CMake to enable the compiler's C99 features in order to avoid the following gcc error for instance:
error: ‘for’ loop initial declarations are only allowed in C99 mode
for (int s = 1; s <= in_para->StepNumber; s++){
^
I also wouldn't like to check for which compiler and append something like:
set(CMAKE_C_FLAGS "-std=c99") # that would be bad
So I found this post: Enabling C99 in CMake and the associated feature request: 0012300: CMake has no cross-platform way to ask for C99. In this Mantis bug I learned about target_compiler_features and after that I found these SOF answers on it: How to activate C++11 in CMake? and How to detect C++11 support of a compiler with CMake.
So my questions are: this target_compiler_features will provide a way to require a C feature as well as a C++ one? What is the most portable way to achive this by now - I'm currently using CMake 2.8.12.2. The target_compiler_features isn't in CMake's most recent release version (3.0.0). Do you know when it is being released?
After creating a target such as a library or executable, put a line like this in your CMakeLists.txt file:
set_property(TARGET tgt PROPERTY C_STANDARD 99)
where tgt is the name of your target.
I think this was added in CMake 3.1, and the documentation is here:
http://www.cmake.org/cmake/help/latest/prop_tgt/C_STANDARD.html
If you need to support versions of CMake older than 3.1, you can use this macro:
macro(use_c99)
if (CMAKE_VERSION VERSION_LESS "3.1")
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
set (CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}")
endif ()
else ()
set (CMAKE_C_STANDARD 99)
endif ()
endmacro(use_c99)
After putting that macro in your top-level file so it is visible everywhere, you can just write use_c99() at the top of any CMakeLists file that defines a target with C99 code in it.
CMake issue #15943 for clang users targeting macOS
If you are using CMake and clang to target MacOS there is a bug that can cause the CMAKE_C_STANDARD feature to simply not work (not add any compiler flags). Make sure that you do one of the following things:
Use cmake_minimum_required to require CMake 3.0 or later, or
Set policy CMP0025 to NEW with the following code at the top of your CMakeLists.txt file before the project command:
# Fix behavior of CMAKE_C_STANDARD when targeting macOS.
if (POLICY CMP0025)
cmake_policy(SET CMP0025 NEW)
endif ()
As this question keeps getting attention I'm summarizing here what I think are the best options today.
The following command sets C99 as a minimum requirement for target:
target_compile_features(target PUBLIC c_std_99)
I consider this the preferred way, as it is per target and exposes a way to control the visibility through the PUBLIC, INTERFACE and PRIVATE keywords - see the reference. Although the target_compile_features command was introduced on the 3.1 version, c_std_99 requires at least CMake 3.8.
Similar to the above, another way to set C99 as the standard for target is the following:
set_property(TARGET target PROPERTY C_STANDARD 99)
This is available since CMake 3.1. A possible drawback is that it doesn't enforce the standard (see the reference). For this reason setting the C_STANDARD_REQUIRED property may be useful:
set_property(TARGET target PROPERTY C_STANDARD_REQUIRED ON)
The above two properties are defaulted to the values of CMAKE_C_STANDARD and CMAKE_C_STANDARD_REQUIRED respectively.
So a possible way to make C99 default for all targets is:
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED TRUE)
As a side note, expanding on the target_compile_features approach, there may be no need to require some specific language standard if all you care about is some specific feature. For instance by setting:
target_compile_features(target PUBLIC c_variadic_macros)
CMake will take care to pick the proper flags that enforce the availability of variadic macros. However currently there are only a few such features available for the C language - see CMAKE_C_KNOWN_FEATURES for the complete list - and loop initial declarations is not among them.
In libevent, add the following in CMakeLists.txt
set (CMAKE_C_FLAGS "-std=gnu99 ${CMAKE_C_FLAGS}")
Edit CMakeLists
add on line > 2
set (CMAKE_C_STANDARD 99)
then
cmake 3 ..
The C_STANDARD property will allow use to apply the C standard to a specific target, rather than globally. The following will apply C99 to mytarget.
set_property(TARGET mytarget PROPERTY C_STANDARD 99)
From CMake 3.0.2, it is possible to use add_compile_options (https://cmake.org/cmake/help/latest/command/add_compile_options.html#command:add_compile_options), it is one of the most portable way I found to set C standart.
Take care to use this command before to declare target (with add_library or add_executable).
Below is a CMake script example setting C standart to C99:
add_compile_options(-std=c99)
add_executable(my_exe ${SOURCES})
Add the following to your CMakeLists.txt file and run cmake again
set(CMAKE_C_FLAGS "std=c99")

cmake if else with option

I have a problem using option together with if-else statement in cmake.
project(test)
option(TESTE "isso é um teste" OFF)
if(TESTE)
message("true")
else()
message("false")
endif()
add_executable(test main.cpp)
It always displays true even if I put OFF in the options, what am I doing wrong?
That's because the value of the option is stored in the cache (CMakeCache.txt).
If you change the default value in the CMakeLists but the actual value is already stored in the cache, it will just load the value from the cache.
So to test the logic in your CMakeLists, delete the cache each time before re-running CMake.
I had a similar problem and was able to solve it using a slightly different approach.
I needed some compilation flags to be added in case cmake was invoked with an option from the command line (i.e cmake -DUSE_MY_LIB=ON).
If the option was missing in the cmake invocation I wanted to go back to default case which was turning the option off.
I ran into the same issues, where the value for this option was being cached between invocations:
cmake -DUSE_MY_LIB=ON .. #invokes cmake and puts USE_MY_LIB=ON in CMake's cache.
cmake .. #invokes cmake with the cached option ON, instead of OFF
The solution I found was clearing the option from within CMakeLists.txt after the option was used:
option(USE_MY_LIB "Use MY_LIB instead of THEIR_LIB" OFF) #OFF by default
if(USE_MY_LIB)
#add some compilation flags
else()
#add some other compilation flags
endif(USE_MY_LIB)
unset(USE_MY_LIB CACHE) # <---- this is the important!!
Note:
The unset option is available since cmake v3.0.2
Try this, it works for me
unset(USE_MY_LIB CACHE)