Can I ignore the key word DEFINED in cmake? - cmake

I have a simple question related to camke keyword DEFINED. I do not know in which condition this keyword is necessary and in which condition this keyword can be ignored. The following example is given to illustrate my question:
cmake_minimum_required(VERSION 2.8)
project(TEST)
if (NOT ABC)
set(ABC "hello ABC")
endif()
message(${ABC})
if (ABC)
message(${ABC})
endif()
if (DEFINED ABC)
message(${ABC})
endif()
As you can see from the example, if (variable) and if (DEFINED variable) function the same. Therefore, a question arises: is DEFINED really necessary? Any idea will be appreciated.

I notice that a variable may be defined but its value can be OFF, and in this case the variable is still defined but not ON, which means if(variable) is false while if(DEFINED variable) is still true.
set(INITIAL_PASS OFF)
if (DEFINED INITIAL_PASS)
message(${INITIAL_PASS})
endif()
if (INITIAL_PASS)
MESSAGE(${INITIAL_PASS})
endif()
set(INITIAL_PASS ON)
if (DEFINED INITIAL_PASS)
message(${INITIAL_PASS})
endif()
if (INITIAL_PASS)
MESSAGE(${INITIAL_PASS})
endif()

Related

How to make a cmake function that collects targets in a outer scoped list variable? [duplicate]

I would like to create a CMake function as:
function(test src_list dst_list)
# do something
endfunction()
usage:
test(${my_list} chg_list)
It means, my_list is a list with several fields, and chg_list will receive a list modified inside test function.
How can I create a function in CMake to do that?
In CMake, functions have their own scope, and by default, all modification of variables are local, unless you pass CACHE or PARENT_SCOPE as parameter to set. Inside a function, if you want to modify a variable in the scope of the caller, you should use:
set(${dst_list} <something> PARENT_SCOPE)
See documentation:
A function opens a new scope: see set(var PARENT_SCOPE) for details.
Check that inside your function you are set()ing not dst_list but ${dst_list}. You need this to return data to the parent scope.
Here's how I solved a similar problem which was marked a duplicate of this question. This function takes a string BinaryName and adds it to the list OutVariable which is available in the parent scope. If the list is not defined it is created. I use this strategy for creating new test targets and adding the name of these targets to a list of targets in the main scope.
Thanks to #Tsyvarev for the comments that lead me to figure this out.
function(AddToListFromFunction BinaryName OutVariable)
if ("${${OutVariable}}" STREQUAL "")
message(STATUS "1")
set(${OutVariable} ${BinaryName} PARENT_SCOPE)
message(STATUS "OutVariable: ${OutVariable} ${${OutVariable}}")
else ()
message(STATUS "2")
set(${OutVariable} "${${OutVariable}}" "${BinaryName}" PARENT_SCOPE)
endif ()
endfunction()
AddToListFromFunction(MyBinary1 MyTests)
AddToListFromFunction(MyBinary2 MyTests)
message(STATUS "MyTests Variable: ${MyTests}")

CMake a few questions about writing my own find_package module

