| // Copyright 2014 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "gn/template.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "gn/err.h" | |
| #include "gn/functions.h" | |
| #include "gn/parse_tree.h" | |
| #include "gn/scope.h" | |
| #include "gn/scope_per_file_provider.h" | |
| #include "gn/settings.h" | |
| #include "gn/trace.h" | |
| #include "gn/value.h" | |
| #include "gn/variables.h" | |
| Template::Template(const Scope* scope, const FunctionCallNode* def) | |
| : closure_(scope->MakeClosure()), definition_(def) {} | |
| Template::Template(std::unique_ptr<Scope> scope, const FunctionCallNode* def) | |
| : closure_(std::move(scope)), definition_(def) {} | |
| Template::~Template() = default; | |
| Value Template::Invoke(Scope* scope, | |
| const FunctionCallNode* invocation, | |
| const std::string& template_name, | |
| const std::vector<Value>& args, | |
| BlockNode* block, | |
| Err* err) const { | |
| // Don't allow templates to be executed from imported files. Imports are for | |
| // simple values only. | |
| if (!EnsureNotProcessingImport(invocation, scope, err)) | |
| return Value(); | |
| ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE_TEMPLATE, template_name); | |
| trace.SetToolchain(scope->settings()->toolchain_label()); | |
| // First run the invocation's block. Need to allocate the scope on the heap | |
| // so we can pass ownership to the template. | |
| std::unique_ptr<Scope> invocation_scope = std::make_unique<Scope>(scope); | |
| if (!FillTargetBlockScope(scope, invocation, template_name, block, args, | |
| invocation_scope.get(), err)) | |
| return Value(); | |
| { | |
| // Don't allow the block of the template invocation to include other | |
| // targets configs, or template invocations. This must only be applied | |
| // to the invoker's block rather than the whole function because the | |
| // template execution itself must be able to define targets, etc. | |
| NonNestableBlock non_nestable(scope, invocation, "template invocation"); | |
| if (!non_nestable.Enter(err)) | |
| return Value(); | |
| block->Execute(invocation_scope.get(), err); | |
| if (err->has_error()) | |
| return Value(); | |
| } | |
| // Set up the scope to run the template and set the current directory for the | |
| // template (which ScopePerFileProvider uses to base the target-related | |
| // variables target_gen_dir and target_out_dir on) to be that of the invoker. | |
| // This way, files don't have to be rebased and target_*_dir works the way | |
| // people expect (otherwise its to easy to be putting generated files in the | |
| // gen dir corresponding to an imported file). | |
| Scope template_scope(closure_.get()); | |
| template_scope.set_source_dir(scope->GetSourceDir()); | |
| // Track the invocation of the template on the template's scope | |
| template_scope.SetTemplateInvocationEntry( | |
| template_name, args[0].string_value(), invocation->GetRange().begin()); | |
| // Propagate build dependency files from invoker scope (template scope already | |
| // propagated via parent scope). | |
| template_scope.AddBuildDependencyFiles( | |
| invocation_scope->CollectBuildDependencyFiles()); | |
| ScopePerFileProvider per_file_provider(&template_scope, true); | |
| // Targets defined in the template go in the collector for the invoking file. | |
| template_scope.set_item_collector(scope->GetItemCollector()); | |
| // We jump through some hoops to avoid copying the invocation scope when | |
| // setting it in the template scope (since the invocation scope may have | |
| // large lists of source files in it and could be expensive to copy). | |
| // | |
| // Scope.SetValue will copy the value which will in turn copy the scope, but | |
| // if we instead create a value and then set the scope on it, the copy can | |
| // be avoided. | |
| template_scope.SetValue(variables::kInvoker, | |
| Value(nullptr, std::unique_ptr<Scope>()), invocation); | |
| Value* invoker_value = template_scope.GetMutableValue( | |
| variables::kInvoker, Scope::SEARCH_NESTED, false); | |
| invoker_value->SetScopeValue(std::move(invocation_scope)); | |
| template_scope.set_source_dir(scope->GetSourceDir()); | |
| const std::string_view target_name(variables::kTargetName); | |
| template_scope.SetValue( | |
| target_name, Value(invocation, args[0].string_value()), invocation); | |
| // Actually run the template code. | |
| Value result = definition_->block()->Execute(&template_scope, err); | |
| if (err->has_error()) { | |
| // If there was an error, append the caller location so the error message | |
| // displays a stack trace of how it got here. | |
| err->AppendSubErr(Err(invocation, "whence it was called.")); | |
| return Value(); | |
| } | |
| // Check for unused variables in the invocation scope. This will find typos | |
| // of things the caller meant to pass to the template but the template didn't | |
| // read out. | |
| // | |
| // This is a bit tricky because it's theoretically possible for the template | |
| // to overwrite the value of "invoker" and free the Scope owned by the | |
| // value. So we need to look it up again and don't do anything if it doesn't | |
| // exist. | |
| invoker_value = template_scope.GetMutableValue(variables::kInvoker, | |
| Scope::SEARCH_NESTED, false); | |
| if (invoker_value && invoker_value->type() == Value::SCOPE) { | |
| if (!invoker_value->scope_value()->CheckForUnusedVars(err)) { | |
| // If there was an error, append the caller location so the error message | |
| // displays a stack trace of how it got here. | |
| err->AppendSubErr(Err(invocation, "whence it was called.")); | |
| return Value(); | |
| } | |
| } | |
| // Check for unused variables in the template itself. | |
| if (!template_scope.CheckForUnusedVars(err)) { | |
| // If there was an error, append the caller location so the error message | |
| // displays a stack trace of how it got here. | |
| err->AppendSubErr(Err(invocation, "whence it was called.")); | |
| return Value(); | |
| } | |
| return result; | |
| } | |
| LocationRange Template::GetDefinitionRange() const { | |
| return definition_->GetRange(); | |
| } |