When I run for the first run: cmake ..\test2 -DABC=abc
I've got output: ABC=abc
And it is OK, but when I run for the 2nd time: cmake ..\test2 without additional parameter, I still have ABC=abc. I didn't remove any output files.
Is it possible to force cmake to use default value (OFF) of argument if it was provided during next run?
if(ABC)
message(STATUS "abc is set")
else()
message(STATUS "abc is not set")
endif()
doesn't work
CMakeList.txt:
option(ABC "test" )
message(STATUS "ABC=${ABC}")
Is it possible to force CMake to use default value (OFF) of argument?
Yes, just unset the cache variable, which can be done with -U option:
cmake ..\test2 -UABC
So option() finds the variable to be not set and assigns default value to it
In CMake, not using -D means "do not change the variable", not a "do not set the variable at all".
Related
I'm currently working on migrating our current build environment from MSBuild to CMake. I have a situation where I need to update the PATH variable in order for the units tests executable to run. This is not a issue for gtest_add_tests, as it uses the source to identify tests. But gtest_discover_tests, which executes the unit tests with the --gtest_list_tests flag, fails to identify any tests because a STATUS_DLL_NOT_FOUND error is encountered during the build.
For example:
add_executable(gTestExe ...)
target_include_directories(gTestExe ...)
target_compile_definitions(gTestExe ...)
target_link_libraries(gTestExe ...)
set (NEWPATH "/path/to/bin;$ENV{PATH}")
STRING(REPLACE ";" "\\;" NEWPATH "${NEWPATH}")
This works:
gtest_add_tests(TARGET gTestExe TEST_LIST allTests)
set_tests_properties(${all_tests} PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")
But this does not:
#set_target_properties(gTestExe PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")
#set_property(DIRECTORY PROPERTY ENVIRONMENT "PATH=${NEWPATH}")
gtest_discover_tests(gTestExe PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")
Edit:
The tests themselves work when added using gtest_add_tests. The issue is the call to discover the tests, during the post build step that gtest_discover_tests registers, fails because the required libraries are not in the PATH.
I came across the same issue this morning and I found a (dirty ?) workaround. The reason why it won't work is a bit complicated, but the workaround is quite simple.
Why it won't work
gtest_discover_tests(gTestExe PROPERTIES ENVIRONMENT "PATH=${NEWPATH}")
Will not work is because the PATH contents are separated by semicolons and therefore are treated by CMake as a list value.
If you look a the GoogleTestAddTests.cmake file (located in C:\Program Files\CMake\share\cmake-3.17\Modules), it treats the PROPERTIES argument with a foreach.
The PROPERTIES value look like this for CMake at this point in the script : ENVIRONMENT;PATH=mypath;mypath2 and will treat mypath2 as a third argument instead of a value for the PATH environment variable.
CMake will then generate the following line :
set_tests_properties( mytest PROPERTIES ENVIRONMENT PATH=mypath mypath2)
Escaping the ; won't work because the list is automatically expended in add_custom_command() in GoogleTest.cmake (l. 420 in cmake 3.17.1) ignoring any form of escaping.
To prevent the cmake foreach to treat each value in the path as a list you can use a bracket argument like :
gtest_discover_tests(gTestExe PROPERTIES ENVIRONMENT "[==[PATH=${NEWPATH}]==]")
The cmake foreach will then treat your argument as one entity. Unfortunately CMake will also put a bracket in the generated code as it contains [ = and maybe spaces :
# This line
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]")
else()
set(_args "${_args} ${_arg}")
endif()
resulting in the following generated script :
set_tests_properties( mytest PROPERTIES ENVIRONMENT [==[ [==[PATH=mypath;mypath2] ]==])
And when executing the test cmake will attempt to read the value only removing the first bracket argument as they don't nest.
Possible workaround
So to do this we need CMake to not use bracket argument on our own bracket argument.
First make a local copy of GoogleTestAddTests.cmake file in your own repository (located in C:\Program Files\CMake\share\cmake-3.17\Modules).
At the beginning of your local copy of GoogleTestAddTests.cmake (l. 12) replace the function add_command by this one :
function(add_command NAME)
set(_args "")
foreach(_arg ${ARGN})
# Patch : allow us to pass a bracket arguments and escape the containing list.
if (_arg MATCHES "^\\[==\\[.*\\]==\\]$")
string(REPLACE ";" "\;" _arg "${_arg}")
set(_args "${_args} ${_arg}")
# end of patch
elseif(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]")
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
This will make cmake don't use bracket list on our bracket list and automatically escape the ; as set_tests_properties also treat the ; as a list.
Finally we need CMake to use our custom GoogleTestAddTests.cmake instead of the one in CMake.
After your call to include(GoogleTest) set the variable _GOOGLETEST_DISCOVER_TESTS_SCRIPT to the path to your local GoogleTestAddTests.cmake :
# Need google test
include(GoogleTest)
# Use our own version of GoogleTestAddTests.cmake
set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/GoogleTestAddTests.cmake
)
Note : In my example the GoogleTestAddTests.cmake is right next to the processing cmake file.
Then a simple call to
gtest_discover_tests(my_target
PROPERTIES ENVIRONMENT "[==[PATH=${my_path};$ENV{PATH}]==]"
)
should work.
In the following CMake code snippet, I am confused by the if elseif check. My understanding is that BL will always be "Ei", so there is no need to check other values. Are there any scenarios where BL could be overwritten by something else? I am new to CMake so need some help here.
set(BL "Ei" CACHE STRING "library")
set_property(CACHE BL PROPERTY STRINGS "Ei;AT;Op")
message(STATUS "The backend of choice:" ${BL})
if(BL STREQUAL "Ei")
...
elseif(BL STREQUAL "AT")
...
elseif(BL STREQUAL "Op")
...
else()
message(FATAL_ERROR "Unrecognized option:" ${BL})
endif()
The code set(BL "Ei" CACHE STRING "library") defines a CMake cache variable. However, without a FORCE option in the set statement, that means that it will not be overwritten if the variable was previously defined in the cache.
One way for a user to set a different value for BL would be on the cmake command line. For example: cmake ../sourcedir -DBL:STRING=AT
By entering the variable in the cache as type STRING (as opposed to type INTERNAL) that also makes the variable available to be configured in cmake-gui or in ccmake. (Furthermore, the set_property(... STRINGS ...) directive tells cmake-gui to produce a drop-down list containing Ei, AT, and Op to select from. However, this isn't enforced for setting the variable from the command line, which is why it's still a good idea to have the default case signalling an error.)
See the section "Set Cache Entry" under CMake's documentation for set for more information.
I have a need to have some variable to be specified and exist in the environment.
In case it does not exist need to stop building.
example
if ( "${VARMUSTEXIST}" STREQUAL "ON" )
message(STATUS is ON)
elif ("${VARMUSTEXIST}" STREQUAL "OFF")
message(STATUS is OFF)
endif()
I don't want to put an if (defined VARMUSTEXIST) everywhere in the script.
In bash there is an option for that "set -u".
Some preliminary points:
if ( "${VARMUSTEXIST}" STREQUAL "ON" ) [...] elif(AGAIN LONG EXPRESSION) [...] endif()normally in cmake is simply: if (VARMUSTEXIST) [...] else() [...] endif()
The command if (DEFINED VARMUSTEXIST) requires DEFINED to be upper case.
You mention bash and environment variables:Environment variables are read using $ENV{VARIABLE_NAME}
For environment variables you will do:
if(NOT DEFINED ENV{VARMUSTEXIST})
message(FATAL_ERROR "You must set VARMUSTEXIST environment variable")
endif()
You say:
I don't want to put an if (defined VARMUSTEXIST) everywhere in the script
This is not clear to me: for each variable you need to check only once, possibly in the main CMakeLists.txt. Of course, you need to add NOT: if (NOT DEFINED VARMUSTEXIST) [stop]
If you can be more precise on your problem, we can design a macro that checks if one or a group of variables are defined or not.
If by environment you mean OS environment variables then the syntax is wrong anyway.
If those are options to be provided by the user, then the literal comparisons with ON and OFF are incorrect, since CMake has more ways to express booleans and they are all in widespread use and became idiomatic. Thus, by expecting either ON or OFF, you're making your build script weird and against everyone's expectations. And you're also making more work for yourself, as you've noticed.
First, you should document the options and give them safe default values using option(). Then it won't ever be that a variable could be undefined.
# early in the top-level CMakeLists.txt
option(VARMUSTEXIST "You can provide it. It exists anyway." NO)
And then you'll check its truth or falsehood rather simply:
# later in top-level file or in subdirectories
if (VARMUSTEXIST)
message("VARMUSTEXIST is true")
else()
message("VARMUSTEXIST is false")
endif()
I think this is the best approach...
CMake Variable:
if(NOT DEFINED VARIABLE)
message(FATAL_ERROR "VARIABLE is not set")
endif(NOT DEFINED VARIABLE)
Environment Variable:
if(NOT DEFINED ENV{VARIABLE})
message(FATAL_ERROR "VARIABLE is not set")
endif(NOT DEFINED ENV{VARIABLE})
I would like to figure out how to efficiently pass all of the CMake variables to another execution step of CMake.
There is a way to get all the variables, but I'm hoping there is an efficient option other than looping over every variable and appending the strings together with set() as follows:
get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
if(NOT ${_variablename} STREQUAL BASIS_PROPERTIES_ON_TESTS_RE)
set(ALL_VARIABLES_COMMAND_LINE "${ALL_VARIABLES_COMMAND_LINE} -D ${_variableName}=\"${${_variableName}}\"\n")
endif()
endforeach()
execute_process (
COMMAND "${CMAKE_COMMAND}" ${COMMON_ARGS}
-D "PROJECT_INCLUDE_DIRS=${INCLUDE_DIRS}"
-D "BINARY_INCLUDE_DIR=${BINARY_INCLUDE_DIR}"
-D "EXTENSIONS=${EXTENSIONS}"
${ALL_VARIABLES_COMMAND_LINE}
-D "CMAKE_FILE=${CMAKE_FILE}"
-P "${BASIS_MODULE_PATH}/ConfigureIncludeFiles.cmake"
RESULT_VARIABLE RT
)
The problem with that method is that it will mess with escape characters and in some cases fail to execute the program.
Note: I currently write all the variables to disk and load them back in from there, but that operation takes 1/2 second. Since I need to run this script for over 100 independent packages the additional configuration time for that technique is too high.
Well, instead of passing the whole list of variables as command-line parameters, I would form a new (temporary) CMake script (with file(WRITE...)) which sets all required variables and then execute that script with another instance of CMAke (execute_process(COMMAND ${CMAKE_COMMAND} -P /path/to/script...)). Thus you'd avoid the necessity to fight against the bash escaping.
Certainly when writing the file you would need to escape variables values too, but in case of of CMake it's a much simpler task. It seems that the basic escaping should be as follows:
set(VAR "A String with \" and \\ ")
message(STATUS "VAR=[${VAR}]")
string(REPLACE "\\" "\\\\" VAR_ ${VAR}) # escape \
string(REPLACE "\"" "\\\"" VAR_ ${VAR_}) # escape "
file(WRITE file.cmake "SET(VAR \"${VAR_}\")\n" )
file(APPEND file.cmake "message(STATUS \"VAR=[\${VAR}]\")\n")
this cmake script creates another cmake script named file.cmake which prints the same variable as the first script.
I am assuming that you are using the ConfigureIncludeFiles module from the CMake BASIS project. One way to improve configuration time for your project is to run ConfigureIncludeFiles as an include script.
Instead of running ConfigureIncludeFiles in a CMake subprocess started with execute_process, run the the module in the main CMake process as a CMake script with the include command for each package that needs to be configured, i.e.:
# set up parameters for package 1
set (PROJECT_INCLUDE_DIRS ${PACKAGE1_INCLUDE_DIRS})
set (BINARY_INCLUDE_DIR ${PACKAGE1_BINARY_INCLUDE_DIR})
set (EXTENSIONS ${PACKAGE1_EXTENSIONS})
set (CMAKE_FILE ${PACKAGE1_CMAKE_FILE})
include(ConfigureIncludeFiles)
...
# set up parameters for package 2
set (PROJECT_INCLUDE_DIRS ${PACKAGE2_INCLUDE_DIRS})
set (BINARY_INCLUDE_DIR ${PACKAGE2_BINARY_INCLUDE_DIR})
set (EXTENSIONS ${PACKAGE2_EXTENSIONS})
set (CMAKE_FILE ${PACKAGE2_CMAKE_FILE})
include(ConfigureIncludeFiles)
Because the module runs in the main CMake process, it will implicitly have access to all CMake variables defined and there is no need to pass the variables explicitly.
I'm wondering if there is a way to print out all accessible variables in CMake. I'm not interested in the CMake variables - as in the --help-variables option. I'm talking about my variables that I defined, or the variables defined by included scripts.
I'm currently including:
INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityCXX.cmake)
And I was hoping that I could just print out all the variables that are here, instead of having to go through all the files and read what was available - I may find some variables I didn't know about that may be useful. It would be good to aid learning & discovery. It is strictly for debugging/development.
This is similar to the question in Print all local variables accessible to the current scope in Lua, but for CMake!
Has anyone done this?
Using the get_cmake_property function, the following loop will print out all CMake variables defined and their values:
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
This can also be embedded in a convenience function which can optionally use a regular expression to print only a subset of variables with matching names
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
To print environment variables, use CMake's command mode:
execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "environment")
Another way is to simply use:
cmake -LAH
From the manpage:
-L[A][H]
List non-advanced cached variables.
List cache variables will run CMake and list all the variables from the CMake cache that are not marked as INTERNAL or ADVANCED. This will effectively display current CMake settings [...].
If A is specified, then it will display also advanced variables.
If H is specified, it will also display help for each variable.
ccmake is a good interactive option to interactively inspect cached variables (option( or set( CACHE:
sudo apt-get install cmake-curses-gui
mkdir build
cd build
cmake ..
ccmake ..
Another way to view all cmake's internal variables, is by executing cmake with the --trace-expand option.
This will give you a trace of all .cmake files executed and variables set on each line.
based on #sakra
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
#case sensitive match
# string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
#
#case insenstitive match
string( TOLOWER "${ARGV0}" ARGV0_lower )
string( TOLOWER "${_variableName}" _variableName_lower )
string(REGEX MATCH ${ARGV0_lower} MATCHED ${_variableName_lower})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
dump_cmake_variables("^Boost")
variable names are case sensitive
btw if you are interested in boost, it is Boost_INCLUDE_DIRS not BOOST_INCLUDE_DIRS, and it is Boost_LIBRARIES not BOOST_LIBRARIES, and by mistake I had BOOST_LIBRARIES instead of Boost_LIBRARIES, https://cmake.org/cmake/help/v3.0/module/FindBoost.html , better example for boost:
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost REQUIRED COMPONENTS RANDOM)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(myfile PRIVATE
${Boost_LIBRARIES}
)
You can use message :
message([STATUS] "SUB_SOURCES : ${SUB_SOURCES}")
None of the current answers allowed me to see the variables in my project subdirectory. Here's a solution:
function(print_directory_variables dir)
# Dump variables:
get_property(_variableNames DIRECTORY ${dir} PROPERTY VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
get_directory_property(_variableValue DIRECTORY ${dir} DEFINITION ${_variableName})
message(STATUS "DIR ${dir}: ${_variableName}=${_variableValue}")
endforeach()
endfunction(print_directory_variables)
# for example
print_directory_variables(.)
print_directory_variables(ui/qt)