How to check if a macro exists in CMake - cmake

How do I correctly check if a macro is defined in CMake?
macro(foo)
message("foo")
endmacro()
if(<what goes here?>)
foo()
endif()

The if command supports a COMMAND clause for that purpose:
if(COMMAND foo)
foo()
endif()

Use MACROS property for a given directory.
get_directory_property(DEFINED_MACROS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} MACROS)
list(FIND DEFINED_MACROS "foo" MACRO_INDEX)
if(MACRO_INDEX EQUAL -1)
# macro foo does not exist
else(MACRO_INDEX EQUAL -1)
# macro foo exists
endif(MACRO_INDEX EQUAL -1)

Related

Check that a string is not a reserved word?

In CMake, given a list of arguments, how can we check if an argument represents a reserved word?
For a concrete example, consider this wrapper around target_link_libraries(), which modifies library names but not reserved keywords like INTERFACE:
macro(modify_library_names LIB_NAMES TO_APPEND OUTPUT_LIST)
foreach(item ${LIB_NAMES})
if (${item} MATCHES "INTERFACE|PUBLIC|PRIVATE")
list(APPEND ${OUTPUT_LIST} "${item}")
else()
list(APPEND ${OUTPUT_LIST} "${item}${TO_APPEND}")
endif()
endforeach()
endmacro()
function(target_link_libraries_multiplexed BASE-NAME)
set(LINK_ARG_SOFT "")
modify_library_names("${ARGN}" "_soft" LINK_ARG_SOFT)
set(LINK_ARG_HARD "")
modify_library_names("${ARGN}" "_hard" LINK_ARG_HARD)
target_link_libraries(${BASE-NAME}_soft
${LINK_ARG_SOFT}
)
target_link_libraries(${BASE-NAME}_hard
${LINK_ARG_HARD}
)
endfunction()
# Usage:
target_link_libraries_multiplexed(util
PUBLIC
math # links util_soft against math_soft and util_hard against math_hard
)
Is there something like if (RESERVED(item)) that would let us get rid of the hardcoded keyword list on line 3?

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 invoking dynamic macro name

I have a two macro names, for example:
macro(my_macro1)
# stuff only to use in this macro
endmacro()
macro(my_macro2)
# stuff only to use in this macro
endmacro()
I'd like to dynamic call the macros based a variable name, for example:
if (...)
set (ver 1)
else ()
set (ver 2)
endif ()
my_macro${ver} # this is my idea
Any help?
As #Tsyvarev has commented CMake doesn't support dynamic function names. So here are some alternatives:
Simple Approach
macro(my_macro ver)
if(${ver} EQUAL 1)
my_macro1()
elseif(${ver} EQUAL 2)
my_macro2()
else()
message(FATAL_ERROR "Unsupported macro")
endif()
endmacro()
set(ver 1)
my_macro(ver)
set(ver 2)
my_macro(ver)
A call() Function Implementation
Building on #Fraser work here is a more generic call() function implementation:
function(call _id)
if (NOT COMMAND ${_id})
message(FATAL_ERROR "Unsupported function/macro \"${_id}\"")
else()
set(_helper "${CMAKE_BINARY_DIR}/helpers/macro_helper_${_id}.cmake")
if (NOT EXISTS "${_helper}")
file(WRITE "${_helper}" "${_id}(\$\{ARGN\})\n")
endif()
include("${_helper}")
endif()
endfunction()
set(ver 1)
call(my_macro${ver})
set(ver 2)
call(my_macro${ver})

CMake exclude files from a given pattern after file(GLOB_RECURSE)

I have an little library which has an file structure like this:
CMakeLists.txt
LibFoo/
Foo.h
FooWin.cpp
FooWin.inl
FooPosix.cpp
FooPosix.inl
And when i have to build the library in a specific OS (for example Windows) they should contain in the list before using file(GLOB_RECURSE) using a macro:
macro( add_recursive dir retVal)
file( GLOB_RECURSE ${retVal} ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl )
endmacro()
and my excluding pattern is like this (when Windows is the build host): *Posix.* but doesn't work, i tried with this approach:
macro( add_recursive dir retVal pattern)
file( GLOB_RECURSE ${retVal} ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl EXCLUDE PATTERN "${pattern}")
endmacro()
but the POSIX files still here anyways, CMake doesn't report an error or anything suggested about that.
You can use list filtering to remove items after the GLOB_RECURSE:
list(FILTER ${retVal} EXCLUDE REGEX "Posix.")
There is no EXCLUDE option for file(GLOB_RECURSE ...) command flow. You probably take this option from file(COPY|INSTALL ...), which is a different command flow.
You may iterate over list, obtained from file(GLOB_RECURSE) and exclude needed files manually:
macro( add_recursive dir retVal)
# Store unfiltered list of files into temporary list
file( GLOB_RECURSE _tmp_list ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl )
# Resulted list is initially empty. Copy all needed elements into it.
set(${retval})
foreach(f ${_tmp_list})
if(NOT f MATCHES "Posix.")
list(APPEND ${retVal} ${f})
endif()
endforeach()
endmacro()
This piece of code almost worked for me.
cmake nags about the set not having enough arguments and discontinues the macro.
This snippet also makes the extention and exclude filter variable
macro( add_recursive retVal dir ext excl)
# Store unfiltered list of files into temporary list
file( GLOB_RECURSE _tmp_list ${dir}/${ext})
# Resulted list is initially empty. Copy all needed elements into it.
set(${retval} "")
foreach(f ${_tmp_list})
if(NOT f MATCHES ${excl})
list(APPEND ${retVal} ${f})
endif()
endforeach()
endmacro( add_recursive )
#example of usage
add_recursive(inc "./" "*.h" "/exclude_folder/")

Cmake ARGV and Macro BUG?

The following is strange!
macro(foo in1)
message("FIRST" ${in1})
message("OPtional1:" ${ARGV1})
message("OPtional2:" ${ARGV2})
if( NOT ${ARGV1} AND ${ARGV2} )
message("IF")
else()
message("ELSE")
endif()
endmacro()
Running the following:
foo("gaga" false true)
(SHOULD GIVE "IF")
WORKS!
BUT
foo("gaga" false)
(SHOULD GIVE "ELSE" because the second optional argument is FALSE!)
Results in error:
cmake:126 (if):
given arguments: "NOT" "false" "AND"
Unknown arguments specified
Is this a bug??
The following works:
if( NOT "${ARGV1}" AND "${ARGV2}" )
message("IF")
else()
message("ELSE")
endif()
WHY???
THANKS For any help!
(With functions it works)
In macros, ARGV1 etc. are not real CMake variables. Instead, ${ARGV1} is the string replacement from the macro call. That means that your call with only one argument expands to
if( NOT false AND )
which explains the error message. By enclosing the string expansions in quotes, you now have
if( NOT "false" AND "")
giving the IF command an empty string to evaluate. Other solutions: use the ${ARGC} string replacement to decide whether the argument is present before trying to expand ARGV2. Or, as you've mentioned, switch to a function instead of a macro. In a function, ARGV1 is an actual CMake variable, and you can write it more sensibly, even without the ${} expansion at all:
function(foo in1)
message("Optional1: " ${ARGV1})
message("Optional2: " ${ARGV2})
if( NOT ARGV1 AND ARGV2 )
message("IF")
else()
message("ELSE")
endif()
endfunction()
Reference:
http://www.cmake.org/cmake/help/v2.8.8/cmake.html#command:macro
if(NOT ARGV1 AND ARGV2)
is the same as
if(ARGV2 AND NOT ARGV1)
Logical negation (and most other unary operators) binds before conjunction.