[CMake] Re: function and raise_scope commands (+ unset bug)

Sebastien BARRE sebastien.barre at kitware.com
Sat Feb 16 15:28:15 EST 2008


At 2/16/2008 02:13 PM, Alexander Neundorf wrote:
> >
> > I don't mean to remove the SET(PARENT_SCOPE) feature, where the main
> > purpose is to set a global variable from within a function.
> > But if SET( ... PARENT_SCOPE) is called outside a function, the effect is
> > that the variable is set in the parent directory. This is the part where
> > I'm not sure it's a good idea.
>
>...this would be the patch, with the patch PARENT_SCOPE only raises the scope
>out of a function, but not to the parent directory anymore.

Hey guys,

Honestly the PARENT_SCOPE "feature" is still limiting the way I'd 
like to use FUNCTION. I've whined about scope at Kitware for a while 
so FUNCTION was very welcome, but I'm wondering if I could have my 
cake and eat it too: I like my macros to call helper macros to divide 
my work in smaller units. Say, if I have a PARSE_COMPANION_CUBE_FILE 
macro that loads a file and parses it, it's very likely said macro 
will just load the file in a string, and call another 
PARSE_COMPANION_CUBE macro. This gives my fellow 
maintainers/developpers the ability to parse a file directly, or grab 
the file in a different way and parse the string later using the 
second macro (since, frankly, most of the logic will be in the parsing macro).

In that scenario, variable names are passed and propagated from 
macros to macros to store whatever result the macro is producing. Has 
worked fine for me so far for years (minus the usual "underscore all 
vars in a macro" trick). No such thing with FUNCTION:

FUNCTION(SET_VAR1 varname)
   SET(${varname} "There's science to do" PARENT_SCOPE)
ENDFUNCTION(SET_VAR1)

FUNCTION(SET_VAR2 varname)
   SET_VAR1(${varname})
ENDFUNCTION(SET_VAR2)

SET_VAR2(foo)
MESSAGE("${foo}")

Obviously foo is not set, since it is now set in SET_VAR2 scope. Bummer.
So now I have to do things like this:

FUNCTION(SET_VAR2 varname)
   SET_VAR1(varname_proxy)
   SET(${varname} ${varname_proxy} PARENT_SCOPE)
ENDFUNCTION(SET_VAR2)

Which I guess I could live with, except that it gets *old* as the 
number of parameters grows, and it kinda goes against my goal of 
sticking with small "understandable" macros...

But wait, it gets weird. In my macros it's not unusual that  I 
*unset* variables (yes, I do), using SET(var). I was wondering if 
that would work. It kinda does, but not quite:

FUNCTION(SET_VAR1 varname)
   SET(${varname} "" PARENT_SCOPE)
ENDFUNCTION(SET_VAR1)

FUNCTION(SET_VAR2 varname)
   SET_VAR1(varname_proxy)
   SET(${varname} "${varname_proxy}" PARENT_SCOPE)
ENDFUNCTION(SET_VAR2)

SET_VAR2(foo)
IF(DEFINED foo)
   MESSAGE("foo is defined")
ELSE(DEFINED foo)
   MESSAGE("foo is NOT defined")
ENDIF(DEFINED foo)

This will display that foo is NOT defined, even though it should be 
defined, if you unroll all the functions into a simple script. Note 
that replacing  SET(${varname} "" PARENT_SCOPE) by  SET(${varname} 
PARENT_SCOPE) will also display that foo is not defined (which was 
the expected behavior). Also note that removing the quote around 
${varname_proxy} doesn't make any difference here.

OK, back to PARENT_SCOPE. Well, Tcl/Tk has 'upvar' (which RAISE_SCOPE 
was inspired from), *and* 'global', which would pretty much declare 
variables inside a procedure/function to be of the "global scope" 
nature. I don't really like any of them. If I had to give a (crazy) 
suggestion though, before 2.6, is that SET would introspect its 
parameters a little more, i.e. before expansion. If the variable to 
be set is a regular variable name (say, SET(bar ..), then it is set 
at the current scope (i.e. local scope for a function). If it is a 
*deferenced* variable (i.e. SET(${bar}), then it should be set in the 
parent scope automatically, *eventually* at the global scope, 
*ideally* propagated back from parent scope to parent scope to the 
scope this "pointer to a variable" was first invoked (i.e. where I 
called SET_VAR2(foo), in my example).

I know about PROPERTIES, but I'm still not convinced, since they 
don't behave like variables. For a simple thing like working on a 
filename across scopes, I would have to: a) store the value in a 
prop, then (later on) b) transfer that prop value to a var, c) do 
whatever I usually do with a var (i.e. *everything in CMake pretty 
much*, inluding cleaning a path), d) transfer back the new value to 
the prop. That's a lot more lines and syntactic detours, which makes 
my varname_proxy trick in SET_VAR2 almost elegant :)

Thanks 




More information about the CMake mailing list