can't access args from a template if it is imported

36 views
Skip to first unread message

Shang Yu

unread,
Apr 2, 2025, 1:19:26 AMApr 2
to gn-dev
Hi guys,
the following script works

declare_args() {
  arg0=1
}
template("t1") {
  forward_variables_from(invoker, "*")
  print(arg0)
}
t1("hello"){}

if I move the template into t1.gni and import it as follow, it will complain Undefined identifier of arg0

declare_args() {
  arg0=1
}
import("t1.gni")
t1("hello"){}

how does this happen?

David Turner

unread,
Apr 2, 2025, 11:05:20 AMApr 2
to Shang Yu, gn-dev
This is due to the way GN implements lookups / evaluation scopes. It is intentional and also why an imported .gni file doesn't see any declarations that happened before in the file that imports it. This is important for correctness and hermeticity of definitions.

In a nutshell, a template definition records the chain of scopes (evaluation contexts) that existed when it was defined, and uses that at call time.

Because each template definition that is recorded by GN has a chain of scopes (evaluation contexts) that is used to perform lookups at runtime.
Each scope contains a series of named variable definitions and named template definitions.

GN starts by evaluating BUILDCONFIG.gn and records the result in a [TOP_LEVEL_SCOPE]. This is where all you global variables live target_cpu live.

GN then creates a child scope for each BUILD.gn file that it evaluates.  [TOP_LEVEL_SCOPE --> BUILD_GN_SCOPE ]

When you import a .gni file, from the BUILD.gn file, this first creates a new sibling scope (child from the same parent scope), i.e. [TOP_LEVEL_SCOPE --> IMPORTED_GNI_SCOPE], and the template records this chain. It doesn't know about BUILD_GN_SCOPE at all.

Later, when you invoke the template, the template scope chain is used for evaluation, not the one from the call site, and will not find a variable named "arg0" in either TOP_LEVEL_SCOPE or IMPORTED_GNI_SCOPE. I am omitting details about invoker scopes here for simplicity.

If you want to access arg0 from the template, you need to import another .gni file that defines it in a declare_args() block from the .gni file that defines the template, and the lookup will work, because the template's scope chain will look like: TOP_LEVEL_SCOPE --> DECLARE_ARGS_SCOPE --> TEMPLATE_SCOPE

If you want to access arg0 from the BUILD.gn file, you will need to import the same .gni file that declares it to have: TOP_LEVEL_SCOPE --> DECLARE_ARGS_SCOPE --> BUILD_GN_SCOPE

Hope this helps,

- Digit



Charles Nicholson

unread,
Apr 2, 2025, 12:08:00 PMApr 2
to David Turner, Shang Yu, gn-dev
I wish this email was part of the official GN documentation somewhere :)

To unsubscribe from this group and stop receiving emails from it, send an email to gn-dev+un...@chromium.org.

Dirk Pranke

unread,
Apr 2, 2025, 6:04:43 PMApr 2
to Charles Nicholson, David Turner, Shang Yu, gn-dev
It is a good explanation. I've attempted to improve the documentation to incorportate it here: https://gn-review.googlesource.com/c/gn/+/18480. Feedback welcome :).

-- Dirk
Reply all
Reply to author
Forward
0 new messages