Can we do it: variable scoping
Paul Smith <[email protected]>
| Newsgroups | gmane.comp.gnu.make.devel |
|---|---|
| Organization | GNU's Not UNIX! |
| Message-ID | <1289344405.3416.3258.camel@psmith-ubeta> |
It seems that every few months, someone wants to use a variable scoping capability; this request seems be becoming more prevalent as more people eschew recursive makefiles and have single make instances including tons of smaller makefiles. Of course you can avoid the need for scoping by choosing make variable name prefixes etc., and with make's ability to use constructed variable names it can be made somewhat less painful. And, you can even clear out variables after you've included a makefile, using the $(.VARIABLES) special variable, to avoid leaking variables between included makefiles for example. Still, abstractly it seems like it would be helpful to provide first-class variable scoping capabilities, so I was thinking about how difficult that would be to do. I think, actually, that it's not that hard for the most part. We already have the ability to have nested variable scopes, to support target-specific variables. This feature would create multiple "global" scopes as well, and individual targets would be hooked to the scope that was active when the target was defined. So a new scope would create a new variable set as the "global" scope, and all targets would be hooked to that as their variable scope. When the scope ended we'd "pop" that scope and reinstall its parent as the global. This is all already basically supported internally. I thought we might also need "variable assignment scopes", as well as target scopes, but I'm not sure we do: I can't think of any situation where a variable is evaluated in a deferred context where it would need to know what scope it was defined in, other than in a target context. I know that didn't make much sense but I can explain further if anyone cares. There are three issues I can see: first, we need to come up with a makefile syntax. I think a simple (nesting) push/pop-style scope is what we want (I definitely don't want to get into "named scopes"). Does anyone know of any other make implementation (or similar build tool) that implements something like this? What syntax do they use? Second, we have to think carefully about how a new variable scope would interact with the existing "target specific variable inheritance" feature. This could be the thing that knocks down the entire proposal. Basically we already have a stack of variable scopes, constructed dynamically based on the prerequisite tree. How does a static variable scoping, tied to the target itself, fit into that existing stack? For example, today if we have: foo: FOO = foo foo: bar FOO = bar bar: ; @echo $(FOO) you'll get "foo" here, because the "bar" target inherited the value of FOO from foo's target-specific variable. If you run "make bar" you'll get "bar". How does this kind of thing work if you add variable scoping? Obviously expanding "FOO" first looks at bar's target-specific variables, but if it's not there where does it look next? Does it look at the parent target's variable list, which might be in an entirely different variable scope? At this point the only realistic, _implementable_ option I see is that it looks in all inherited target-specific variables first, and then where it currently jumps to the "global" scope it would then instead jump into the variable scope. That will definitely lead to some odd results, but it's at least deterministic. More and more I am sorry I kept the target variable inheritance feature from Solaris make... ugh! Finally, I'm concerned about maintainability/debug-ability. It's already tricky, often, to figure out where the value of a variable came from. This may be the proverbial straw that forces us to do some kind of variable expansion tracking--although how to do this without completely inundating the user with obscene amounts of information is going to be "interesting". Related to the previous issue, what about -p output? I can't see any real way to provide -p output that makes sense. In order to reconstruct the scoping we would have to track, somewhere, all the targets that were declared in a given scope. There is no other reason to track this and it seems like a lot of effort. Of course, I suppose we could reconstruct it dynamically based on the variable scoping structure itself (go through all the targets and figure out their scope based on which variable set they reference).