Ignoring CMake cached variable - cmake

How can I force CMake to ignore cached value? I want to require the caller to pass variable each time, for example
cmake .. -DSOME_VAR=value
and if I call CMake like
cmake ..
without SOME_VAR I want that this piece of CMake code to fail:
if (NOT DEFINED)
message(FATAL_ERROR " SOME_VAR is missing.")
endif()
It seems that unset(SOME_VAR CACHE) doesn't do what I expect. I actually don't care for cached variables at all, I don't mind if it slower.

You can achieve that by testing for the SOME_VAR before the project statement, i.e. your root CMakeLists.txt should contain a piece of code like this:
#very first lines of a CMakeLists.txt
#note: the test must be done **BEFORE** project statement
if(NOT SOME_VAR)
message(FATAL_ERROR "SOME_VAR must be defined in command line")
endif()
project(project_requiring_some_var_in_cmd_line)
#the rest of your CMakeLists.txt
Also, as already pointed out, note that this is not the natural way of using CMake. It may be beneficial if you explained the problem you are trying to solve.

Related

How can cmake detect misspelled variable names on command line?

My CMakeLists.txt can take variables and values when the user specifies them on the command line in the usual form -Dname=value. E.g.
% cmake -DmyVariable=someValue ..
How can CMakeLists.txt detect variables that aren’t actually relevant, e.g. in case the user mispells them:
% cmake -Dmyxvarble=someValue ..
For example, can CMakeLists.txt process each defined variable on the command line sequentially, thereby spotting misspelled variable names?
I’m running cmake version 3.18.0-rc2. Thanks!
You could query the cache entries of the toplevel dir and match against patterns of expected entries. Note though that this is not easy to maintain, since functionality like find_package relies on cache variables.
set(CACHE_VARIABLE_WHITELIST
MyProject_BINARY_DIR
MyProject_IS_TOP_LEVEL
MyProject_SOURCE_DIR
...
)
get_directory_property(CACHE_VARS DIRECTORY ${CMAKE_SOURCE_DIR} CACHE_VARIABLES)
foreach(CACHE_VAR IN LISTS CACHE_VARS)
# fatal error for any non-advanced cache variable
# not in the whitelist and not starting with CMAKE_
get_property(IS_ADVANCED CACHE ${CACHE_VAR} PROPERTY ADVANCED)
if (NOT IS_ADVANCED AND NOT CACHE_VAR MATCHES "^CMAKE_.*" AND NOT CACHE_VAR IN_LIST CACHE_VARIABLE_WHITELIST)
message(FATAL_ERROR "Unexpected cache variable set: ${CACHE_VAR}")
endif()
endforeach()

CMake: show all modified variables

I would like to have a command or option to list all the modified cache variables of the current build configuration. While cmake -L[AH] is nice, it is also quite overwhelming and doesn't show which are non-default values.
There seems to be a variable property MODIFIED that sounds exactly like what I'm looking for - but the documentation is not very reassuring:
Internal management property. Do not set or get.
This is an internal cache entry property managed by CMake to track interactive user modification of entries. Ignore it.
This question also didn't help: CMAKE: Print out all accessible variables in a script
There are so many ways you could change or initialize variables in CMake (command line, environment variables, script files, etc.) that you won't be able to cover them all.
I just came up with the following script that covers the command line switches. Put the following file in your CMake project's root folder and you get the modified variables printed:
PreLoad.cmake
set(_file "${CMAKE_BINARY_DIR}/UserModifiedVars.txt")
get_directory_property(_vars CACHE_VARIABLES)
list(FIND _vars "CMAKE_BACKWARDS_COMPATIBILITY" _idx)
if (_idx EQUAL -1)
list(REMOVE_ITEM _vars "CMAKE_COMMAND" "CMAKE_CPACK_COMMAND" "CMAKE_CTEST_COMMAND" "CMAKE_ROOT")
file(WRITE "${_file}" "${_vars}")
else()
file(READ "${_file}" _vars)
endif()
foreach(_var IN LISTS _vars)
message(STATUS "User modified ${_var} = ${${_var}}")
endforeach()
This will load before anything else and therefore can relatively easily identify the user modified variables and store them into a file for later reference.
The CMAKE_BACKWARDS_COMPATIBILITY is a cached variable set by CMake at the end of a configuration run and therefor is used here to identify an already configured CMake project.
Reference
CMake: In which Order are Files parsed (Cache, Toolchain, …)?

