Thread: [A-a-p-user] Scopes
Brought to you by:
vimboss
From: Steve H. <ho...@ca...> - 2003-01-13 08:29:46
|
Hello all, I'm confused about the :global command and the way scopes work in A-A-P. If a variable is declared as being :global, shouldn't it be accessible (and modifiable) from any target in the same recipe? If not, what's the :global command for ? Take this simple script: ##################################### :global Var1 Var1 = 'aaa' target1: :print Var1 before assignment is $Var1 Var1 = bbb :print Var1 after assignment is $Var1 target2: :print Var1 in target2 is $Var1 all: target1 target2 ##################################### It will output: Var1 before assignment is 'aaa' Var1 after assignment is bbb Var1 in target2 is 'aaa' Is this correct ? Do I really have to :export each modified variable inside a target to have it available to other targets ? I think that, to match Python's scope, even without the :global command, any variable declared in the same recipe should be global in the same recipe. Also, it's very confusing that the variable can be imported into a target (as it can see in the first output's line) but not exported automatically. Any comments ? -- Best regards, Steve Howe mailto:ho...@ca... |
From: Bram M. <Br...@mo...> - 2003-01-13 10:57:19
|
[ copied to a-a-p-develop list, since it's also about development ] Steve Howe wrote: > I'm confused about the :global command and the way scopes work in > A-A-P. If a variable is declared as being :global, shouldn't it be > accessible (and modifiable) from any target in the same recipe? If > not, what's the :global command for ? > > Take this simple script: > > ##################################### > :global Var1 > Var1 = 'aaa' > > target1: > :print Var1 before assignment is $Var1 > Var1 = bbb > :print Var1 after assignment is $Var1 > > target2: > :print Var1 in target2 is $Var1 > > all: target1 target2 > ##################################### > > It will output: > > Var1 before assignment is 'aaa' > Var1 after assignment is bbb > Var1 in target2 is 'aaa' > > Is this correct ? Do I really have to :export each modified variable > inside a target to have it available to other targets ? Currently the ":export" is necessary if you set a variable in the build commands of a dependency. I have been struggling with the scope of variables. Until recently I thought that the ":export" was the only way to pass a change to a variable on to outer scopes. But Steve showed me how we can make a modification on the Python dictionary class, I assume this can also be used to intercept an assignment to a variable and pass the value on the recipe the dependency was defined in, the scope the dependency was invoked from or both. Additionally, exporting to the intermediate scopes would be nice, this currently is very clumsy. This especially applies to actions (e.g., the ":update gcccheck" in the default.aap recipe). > I think that, to match Python's scope, even without the :global > command, any variable declared in the same recipe should be global in > the same recipe. Global in the commands at the toplevel of the recipe, yes. But also in the scope of the build commands in a dependency? Suppose you have something like this: # Most things compile with some optimization CFLAGS = -O2 foo.o : foo.c # Compile this one with extra optimization CFLAGS = -O4 :do compile $source bar.o : bar.c :do compile $source You don't know in what order foo.c and bar.c will be compiled, thus making the value of CFLAGS global makes it very unpredictable. Perhaps this isn't the best example. Using a {var_CFLAGS = -O4} attribute on foo.c would be the normal way to do this. What to most users expect will happen? We can't fall back to the behavior of make, it doesn't allow variable assignment in build commands. > Also, it's very confusing that the variable can be imported into a > target (as it can see in the first output's line) but not exported > automatically. If we use the customized dictionary class this would be possible. Thus the question is how it should work. For reference, here are the current docs for ":global" and ":local": :global name ... Define variable "name" to be global to other recipes. Even when "name" has been assigned a value, build commands (actions, dependencies or rules) will still use the value passed on by who invoked the build commands. Example: CFLAGS = -O4 :global CFLAGS This means that when the value of CFLAGS is changed this value will be used for build commands defined in this recipe. :local name ... Define variable "name" to be local to this recipe. It will not be passed on to child recipes. Build commands defined in this recipe (actions, dependencies or rules) will use the value from the recipe instead of a value passed from who invoked the commands. -- Why don't cannibals eat clowns? Because they taste funny. /// Bram Moolenaar -- Br...@mo... -- http://www.moolenaar.net \\\ /// Creator of Vim - Vi IMproved -- http://www.vim.org \\\ \\\ Project leader for A-A-P -- http://www.a-a-p.org /// \\\ Lord Of The Rings helps Uganda - http://iccf-holland.org/lotr.html /// |
From: Steve H. <ho...@ca...> - 2003-01-14 09:44:05
|
Hello Bram, Monday, January 13, 2003, 7:57:03 AM, you wrote: BM> Steve Howe wrote: >> Is this correct ? Do I really have to :export each modified variable >> inside a target to have it available to other targets ? BM> Currently the ":export" is necessary if you set a variable in the build BM> commands of a dependency. Well at least :export should allow multiple arguments I think :) So that no multi export statements need to be used. The same would be nice to :global and :local:. >> I think that, to match Python's scope, even without the :global >> command, any variable declared in the same recipe should be global in >> the same recipe. BM> Global in the commands at the toplevel of the recipe, yes. But also in BM> the scope of the build commands in a dependency? Suppose you have BM> something like this: BM> # Most things compile with some optimization BM> CFLAGS = -O2 BM> foo.o : foo.c BM> # Compile this one with extra optimization BM> CFLAGS = -O4 BM> :do compile $source BM> bar.o : bar.c BM> :do compile $source BM> You don't know in what order foo.c and bar.c will be compiled, thus BM> making the value of CFLAGS global makes it very unpredictable. I think if the user don't want that to become global, he should flag that with :local. A-A-P should look into the local scope, then then global, for any variables (local always override global). BM> Perhaps this isn't the best example. Using a {var_CFLAGS = -O4} BM> attribute on foo.c would be the normal way to do this. I think if a user don't want a variable to become global, he should add a local variable that has that name, and then use it; something like: :compile: :local CFLAGS # Global CFLAGS remains unchanged CFLAGS = -O4 BM> What to most users expect will happen? We can't fall back to the BM> behavior of make, it doesn't allow variable assignment in build BM> commands. That indeed is out of question. I would expect the above behavior which is as close as possible to Python's. In summary: Global variables are defined in a context called "global" that is always available to all recipes and dependencies. Local variables override global variables but are local to the the dependency being run. Local variables override global variables, i.e., first lookup on local context; if not found, look into global. -- Now it remains the question of how should :local variables be handled outside of a dependency, i.e. in the global context: should those be allowed ? Or should a local variable in the topmost level (outside any recipes) be private to that level ? In the later case, both :global and :local should not be allowed outside a dependency, since it's context would be always global. I think the second mode would be better and more intuitive. The :export command would possibly be deprecated since in a subrecipe, the :global command would export into the global context as expected. Or even better then deprecating :export, use it to assign a variable to the global context, even if it was local. This should help handling older recipes, but a "deprecated" warning should be printed. Any comments ? ------------- Best regards, Steve Howe mailto:ho...@ca... |
From: Bram M. <Br...@mo...> - 2003-01-14 11:25:43
|
Steve Howe wrote: > Well at least :export should allow multiple arguments I think :) So > that no multi export statements need to be used. > The same would be nice to :global and :local:. ":export", ":global" and ":local" do accept multiple arguments. > BM> Global in the commands at the toplevel of the recipe, yes. But also in > BM> the scope of the build commands in a dependency? Suppose you have > BM> something like this: > > BM> # Most things compile with some optimization > BM> CFLAGS = -O2 > > BM> foo.o : foo.c > BM> # Compile this one with extra optimization > BM> CFLAGS = -O4 > BM> :do compile $source > > BM> bar.o : bar.c > BM> :do compile $source > > BM> You don't know in what order foo.c and bar.c will be compiled, thus > BM> making the value of CFLAGS global makes it very unpredictable. > I think if the user don't want that to become global, he should flag > that with :local. > A-A-P should look into the local scope, then then global, for any > variables (local always override global). It appears you are looking at this as if the recipe is a programming language and see the build commands of a dependency as if they are in a function. Let's forget about the current meaning of ":global", ":local" and ":export" for a moment. We can make these work as we want or even omit them. We first need to find out how it should work, then define commands to make it work that way. Currently the variables are passed on following the dependency tree and ":update" commands. This has subtle differences from how scopes work in a programming language. The invoked build commands obtain the variables of the scope where they were invoked from: ABC = one foo.x : foo.y ABC = two :update foobar.x foobar.x : foobar.y :print $ABC What should be printed here? 1. "one", the global value of $ABC 2. "two", the value of $ABC before the build commands for "foobar.x" were invoked by the ":update" command. There is something to say for both alternatives. Sometimes you want to use the global value, sometimes the value passed from the build commands where the ":update" appears. Another issue is whether "ABC" is used in several recipes or not. recipe one.aap: :child two.aap ABC = in one.aap foo.x : foo.y ABC = from foo :update foobar.x recipe two.aap ABC = in two.aap foobar.x : foobar.y :print $ABC Now there are three values of $ABC to choose from. If ABC is global to all recipes it is changed after the ":child" command and the value "in two.aap" is lost. This is a how environment variables work (more or less). This is getting complicated, there are too many alternatives. We should stick to a known mechanism that programmers know about, otherwise it will be very difficult to explain and understand. What known mechanism would be the best to use for A-A-P recipes? Steve suggested to use the Python mechanism: > Global variables are defined in a context called "global" that is > always available to all recipes and dependencies. > > Local variables override global variables but are local to the the > dependency being run. > > Local variables override global variables, i.e., first lookup on local > context; if not found, look into global. A problem with this mechanism is that it is impossible to pass a variable value to the build commands invoked with ":update" or ":do". Everything would have to be done with attributes. Another issue: recipe main.aap: ABC = something :child foo.aap :update foo.x recipe foo.aap: foo.x : foo.y :print $ABC Should this result in an error message? I think most people would expect $ABC to be obtained from the parent recipe automatically. Another mechanism that could be used is adding something to the variable name to indicate its scope. Python uses a dot for this. We could use scopes like: name no scope, variable local to recipe or build commands recipe.name scope of the recipe the build commands appear in caller.name scope of the invoking build commands callee.name scope of the build commands that a following ":do" or ":update" will invoke. Example: ABC = in recipe foo.x : foo.y ABC = in dependency :print $recipe.ABC # will print "in recipe" callee.XYZ = from dependency :update foobar.x foobar.x : foobar.y :print $caller.ABC # will print "in dependency" :print $XYZ # will print "from dependency" A problem with this mechanism is that most people expect a global value to be used without "recipe." prepended. We could use the Python "global" command to distinguish between global and local variables, but I have always found that illogical, most non-Python programmers will be confused. Additionally we could use "parent.name" to access variables in a parent recipe. "child.name" would not work, because there can be several children and we don't know which one. Opinions? I'm especially interested to find out if there is an existing language or tool that we can use as an example. -- ARTHUR: CHARGE! [The mighty ARMY charges. Thundering noise of feet. Clatter of coconuts. Shouts etc. Suddenly there is a wail of a siren and a couple of police cars roar round in front of the charging ARMY and the POLICE leap out and stop them. TWO POLICEMAN and the HISTORIAN'S WIFE. Black Marias skid up behind them.] HISTORIAN'S WIFE: They're the ones, I'm sure. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Br...@mo... -- http://www.moolenaar.net \\\ /// Creator of Vim - Vi IMproved -- http://www.vim.org \\\ \\\ Project leader for A-A-P -- http://www.a-a-p.org /// \\\ Lord Of The Rings helps Uganda - http://iccf-holland.org/lotr.html /// |
From: Steve H. <ho...@ca...> - 2003-01-14 15:24:20
|
Hello Bram, Tuesday, January 14, 2003, 8:25:37 AM, you wrote: BM> Steve Howe wrote: >> Well at least :export should allow multiple arguments I think :) So >> that no multi export statements need to be used. >> The same would be nice to :global and :local:. BM> ":export", ":global" and ":local" do accept multiple arguments. Huh, I must have made some confusion. I had the impression that only single arguments could be used... BM> It appears you are looking at this as if the recipe is a programming BM> language and see the build commands of a dependency as if they are in a BM> function. In fact, I think that's what I really thought on :) BM> Steve suggested to use the Python mechanism: >> Global variables are defined in a context called "global" that is >> always available to all recipes and dependencies. >> >> Local variables override global variables but are local to the the >> dependency being run. >> >> Local variables override global variables, i.e., first lookup on local >> context; if not found, look into global. BM> A problem with this mechanism is that it is impossible to pass a BM> variable value to the build commands invoked with ":update" or ":do". BM> Everything would have to be done with attributes. In this case, you seem to be considering a build command like a function, i.e. something that can receive parameters. Is this currently possible ? if not, that would require a specific syntax, I think. Something like passing a dictionary or list to each build command. This would be Currently, a build A way to pass values to these build commands is simply setting a global variable value. The global variables will be available to these build commands too, and everything should work. By using a :global command, the variable could even be declared locally in a build command. BM> Another issue: BM> recipe main.aap: BM> ABC = something BM> :child foo.aap BM> :update foo.x BM> recipe foo.aap: BM> foo.x : foo.y BM> :print $ABC BM> Should this result in an error message? I think most people would BM> expect $ABC to be obtained from the parent recipe automatically. That should not raise an error message: the global scope is passed to the child recipe, or the child recipe is imported into the current context (whatever is the method used) and thus inherits the global scope. I don't see why this would cause an error. BM> Another mechanism that could be used is adding something to the variable BM> name to indicate its scope. Python uses a dot for this. We could use BM> scopes like: BM> name no scope, variable local to recipe or build commands BM> recipe.name scope of the recipe the build commands appear in BM> caller.name scope of the invoking build commands BM> callee.name scope of the build commands that a following BM> ":do" or ":update" will invoke. BM> Example: BM> ABC = in recipe BM> foo.x : foo.y BM> ABC = in dependency BM> :print $recipe.ABC # will print "in recipe" BM> callee.XYZ = from dependency BM> :update foobar.x BM> foobar.x : foobar.y BM> :print $caller.ABC # will print "in dependency" BM> :print $XYZ # will print "from dependency" BM> A problem with this mechanism is that most people expect a global value BM> to be used without "recipe." prepended. We could use the Python BM> "global" command to distinguish between global and local variables, but BM> I have always found that illogical, most non-Python programmers will be BM> confused. I agree. BM> Additionally we could use "parent.name" to access variables in a parent BM> recipe. "child.name" would not work, because there can be several BM> children and we don't know which one. I have considered this model, but this lead us to another issue, which is how to name the modules. Most modules will be called "main.aap", and that would cause naming conflicts. How should the left part of the full variable name be called ? Should we explicitly declare a name for each module ? This seems to confusing and a lot of work for every script. BM> Opinions? I'm especially interested to find out if there is an existing BM> language or tool that we can use as an example. Well, Python uses more or less my example :) CMake http://www.cmake.org/ only seems to have a global scope and the sub scripts inherit the environment. I think having a local scope can have it's advantages over that scheme. SCons (http://www.scons.org) are pythin scripts so they follow python conventions. Ant (http://jakarta.apache.org/ant/manual/index.html) inherits teh XML scope, which is global and inheriteable. I didn't look much futher then that, but it seems like most tools have a single, global scope and the subscripts just inherit it. ------------- Best regards, Steve Howe mailto:ho...@ca... |
From: Bram M. <Br...@mo...> - 2003-01-15 14:38:29
|
Steve Howe wrote: > BM> A problem with this mechanism is that it is impossible to pass a > BM> variable value to the build commands invoked with ":update" or ":do". > BM> Everything would have to be done with attributes. > In this case, you seem to be considering a build command like a > function, i.e. something that can receive parameters. > > Is this currently possible ? if not, that would require a specific > syntax, I think. Something like passing a dictionary or list to each > build command. This would be > > Currently, a build [something is missing here...] > A way to pass values to these build commands is simply setting a > global variable value. The global variables will be available to these > build commands too, and everything should work. By using a > :global command, the variable could even be declared locally in a > build command. The above is contradictory. Here is the example again I gave in a previous message: recipe main.aap: ABC = something :child foo.aap :update foo.x recipe foo.aap: foo.x : foo.y :print $ABC - If the build commands for foo.x are considered like they are in a function, they would not be able to access the ABC variable, since it only exists in another recipe: main.aap. - If the build commands use the global scope and can access the ABC variable there can't be a local variable ABC with a different value (without additional scope specs). I think we should come to the conclusion that using the scope rules of a programming language (like C) will not work for recipes. There are two scope issues here: Static scope: The scope of each recipe file and the scope of build commands defined in them for dependencies/rules/actions. Dynamic scope: The chain of execution, starting at the toplevel recipe, following the building of targets that depend on sources and ":do" and ":update" commands. Let's start with the obvious rule that each recipe has a scope that can contain variables that are kept until Aap has finished. Thus there can be a "SOURCE" variable in main.aap and another "SOURCE" variable in foo.aap. This allows using variables that are local to a recipe and are not available to a parent recipe. There are these two demands for variables in recipes to start with: - Some variables must be global to all recipes, for example CFLAGS. Normally these are defined at the toplevel and inherited by all child recipes. - There could be a child that defines a variable ABC that is to be used in all its children. Thus we have a nested scope of recipes: variables are visible in all children and grandchildren. Example: main.aap: SOURCE = one.c two.c :child foo.aap FLAGS = -boo prog: $SOURCE :do build $source foo.aap: SOURCE = two.c.in two.c: $SOURCE :sys convert $FLAGS < $SOURCE > two.c When reading the recipes there are no conflicts: $FLAGS is only given a valuein main.aap, $SOURCE is set to a different value in foo.aap, thus ignoring the value from the parent. It's clear that the value of $SOURCE in main.aap remains unchanged, it is in a different Static scope. Now we start building "prog". "prog" depends on "one.c" and "two.c". "two.c" depends on "two.c.in". This causes the build commands for two.c to be executed. Now, what variable values will it get? VARIABLE DEFINED IN SHOULD USE $SOURCE main.aap and foo.aap foo.aap $FLAGS main.aap main.aap The chain of execution starts at the toplevel, in main.aap. The build commands are in foo.aap. When invoking the build commands, we want to pass on $FLAGS through the Dynamic scope. But we want to use the $SOURCE value from the static scope. How this worked so far was to take the variables from the dynamic scope ($SOURCE and $FLAGS), then use the variables from the static scope ($SOURCE), so that these overrule the variables of the dynamic scope. We end up with the values we wanted to use. Now the exceptions: 1. A child recipe wants to change a variable that was defined by the parent. For example add "-DFOOBAR" to $CPPFLAGS. This new value should also be used in build commands of the parent recipe and other children. 2. The build commands want to change a variable in the recipe where it was defined. For example, the "gcccheck" target finds out if gcc is being used and sets $HASGCC to "yes" or "no". Other build commands in the same recipe may then use $HASGCC to decide what to do (this is used in the startup recipe default.aap). 3. The build commands want to return a value to where it was invoked from. For example, an action that wants to return an error message to where the ":do" command was used. So far this was done with the ":export" command. Unfortunately, it's not always clear what you want to export to: To the Static scope (as in example 1 and 2) or to the Dynamic scope (as in example 3). It seems we were already using an automatic mechanism to find out which scope to use for a variable: We knew $SOURCE of the foo.aap recipe was to be used (see SHOULD USE above) because $SOURCE was given a value in foo.aap. If we use this consistently, assigning a value to SOURCE in build commands also means this is a local variable and its value is not set in another scope, except that it is passed on along the chain of execution. Example: SOURCE = nothing 1.out : 1.in SOURCE = one :do something 2.out : 2.in SOURCE = two :do something After building "1.out" and "2.out" the value of $SOURCE in the scope of the recipe will still be "nothing". Now, how do we handle the second exception above, to let an action change the value of $HASGCC in the recipe scope? - Use ":export HASGCC", as it was done until now. This has the already mentioned disadvantage of not being clear what the value is to be exported to. - Specify that "HASGCC" a is variable in the recipe scope. This could be done with ":global HASGCC", similar to how Python works. The second alternative sounds good. We can use the similar mechanism for the first example: :global CPPFLAGS CPPFLAGS += -DFOOBAR Thus ":global" is used to specify that a variable is not local but comes from the Static scope it exists in. A complication: In build commands it should probably both be possible to specify that either a variable at the recipe level is to be used or in the toplevel scope. Perhaps we should use ":toplevel" for using the toplevel scope and ":global" for the recipe of build commands? (hopefully there are better names!) ":global" doesn't work for the third example. It does sound logical to use the ":export" command for this: :action build outfile infile MSG = "" @if something-went_wrong: MSG = "oops!" :export MSG This would not affect a MSG variable in the static recipe scope, $MSG is only set in the scope where the action was triggered, where the ":do" command is. There is one more issue: sometimes you really want to keep a variable local (to the recipe or to build commands) and not make it available in children or executed build commands. In that case ":local" could be used. Example: main.aap: :local ABC ABC = something :child foo.aap foo.aap: ABC ?= another :print $ABC This would print "another". Implementing all this won't be easy, but I find it very important that recipes work as most people would expect and all necessary mechanisms are available. Sorry for the long text, hope you are still with me. Comments? -- System administrators are just like women: You can't live with them and you can't live without them. /// Bram Moolenaar -- Br...@mo... -- http://www.moolenaar.net \\\ /// Creator of Vim - Vi IMproved -- http://www.vim.org \\\ \\\ Project leader for A-A-P -- http://www.a-a-p.org /// \\\ Lord Of The Rings helps Uganda - http://iccf-holland.org/lotr.html /// |
From: Steve H. <ho...@ca...> - 2003-01-15 19:28:32
|
Hello Bram, Wednesday, January 15, 2003, 11:38:23 AM, you wrote: BM> Steve Howe wrote: BM> The above is contradictory. Here is the example again I gave in a BM> previous message: BM> recipe main.aap: BM> ABC = something BM> :child foo.aap BM> :update foo.x BM> recipe foo.aap: BM> foo.x : foo.y BM> :print $ABC BM> - If the build commands for foo.x are considered like they are in a BM> function, they would not be able to access the ABC variable, since it BM> only exists in another recipe: main.aap. BM> - If the build commands use the global scope and can access the ABC BM> variable there can't be a local variable ABC with a different value BM> (without additional scope specs). My original idea was that all recipes (main and children) share a common "global" scope. If that is true, ABC will be passed to all child recipes and ABC will be available. However, that will lead us to another trouble situation, where a child recipe could mess the main recipe's variables. I'll discuss this further below. [...] (I have omitted Bram's original proposal to keep the message shorter) BM> Sorry for the long text, hope you are still with me. Oh, of course I am you and I'm very interested in the subject. I agree with your points and you have raised several good case situations. I have an enhanced proposal that builds on your ideas and concerns but I'm not sure if you'll like it. After reading it your message, I came to the conclusion that in fact we have (or should have, in implementation) 3 scopes: "global", "recipe" and "local" (the names could probably be others I guess): Global: scope available to all recipes. Module: scope available to the current recipe. Local: scope available to the current build. The precedence of scopes would be this: local->module->global (local overrides module which overrides global) To specify which scope is to be used, we could qualify the scope using statements such as: local.myvar (refers to myvar from the [local] scope) recipe.myvar (refers to myvar from the [recipe] scope) global.myvar (refers to myvar from the [global] scope) When referring to a variable without scope, the [local] scope should be looked for; if the variable name is not found, the [recipe] scope; if not found, the [global] scope; if not found, then an Exception should be raised. If a variable is assigned without any scope, and is outside a build command, it will belong to [module]; if used in a build command, it will belong to [local]. Only variables declared as [global] (i.e. global.myvar) should be inherited by child recipes. Of course the CFLAGS, CC, and other important/default variables that are global in nature should be declared by A-A-P as being in the global context of course. This would make them available to child recipes too, and even if them are redefined in a child recipe: CFLAGS = -O2 ... they would belong to [module] and this wouldn't modify the global variable. But if the global variable needed to be modified, then something as global.CFLAGS = -O4 could be used. The :global, :local and :export commands could even be deprecated by introduction this scheme. This would avoid the :export problems, since the exact scope to export to is specified. Child recipes could change global variables or have their own in their [recipe] or [local] scopes, whose would override the [global] scope. It would avoid the scopes conflicts but keep flexibility. I think this covers all situations... doesn't it ? Can you think of some situation it wouldn't cover ? Also it specifies a clear way of declaring variables in any scope, and also a clear way of referring to any scope. Any comments ? ------------- Best regards, Steve Howe mailto:ho...@ca... |
From: Bram M. <Br...@mo...> - 2003-01-15 22:53:24
|
Steve Howe wrote: > After reading it your message, I came to the conclusion that in fact > we have (or should have, in implementation) 3 scopes: "global", > "recipe" and "local" (the names could probably be others I guess): > > Global: scope available to all recipes. > Module: scope available to the current recipe. > Local: scope available to the current build. OK. Keep in mind that for the module scope there is one for each recipe, and for the local scope there is one for each set of build commands. The module scope of the toplevel recipe is equal to the global scope, right? > The precedence of scopes would be this: > local->module->global (local overrides module which overrides global) That is the same as what I proposed. And a variable exists in a scope if it was assigned a value in that scope. > To specify which scope is to be used, we could qualify the scope using > statements such as: > > local.myvar (refers to myvar from the [local] scope) > recipe.myvar (refers to myvar from the [recipe] scope) > global.myvar (refers to myvar from the [global] scope) > > When referring to a variable without scope, the [local] scope should > be looked for; if the variable name is not found, the [recipe] scope; > if not found, the [global] scope; if not found, then an Exception > should be raised. Thus in build commands it would be possible to do: recipe.ABC = value :print $ABC This prints "value" if there is no local ABC variable. > If a variable is assigned without any scope, and is outside a build > command, it will belong to [module]; if used in a build command, it > will belong to [local]. Only variables declared as [global] (i.e. > global.myvar) should be inherited by child recipes. Hmm, I think it's more logical to let child recipes also inherit the variables defined in the recipe. Thus if a child recipe sets CFLAGS, a child of this recipe uses this value instead of the global CFLAGS (unless it uses $global.CFLAGS). Thus a variable without a specified scope would follow this search order: - current build commands scope - current recipe - parent recipe (and grandparents, etc.) - global value (in toplevel recipe) What is missing here is the scope of the invoking build commands. Thus this would not work: foo.x : foo.y CFLAGS = -O4 :do build foo.y The action invoked by the ":do" command can't access the CFLAGS value set just above it. I wonder if this is a problem. It would require passing the value with an attribute: {var_CFLAGS = -O4}. > Of course the CFLAGS, CC, and other important/default variables that > are global in nature should be declared by A-A-P as being in the > global context of course. This would make them available to child > recipes too, and even if them are redefined in a child recipe: I don't think we should make exceptions for a few variables. That would make it very complicated. It it appears there is no reason to make an exception for a variable like CFLAGS. > CFLAGS = -O2 > > ... they would belong to [module] and this wouldn't modify the global > variable. But if the global variable needed to be modified, then > something as > > global.CFLAGS = -O4 > > could be used. OK, but how change the value of CFLAGS of the parent recipe, if this is not the toplevel recipe? There could be three or more levels of children. Similarly, how could build commands return a value to the build commands that invoked them (with ":update" or ":do")? > The :global, :local and :export commands could even be deprecated by > introduction this scheme. > > This would avoid the :export problems, since the exact scope to export > to is specified. Child recipes could change global variables or have > their own in their [recipe] or [local] scopes, whose would override > the [global] scope. It would avoid the scopes conflicts but keep > flexibility. > > I think this covers all situations... doesn't it ? Can you think of > some situation it wouldn't cover ? Also it specifies a clear way of > declaring variables in any scope, and also a clear way of referring to > any scope. I think we still need ":export", to be able to give a variable value to the parent recipe or invoking build commands. An alternative would be to use "parent.var". Is that better than using ":export"? At least this also gives the opportuinity to obtain a value from the invoking build commands. Otherwise, using global.var and recipe.var sounds better than using a separate ":global var" command. -- THEOREM: VI is perfect. PROOF: VI in roman numerals is 6. The natural numbers < 6 which divide 6 are 1, 2, and 3. 1+2+3 = 6. So 6 is a perfect number. Therefore, VI is perfect. QED -- Arthur Tateishi /// Bram Moolenaar -- Br...@mo... -- http://www.moolenaar.net \\\ /// Creator of Vim - Vi IMproved -- http://www.vim.org \\\ \\\ Project leader for A-A-P -- http://www.a-a-p.org /// \\\ Lord Of The Rings helps Uganda - http://iccf-holland.org/lotr.html /// |
From: Steve H. <ho...@ca...> - 2003-01-16 02:15:16
|
Hello Bram, Wednesday, January 15, 2003, 7:52:08 PM, you wrote: BM> Steve Howe wrote: >> After reading it your message, I came to the conclusion that in fact >> we have (or should have, in implementation) 3 scopes: "global", >> "recipe" and "local" (the names could probably be others I guess): >> >> Global: scope available to all recipes. >> Module: scope available to the current recipe. >> Local: scope available to the current build. BM> OK. Keep in mind that for the module scope there is one for each BM> recipe, and for the local scope there is one for each set of build BM> commands. BM> The module scope of the toplevel recipe is equal to the global scope, BM> right? Yes, that probably would be the best thing to do. Then the [recipe] scope would always be empty for the topmost recipe, or it would be a duplicate of the [global] scope (for non-qualified variables) ? The second form should be more compatible and intuitive. I'll talk more about it below. >> The precedence of scopes would be this: >> local->module->global (local overrides module which overrides global) BM> That is the same as what I proposed. And a variable exists in a scope BM> if it was assigned a value in that scope. Exactly :) It will belong to that scope's dictionary. >> To specify which scope is to be used, we could qualify the scope using >> statements such as: >> >> local.myvar (refers to myvar from the [local] scope) >> recipe.myvar (refers to myvar from the [recipe] scope) >> global.myvar (refers to myvar from the [global] scope) >> >> When referring to a variable without scope, the [local] scope should >> be looked for; if the variable name is not found, the [recipe] scope; >> if not found, the [global] scope; if not found, then an Exception >> should be raised. BM> Thus in build commands it would be possible to do: BM> recipe.ABC = value BM> :print $ABC BM> This prints "value" if there is no local ABC variable. Yes too :) >> If a variable is assigned without any scope, and is outside a build >> command, it will belong to [module]; if used in a build command, it >> will belong to [local]. Only variables declared as [global] (i.e. >> global.myvar) should be inherited by child recipes. BM> Hmm, I think it's more logical to let child recipes also inherit the BM> variables defined in the recipe. Thus if a child recipe sets CFLAGS, BM> a child of this recipe uses this value instead of the global CFLAGS BM> (unless it uses $global.CFLAGS). That's something I didn't think about but it's important. So all child recipes inherit a [parent] scope which is equal to the [recipe] scope of the calling recipe ? Would a grandchild recipe inherit it's grandfather scope ? How far would a scope be nested ? If there is full nesting, then probably we would not need to initiate the topmost variables in the [global] scope because doing so in the [recipe] scope for the topmost recipe would be passed to all children recipes. This would be better then my initial proposal. On full nesting, there is still the problem of how "far" an assignment will reach. Only the parent level (1 level above) recipe ? Or as far as the variable was originally declared ? Until the topmost level is not concise, since it would be the same as a [global] scope. Whatever do we use, I think that reading and from and assigning to a variable should be concise (i.e. have the same behavior and be from the same scope). BM> Thus a variable without a specified scope would follow this search BM> order: BM> - current build commands scope BM> - current recipe BM> - parent recipe (and grandparents, etc.) BM> - global value (in toplevel recipe) BM> What is missing here is the scope of the invoking build commands. Thus BM> this would not work: BM> foo.x : foo.y BM> CFLAGS = -O4 BM> :do build foo.y BM> The action invoked by the ":do" command can't access the CFLAGS value BM> set just above it. I wonder if this is a problem. It would require BM> passing the value with an attribute: {var_CFLAGS = -O4}. Python had similar problems. I think we have to care about this kind of scope. Probably build commands would have to inherit a [parentbuild] scope, and have a [local] scope too. What do you think ? This is similar to nested functions in python. >> Of course the CFLAGS, CC, and other important/default variables that >> are global in nature should be declared by A-A-P as being in the >> global context of course. This would make them available to child >> recipes too, and even if them are redefined in a child recipe: BM> I don't think we should make exceptions for a few variables. That would BM> make it very complicated. It it appears there is no reason to make an BM> exception for a variable like CFLAGS. Oh, you're right. I just wanted to make sure that those variables are able to be modified globally. But if we declare the topmost recipe scope as [global], or better yet, use the above mentioned [parent] scope, this would be not necessary. >> CFLAGS = -O2 >> >> ... they would belong to [module] and this wouldn't modify the global >> variable. But if the global variable needed to be modified, then >> something as >> >> global.CFLAGS = -O4 >> >> could be used. BM> OK, but how change the value of CFLAGS of the parent recipe, if this is BM> not the toplevel recipe? There could be three or more levels of BM> children. Using the above [parent] scope should solve this. However, if we had not the [parent] scope, this could be achieved by setting a global variable that would be used to set CFLAGS in the parent: main.aap: CFLAGS = -02 :child child.aap child.aap: :child grandchild.aap CFLAGS = global.CFLAGS granchild.aap: global.CFLAGS = -O4 BM> Similarly, how could build commands return a value to the build commands BM> that invoked them (with ":update" or ":do")? Probably the best way would be setting a variable like $result to the [local] scope of the build command. By the way, a [local] scope outside a build command should match the [recipe] scope. BM> I think we still need ":export", to be able to give a variable value to BM> the parent recipe or invoking build commands. An alternative would be BM> to use "parent.var". Is that better than using ":export"? At least BM> this also gives the opportuinity to obtain a value from the invoking BM> build commands. I prefer the alternative. It's more logical, and flexible. Talking about :export, I think we should deprecate these commands but still accept them for backwards compatibility: :global myvar # same as global.myvar :local myvar # same as local.myvar :export myvar # same as parent.myvar BM> Otherwise, using global.var and recipe.var sounds better than using a BM> separate ":global var" command. I agree, but maybe it would be a good idea keeping such commands to not break existing recipes. About the scope names: do you like global, recipe, parent and local ? Alternative names could be: * self, for [recipe] * inherited, for [parent] Any names would be good for me, as they are intuitive. Any comments ? ------------- Best regards, Steve Howe mailto:ho...@ca... |