I am a novice in the filed of CMake and I learn how to write my own find_package() module by following the example in the book CMake Cookbook. The following CMakeLists.txt file is provided with the official example.
if(NOT ZeroMQ_ROOT)
set(ZeroMQ_ROOT "$ENV{ZeroMQ_ROOT}")
endif()
if(NOT ZeroMQ_ROOT)
find_path(_ZeroMQ_ROOT NAMES include/zmq.h)
else()
set(_ZeroMQ_ROOT "${ZeroMQ_ROOT}")
endif()
find_path(ZeroMQ_INCLUDE_DIRS NAMES zmq.h HINTS ${_ZeroMQ_ROOT}/include)
if(ZeroMQ_INCLUDE_DIRS)
set(_ZeroMQ_H ${ZeroMQ_INCLUDE_DIRS}/zmq.h)
function(_zmqver_EXTRACT _ZeroMQ_VER_COMPONENT _ZeroMQ_VER_OUTPUT)
set(CMAKE_MATCH_1 "0")
set(_ZeroMQ_expr "^[ \\t]*#define[ \\t]+${_ZeroMQ_VER_COMPONENT}[ \\t]+([0-9]+)$")
file(STRINGS "${_ZeroMQ_H}" _ZeroMQ_ver REGEX "${_ZeroMQ_expr}")
string(REGEX MATCH "${_ZeroMQ_expr}" ZeroMQ_ver "${_ZeroMQ_ver}")
set(${_ZeroMQ_VER_OUTPUT} "${CMAKE_MATCH_1}" PARENT_SCOPE)
endfunction()
_zmqver_EXTRACT("ZMQ_VERSION_MAJOR" ZeroMQ_VERSION_MAJOR)
_zmqver_EXTRACT("ZMQ_VERSION_MINOR" ZeroMQ_VERSION_MINOR)
_zmqver_EXTRACT("ZMQ_VERSION_PATCH" ZeroMQ_VERSION_PATCH)
# We should provide version to find_package_handle_standard_args in the same format as it was requested,
# otherwise it can't check whether version matches exactly.
if(ZeroMQ_FIND_VERSION_COUNT GREATER 2)
set(ZeroMQ_VERSION "${ZeroMQ_VERSION_MAJOR}.${ZeroMQ_VERSION_MINOR}.${ZeroMQ_VERSION_PATCH}")
else()
# User has requested ZeroMQ version without patch part => user is not interested in specific patch =>
# any patch should be an exact match.
set(ZeroMQ_VERSION "${ZeroMQ_VERSION_MAJOR}.${ZeroMQ_VERSION_MINOR}")
endif()
if(NOT ${CMAKE_C_PLATFORM_ID} STREQUAL "Windows")
find_library(ZeroMQ_LIBRARIES
NAMES
zmq
HINTS
${_ZeroMQ_ROOT}/lib
${_ZeroMQ_ROOT}/lib/x86_64-linux-gnu
)
else()
find_library(ZeroMQ_LIBRARIES
NAMES
libzmq
"libzmq-mt-${ZeroMQ_VERSION_MAJOR}_${ZeroMQ_VERSION_MINOR}_${ZeroMQ_VERSION_PATCH}"
"libzmq-${CMAKE_VS_PLATFORM_TOOLSET}-mt-${ZeroMQ_VERSION_MAJOR}_${ZeroMQ_VERSION_MINOR}_${ZeroMQ_VERSION_PATCH}"
libzmq_d
"libzmq-mt-gd-${ZeroMQ_VERSION_MAJOR}_${ZeroMQ_VERSION_MINOR}_${ZeroMQ_VERSION_PATCH}"
"libzmq-${CMAKE_VS_PLATFORM_TOOLSET}-mt-gd-${ZeroMQ_VERSION_MAJOR}_${ZeroMQ_VERSION_MINOR}_${ZeroMQ_VERSION_PATCH}"
HINTS
${_ZeroMQ_ROOT}/lib
)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ZeroMQ
FOUND_VAR
ZeroMQ_FOUND
REQUIRED_VARS
ZeroMQ_INCLUDE_DIRS
ZeroMQ_LIBRARIES
VERSION_VAR
ZeroMQ_VERSION
)
I have two questions toward the above example. The first one is that in the function scope _zmqver_EXTRACT we first set the CMAKE_MATCH_1 to 0 and then we do this command set(${_ZeroMQ_VER_OUTPUT} "${CMAKE_MATCH_1}" PARENT_SCOPE). However, it seems that the value of CMAKE_MATCH_1 is always 0. In my opinion, the version information should be stored in the variable ZeroMQ_ver.
What's more, I do not know what the usage of the variable ZeroMQ_FIND_VERSION_COUNT. It seems that this variable is undefined.

How to specify the variable which must be set and exist in cmake

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

CMake: Print out all accessible variables in a script

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)

Passing a list to a CMake macro

