Skip to content

Commit d43e8ca

Browse files
committed
another pass at documenting variable scope
1 parent 8677434 commit d43e8ca

File tree

1 file changed

+46
-39
lines changed

1 file changed

+46
-39
lines changed

doc/design_notes.md

+46-39
Original file line numberDiff line numberDiff line change
@@ -240,62 +240,69 @@ PS: in writing this section I noticed that cmd has
240240

241241
## Variable scope
242242

243-
Ninja syntactic structures (`build`, `rule`) are at some level just
244-
lists of key-value bindings that ultimately combine to set properties on
245-
individual build steps, such as the `command = ...` command line.
243+
Ninja syntactic structures (`build`, `rule`) are at some level just lists of
244+
key-value bindings that ultimately combine to set properties on individual build
245+
steps, such as the `command = ...` command line.
246246

247247
Additionally, bindings can be referenced by other bindings via the `$foo` or
248-
`${foo}` syntax. This means variable lookup can recurse through a hierarchy of
249-
scopes. The intent was this was simple enough that the
250-
behavior is straightforward, which in retrospect's insight really just means
251-
"underspecified". (Forgive me! I hacked Ninja together in a few weekends and
252-
made the common mistake of "this is so simple I don't really need to think it
253-
through".)
248+
`${foo}` syntax. This means variable lookup traverses through a hierarchy of
249+
scopes. The intent was this was simple enough that the behavior is
250+
straightforward, which in retrospect's insight really just means
251+
"underspecified". (Forgive me! I hacked Ninja together in a few weekends and
252+
made the all too easy mistake of "this is so simple I don't really need to think
253+
it through".)
254254

255-
Aside from a conceptual model of the system's rules, ultimately what matters is
256-
what Ninja files in the wild do, which per [Hyrum's Law](https://www.hyrumslaw.com/)
257-
contain whatever the Ninja implementation allows. However, I don't think it's
258-
really worth fleshing out all the idiosyncracies of Ninja's implementation as long
259-
as existing builds work.
255+
### Basics
260256

261-
Consider the following build file:
257+
First, here's a high level description that conveys the idea but omits details.
258+
259+
Consider a build file like the following:
262260

263261
```
264262
var = $A
265263
266264
rule r
267265
command = $B
268266
269-
var = $C
270-
271-
build output-$D: r input-$E
272-
var2 = $F
273-
var2 = $G
267+
build output-$C: r
268+
var2 = $D
274269
```
275270

276-
These are the scopes to consider, in order of nesting:
277-
1. The toplevel scope, the unindented lines marked `$A` and `$C`.
278-
1. The build variable scope, the indented lines in the `build` block.
279-
1. The build file list scope, the implicit `$in` etc. variables.
280-
1. The rule scope, the indented lines in the `rule` block.
271+
The `build` block stamps out a build step using the rule `r`, and the properties
272+
of that build step are found by looking up attributes like `command`.
273+
274+
When evaluating the expressions marked `$A`/`$B`/`$C`/`$D` above, these are the
275+
scopes to consider, in order of nesting:
281276

282-
In summary, lookup for bindings in each can refer to the scopes above them in the
283-
list.
277+
1. The toplevel scope, which defines `var` above.
278+
1. The build variable scope, which defines `var2` above.
279+
1. The build file list scope, which defines the implicit `$in` etc. variables.
280+
1. The rule scope, which defines `command` above.
284281

285-
Working it through on this example:
282+
References found in each may refer to the scopes above in the list. For example,
283+
at the time the `command = ...` is evaluated, the in-scope variables include
284+
`$in`, `$var2`, and `$var`.
286285

287-
- Lookup for a toplevel binding (line `$A` and `$C`) uses the scope
288-
of toplevel bindings. Consequence: the binding on line `$C` can refer
289-
to the first, e.g. if you wrote `var = ${var}2`.
286+
### Details
290287

291-
- Lookup for a build variable (for the value of `$F` and `$G`) also only refer
292-
to toplevel bindings. (Lookup for line `$G` will not see the binding in line `$F`,
293-
which differs from toplevel, whoops.)
288+
Unfortunately, [Hyrum's Law](https://www.hyrumslaw.com/) means that Ninja files
289+
in the wild depend on whatever the Ninja implementation allows, which is more
290+
complex than the above. I don't think it's really worth fleshing out all the
291+
idiosyncracies of Ninja's implementation as long as existing builds work, but
292+
some details do matter.
294293

295-
- Lookup for input/output files (`output-$D`, `input-$E`) refer to build scope
296-
(the value bound in line `$G`, which shadows line `$F`), and then toplevel.
294+
In particular, Ninja has particular behaviors around variable references found
295+
within the same scope. Consider:
297296

298-
- Lookup for rule variables can refer to `$in`/`$out`, build scope, then toplevel.
297+
```
298+
var = 1
299+
var = ${var}2
300+
rule ...
301+
command = echo $depfile
302+
depfile = abc
303+
```
299304

300-
All the lookups happen when the `build` block is visited, which means even
301-
if the `rule` block referenced `var` it would see the binding from line `$C`.
305+
In Ninja, the value of `var` is `12`: the assignment proceeds from top down. But
306+
within a `rule` block, the variable lookup of `$depfile` instead refers forward
307+
to `abc`, which means there is a possibility of circular references, along with
308+
logic that attempts to detect and warn in those cases(!).

0 commit comments

Comments
 (0)