cmake does not override set variable from command line - c++

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")

Related

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".

Setting CMAKE_CXX_STANDARD to various values

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")

CMake check hash (MD5 / SHA256) for downloaded file

I am using CMake add_custom_command In a Util.cmake script to download a couple of files that will later be used in the build process. These files however may change and I would like to add a way to check the hash value of the local file against a provided value (within CMake) to decide if the file needs to be re-downloaded.
Currently, once the file has been downloaded, CMake will not consider re-downloading it, because the file already exists locally.
In the future, I want to provide a MD5 / SHA256 checksum of that file and make sure the local file is the corect one.
Here is what I am trying to do (this is just an concept example):
add_custom_command( OUTPUT ./file.dat
COMMAND wget ${FILE_PATH}
)
if (opt_HASH)
add_custom_command(OUTPUT ${local_HASH}
COMMAND local_HASH=$(sha256sum ./file.dat)
DEPENDS ./file.dat
)
if (NOT ${opt_HASH} STREQUAL ${local_HASH})
# throw ERROR
endif()
endif()
As you can see I only want to detect a mismatch right now and don't even want to auto-download the changed file. The opt_HASH is obviously provided through CMake, but what is important is that this call needs to depend on the file already being downloaded and I seem to be able to do that with a more simpler call to FILE().
PS: If it's somehow easier, I could also use MD5 over SHA256.
cmakes FILE command supports hashing:
https://cmake.org/cmake/help/v3.8/command/file.html
file(SHA256 ./file.dat CHECKSUM_VARIABLE)
should put the hash into the CHECKSUM_VARIABLE
The usage of add_custom_target could be one solution. By default it will be executed always. The following should work on linux:
add_custom_target(UpdateExternalFiles
COMMAND "sha256sum -c file.dat.checksum ./file.dat || wget ${FILE_PATH}"
COMMAND "sha256sum ./file.dat >> file.dat.checksum"
)
First line verifies the checksum and loads the file on differences. The second line updates the checksum.
Note: This snipped assume that file.dat.checksum will be created with the second command.

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.