Setting CMAKE_CXX_STANDARD to various values - c++

I have a C++ library, that's intended to be usable across several compiler versions and several C++ standards. I have tests for this library - and I need to ensure that these tests pass for this matrix of compilers/versions that I wish to support.
I can provide -DCMAKE_CXX_STANDARD=xx (for 11, 14, 17) on the command line, and this seems to work fine. How do I provide a default value for this field? I would like that, if not provided by the user, that default is 11. It seems that when I do:
$ CXX=/path/to/gcc-7 cmake .. -DCMAKE_CXX_STANDARD=17
# does stuff, CMAKE_CXX_STANDARD is 17
$ CXX=/path/to/gcc-7 cmake ..
# does stuff, CMAKE_CXX_STANDARD is still 17
which is counterintuitive to me. Is there a way to make the latter use the desired default value of 11?
Also, if I just rerun cmake with a different value in the same build directory, would that be enough to trigger a rebuild or would I need a new build directory?

You can use the [option][1] command to let the user choose and give a default value yourself:
option(Barry_CXX_STANDARD "C++ standard" 11)
set(CMAKE_CXX_STANDARD Barry_CXX_STANDARD)
The variable name Barry_CXX_STANDARD indicated that it is specific to your project and should be the same prefix as all project-specific variables are named.
The downside of this approach is, that experienced CMake users would be surprised and set CMAKE_CXX_STANDARD directly.
Another approach is to check whether the variable is set.
if(NOT "${CMAKE_CXX_STANDARD}")
set(CMAKE_CXX_STANDARD 11)
endif()
If CMake provides already a variable, I would use the second approach. If it is only your project-specific variable, the first one is better.
In any case, if you want to change the value you have to delete the CMakeCache.txt in your build directory. Otherwise the caching hides the change.

In CMake world, first invocation of cmake differs from later ones (from the view of setting options in command line):
First invocation:
If option's value is given in command line(-DOPTION=VALUE), this value is used.
If option's value is not given in command line, default value is used.
Later invocations:
If option's value is given in command line(-DOPTION=VALUE), this value is used.
If option's value is not given in command line, previous value is used.
If option is unset (-UOPTION), default value is used.
Thus, having one cmake invocation already done, one may:
modify some of options and leave other unchanged
For doing that, pass modified options' values with -D.
reset some of options and leave other unchanged
For doing that, pass reset options' values with -U.
set some options, and reset others to default values
For doing that, make a clean rebuild by removing CMakeCache.txt from build directory or removing all files from build directory.
For assign default value for an option in the project, use common CACHE variables:
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used")

Related

CMake non-configurable option

In CMake, how can I define an option that is not configurable by the user, but automatically calculated?
I would like to do the following:
if (FOO AND NOT BAR)
option(BOO "Foo and not bar" ON)
endif()
And then I can use BOO in different CMake files:
if (BOO)
# do something
endif()
And also in sources:
#ifdef BOO
// do something
#endif
So BOO behaves like a regular option, but is automatically calculated and not configurable by user running cmake.
EDIT: I now realize that options are not implicitly available in sources, but need to be defined explicitly with target_add_definitions or other means. So now it becomes obvious that the solution is to define BOO as a CMake variable, rather than an option.
Your "non-user-configurable" option sounds like a variable, which you can create like:
if(FOO AND NOT BAR)
set(BOO TRUE)
else()
set(BOO FALSE)
endif()
To use this variable in C++ code, you need to tell CMake to create a C++ file which defines this information. Take a look at CMake's configure_file function. Here's the official documentation, and here's part of the official CMake tutorial which walks through a simple usage of this function. I would type out some example code, but it would be a lot of boilerplate, and the linked documents do the job and should be better at answering your questions.
There is an alternative to configure_file: using target_compile_definitions. The tradeoff to make is between simplicity of implementation and build times. If the value of a compile definitions changes upon reconfiguration, then CMake has to assume that every source file of the target needs to be recompiled, since it has no information about how that definition is being used (ie. target_compile_definitions is easy to implement, but can lead to much slower build times in specific scenarios). When you use configure_file, only the files that include the file that defines those definitions need to be recompiled. There's also potentially an argumen to be made about "readability": if your target gets installed, it's easier for someone reading the installed headers to find the definition versus having to read generated CMake files to find it.
This could be done using a normal variable, but in some scenarios this information will not be available everywhere, e.g. if the subdirectory structure created by add_subdirectory does not ensure the information is required only in the CMakeLists.txt file where the info is introduced or in one of it's descendant directories.
In this case you should be using a cache variable with type internal INTERNAL. This has the added benefit of allowing you to persist the info for use during reconfiguration. You could also use FORCE to overwrite the value of any cache variable.
Example
if (... BOO not yet set properly ...)
set(BOO_VALUE ...)
set(BOO ${BOO_VALUE} CACHE INTERNAL "") # internal implies FORCE
endif()
Note that this won't make the preprocessor define available. You still need to use something like
target_compile_definitions(mytarget PRIVATE "BOO=${BOO}")
You can just use variable with set(BOO <what you want>).
You can find the documentation here: https://cmake.org/cmake/help/latest/command/set.html
But there is no option that will restrict the value of your variable for an internal settings. Only you.

