[A-A-P-develop] Re: Scopes (long)
Brought to you by:
vimboss
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 /// |