I am trying to write a macro which goes through a given list of libraries. However the message call in the macro prints only the first item of the list. What am I doing wrong here?
Code:
macro( FindLibs LIBRARY_NAMES_LIST )
message( "inside ${LIBRARY_NAMES_LIST}" )
endmacro()
set( LIBRARY_NAMES_LIST lib1 lib2 lib3)
message( "outside ${LIBRARY_NAMES_LIST}" )
FindLibs(${LIBRARY_NAMES_LIST})
Output:
message( "outside lib1 lib2 lib3" )
message( "inside lib1" )
Quote the variable as you pass it to the macro:
FindLibs("${LIBRARY_NAMES_LIST}")
The provided answers by others are correct. The best solution is indeed to provide the list in double quotes like this:
FindLibs( "${LIBRARY_NAMES_LIST}" )
But if you really don't want to force the user to use double quotes and also want to see the LIBRARY_NAMES_LIST argument in your macro declaration, here's how I would do it:
macro( FindLibs LIBRARY_NAMES_LIST )
set( _LIBRARY_NAMES_LIST ${LIBRARY_NAMES_LIST} ${ARGN} ) # Merge them together
message( "inside ${_LIBRARY_NAMES_LIST}" )
endmacro()
And it would be used like this (your expectation):
FindLibs( ${LIBRARY_NAMES_LIST} )
This is nice, because it will force the user to provide at least one library. Calling it like
FindLibs()
won't work. CMake will report the following error:
FindLibs Macro invoked with incorrect arguments for macro named: FindLibs
If you are using CMake 2.8.3 or newer, another option is to use the CmakeParseArguments, but it will require you to put a keyword argument in front of your list. But this technique is probably the easiest way to manage more than one list, and provides high flexibility. For that reason, it is very handy to know. It is also my preferred way of doing it. Here's how to do it:
include( CMakeParseArguments )
macro( FindLibs )
set( _OPTIONS_ARGS )
set( _ONE_VALUE_ARGS )
set( _MULTI_VALUE_ARGS NAMES DEPENDS )
cmake_parse_arguments( _FINDLIBS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )
# Mandatory
if( _FINDLIBS_NAMES )
message( STATUS "inside NAMES=${_FINDLIBS_NAMES}" )
else()
message( FATAL_ERROR "FindLibs: 'NAMES' argument required." )
endif()
# Optional
if( _FINDLIBS_DEPENDS )
message( STATUS "inside DEPENDS=${_FINDLIBS_DEPENDS}" )
endif()
endmacro()
Unfortunately, you have to do your argument enforcement yourself, but at least it gives you the option to chose which arguments are mandatory, and which ones are not (DEPENDS is optional in my example above).
And it would be used like this:
FindLibs( NAMES ${LIBRARY_NAMES_LIST} )
FindLibs( NAMES ${LIBRARY_NAMES_LIST} DEPENDS ${LIBRARY_DEPENDENCY_LIST} )
# You can change the order
FindLibs( DEPENDS ${LIBRARY_DEPENDENCY_LIST} NAMES ${LIBRARY_NAMES_LIST} )
# You can even build your lists on the fly
FindLibs(
NAMES
zlib
png
jpeg
DEPENDS
otherProject1
otherProject2
)
And if I do this:
FindLibs()
# or that:
FindLibs( DEPENDS ${LIBRARY_DEPENDENCY_LIST} )
Then I would get my custom error message:
error: FindLibs: 'NAMES' argument required.
And here the link to the CMakeParseArguments documentation if you want to learn more about it.
I hope it helps :-)
Your macro should look like this:
macro(FindLibs list_var_name)
message( "inside ${${list_var_name}}" )
endmacro()
and call the macro like this:
FindLibs(LIBRARY_NAMES_LIST)
So inside the macro: ${list_var_name} = LIBRARY_NAMES_LIST, ${${list_var_name}} = ${LIBRARY_NAMES_LIST} = your list.
Another solution could be (somewhat more obscure):
macro(FindLibs)
message( "inside ${ARGN}" )
endmacro()
FindLibs(${LIBRARY_NAMES_LIST})
In the first solution you pass only the name of the list-variable to the macro (one argument). In the second solution you expand the list before calling the macro and pass N parameters (N = length of the list).
According to (almost) current CMake documentation, the ${ARGV} symbol should expand to the entire list of arguments. This should keep things simpler at the calling location.