cmake does not override set variable from command line

I am using a variable that I want the user to be able to pass using the command line. However, the variable is not overridden as I was expecting. The reference doc I am using is Cmake Cache
My CMakeLists.txt looks like this
cmake_minimum_required(VERSION 3.23.0)
project(design_principles)
set(PRINCIPLE "solid/single_responsibility" CACHE STRING "Enter the principle to be run" FORCE)
message(${PRINCIPLE})
add_executable(
${PROJECT_NAME}
${PRINCIPLE}.cpp
)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
The command I am passing through the command line is
cmake -S . -B build -DPRINCIPLE=temp
However, it appears that the build is always using the default value from CMakeLists.txt
From the documentation:
Since cache entries are meant to provide user-settable values this does not overwrite existing cache entries by default. Use the FORCE option to overwrite existing entries. [...] If the cache entry does not exist prior to the call or the FORCE option is given then the cache entry will be set to the given value.
The problem is clear: since you have written FORCE, the set() command will always overwrite the cache entry. The solution is to simply get rid of FORCE:
set(PRINCIPLE "solid/single_responsibility"
CACHE STRING "Enter the principle to be run")

cmake set value of variable for first run after command "project"

If in my CMakeLists.txt I have (order #1)
...
set (CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON")
project ("my_project")
...
on first run I get in ccmake
CMAKE_VERBOSE_MAKEFILE *ON
But if I invert the order of set (CMAKE_VERBOSE_MAKEFILE ... and project ... (order #2)
...
project ("my_project")
set (CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON")
...
I get on first run
CMAKE_VERBOSE_MAKEFILE *OFF
Since I have many projects largely sharing configuration, I want to have a common CMakeLsts.txt, and a project-specific CMakeLsts.txt with the minimum project-specific contents, and an include
project ("my_project")
set (COMMON_DIR "${CMAKE_SOURCE_DIR}/../common")
include ("${COMMON_DIR}/CMakeLists.txt")
and setting CMAKE_VERBOSE_MAKEFILE=ON in ${COMMON_DIR}/CMakeLists.txt.
So I seem to be forced to work with order #2.
Under these conditions,
Only having the minimum project-specific contents in the project CMakeLsts.txt (so excluding setting CMAKE_VERBOSE_MAKEFILE=ON).
Setting CMAKE_VERBOSE_MAKEFILE=ON for the first run in the common CMakeLsts.txt.
what are (perhaps more than one) possible ways of getting CMAKE_VERBOSE_MAKEFILE=ON on first run, and then using the cache?
From what I tried, using FORCE is not useful, since if I change the value to something different (OFF in this case), it will revert the value to ON with each run.
I wouldn't want to move setting CMAKE_VERBOSE_MAKEFILE=ON to my project-specific CMakeLists.txt.
Related
Overriding a default option(...) value in CMake from a parent CMakeLists.txt

cmake + clang_tidy - disable checking in directory

I have large project using CMake. I want to add clang_tidy-8 support with following code:
set(BORG_CLANG_TIDY OFF CACHE STRING "If enabled, clang-tidy will be used. If set to 'fix', fixes will be done on source")
set_property(CACHE BORG_CLANG_TIDY PROPERTY STRINGS ON OFF fix)
if(BORG_CLANG_TIDY)
if (BORG_CLANG_TIDY STREQUAL "fix")
set(maybe_fix -fix)
endif()
set(CMAKE_CXX_CLANG_TIDY clang-tidy-8 -extra-arg=-Wno-unknown-warning-option -format-style=file ${maybe_fix} )
endif()
I put proper .clang-tidy in root directory of project (proper = with desired checks). However, there are directories that I don't want clang tidy to check/fix (3rdparty and legacy code that can't be modified because it is brittle). So I tried putting empty .clang-tidy file in those directories (empty = with -checks=-*). This doesn't work because Error: no checks enabled.
I hoped to find some some fake -checks=-*,hello-world-do-nothing-check but nothing presented itself.
Is there other way to disable checks in selected subdirectories (/subtrees)? Those directories are static and may be hardcoded in CMake if needed.
If you want a dummy check that would do nothing there's at least one that's pretty easy to disable by its options: misc-definitions-in-headers
The HeaderFileExtensions option can be used to make the check work with only certain header file suffixes. If you set it to something non-existent line "x" then you have a hello-world-do-nothing-check alternative. Your clang-tidy file would then look something like this:
Checks: '-*,misc-definitions-in-headers'
CheckOptions:
- { key: HeaderFileExtensions, value: "x" }
You can also check https://stackoverflow.com/a/56319752/9874699 and try to adapt the line-filter to filter out files from certain directories.
Is there other way to disable checks in selected subdirectories (/subtrees)?
In CMakeList.txt files contained in those subdirectories, add the following line:
set(CMAKE_CXX_CLANG_TIDY "")
But this is not a good solution: it creates a binding between the build system and a toolchain-specific tool. CMAKE_CXX_CLANG_TIDY should only ever be set via the configuration command (or possibly via a tool-chain file).
Here's what I used in this scenario:
# Disable most checks as this is third-party code
# Have to enable at least one check, so pick a benign one!
#InheritParentConfig: false
Checks: cppcoreguidelines-avoid-goto
I think we can all agree "Go To Statement Considered Harmful".

Difference between cmake variables and cmake arguments

I have been given some source code, along with a CMakeLists.txt file, and told to run cmake by:
cmake ../src -DOPEN_NI_ROOT=/home/karnivaurus/OpenNI
I have also noticed that there is a file called FindOpenNI.cmake, which I believe is used when find_package(OpenNI) is called by cmake.
Therefore, I am guessing that OPEN_NI_ROOT is some kind of variable that is used by cmake for the remainder of setup.
However, I have tried inserting the line set(OPEN_NI_ROOT "/home/karnivaurus/OpenNI") into my CMakeLists.txt file, in an attempt to avoid the need to add it as an argument at the command line. But this does not seem to do the same thing.
Can somebody please explain how these two variable types are different?
The file FindOpenNI.cmake is open source and can be found at:
https://github.com/victorprad/InfiniTAM/blob/master/InfiniTAM/cmake/FindOpenNI.cmake
The issue is this line in FindOpenNI.cmake (link):
set(OPEN_NI_ROOT "/usr/local" CACHE FILEPATH "Root directory of OpenNI2")
This will set OPEN_NI_ROOT unless it's already in the cache. A simple call to:
set(OPEN_NI_ROOT "/home/karnivaurus/OpenNI")
does not set the variable in the cache, so it will be overridden when the line in FindOpenNI.cmake is hit. Using the command line form of setting the variable will set it in the cache, which is why it works just fine.
The easiest way to avoid having to set the command line option is to set the cache explicitly in your own CMakeLists.txt:
set(OPEN_NI_ROOT "/home/karnivaurus/OpenNI" CACHE FILEPATH "Root directory of OpenNI2")
If you're working from a dirty build directory, it's likely this cache variable already exists, so this line would have no effect. In that case, either work from a clean build directory, or set the FORCE option:
set(OPEN_NI_ROOT "/home/karnivaurus/OpenNI" CACHE FILEPATH "Root directory of OpenNI2" FORCE)
This will write over the cached value. Note that this would prevent you from setting the option in the command line in the future, which is why this method isn't preferred. You can find some more information about the mechanics of this here.