How to write a variable to the parents' parent scope in CMake? - cmake

I know that when I am in a function, I can set the variable in the calling scope by using:
set(MYVAR 1 PARENT_SCOPE)
What I want is to be able to set a variable in the grandparent scope (if it exists).
I know that if I redefine my function as a macro, then set(MYVAR 1 PARENT_SCOPE) effectively does what I need. But unfortunately, I cannot do that for unrelated reasons.
A solution would work for me as a walkaround for the problem in How to check if the variable is set in the parent scope, (as opposed to being inherited from grandparents) in CMake? .

Related

How do I make Meson object constant?

As explained here, I like to create file objects in subdirs, and library / executables in the top-level file. However, since all the variables end up in global scope, two subdir files could accidentally use the same variable names. For example:
# Top-level meson.build
subdir('src/abc')
subdir('src/def')
# src/abc/meson.build
my_files=files('1.c','2.c')
# src/def/meson.build
my_files=files('3.c','4.c')
I want meson to throw an error when src/def/meson.build tries to assign a value to my_files. Is this possible in Meson 0.50?
Reassigning variables is rather legitimate operation in meson, so it looks as it is not possible to generate error in standard way. One way of avoiding this problem is following some naming rules e.g. according to folders/sub-folders' names (abc_files, def_files in your case).
But if you really need to have variables with the same name and make sure they are not reassigned, you can use is_variable() function which returns true if variable with given name has been assigned. So, place the following assert before each assignment:
assert(not is_variable('my_files'), 'my_files already assigned!!!')
my_files=files('3.c','4.c')

can't access cmake variable defined inside function using PARENT_SCOPE

So I have the following CMake code:
function(get_sources output_var)
set(${output_var} "a" PARENT_SCOPE)
message(${output_var}) # prints "SOURCES" (as expected)
message(${${output_var}}) # throws an error - why?
...
endfunction(get_sources)
get_sources(SOURCES)
message(${SOURCES}) # prints "a" - ok, so the above function created a var
The get_sources function is supposed to create a variable with given name in parent scope and fill it with some content.
It seems like get_sources(SOURCES) creates the variable as expected - message(${SOURCES}) outside function prints "a", but there's a problem with the line message(${${output_var}}). It should evaluate to message(a) and print "a", but instead it throws an error:
CMake Error at CMakeLists.txt:41 (message):
message called with incorrect number of arguments
I am confused. Is it supposed to be like that? Should I make a function scoped variable, fill it in and then at the end copy its contents to a parent scope variable? I checked it - it works, but I wanted it to be without any additional variables.
EDIT:
I enclosed a ${${output_var}} in quotation marks and now it doesn't throw an error, but it prints nothing.
PARENT_SCOPE sets variable only for parent scope, not for the current one. This is explicitely stated in CMake documentation about 'set' command:
If the PARENT_SCOPE option is given the variable will be set in the scope above the current scope. Each new directory or function creates a new scope. This command will set the value of a variable into the parent directory or calling function (whichever is applicable to the case at hand). The previous state of the variable’s value stays the same in the current scope (e.g., if it was undefined before, it is still undefined and if it had a value, it is still that value).
This is a proper way for handle PARENT_SCOPE variables:
Should I make a function scoped variable, fill it in and then at the end copy its contents to a parent scope variable?

Set a variable in current scope and PARENT_SCOPE?

If I do:
set(SourceDir ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
message("SourceDir: " ${SourceDir})
The message says that SourceDir is empty. Presumably it is being set in the parent scope only. Is there a way to set the variable in the current scope AND the parent scope? So that I don't have to do:
set(SourceDir ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
set(SourceDir ${CMAKE_CURRENT_SOURCE_DIR})
message("SourceDir: " ${SourceDir})
When you set it for PARENT_SCOPE, it doesn't do it for current scope somehow. But a two step process works. The first line sets it locally, the second line exports it to the parent scope.
set (LIB_VER 6)
set (LIB_VER ${LIB_VER} PARENT_SCOPE)
I think you can not. The documentation says:
Each new directory or function creates a new scope.
If I understand it correctly when your SET gets executed, its scope is already created by copying the parent scope. So no matter what you do to the original (PARENT_SCOPE) your local scope won't change.
You'd better ask the question on CMake's user list to verify that they don't do fallback to parent when a variable is not defined in the local scope. If they do however, this is a bug.

CMake: Access variables across sub directories

I have a two sub directories from root on which one has the line:
set(${LIBNAME}_publicheaders
LocalizeResource.h
)
I want to be able to access this variable from the other subdirectory. How can I do this?
#JoakimGebart's answer is probably the more common way to go about this. However, you can also use get_directory_property directly from within the second subdir to achieve what you're after.
I see that in your comment, you've used ${LIB_NAME}_publicheaders, but in your question you have ${LIBNAME}_publicheaders. This could be the cause of your problems, since the command should work like this:
get_directory_property(MyVar
DIRECTORY ${CMAKE_SOURCE_DIR}/abc
DEFINITION ${LIBNAME}_publicheaders)
However, there are a couple of provisos:
This has to be called after setting the variable in the subdir. i.e. you'd have to ensure add_subdirectory(abc) was called before the add_subdirectory for the one where this will be used.
If LIBNAME is also set inside the same subdir (abc), you'll need to retrieve the value for that first.
So, while this is probably a less common solution, it has the advantage that it doesn't "pollute" the global namespace with subdir-specific variables - this works from with a subdir referring to another subdir.
You can set the variable in the parent scope using the PARENT_SCOPE option to set()
Example:
set(${LIBNAME}_publicheaders
LocalizeResource.h
PARENT_SCOPE
)
See http://www.cmake.org/cmake/help/v2.8.10/cmake.html#command:set
This, however, means that the variable is available not only in the other subdirectory, but in any other subdirectories on the same level as well.

Strange function argument name behaviour

I asked a questions about cmake and passing variables here. I can make it work, but only if I name my variable in the function differently than the variable in parent scope I call the function for. So in essence:
function(strange name)
message(STATUS ${name})
message(STATUS ${${name}})
endfunction()
set(name foo)
set(anothername foo)
strange(name)
strange(anothername)
Which results in:
-- name (message(STATUS ${name}) for var "name")
-- name (message(STATUS ${${name}}) for var "name")
-- anothername message(STATUS ${name}) for var "anothername")
-- foo (message(STATUS ${${name}}) for var "anothername")
Isn't that a little weird? What's happening?
I think the behaviour of a function should not depend on the naming of variable in the parent scope - should it?!
Any clarification is much appreciated!
Unfortunately, the language of CMake is extremely primitive. In this case, the naming of local variables may interact with variables with the same name in outer scopes.
Concretely, in the two calls ${${name}} expands to ${name} and ${anothername}, respectively. In the former name is the name of a local variable whose value is used. In the latter, the anothername from the outer scope is used.
I don't know of any way around this, except using obfuscated variable names in function accept variable names as arguments.
Maybe we should petition for a ${name:PARENT_SCOPE} from the CMake team?
If you want to avoid the local scoping issues described in #Lindydancer's concise answer, you can change your function to a macro
From the documentation of macro:
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 you should look at the function command.
If you change to use macro, your output becomes
-- name
-- foo
-- anothername
-- foo