1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/gn/template.h"
6
7 #include "tools/gn/err.h"
8 #include "tools/gn/functions.h"
9 #include "tools/gn/parse_tree.h"
10 #include "tools/gn/scope.h"
11 #include "tools/gn/scope_per_file_provider.h"
12 #include "tools/gn/value.h"
13
Template(const Scope * scope,const FunctionCallNode * def)14 Template::Template(const Scope* scope, const FunctionCallNode* def)
15 : closure_(scope->MakeClosure()),
16 definition_(def) {
17 }
18
Template(scoped_ptr<Scope> scope,const FunctionCallNode * def)19 Template::Template(scoped_ptr<Scope> scope, const FunctionCallNode* def)
20 : closure_(scope.Pass()),
21 definition_(def) {
22 }
23
~Template()24 Template::~Template() {
25 }
26
Invoke(Scope * scope,const FunctionCallNode * invocation,const std::vector<Value> & args,BlockNode * block,Err * err) const27 Value Template::Invoke(Scope* scope,
28 const FunctionCallNode* invocation,
29 const std::vector<Value>& args,
30 BlockNode* block,
31 Err* err) const {
32 // Don't allow templates to be executed from imported files. Imports are for
33 // simple values only.
34 if (!EnsureNotProcessingImport(invocation, scope, err))
35 return Value();
36
37 // First run the invocation's block. Need to allocate the scope on the heap
38 // so we can pass ownership to the template.
39 scoped_ptr<Scope> invocation_scope(new Scope(scope));
40 if (!FillTargetBlockScope(scope, invocation,
41 invocation->function().value().as_string(),
42 block, args, invocation_scope.get(), err))
43 return Value();
44 block->ExecuteBlockInScope(invocation_scope.get(), err);
45 if (err->has_error())
46 return Value();
47
48 // Set up the scope to run the template and set the current directory for the
49 // template (which ScopePerFileProvider uses to base the target-related
50 // variables target_gen_dir and target_out_dir on) to be that of the invoker.
51 // This way, files don't have to be rebased and target_*_dir works the way
52 // people expect (otherwise its to easy to be putting generated files in the
53 // gen dir corresponding to an imported file).
54 Scope template_scope(closure_.get());
55 template_scope.set_source_dir(scope->GetSourceDir());
56
57 ScopePerFileProvider per_file_provider(&template_scope, true);
58
59 // Targets defined in the template go in the collector for the invoking file.
60 template_scope.set_item_collector(scope->GetItemCollector());
61
62 // We jump through some hoops to avoid copying the invocation scope when
63 // setting it in the template scope (since the invocation scope may have
64 // large lists of source files in it and could be expensive to copy).
65 //
66 // Scope.SetValue will copy the value which will in turn copy the scope, but
67 // if we instead create a value and then set the scope on it, the copy can
68 // be avoided.
69 const char kInvoker[] = "invoker";
70 template_scope.SetValue(kInvoker, Value(NULL, scoped_ptr<Scope>()),
71 invocation);
72 Value* invoker_value = template_scope.GetMutableValue(kInvoker, false);
73 invoker_value->SetScopeValue(invocation_scope.Pass());
74 template_scope.set_source_dir(scope->GetSourceDir());
75
76 const base::StringPiece target_name("target_name");
77 template_scope.SetValue(target_name,
78 Value(invocation, args[0].string_value()),
79 invocation);
80
81 // Actually run the template code.
82 Value result =
83 definition_->block()->ExecuteBlockInScope(&template_scope, err);
84 if (err->has_error())
85 return Value();
86
87 // Check for unused variables in the invocation scope. This will find typos
88 // of things the caller meant to pass to the template but the template didn't
89 // read out.
90 //
91 // This is a bit tricky because it's theoretically possible for the template
92 // to overwrite the value of "invoker" and free the Scope owned by the
93 // value. So we need to look it up again and don't do anything if it doesn't
94 // exist.
95 invoker_value = template_scope.GetMutableValue(kInvoker, false);
96 if (invoker_value && invoker_value->type() == Value::SCOPE) {
97 if (!invoker_value->scope_value()->CheckForUnusedVars(err))
98 return Value();
99 }
100
101 // Check for unused variables in the template itself.
102 if (!template_scope.CheckForUnusedVars(err))
103 return Value();
104
105 return result;
106 }
107
GetDefinitionRange() const108 LocationRange Template::GetDefinitionRange() const {
109 return definition_->GetRange();
110 }
111