Check that a string is not a reserved word? - cmake

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?

Related

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.

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

How do I correctly pass CMake list (semicolon-sep) of flags to set_target_properties?

CMake lists are essentially just semicolon-separated strings, but if you pass such a variable to a command, it does get expanded into multiple arguments - for example,
set(FLAGS f1 f2 f3)
# now FLAGS == 'f1;f2;f3'
add_custom_command(
...
COMMAND my_cmd ${FLAGS}
...
)
will correctly call my_cmd f1 f2 f3.
Now if I do this with
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${LD_FLAGS}"
)
the expansion does not occur, and I end up with a single LD_FLAG that contains semicolons -- useless, instead of expanding it into a space-separated string.
Is there any way to make it so that when I pass a list to the LINK_FLAGS property (or any property that is), it gets expanded into multiple arguments rather than just one?
I don't think set_target_properties can do the expansion automatically, but you can use string (REPLACE ...) to expand a list into a space separated string:
string (REPLACE ";" " " LD_FLAGS_STR "${LD_FLAGS}")
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${LD_FLAGS_STR}"
)
For using a cmake List as list, use
${LD_FLAG}
For using a cmake list as string, (i.e. list items are separated with ';'), use
"${LD_FLAG}"
So in your case, just remove "" should be sufficient.
The set_property command is designed for this
http://www.cmake.org/cmake/help/v3.0/command/set_property.html
set_property(TARGET tgt PROPERTY LINK_FLAGS foo bar)
I use it like strings
set(FLAGS " f1 f2 f3")
Note the space in the front, it allows you to concatenate other sets of flags.
For more complex projects, instead of if-elses you may also do a double expansion trick:
set(GCC_FLAGS " -Wl,--relax")
set(DIAB_FLAGS " -tPPCE500ES:cross")
set(MSVC_FLAGS " /RAINBOW_CRAP)
# ...
# ...LINUX_FLAGS, WINDOWS_FLAGS, etc...
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${${COMPILER}_FLAGS} ${${SYSTEM}_FLAGS}"
)
# COMPILER and SYSTEM is set somewhere else, in toolchain files for example
separate_arguments(LD_FLAGS_AS_LIST NATIVE_COMMAND LD_FLAGS)
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${LD_FLAGS_AS_LIST}"
)
As of CMake 3.12, you can use the list(JOIN <list> <glue> <output variable>) command described here:
set(FLAGS f1 f2 f3)
# now FLAGS == 'f1;f2;f3'
list(JOIN FLAGS " " FLAGS) # Note: Overwrites FLAGS variable
# now FLAGS == 'f1 f2 f3'
add_custom_command(
...
COMMAND my_cmd ${FLAGS}
...
)
This is a little more direct than using string(REPLACE ...).
In cmake 3.x there are a few approaches
The top level CMakeLists.txt file then looks something like this:
# The name of the included file could be anything,
# it doesn't have to be called CMakeLists.txt
include(foo/CMakeLists.txt)
include(bar/CMakeLists.txt)
add_executable(myApp ${myApp_SOURCES})
with the subdirectory files structured something like this:
list(APPEND myApp_SOURCES
${CMAKE_CURRENT_LIST_DIR}/foo.cpp
${CMAKE_CURRENT_LIST_DIR}/foo_p.cpp
)
Enhanced source file handling with target_sources()

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.