I'm looking for a way to interpret a string as variable name in cmake.
Given:
set(MY_SECRET_VAR "foo")
# later only the name of the variable is known.
set(THE_NAME "MY_SECRET_VAR")
# Now i'm looking for a way to get the value "foo" from the name
# something like:
set(THE_VALUE "${THE_NAME}")
# THE_VALUE should be "foo"
A second level of unwrapping:
set(THE_VALUE "${${THE_NAME}}")
Related
I create a dictionary in python and save the path to the directories (that I want the software to run on) as the keys and the corresponding values are a list of the expected output for each directory. Right now I have a structure like this:
sampleDict = {'/path_to_directory1': ["sample1","sample2","sample3"],
'/path_to_directory2': ["sample1","sample2"],
'/path_to_directory3': ["sample1","sample2","sample3"]}
# sampleDict looks pretty much like this
# key is a path to the directory that I want the rule to be executed on and the corresponding value sampleDict[key] is an array e.g. ["a","b","c"]
def input():
input=[]
for key in dirSampleDict:
input.extend(expand('{dir}/{sample}*.foo', dir = key, sample=dirSampleDict[key]))
return input
rule all:
input:
input()
# example should run some software on different directories for each set of directories and their expected output samples
rule example:
input:
# the path to each set of samples should be the wildcard
dir = lambda wildcards: expand("{dir}", dir=dirSampleDict.keys())
params:
# some params
output:
expand('{dir}/{sample}*.foo', dir = key, sample=dirSampleDict[key])
log:
log = '{dir}/{sample}.log'
run:
cmd = "software {dir}"
shell(cmd)
Doing this I receive the following error:
No values given for wildcard 'dir
Edit: Maybe it was not so clear what I actually want to do so I filled in some data.
I also tried using the wildcards I set up in rule all as follows:
sampleDict = {'/path_to_directory1': ["sample1","sample2","sample3"],
'/path_to_directory2': ["sample1","sample2"],
'/path_to_directory3': ["sample1","sample2","sample3"]}
# sampleDict looks pretty much like this
# key is a path to the directory that I want the rule to be executed on and the corresponding value sampleDict[key] is an array e.g. ["a","b","c"]
def input():
input=[]
for key in dirSampleDict:
input.extend(expand('{dir}/{sample}*.foo', dir = key, sample=dirSampleDict[key]))
return input
rule all:
input:
input()
# example should run some software on different directories for each set of directories and their expected output samples
rule example:
input:
# the path to each set of samples should be the wildcard
dir = "{{dir}}"
params:
# some params
output:
'{dir}/{sample}*.foo'
log:
log = '{dir}/{sample}.log'
run:
cmd = "software {dir}"
shell(cmd)
Doing this I receive the following error:
Not all output, log and benchmark files of rule example contain the
same wildcards. This is crucial though, in order to avoid that two or
more jobs write to the same file.
I'm pretty sure the second part is more likely what I actually want to do, since expand() as output would only run the rule once but I need to run it for every key value pair in the dictonary.
First of all, what do you expect from the asterisk in the output?
output:
'{dir}/{sample}*.foo'
The output has to be a list of valid filenames that can be formed with substitution of each wildcard with some string.
Next problem is that you are using the "{dir}" in the run: section. There is no variable dir defined in the script used for run. If you want to use the wildcard, you need to address it using wildcards.dir. However the run: can be substituted with a shell: section:
shell:
"software {wildcards.dir}"
Regarding your first script: there is no dir wildcard defined (actually there are no wildcards at all):
output:
expand('{dir}/{sample}*.foo', dir = key, sample=dirSampleDict[key])
Both {dir} and {sample} are the variables in the context of expand function, and they are fully substituted with the named parameters.
Now the second script. What did you mean by this input?
input:
dir = "{{dir}}"
Here the "{{dir}}" is not a wildcard, but a reference to a global variable (you haven't provided the rest of your script, so I cannot judge whether it is defined or not). Moreover, what's the need in the input? You never use the {input} variable at all, and there is no dependencies that are needed to connect the rule example with any other rule to produce the input for rule example.
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
The official document of CMake 2.8.12 says about macro
When it is invoked, the commands recorded in the macro are first
modified by replacing formal parameters (${arg1}) with the arguments
passed, and then invoked as normal commands.
and about function
When it is invoked, the commands recorded in the function are first
modified by replacing formal parameters (${arg1}) with the arguments
passed, and then invoked as normal commands.
Obviously, the two quotes are almost the same but it's confusing. Does parameter replacement behave the same in functions and macros?
I wrote a sample code below:
set(var "ABC")
macro(Moo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endmacro()
message("=== Call macro ===")
Moo(${var})
function(Foo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endfunction()
message("=== Call function ===")
Foo(${var})
and the output is:
=== Call macro ===
arg = ABC
# After change the value of arg.
arg = ABC
=== Call function ===
arg = ABC
# After change the value of arg.
arg = abc
So it seems arg is assigned the value of var when calling Foo and ${arg} is just string replaced with ${var} when calling Moo.
So I think the above two quotes are very easy to make one confused, although the official documents also said that:
Note that the parameters to a macro and values such as ARGN are not variables in the usual CMake sense. They are string replacements much like the C preprocessor would do
with a macro. If you want true CMake variables and/or better CMake
scope control you should look at the function command.
UPDATE (1/29/2021)
Add the following statement after the statement Moo(${var}) to make the difference between macro and function even more clear.
message(${arg})
This statement will print out abc.
In other words, function pushes and pops new variable scope (variables created and changed exist only in the function), macro does not. However, you can override the function default behaviour with the PARENT_SCOPE parameter of the set command.
The cmake documentation you quoted is so misleading that it's basically wrong. It should be clarified/fixed like this:
macro: when it is invoked, the commands recorded in the macro are first all modified before any is run by replacing formal parameters (${arg1}) with the arguments passed.
cmake --trace-expand shows exactly what happens.
The cmake 3.13.3 doc hasn't changed compared to 2.8.12 with respect to this.
The macro expansion, answered by Yantao Xie really opens my eyes!
I also found the tutorial below comes with some concrete examples, which is helpful to understand the variable scope concept.
Cited from Learn cmake in 15 mins:
In CMake, you can use a pair of function/endfunction commands to define a function. Here’s one that doubles the numeric value of its argument, then prints the result:
function(doubleIt VALUE)
math(EXPR RESULT "${VALUE} * 2")
message("${RESULT}")
endfunction()
doubleIt("4") # Prints: 8
Functions run in their own scope. None of the variables defined in a function pollute the caller’s scope. If you want to return a value, you can pass the name of a variable to your function, then call the set command with the special argument PARENT_SCOPE:
function(doubleIt VARNAME VALUE)
math(EXPR RESULT "${VALUE} * 2")
set(${VARNAME} "${RESULT}" PARENT_SCOPE) # Set the named variable in caller's scope
endfunction()
doubleIt(RESULT "4") # Tell the function to set the variable named RESULT
message("${RESULT}") # Prints: 8
Similarly, a pair of macro/endmacro commands defines a macro. Unlike functions, macros run in the same scope as their caller. Therefore, all variables defined inside a macro are set in the caller’s scope. We can replace the previous function with the following:
macro(doubleIt VARNAME VALUE)
math(EXPR ${VARNAME} "${VALUE} * 2") # Set the named variable in caller's scope
endmacro()
doubleIt(RESULT "4") # Tell the macro to set the variable named RESULT
message("${RESULT}") # Prints: 8
Both functions and macros accept an arbitrary number of arguments. Unnamed arguments are exposed to the function as a list, through a special variable named ARGN.
Here’s a function that doubles every argument it receives, printing each one on a separate line:
function(doubleEach)
foreach(ARG ${ARGN}) # Iterate over each argument
math(EXPR N "${ARG} * 2") # Double ARG's numeric value; store result in N
message("${N}") # Print N
endforeach()
endfunction()
doubleEach(5 6 7 8) # Prints 10, 12, 14, 16 on separate lines
Another notable difference between function() and macro() is the behavior of return().
From the cmake documentation of return():
Note that a macro, unlike a function, is expanded in place and therefore cannot handle return().
So because it is expanded in place, in a macro() it returns from the caller. While in a function it just exits the function()
Example:
macro(my_macro)
return()
endmacro()
function(my_function)
return()
endfunction()
my_function()
message(hello) # is printed
my_macro()
message(hi) # is not printed
I want to parse/process passed console parameters in CMake, so that if I run this in console:
cmake -DCMAKE_BUILD_TYPE=Release -DSOME_FLAG=1 ..
I want to obtain the -DCMAKE_BUILD_TYPE=Release and -DSOME_FLAG=1 from this inside CMake script (and every other parameter passed) and save them somewhere.
The reason I want it is to pass all parameters through custom CMake script (which calls execute_process(cmake <something>) after) e.g.
cmake -DCMAKE_BUILD_TYPE=Release -P myscript.cmake
There is CMAKE_ARGC variable which contains amount of variables passed to CMake (divided by whitespaces), and CMAKE_ARGV0, CMAKE_ARGV1, ... which contains actual values.
It's common for languages for C++ that first (zero) variable holds the command you called (cmake in this situation), so we will need everything except CMAKE_ARGV0. Let's make a simple loop then:
set(PASSED_PARAMETERS "") # it will contain all params string
set(ARG_NUM 1) # current index, starting with 1 (0 ignored)
# you can subtract something from that if you want to ignore some last variables
# using "${CMAKE_ARGC}-1" for example
math(EXPR ARGC_COUNT "${CMAKE_ARGC}")
while(ARG_NUM LESS ARGC_COUNT)
# making current arg named "CMAKE_ARGV" + "CURRENT_INDEX"
# in CMake like in Bash it's easy
set(CURRENT_ARG ${CMAKE_ARGV${ARG_NUM}})
message(STATUS "Doing whatever with ${CURRENT_ARG}")
# adding current param to the list
set(PASSED_PARAMETERS ${PASSED_PARAMETERS} ${CURRENT_ARG})
math(EXPR ARG_NUM "${ARG_NUM}+1") # incrementing current index
endwhile()
(Answering my own question, didn't find anything like that in SO, maybe it'll help someone)
When writing a script that loads data, it's a waste of time to wait for it to load each time.
How to check to see if the variable is defined?
You can use the exist function in Octave to do the work. It can be used to check the existence of given name as a variable, built in function, file, or directory. In you case, to check the existence of a variable, you may use something like this:
if (exist("your_var_name", "var") == 1)
printf("varname exists");
else
printf("varname not exists");
endif
You may refer the following links for detailed information:
Built-in Function: exist (name, type)
Status of Variables
Need to put the variable name in quotes too,
exist("varname", "var")
if (exist("itemcount") == 1)
% here it checks if itemcount is a variable, by changing the value after ==, you can check for function name, file name, dir, path etc.
end
Note itemcount is in double quotes.
By changing the value after ==, you can check for function name, file name, dir, path etc.
from / more info at:
https://www.gnu.org/software/octave/doc/interpreter/Status-of-Variables.html#XREFexist
other return values ..
2 if the name is an absolute file name, an ordinary file in Octave’s path, or (after appending ‘.m’) a function file in Octave’s path, 3 if the name is a ‘.oct’ or ‘.mex’ file in Octave’s path, 5 if the name is a built-in function, 7 if the name is a directory, or 103 if the name is a function not associated with a file (entered on the command line). Otherwise, return 0.