• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gn/err.h"
6 #include "gn/functions.h"
7 #include "gn/parse_tree.h"
8 #include "gn/scope.h"
9 
10 namespace functions {
11 
12 namespace {
13 
ForwardAllValues(const FunctionCallNode * function,Scope * source,Scope * dest,const std::set<std::string> & exclusion_set,Err * err)14 void ForwardAllValues(const FunctionCallNode* function,
15                       Scope* source,
16                       Scope* dest,
17                       const std::set<std::string>& exclusion_set,
18                       Err* err) {
19   Scope::MergeOptions options;
20   // This function needs to clobber existing for it to be useful. It will be
21   // called in a template to forward all values, but there will be some
22   // default stuff like configs set up in both scopes, so it would always
23   // fail if it didn't clobber.
24   options.clobber_existing = true;
25   options.skip_private_vars = true;
26   options.mark_dest_used = false;
27   options.excluded_values = exclusion_set;
28   source->NonRecursiveMergeTo(dest, options, function, "source scope", err);
29   source->MarkAllUsed();
30 }
31 
ForwardValuesFromList(Scope * source,Scope * dest,const std::vector<Value> & list,const std::set<std::string> & exclusion_set,Err * err)32 void ForwardValuesFromList(Scope* source,
33                            Scope* dest,
34                            const std::vector<Value>& list,
35                            const std::set<std::string>& exclusion_set,
36                            Err* err) {
37   for (const Value& cur : list) {
38     if (!cur.VerifyTypeIs(Value::STRING, err))
39       return;
40     if (exclusion_set.find(cur.string_value()) != exclusion_set.end())
41       continue;
42     const Value* value = source->GetValue(cur.string_value(), true);
43     if (value) {
44       // Use the storage key for the original value rather than the string in
45       // "cur" because "cur" is a temporary that will be deleted, and Scopes
46       // expect a persistent std::string_view (it won't copy). Not doing this
47       // will lead the scope's key to point to invalid memory after this
48       // returns.
49       std::string_view storage_key = source->GetStorageKey(cur.string_value());
50       if (storage_key.empty()) {
51         // Programmatic value, don't allow copying.
52         *err =
53             Err(cur, "This value can't be forwarded.",
54                 "The variable \"" + cur.string_value() + "\" is a built-in.");
55         return;
56       }
57 
58       // Don't allow clobbering existing values.
59       const Value* existing_value = dest->GetValue(storage_key);
60       if (existing_value) {
61         *err = Err(
62             cur, "Clobbering existing value.",
63             "The current scope already defines a value \"" +
64                 cur.string_value() +
65                 "\".\nforward_variables_from() won't clobber "
66                 "existing values. If you want to\nmerge lists, you'll need to "
67                 "do this explicitly.");
68         err->AppendSubErr(Err(*existing_value, "value being clobbered."));
69         return;
70       }
71 
72       // Keep the origin information from the original value. The normal
73       // usage is for this to be used in a template, and if there's an error,
74       // the user expects to see the line where they set the variable
75       // blamed, rather than a template call to forward_variables_from().
76       dest->SetValue(storage_key, *value, value->origin());
77     }
78   }
79 }
80 
81 }  // namespace
82 
83 const char kForwardVariablesFrom[] = "forward_variables_from";
84 const char kForwardVariablesFrom_HelpShort[] =
85     "forward_variables_from: Copies variables from a different scope.";
86 const char kForwardVariablesFrom_Help[] =
87     R"(forward_variables_from: Copies variables from a different scope.
88 
89   forward_variables_from(from_scope, variable_list_or_star,
90                          variable_to_not_forward_list = [])
91 
92   Copies the given variables from the given scope to the local scope if they
93   exist. This is normally used in the context of templates to use the values of
94   variables defined in the template invocation to a template-defined target.
95 
96   The variables in the given variable_list will be copied if they exist in the
97   given scope or any enclosing scope. If they do not exist, nothing will happen
98   and they be left undefined in the current scope.
99 
100   As a special case, if the variable_list is a string with the value of "*",
101   all variables from the given scope will be copied. "*" only copies variables
102   set directly on the from_scope, not enclosing ones. Otherwise it would
103   duplicate all global variables.
104 
105   When an explicit list of variables is supplied, if the variable exists in the
106   current (destination) scope already, an error will be thrown. If "*" is
107   specified, variables in the current scope will be clobbered (the latter is
108   important because most targets have an implicit configs list, which means it
109   wouldn't work at all if it didn't clobber).
110 
111   The sources assignment filter (see "gn help set_sources_assignment_filter")
112   is never applied by this function. It's assumed than any desired filtering
113   was already done when sources was set on the from_scope.
114 
115   If variables_to_not_forward_list is non-empty, then it must contains a list
116   of variable names that will not be forwarded. This is mostly useful when
117   variable_list_or_star has a value of "*".
118 
119 Examples
120 
121   # forward_variables_from(invoker, ["foo"])
122   # is equivalent to:
123   assert(!defined(foo))
124   if (defined(invoker.foo)) {
125     foo = invoker.foo
126   }
127 
128   # This is a common action template. It would invoke a script with some given
129   # parameters, and wants to use the various types of deps and the visibility
130   # from the invoker if it's defined. It also injects an additional dependency
131   # to all targets.
132   template("my_test") {
133     action(target_name) {
134       forward_variables_from(invoker, [ "data_deps", "deps",
135                                         "public_deps", "visibility"])
136       # Add our test code to the dependencies.
137       # "deps" may or may not be defined at this point.
138       if (defined(deps)) {
139         deps += [ "//tools/doom_melon" ]
140       } else {
141         deps = [ "//tools/doom_melon" ]
142       }
143     }
144   }
145 
146   # This is a template around a target whose type depends on a global variable.
147   # It forwards all values from the invoker.
148   template("my_wrapper") {
149     target(my_wrapper_target_type, target_name) {
150       forward_variables_from(invoker, "*")
151     }
152   }
153 
154   # A template that wraps another. It adds behavior based on one
155   # variable, and forwards all others to the nested target.
156   template("my_ios_test_app") {
157     ios_test_app(target_name) {
158       forward_variables_from(invoker, "*", ["test_bundle_name"])
159       if (!defined(extra_substitutions)) {
160         extra_substitutions = []
161       }
162       extra_substitutions += [ "BUNDLE_ID_TEST_NAME=$test_bundle_name" ]
163     }
164   }
165 )";
166 
167 // This function takes a ListNode rather than a resolved vector of values
168 // both avoid copying the potentially-large source scope, and so the variables
169 // in the source scope can be marked as used.
RunForwardVariablesFrom(Scope * scope,const FunctionCallNode * function,const ListNode * args_list,Err * err)170 Value RunForwardVariablesFrom(Scope* scope,
171                               const FunctionCallNode* function,
172                               const ListNode* args_list,
173                               Err* err) {
174   const auto& args_vector = args_list->contents();
175   if (args_vector.size() != 2 && args_vector.size() != 3) {
176     *err = Err(function, "Wrong number of arguments.",
177                "Expecting two or three arguments.");
178     return Value();
179   }
180 
181   Value* value = nullptr;  // Value to use, may point to result_value.
182   Value result_value;      // Storage for the "evaluate" case.
183   const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
184   if (identifier) {
185     // Optimize the common case where the input scope is an identifier. This
186     // prevents a copy of a potentially large Scope object.
187     value = scope->GetMutableValue(identifier->value().value(),
188                                    Scope::SEARCH_NESTED, true);
189     if (!value) {
190       *err = Err(identifier, "Undefined identifier.");
191       return Value();
192     }
193   } else {
194     // Non-optimized case, just evaluate the argument.
195     result_value = args_vector[0]->Execute(scope, err);
196     if (err->has_error())
197       return Value();
198     value = &result_value;
199   }
200 
201   // Extract the source scope.
202   if (!value->VerifyTypeIs(Value::SCOPE, err))
203     return Value();
204   Scope* source = value->scope_value();
205 
206   // Extract the exclusion list if defined.
207   std::set<std::string> exclusion_set;
208   if (args_vector.size() == 3) {
209     Value exclusion_value = args_vector[2]->Execute(scope, err);
210     if (err->has_error())
211       return Value();
212 
213     if (exclusion_value.type() != Value::LIST) {
214       *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
215                  "Expecting a list of strings.");
216       return Value();
217     }
218 
219     for (const Value& cur : exclusion_value.list_value()) {
220       if (!cur.VerifyTypeIs(Value::STRING, err))
221         return Value();
222 
223       exclusion_set.insert(cur.string_value());
224     }
225   }
226 
227   // Extract the list. If all_values is not set, the what_value will be a list.
228   Value what_value = args_vector[1]->Execute(scope, err);
229   if (err->has_error())
230     return Value();
231   if (what_value.type() == Value::STRING) {
232     if (what_value.string_value() == "*") {
233       ForwardAllValues(function, source, scope, exclusion_set, err);
234       return Value();
235     }
236   } else {
237     if (what_value.type() == Value::LIST) {
238       ForwardValuesFromList(source, scope, what_value.list_value(),
239                             exclusion_set, err);
240       return Value();
241     }
242   }
243 
244   // Not the right type of argument.
245   *err = Err(what_value, "Not a valid list of variables to copy.",
246              "Expecting either the string \"*\" or a list of strings.");
247   return Value();
248 }
249 
250 }  // namespace functions
251