How to revert a function definition in CMake? - cmake

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.

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

Why is my cmake function not assigning value to argument?

I write this function that takes a list and appends new values to it. When
I print it only prints dir.
function(test dst_list)
# do somethin
set(my_list "dir1" "dir2")
set(${dst_list} ${my_list})
# message(${dst_list})
endfunction()
set(my_list "dir")
test(my_list)
message("${my_list}")
Trick here if you set() some variable before function and then change it in function and returned so you can read new value, you must add to function bare variable name as in your set() and separately variable value. Other trick you must use is PARENT_SCOPE so set() variables in function can be returned (I think in this case they are rewritten by the same name).
function(test var_rtn var_val)
set(my_list "dir1" "dir2")
set(${var_rtn} ${var_val} ${my_list} PARENT_SCOPE)
endfunction()
set(my_list "dir")
test(my_list "${my_list}")
message("ANSWER: ${my_list}")
Your output will be now: ANSWER: dir;dir1;dir2

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

Can I ignore the key word DEFINED in 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()