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})
Related
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?
I'm writing a utility macro in cmake for setting a variable dependent on platform. Theoretically it should be simple but I don't know why my variable isn't being set. Here's the macro:
macro(SetCrossPlatform name msvc_val linux_val macos_val)
#macro(SetCrossPlatform VAR name MSVC msvc_val LINUX linux_val MACOS macos_val) # alternative signature that I tried
MESSAGE(STATUS "PLATFORM ${PLATFORM}")
if (PLATFORM STREQUAL "windows-msvc")
set(name ${msvc_val})
elseif (PLATFORM STREQUAL "linux")
set(name ${linux_val})
elseif (PLATFORM STREQUAL "macos")
set(name ${macos_val})
endif ()
endmacro()
which is invoked like:
#SetCrossPlatform(VAR Variable MSVC "microsoft" LINUX "ubuntu" MACOS "apple") # try other signature
SetCrossPlatform(Variable "microsoft" "ubuntu" "apple")
message(STATUS "Variable ${Variable}")
This produces:
-- PLATFORM windows-msvc
-- Variable # <--- Should print out "-- Variable microsoft"
-- Configuring done
-- Generating done
Anybody know what I'm doing wrong?
Inside the macro you need to dereference name for obtain the value of the parameter:
set(${name} ${msvc_val})
By current code
set(name ${msvc_val})
you just define the variable with the name "name".
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.
CMake allows masking any function like this
function(add_executable)
#...custom code, that may use _add_executable(${ARGN}) to call the original function
endfunction()
But it seems that once CMake parsed that function definition, it set in global CMake scope and nesting of function definitions is not supported:
function(echo)
message(STATUS "Original function")
endfunction()
echo() #Original function
function(nesting)
function(echo)
message(STATUS "Overwritten function")
# _echo() # Original function
endfunction()
echo() #Overwritten function
endfunction()
echo() #Still Overwritten function, expected Original function
It will also not work if you include the new definition, or even if you define it in CMakeLists added by add_subdirectory. Using macros also does not help:
macro(echo)
_echo(${ARGN})
endmacro(echo)
echo() #Still Overwritten function, but now even _echo() is overwritten
Is there any way to revert the old meaning of the function?
Here is one walkaround I found. It is not a true answer, but it works.
function(echo)
message(STATUS "Original function")
endfunction()
echo() #Original function
function(echo)
if(__ECHO_BEHAVIORAL_SWITCH)
message(STATUS "Overwritten function")
else()
_echo()
endif()
endfunction()
function(nesting)
set(__ECHO_BEHAVIORAL_SWITCH 1)
echo() #Overwritten function
endfunction()
nesting() #Overwritten function
echo() #Original function
It does not revert the definition of the function, but rather bounds the behaviour of the overwritten function to the external variable, that may be cached inside functions or add_subdirectory just like I expected.
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)