CMake use foreach and find_library to return full path of libraries

I used a list to store names of libraries, and I would like to use foreach and find_library to find full path of each library. But find_library just returned the path of the first library. I checked this post, but problem still exist. My CMake version is 3.4.3.
SET(VTKLIBS_DIR)
FOREACH(LIB ${VTKLIBS})
SET(FOUND_LIB)
FIND_LIBRARY(FOUND_LIB ${LIB})
LIST(APPEND VTKLIBS_DIR ${FOUND_LIB})
MESSAGE("Lib: ${LIB}")
MESSAGE("Found Lib: ${FOUND_LIB}")
UNSET(FOUND_LIB)
ENDFOREACH(LIB)
Command find_library sets cached variable, but simple form of command unset remove only simple variable's definition.
As noted by the link you provide, you need to store special value FOUND_LIB-NOTFOUND to the variable FOUND_LIB for force find_library to search another library while variable already contains path to the previous library:
FOREACH(LIB ${VTKLIBS})
SET(FOUND_LIB "FOUND_LIB-NOTFOUND")
FIND_LIBRARY(FOUND_LIB ${LIB})
LIST(APPEND VTKLIBS_DIR ${FOUND_LIB})
MESSAGE("Lib: ${LIB}")
MESSAGE("Found Lib: ${FOUND_LIB}")
ENDFOREACH(LIB)
Actually, this is some kind of trick, as cached variable FOUND_LIB isn't changed by simple set command. But when find_library implementation attempts to read cached value of the variable, it actually read value of simple variable with the same name.
Because find_library treats only *-NOTFOUND cached values as "library not found", your trick with assigning empty value to the variable doesn't work.
The better approach, as noted by #arrowd, would be using different names for variable, used in different find_library() call:
FOREACH(LIB ${VTKLIBS})
FIND_LIBRARY(FOUND_LIB_${LIB} ${LIB})
LIST(APPEND VTKLIBS_DIR ${FOUND_LIB_${LIB}})
MESSAGE("Lib: ${LIB}")
MESSAGE("Found Lib: ${FOUND_LIB_${LIB}}")
ENDFOREACH(LIB)
Such a way results for every find_library call will be stored separately, and the same library will not be searched again at the next time cmake being invoked. Also, such approach allows user to modify (in cache) paths to concrete libraries without affecting other ones.

Getting CMake CHECK_CXX_COMPILER_FLAG to work

