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