| Index: tools/gn/function_forward_variables_from.cc
|
| diff --git a/tools/gn/function_forward_variables_from.cc b/tools/gn/function_forward_variables_from.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9c97a51c60b4ba21366ba57ba0c679d89b61ae56
|
| --- /dev/null
|
| +++ b/tools/gn/function_forward_variables_from.cc
|
| @@ -0,0 +1,182 @@
|
| +// 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 "tools/gn/err.h"
|
| +#include "tools/gn/functions.h"
|
| +#include "tools/gn/parse_tree.h"
|
| +#include "tools/gn/scope.h"
|
| +
|
| +namespace functions {
|
| +
|
| +namespace {
|
| +
|
| +void ForwardAllValues(const FunctionCallNode* function,
|
| + Scope* source,
|
| + Scope* dest,
|
| + Err* err) {
|
| + Scope::MergeOptions options;
|
| + // This function needs to clobber existing for it to be useful. It will be
|
| + // called in a template to forward all values, but there will be some
|
| + // default stuff like configs set up in both scopes, so it would always
|
| + // fail if it didn't clobber.
|
| + options.clobber_existing = true;
|
| + options.skip_private_vars = true;
|
| + options.mark_dest_used = false;
|
| + source->NonRecursiveMergeTo(dest, options, function,
|
| + "source scope", err);
|
| + source->MarkAllUsed();
|
| +}
|
| +
|
| +void ForwardValuesFromList(Scope* source,
|
| + Scope* dest,
|
| + const std::vector<Value>& list,
|
| + Err* err) {
|
| + for (const Value& cur : list) {
|
| + if (!cur.VerifyTypeIs(Value::STRING, err))
|
| + return;
|
| + const Value* value = source->GetValue(cur.string_value(), true);
|
| + if (value) {
|
| + // Use the storage key for the original value rather than the string in
|
| + // "cur" because "cur" is a temporary that will be deleted, and Scopes
|
| + // expect a persistent StringPiece (it won't copy). Not doing this will
|
| + // lead the scope's key to point to invalid memory after this returns.
|
| + base::StringPiece storage_key = source->GetStorageKey(cur.string_value());
|
| + if (storage_key.empty()) {
|
| + // Programmatic value, don't allow copying.
|
| + *err = Err(cur, "This value can't be forwarded.",
|
| + "The variable \"" + cur.string_value() + "\" is a built-in.");
|
| + return;
|
| + }
|
| +
|
| + // Keep the origin information from the original value. The normal
|
| + // usage is for this to be used in a template, and if there's an error,
|
| + // the user expects to see the line where they set the variable
|
| + // blamed, rather than a template call to forward_variables_from().
|
| + dest->SetValue(storage_key, *value, value->origin());
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +const char kForwardVariablesFrom[] = "forward_variables_from";
|
| +const char kForwardVariablesFrom_HelpShort[] =
|
| + "forward_variables_from: Copies variables from a different scope.";
|
| +const char kForwardVariablesFrom_Help[] =
|
| + "forward_variables_from: Copies variables from a different scope.\n"
|
| + "\n"
|
| + " forward_variables_from(from_scope, variable_list_or_star)\n"
|
| + "\n"
|
| + " Copies the given variables from the given scope to the local scope\n"
|
| + " if they exist. This is normally used in the context of templates to\n"
|
| + " use the values of variables defined in the template invocation to\n"
|
| + " a template-defined target.\n"
|
| + "\n"
|
| + " The variables in the given variable_list will be copied if they exist\n"
|
| + " in the given scope or any enclosing scope. If they do not exist,\n"
|
| + " nothing will happen and they be left undefined in the current scope.\n"
|
| + "\n"
|
| + " As a special case, if the variable_list is a string with the value of\n"
|
| + " \"*\", all variables from the given scope will be copied. \"*\" only\n"
|
| + " copies variables set directly on the from_scope, not enclosing ones.\n"
|
| + " Otherwise it would duplicate all global variables.\n"
|
| + "\n"
|
| + " When an explicit list of variables is supplied, if the variable exists\n"
|
| + " in the current (destination) scope already, an error will be thrown.\n"
|
| + " If \"*\" is specified, variables in the current scope will be\n"
|
| + " clobbered (the latter is important because most targets have an\n"
|
| + " implicit configs list, which means it wouldn't work at all if it\n"
|
| + " didn't clobber).\n"
|
| + "\n"
|
| + " The sources assignment filter (see \"gn help "
|
| + "set_sources_assignment_filter\")\n"
|
| + " is never applied by this function. It's assumed than any desired\n"
|
| + " filtering was already done when sources was set on the from_scope.\n"
|
| + "\n"
|
| + "Examples\n"
|
| + "\n"
|
| + " # This is a common action template. It would invoke a script with\n"
|
| + " # some given parameters, and wants to use the various types of deps\n"
|
| + " # and the visibility from the invoker if it's defined. It also injects\n"
|
| + " # an additional dependency to all targets.\n"
|
| + " template(\"my_test\") {\n"
|
| + " action(target_name) {\n"
|
| + " forward_variables_from(invoker, [ \"data_deps\", \"deps\",\n"
|
| + " \"public_deps\", \"visibility\" "
|
| + "])\n"
|
| + " # Add our test code to the dependencies.\n"
|
| + " # \"deps\" may or may not be defined at this point.\n"
|
| + " if (defined(deps)) {\n"
|
| + " deps += [ \"//tools/doom_melon\" ]\n"
|
| + " } else {\n"
|
| + " deps = [ \"//tools/doom_melon\" ]\n"
|
| + " }\n"
|
| + " }\n"
|
| + " }\n"
|
| + "\n"
|
| + " # This is a template around either a target whose type depends on a\n"
|
| + " # global variable. It forwards all values from the invoker.\n"
|
| + " template(\"my_wrapper\") {\n"
|
| + " target(my_wrapper_target_type, target_name) {\n"
|
| + " forward_variables_from(invoker, \"*\")\n"
|
| + " }\n"
|
| + " }\n";
|
| +
|
| +// This function takes a ListNode rather than a resolved vector of values
|
| +// both avoid copying the potentially-large source scope, and so the variables
|
| +// in the source scope can be marked as used.
|
| +Value RunForwardVariablesFrom(Scope* scope,
|
| + const FunctionCallNode* function,
|
| + const ListNode* args_list,
|
| + Err* err) {
|
| + const std::vector<const ParseNode*>& args_vector = args_list->contents();
|
| + if (args_vector.size() != 2) {
|
| + *err = Err(function, "Wrong number of arguments.",
|
| + "Expecting exactly two.");
|
| + return Value();
|
| + }
|
| +
|
| + // Extract the scope identifier. This assumes the first parameter is an
|
| + // identifier. It is difficult to write code where this is not the case, and
|
| + // this saves an expensive scope copy. If necessary, this could be expanded
|
| + // to execute the ParseNode and get the value out if it's not an identifer.
|
| + const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
|
| + if (!identifier) {
|
| + *err = Err(args_vector[0], "Expected an identifier for the scope.");
|
| + return Value();
|
| + }
|
| +
|
| + // Extract the source scope.
|
| + Value* value = scope->GetMutableValue(identifier->value().value(), true);
|
| + if (!value) {
|
| + *err = Err(identifier, "Undefined identifier.");
|
| + return Value();
|
| + }
|
| + if (!value->VerifyTypeIs(Value::SCOPE, err))
|
| + return Value();
|
| + Scope* source = value->scope_value();
|
| +
|
| + // Extract the list. If all_values is not set, the what_value will be a list.
|
| + Value what_value = args_vector[1]->Execute(scope, err);
|
| + if (err->has_error())
|
| + return Value();
|
| + if (what_value.type() == Value::STRING) {
|
| + if (what_value.string_value() == "*") {
|
| + ForwardAllValues(function, source, scope, err);
|
| + return Value();
|
| + }
|
| + } else {
|
| + if (what_value.type() == Value::LIST) {
|
| + ForwardValuesFromList(source, scope, what_value.list_value(), err);
|
| + return Value();
|
| + }
|
| + }
|
| +
|
| + // Not the right type of argument.
|
| + *err = Err(what_value, "Not a valid list of variables to copy.",
|
| + "Expecting either the string \"*\" or a list of strings.");
|
| + return Value();
|
| +}
|
| +
|
| +} // namespace functions
|
|
|