Note: This is my first time using CMake. I don't know much about it, so I'm just posting a bunch of information to see if anyone can see my problem.
I would like the ability to automatically determine which c++11 flag is appropriate, given my compiler. There are many examples of this line. Here is my CMakeLists.txt following such an example:
cmake_minimum_required (VERSION 2.8)
#Add the c++11 flag, whatever it is
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(-std=c++11 COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG(-std=c++0x COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
project(AnalyzeGames)
set(AnalyzeGames_SRCS AnalyzeGames.cpp)
add_executable(AnalyzeGames ${AnalyzeGames_SRCS})
Here is my cmake output when trying to use this file: http://pastebin.com/3AUwqffD
Here is CMakeError.log: http://pastebin.com/EbNKvGt8
Here is CMakeOutput.log: http://pastebin.com/kVJ0enJC
echo $CC: /usr/bin/gcc
echo $CXX: /usr/bin/g++
I can compile a simple test executable with g++ using either flag manually.
cmake --version: cmake version 2.8.12.2
For some reason CMake is not recognizing that my compiler does support both of those flags.
The cmake output tells you that it does not recognize the '.cxx' extension because it doesn't know that your project is a C++ project. To fix this, you should enable C++ in the project command. Try to change the following line:
project(AnalyzeGames)
to:
project(AnalyzeGames CXX)
and then move it to the 2nd line of the CMakeLists.txt, right under cmake_minimum_required. The configuration should work as expected after this.
TLDR
Compiler checks are only performed in the variable passed is not previously defined, which includes in the cache from previous failed attempts. Use unset(my_var CACHE) to force checking to always occur, or just be aware of this behaviour and clear the cache manually when needed.
Detail
I too had this problem (with cmake 2.8.12.2) and I had to turn on trace output, and step through the code to get a similar toy build to work I had make sure the variables I used (COMPILER_SUPPORTS_CXX11_*) in these calls:
CHECK_CXX_COMPILER_FLAG(-std=c++11 COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG(-std=c++0x COMPILER_SUPPORTS_CXX0X)
Were set such that they named themselves:
set(COMPILER_SUPPORTS_CXX11 "COMPILER_SUPPORTS_CXX11")
The other posters solution didn't work for me, it mainly just seemed to limit the detecting of compilers to just CXX and ignored the C compiler.
The issue appears to be with this line of code in the cmake module:
if("${VAR}" MATCHES "^${VAR}$")
Which in the trace output is:
/usr/share/cmake/Modules/CheckCXXSourceCompiles.cmake(30): if(COMPILER_SUPPORTS_CXX0X MATCHES ^COMPILER_SUPPORTS_CXX0X$ )
It looks as if the expression on the left of the MATCHES is replaced with the variables value, but the expression on the right is assumed to be plain text.
If the MATCH fails then the main part of the macro is skipped and according the to the log the check fails.
Looking at later versions of this macro online it looks as if this line has changed to only perform the compile check if the variable is undefined.
It as at this point that I realise that this is the intent / hack of the original code; if the X is undefined then "X" MATCHES "^X$" will be true, but then the compile check can be performed, fail for some other reason and then never be performed again.
So the solution is either force unset of variable in cache before calling the macro using:
unset(COMPILER_SUPPORTS_CXX0X CACHE)
Or clear the cache manually and be prepared for this behaviour.

Proper way to call a found executable in a custom command?

I have a program on my computer, let's say C:/Tools/generate_v23_debug.exe
I have a FindGenerate.cmake file which allows CMake to find that exact path to the executable.
So in my CMake code, I do:
find_program(Generate)
if (NOT Generate_FOUND)
message(FATAL_ERROR "Generator not found!")
So CMake has found the executable. Now I want to call this program in a custom command statement. Should I use COMMAND Generator or COMMAND ${GENERATOR_EXECUTABLE}? Will both of these do the same thing? Is one preferred over the other? Is name_EXECUTABLE a variable that CMake will define (it's not in the FindGenerate.cmake file), or is it something specific to someone else's example code I'm looking at? Will COMMAND Generator be expanded to the correct path?
add_custom_command(
OUTPUT blahblah.txt
COMMAND Generator inputfile1.log
DEPENDS Generator
)
find_program stores its result into the variable given as a first argument. You can verify this by inserting some debug output:
find_program(GENERATOR Generate)
message(${GENERATOR})
Note that find_program does not set any additional variables beyond that. In particular, you mentioned Generate_FOUND and GENERATOR_EXECUTABLE in your question and neither of those gets introduced implicitly by the find_program call.
The second mistake in your program is the use of the DEPENDS option on the add_custom_command. DEPENDS is used to model inter-target dependencies at build time and not to manipulate control flow in the CMakeLists. For example, additional custom command can DEPEND on the output of your command (blahblah.txt), but a custom command cannot DEPEND on the result of a previous find operation.
A working example might look something like this:
find_program(GENERATOR Generate)
if(NOT GENERATOR)
message(FATAL_ERROR "Generator not found!")
endif()
add_custom_command(
OUTPUT blahblah.txt
COMMAND ${GENERATOR} inputfile1.log
)
P.S.: You asked why the code examples were not properly formatted in your question. You indented everything correctly, but you need an additional newline between normal text and code paragraphs. I edited your question accordingly.