Update GN toolchain_args to be a variable.
This makes toolchain_args on a toolchain definition a variable instead of a function call.
The function call is kept for backwards compatibility (for now) and it just sets the variable.
forward_variables_from now accepts a first parameter of any type that evaluates to a scope, which allows things like "invoker.toolchain_args" to be used there.
Updates "gn format" to format scope assignments without extra indents.
BUG=634446
Review-Url: https://codereview.chromium.org/2219083002
Cr-Original-Commit-Position: refs/heads/master@{#410123}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 22227896dc68aa88ba1836cd53fd261ba21bef88
diff --git a/tools/gn/args.cc b/tools/gn/args.cc
index 581c8ac..9aaafb0 100644
--- a/tools/gn/args.cc
+++ b/tools/gn/args.cc
@@ -35,7 +35,7 @@
" toolchain_args section of a toolchain definition. The use-case for\n"
" this is that a toolchain may be building code for a different\n"
" platform, and that it may want to always specify Posix, for example.\n"
- " See \"gn help toolchain_args\" for more.\n"
+ " See \"gn help toolchain\" for more.\n"
"\n"
" If you specify an override for a build argument that never appears in\n"
" a \"declare_args\" call, a nonfatal error will be displayed.\n"
diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc
index 7b8f2f3..13d8cc8 100644
--- a/tools/gn/command_format.cc
+++ b/tools/gn/command_format.cc
@@ -507,17 +507,28 @@
bool is_assignment = binop->op().value() == "=" ||
binop->op().value() == "+=" ||
binop->op().value() == "-=";
- // A sort of funny special case for the long lists that are common in .gn
- // files, don't indent them + 4, even though they're just continuations when
- // they're simple lists like "x = [ a, b, c, ... ]"
- const ListNode* right_as_list = binop->right()->AsList();
- int indent_column =
- (is_assignment &&
- (!right_as_list || (!right_as_list->prefer_multiline() &&
- !ListWillBeMultiline(right_as_list->contents(),
- right_as_list->End()))))
- ? margin() + kIndentSize * 2
- : start_column;
+
+ int indent_column = start_column;
+ if (is_assignment) {
+ // Default to a double-indent for wrapped assignments.
+ indent_column = margin() + kIndentSize * 2;
+
+ // A special case for the long lists and scope assignments that are
+ // common in .gn files, don't indent them + 4, even though they're just
+ // continuations when they're simple lists like "x = [ a, b, c, ... ]" or
+ // scopes like "x = { a = 1 b = 2 }". Put back to "normal" indenting.
+ const ListNode* right_as_list = binop->right()->AsList();
+ if (right_as_list) {
+ if (right_as_list->prefer_multiline() ||
+ ListWillBeMultiline(right_as_list->contents(),
+ right_as_list->End()))
+ indent_column = start_column;
+ } else {
+ const BlockNode* right_as_block = binop->right()->AsBlock();
+ if (right_as_block)
+ indent_column = start_column;
+ }
+ }
if (stack_.back().continuation_requires_indent)
indent_column += kIndentSize * 2;
diff --git a/tools/gn/command_format_unittest.cc b/tools/gn/command_format_unittest.cc
index 7c79d41..d753b33 100644
--- a/tools/gn/command_format_unittest.cc
+++ b/tools/gn/command_format_unittest.cc
@@ -104,3 +104,4 @@
FORMAT_TEST(064)
FORMAT_TEST(065)
FORMAT_TEST(066)
+FORMAT_TEST(067)
diff --git a/tools/gn/format_test_data/067.gn b/tools/gn/format_test_data/067.gn
new file mode 100644
index 0000000..b3d5eaa
--- /dev/null
+++ b/tools/gn/format_test_data/067.gn
@@ -0,0 +1,8 @@
+# Scope wrapping.
+
+myscope = {
+}
+myscope = { a = 1 }
+myscope = { a = 1 b = 2}
+# Comment
+SomeFunction({a=1}, "foo")
diff --git a/tools/gn/format_test_data/067.golden b/tools/gn/format_test_data/067.golden
new file mode 100644
index 0000000..9ce2fa7
--- /dev/null
+++ b/tools/gn/format_test_data/067.golden
@@ -0,0 +1,17 @@
+# Scope wrapping.
+
+myscope = {
+}
+myscope = {
+ a = 1
+}
+myscope = {
+ a = 1
+ b = 2
+}
+
+# Comment
+SomeFunction({
+ a = 1
+ },
+ "foo")
diff --git a/tools/gn/function_forward_variables_from.cc b/tools/gn/function_forward_variables_from.cc
index 8c7a558..0445f8b 100644
--- a/tools/gn/function_forward_variables_from.cc
+++ b/tools/gn/function_forward_variables_from.cc
@@ -172,23 +172,27 @@
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.
+ Value* value = nullptr; // Value to use, may point to result_value.
+ Value result_value; // Storage for the "evaluate" case.
const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
- if (!identifier) {
- *err = Err(args_vector[0].get(), "Expected an identifier for the scope.");
- return Value();
+ if (identifier) {
+ // Optimize the common case where the input scope is an identifier. This
+ // prevents a copy of a potentially large Scope object.
+ value = scope->GetMutableValue(identifier->value().value(),
+ Scope::SEARCH_NESTED, true);
+ if (!value) {
+ *err = Err(identifier, "Undefined identifier.");
+ return Value();
+ }
+ } else {
+ // Non-optimized case, just evaluate the argument.
+ result_value = args_vector[0]->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ value = &result_value;
}
// Extract the source scope.
- Value* value = scope->GetMutableValue(
- identifier->value().value(), Scope::SEARCH_NESTED, true);
- if (!value) {
- *err = Err(identifier, "Undefined identifier.");
- return Value();
- }
if (!value->VerifyTypeIs(Value::SCOPE, err))
return Value();
Scope* source = value->scope_value();
diff --git a/tools/gn/function_forward_variables_from_unittest.cc b/tools/gn/function_forward_variables_from_unittest.cc
index 2e6f5a2..84d8b6e 100644
--- a/tools/gn/function_forward_variables_from_unittest.cc
+++ b/tools/gn/function_forward_variables_from_unittest.cc
@@ -50,6 +50,28 @@
}
}
+TEST(FunctionForwardVariablesFrom, LiteralList) {
+ Scheduler scheduler;
+ TestWithScope setup;
+
+ // Forwards all variables from a literal scope into another scope definition.
+ TestParseInput input(
+ "a = {\n"
+ " forward_variables_from({x = 1 y = 2}, \"*\")\n"
+ " z = 3\n"
+ "}\n"
+ "print(\"${a.x} ${a.y} ${a.z}\")\n");
+
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ("1 2 3\n", setup.print_output());
+ setup.print_output().clear();
+}
+
TEST(FunctionForwardVariablesFrom, ListWithExclusion) {
Scheduler scheduler;
TestWithScope setup;
@@ -94,7 +116,7 @@
Err err;
invalid_source.parsed()->Execute(setup.scope(), &err);
EXPECT_TRUE(err.has_error());
- EXPECT_EQ("Expected an identifier for the scope.", err.message());
+ EXPECT_EQ("This is not a scope.", err.message());
// Type check the list. We need to use a new template name each time since
// all of these invocations are executing in sequence in the same scope.
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index faa190e..852d25a 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -314,10 +314,26 @@
" The tool() function call specifies the commands commands to run for\n"
" a given step. See \"gn help tool\".\n"
"\n"
- " toolchain_args()\n"
- " List of arguments to pass to the toolchain when invoking this\n"
- " toolchain. This applies only to non-default toolchains. See\n"
- " \"gn help toolchain_args\" for more.\n"
+ " toolchain_args\n"
+ " Overrides for build arguments to pass to the toolchain when invoking\n"
+ " it. This is a variable of type \"scope\" where the variable names\n"
+ " correspond to varibles in declare_args() blocks.\n"
+ "\n"
+ " When you specify a target using an alternate toolchain, the master\n"
+ " build configuration file is re-interpreted in the context of that\n"
+ " toolchain (see \"gn help toolchain\"). The toolchain_args allows you\n"
+ " to control the arguments passed into this alternate invocation of\n"
+ " the build.\n"
+ "\n"
+ " Any default system arguments or arguments passed in via \"gn args\"\n"
+ " will also be passed to the alternate invocation unless explicitly\n"
+ " overridden by toolchain_args.\n"
+ "\n"
+ " The toolchain_args will be ignored when the toolchain being defined\n"
+ " is the default. In this case, it's expected you want the default\n"
+ " argument values.\n"
+ "\n"
+ " See also \"gn help buildargs\" for an overview of these arguments.\n"
"\n"
" deps\n"
" Dependencies of this toolchain. These dependencies will be resolved\n"
@@ -350,18 +366,19 @@
" by the toolchain label).\n"
" 2. Re-runs the master build configuration file, applying the\n"
" arguments specified by the toolchain_args section of the toolchain\n"
- " definition (see \"gn help toolchain_args\").\n"
+ " definition.\n"
" 3. Loads the destination build file in the context of the\n"
" configuration file in the previous step.\n"
"\n"
- "Example:\n"
+ "Example\n"
+ "\n"
" toolchain(\"plugin_toolchain\") {\n"
" tool(\"cc\") {\n"
" command = \"gcc {{source}}\"\n"
" ...\n"
" }\n"
"\n"
- " toolchain_args() {\n"
+ " toolchain_args = {\n"
" is_plugin = true\n"
" is_32bit = true\n"
" is_64bit = false\n"
@@ -412,6 +429,17 @@
return Value();
}
+ // Read toolchain args (if any).
+ const Value* toolchain_args = block_scope.GetValue("toolchain_args", true);
+ if (toolchain_args) {
+ if (!toolchain_args->VerifyTypeIs(Value::SCOPE, err))
+ return Value();
+
+ Scope::KeyValueMap values;
+ toolchain_args->scope_value()->GetCurrentScopeValues(&values);
+ toolchain->args() = values;
+ }
+
if (!block_scope.CheckForUnusedVars(err))
return Value();
@@ -1015,79 +1043,45 @@
return Value();
}
-// toolchain_args --------------------------------------------------------------
-
extern const char kToolchainArgs[] = "toolchain_args";
extern const char kToolchainArgs_HelpShort[] =
"toolchain_args: Set build arguments for toolchain build setup.";
extern const char kToolchainArgs_Help[] =
"toolchain_args: Set build arguments for toolchain build setup.\n"
"\n"
- " Used inside a toolchain definition to pass arguments to an alternate\n"
- " toolchain's invocation of the build.\n"
+ " DEPRECATED. Instead use:\n"
+ " toolchain_args = { ... }\n"
"\n"
- " When you specify a target using an alternate toolchain, the master\n"
- " build configuration file is re-interpreted in the context of that\n"
- " toolchain (see \"gn help toolchain\"). The toolchain_args function\n"
- " allows you to control the arguments passed into this alternate\n"
- " invocation of the build.\n"
- "\n"
- " Any default system arguments or arguments passed in on the command-\n"
- " line will also be passed to the alternate invocation unless explicitly\n"
- " overridden by toolchain_args.\n"
- "\n"
- " The toolchain_args will be ignored when the toolchain being defined\n"
- " is the default. In this case, it's expected you want the default\n"
- " argument values.\n"
- "\n"
- " See also \"gn help buildargs\" for an overview of these arguments.\n"
- "\n"
- "Example:\n"
- " toolchain(\"my_weird_toolchain\") {\n"
- " ...\n"
- " toolchain_args() {\n"
- " # Override the system values for a generic Posix system.\n"
- " is_win = false\n"
- " is_posix = true\n"
- "\n"
- " # Pass this new value for specific setup for my toolchain.\n"
- " is_my_weird_system = true\n"
- " }\n"
- " }\n";
+ " See \"gn help toolchain\" for documentation.\n";
Value RunToolchainArgs(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
- // Find the toolchain definition we're executing inside of. The toolchain
- // function will set a property pointing to it that we'll pick up.
- Toolchain* toolchain = reinterpret_cast<Toolchain*>(
- scope->GetProperty(&kToolchainPropertyKey, nullptr));
- if (!toolchain) {
- *err = Err(function->function(),
- "toolchain_args() called outside of toolchain().",
- "The toolchain_args() function can only be used inside a "
- "toolchain() definition.");
- return Value();
- }
-
+ // This is a backwards-compatible shim that converts the old form of:
+ // toolchain_args() {
+ // foo = bar
+ // }
+ // to the new form:
+ // toolchain_args = {
+ // foo = bar
+ // }
+ // It will be deleted when all users of toolchain_args as a function are
+ // deleted.
if (!args.empty()) {
*err = Err(function->function(), "This function takes no arguments.");
return Value();
}
- // This function makes a new scope with various variable sets on it, which
- // we then save on the toolchain to use when re-invoking the build.
- Scope block_scope(scope);
- block->Execute(&block_scope, err);
+ std::unique_ptr<Scope> block_scope(new Scope(scope));
+ block->Execute(block_scope.get(), err);
if (err->has_error())
return Value();
- Scope::KeyValueMap values;
- block_scope.GetCurrentScopeValues(&values);
- toolchain->args() = values;
-
+ block_scope->DetachFromContaining();
+ scope->SetValue("toolchain_args", Value(function, std::move(block_scope)),
+ function);
return Value();
}