Allow GN toolchains to specify runtime deps outputs.

Previously the file considered for the runtime deps computation for linked
targets was just the first output, or, for shared libraries, the
runtime_link_output.

This patch changes that to a list of files for all linker tools. This will
allow us, for example, to be able to automatically bundle symbol files
generated by the build (but not .lib files in the case of shared libraries) to
the swarming bots for testing.

The tool class setters now take objects by copies and move them to avoid extra
copies in many cases. Add a defined_from member to the Tool class.

BUG=631242

Review-Url: https://codereview.chromium.org/2178173002
Cr-Original-Commit-Position: refs/heads/master@{#407852}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 15e0e74f4dbd7616a722702eedd6620b70f50290
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
index b77d2af..56f3070 100644
--- a/tools/gn/BUILD.gn
+++ b/tools/gn/BUILD.gn
@@ -289,6 +289,7 @@
     "function_get_target_outputs_unittest.cc",
     "function_process_file_template_unittest.cc",
     "function_rebase_path_unittest.cc",
+    "function_toolchain_unittest.cc",
     "function_write_file_unittest.cc",
     "functions_target_unittest.cc",
     "functions_unittest.cc",
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index 210cc2c..3501ec1 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -47,7 +47,7 @@
 bool ReadString(Scope* scope,
                 const char* var,
                 Tool* tool,
-                void (Tool::*set)(const std::string&),
+                void (Tool::*set)(std::string),
                 Err* err) {
   const Value* v = scope->GetValue(var, true);
   if (!v)
@@ -64,9 +64,8 @@
 bool ReadLabel(Scope* scope,
                const char* var,
                Tool* tool,
-               const ParseNode* origin,
                const Label& current_toolchain,
-               void (Tool::*set)(const LabelPtrPair<Pool>&),
+               void (Tool::*set)(LabelPtrPair<Pool>),
                Err* err) {
   const Value* v = scope->GetValue(var, true);
   if (!v)
@@ -78,9 +77,9 @@
     return false;
 
   LabelPtrPair<Pool> pair(label);
-  pair.origin = origin;
+  pair.origin = tool->defined_from();
 
-  (tool->*set)(pair);
+  (tool->*set)(std::move(pair));
   return true;
 }
 
@@ -105,7 +104,7 @@
                  const char* name,
                  bool (*validate)(SubstitutionType),
                  Tool* tool,
-                 void (Tool::*set)(const SubstitutionPattern&),
+                 void (Tool::*set)(SubstitutionPattern),
                  Err* err) {
   const Value* value = scope->GetValue(name, true);
   if (!value)
@@ -119,7 +118,31 @@
   if (!ValidateSubstitutionList(pattern.required_types(), validate, value, err))
     return false;
 
-  (tool->*set)(pattern);
+  (tool->*set)(std::move(pattern));
+  return true;
+}
+
+bool ReadPatternList(Scope* scope,
+                     const char* name,
+                     bool (*validate)(SubstitutionType),
+                     Tool* tool,
+                     void (Tool::*set)(SubstitutionList),
+                     Err* err) {
+  const Value* value = scope->GetValue(name, true);
+  if (!value)
+    return true;  // Not present is fine.
+  if (!value->VerifyTypeIs(Value::LIST, err))
+    return false;
+
+  SubstitutionList list;
+  if (!list.Parse(*value, err))
+    return false;
+
+  // Validate the right kinds of patterns are used.
+  if (!ValidateSubstitutionList(list.required_types(), validate, value, err))
+    return false;
+
+  (tool->*set)(std::move(list));
   return true;
 }
 
@@ -182,35 +205,6 @@
   return true;
 }
 
-bool ReadOutputs(Scope* scope,
-                 const FunctionCallNode* tool_function,
-                 bool (*validate)(SubstitutionType),
-                 Tool* tool,
-                 Err* err) {
-  const Value* value = scope->GetValue("outputs", true);
-  if (!value) {
-    *err = Err(tool_function, "\"outputs\" must be specified for this tool.");
-    return false;
-  }
-
-  SubstitutionList list;
-  if (!list.Parse(*value, err))
-    return false;
-
-  // Validate the right kinds of patterns are used.
-  if (!ValidateSubstitutionList(list.required_types(), validate, value, err))
-    return false;
-
-  // There should always be at least one output.
-  if (list.list().empty()) {
-    *err = Err(*value, "Outputs list is empty.", "I need some outputs.");
-    return false;
-  }
-
-  tool->set_outputs(list);
-  return true;
-}
-
 bool IsCompilerTool(Toolchain::ToolType type) {
   return type == Toolchain::TYPE_CC ||
          type == Toolchain::TYPE_CXX ||
@@ -238,6 +232,68 @@
   return false;
 }
 
+
+bool ValidateOutputs(const Tool* tool, Err* err) {
+  if (tool->outputs().list().empty()) {
+    *err = Err(tool->defined_from(),
+               "\"outputs\" must be specified for this tool.");
+    return false;
+  }
+  return true;
+}
+
+// Validates either link_output or depend_output. To generalize to either, pass
+// the associated pattern, and the variable name that should appear in error
+// messages.
+bool ValidateLinkAndDependOutput(const Tool* tool,
+                                 Toolchain::ToolType tool_type,
+                                 const SubstitutionPattern& pattern,
+                                 const char* variable_name,
+                                 Err* err) {
+  if (pattern.empty())
+    return true;  // Empty is always OK.
+
+  // It should only be specified for certain tool types.
+  if (tool_type != Toolchain::TYPE_SOLINK &&
+      tool_type != Toolchain::TYPE_SOLINK_MODULE) {
+    *err = Err(tool->defined_from(),
+        "This tool specifies a " + std::string(variable_name) + ".",
+        "This is only valid for solink and solink_module tools.");
+    return false;
+  }
+
+  if (!IsPatternInOutputList(tool->outputs(), pattern)) {
+    *err = Err(tool->defined_from(), "This tool's link_output is bad.",
+               "It must match one of the outputs.");
+    return false;
+  }
+
+  return true;
+}
+
+bool ValidateRuntimeOutputs(const Tool* tool,
+                            Toolchain::ToolType tool_type,
+                            Err* err) {
+  if (tool->runtime_outputs().list().empty())
+    return true;  // Empty is always OK.
+
+  if (!IsLinkerTool(tool_type)) {
+    *err = Err(tool->defined_from(), "This tool specifies runtime_outputs.",
+        "This is only valid for linker tools (alink doesn't count).");
+    return false;
+  }
+
+  for (const SubstitutionPattern& pattern : tool->runtime_outputs().list()) {
+    if (!IsPatternInOutputList(tool->outputs(), pattern)) {
+      *err = Err(tool->defined_from(), "This tool's runtime_outputs is bad.",
+                 "It must be a subset of the outputs. The bad one is:\n  " +
+                  pattern.AsString());
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace
 
 // toolchain -------------------------------------------------------------------
@@ -527,9 +583,7 @@
     "\n"
     "        If you specify more than one output for shared library links,\n"
     "        you should consider setting link_output, depend_output, and\n"
-    "        runtime_link_output. Otherwise, the first entry in the\n"
-    "        outputs list should always be the main output which will be\n"
-    "        linked to.\n"
+    "        runtime_outputs.\n"
     "\n"
     "        Example for a compiler tool that produces .obj files:\n"
     "          outputs = [\n"
@@ -555,16 +609,14 @@
     "\n"
     "    link_output  [string with substitutions]\n"
     "    depend_output  [string with substitutions]\n"
-    "    runtime_link_output  [string with substitutions]\n"
     "        Valid for: \"solink\" only (optional)\n"
     "\n"
-    "        These three files specify which of the outputs from the solink\n"
+    "        These two files specify which of the outputs from the solink\n"
     "        tool should be used for linking and dependency tracking. These\n"
     "        should match entries in the \"outputs\". If unspecified, the\n"
     "        first item in the \"outputs\" array will be used for all. See\n"
     "        \"Separate linking and dependencies for shared libraries\"\n"
-    "        below for more. If link_output is set but runtime_link_output\n"
-    "        is not set, runtime_link_output defaults to link_output.\n"
+    "        below for more.\n"
     "\n"
     "        On Windows, where the tools produce a .dll shared library and\n"
     "        a .lib import library, you will want the first two to be the\n"
@@ -642,6 +694,14 @@
     "            rspfile_content = \"{{inputs}} {{solibs}} {{libs}}\"\n"
     "          }\n"
     "\n"
+    "    runtime_outputs  [string list with substitutions]\n"
+    "        Valid for: linker tools\n"
+    "\n"
+    "        If specified, this list is the subset of the outputs that should\n"
+    "        be added to runtime deps (see \"gn help runtime_deps\"). By\n"
+    "        default (if runtime_outputs is empty or unspecified), it will be\n"
+    "        the link_output.\n"
+    "\n"
     "Expansions for tool variables\n"
     "\n"
     "  All paths are relative to the root build directory, which is the\n"
@@ -915,6 +975,7 @@
   }
 
   std::unique_ptr<Tool> tool(new Tool);
+  tool->set_defined_from(function);
 
   if (!ReadPattern(&block_scope, "command", subst_validator, tool.get(),
                    &Tool::set_command, err) ||
@@ -932,8 +993,8 @@
                    &Tool::set_link_output, err) ||
       !ReadPattern(&block_scope, "depend_output", subst_validator, tool.get(),
                    &Tool::set_depend_output, err) ||
-      !ReadPattern(&block_scope, "runtime_link_output", subst_validator,
-                   tool.get(), &Tool::set_runtime_link_output, err) ||
+      !ReadPatternList(&block_scope, "runtime_outputs", subst_validator,
+                       tool.get(), &Tool::set_runtime_outputs, err) ||
       !ReadString(&block_scope, "output_prefix", tool.get(),
                   &Tool::set_output_prefix, err) ||
       !ReadPattern(&block_scope, "default_output_dir", subst_validator,
@@ -944,7 +1005,7 @@
                    &Tool::set_rspfile, err) ||
       !ReadPattern(&block_scope, "rspfile_content", subst_validator, tool.get(),
                    &Tool::set_rspfile_content, err) ||
-      !ReadLabel(&block_scope, "pool", tool.get(), function, toolchain->label(),
+      !ReadLabel(&block_scope, "pool", tool.get(), toolchain->label(),
                  &Tool::set_pool, err)) {
     return Value();
   }
@@ -955,59 +1016,27 @@
       tool_type != Toolchain::TYPE_COMPILE_XCASSETS) {
     // All tools should have outputs, except the copy, stamp, copy_bundle_data
     // and compile_xcassets tools that generate their outputs internally.
-    if (!ReadOutputs(&block_scope, function, subst_output_validator,
-                     tool.get(), err))
+    if (!ReadPatternList(&block_scope, "outputs", subst_output_validator,
+                         tool.get(), &Tool::set_outputs, err) ||
+        !ValidateOutputs(tool.get(), err))
       return Value();
   }
+  if (!ValidateRuntimeOutputs(tool.get(), tool_type, err))
+    return Value();
 
-  // Validate that the link_output, depend_output, and runtime_link_output
-  // refer to items in the outputs and aren't defined for irrelevant tool
-  // types.
-  if (!tool->link_output().empty()) {
-    if (tool_type != Toolchain::TYPE_SOLINK &&
-        tool_type != Toolchain::TYPE_SOLINK_MODULE) {
-      *err = Err(function, "This tool specifies a link_output.",
-          "This is only valid for solink and solink_module tools.");
-      return Value();
-    }
-    if (!IsPatternInOutputList(tool->outputs(), tool->link_output())) {
-      *err = Err(function, "This tool's link_output is bad.",
-                 "It must match one of the outputs.");
-      return Value();
-    }
-  }
-  if (!tool->depend_output().empty()) {
-    if (tool_type != Toolchain::TYPE_SOLINK &&
-        tool_type != Toolchain::TYPE_SOLINK_MODULE) {
-      *err = Err(function, "This tool specifies a depend_output.",
-          "This is only valid for solink and solink_module tools.");
-      return Value();
-    }
-    if (!IsPatternInOutputList(tool->outputs(), tool->depend_output())) {
-      *err = Err(function, "This tool's depend_output is bad.",
-                 "It must match one of the outputs.");
-      return Value();
-    }
-  }
+  // Validate link_output and depend_output.
+  if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->link_output(),
+                                   "link_output", err))
+    return Value();
+  if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->depend_output(),
+                                   "depend_output", err))
+    return Value();
   if ((!tool->link_output().empty() && tool->depend_output().empty()) ||
       (tool->link_output().empty() && !tool->depend_output().empty())) {
     *err = Err(function, "Both link_output and depend_output should either "
         "be specified or they should both be empty.");
     return Value();
   }
-  if (!tool->runtime_link_output().empty()) {
-    if (tool_type != Toolchain::TYPE_SOLINK &&
-        tool_type != Toolchain::TYPE_SOLINK_MODULE) {
-      *err = Err(function, "This tool specifies a runtime_link_output.",
-          "This is only valid for solink and solink_module tools.");
-      return Value();
-    }
-    if (!IsPatternInOutputList(tool->outputs(), tool->runtime_link_output())) {
-      *err = Err(function, "This tool's runtime_link_output is bad.",
-                 "It must match one of the outputs.");
-      return Value();
-    }
-  }
 
   // Make sure there weren't any vars set in this tool that were unused.
   if (!block_scope.CheckForUnusedVars(err))
diff --git a/tools/gn/function_toolchain_unittest.cc b/tools/gn/function_toolchain_unittest.cc
new file mode 100644
index 0000000..c7ffbc7
--- /dev/null
+++ b/tools/gn/function_toolchain_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2016 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 "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/test_with_scope.h"
+
+TEST(FunctionToolchain, RuntimeOutputs) {
+  Scheduler scheduler;
+  TestWithScope setup;
+
+  // These runtime outputs are a subset of the outputs so are OK.
+  {
+    TestParseInput input(
+        "toolchain(\"good\") {\n"
+        "  tool(\"link\") {\n"
+        "    outputs = [ \"foo\" ]\n"
+        "    runtime_outputs = [ \"foo\" ]\n"
+        "  }\n"
+        "}\n");
+    ASSERT_FALSE(input.has_error());
+
+    Err err;
+    input.parsed()->Execute(setup.scope(), &err);
+    ASSERT_FALSE(err.has_error()) << err.message();
+
+    // It should have generated a toolchain.
+    ASSERT_EQ(1u, setup.items().size());
+    const Toolchain* toolchain = setup.items()[0]->AsToolchain();
+    ASSERT_TRUE(toolchain);
+
+    // The toolchain should have a link tool with the two outputs.
+    const Tool* link = toolchain->GetTool(Toolchain::TYPE_LINK);
+    ASSERT_TRUE(link);
+    ASSERT_EQ(1u, link->outputs().list().size());
+    EXPECT_EQ("foo", link->outputs().list()[0].AsString());
+    ASSERT_EQ(1u, link->runtime_outputs().list().size());
+    EXPECT_EQ("foo", link->runtime_outputs().list()[0].AsString());
+  }
+
+  // This one is not a subset so should throw an error.
+  {
+    TestParseInput input(
+        "toolchain(\"bad\") {\n"
+        "  tool(\"link\") {\n"
+        "    outputs = [ \"foo\" ]\n"
+        "    runtime_outputs = [ \"bar\" ]\n"
+        "  }\n"
+        "}\n");
+    ASSERT_FALSE(input.has_error());
+
+    Err err;
+    input.parsed()->Execute(setup.scope(), &err);
+    ASSERT_TRUE(err.has_error()) << err.message();
+  }
+}
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp
index 6852ffe..1aa23d3 100644
--- a/tools/gn/gn.gyp
+++ b/tools/gn/gn.gyp
@@ -257,6 +257,7 @@
         'function_get_target_outputs_unittest.cc',
         'function_process_file_template_unittest.cc',
         'function_rebase_path_unittest.cc',
+        'function_toolchain_unittest.cc',
         'function_write_file_unittest.cc',
         'functions_target_unittest.cc',
         'functions_unittest.cc',
diff --git a/tools/gn/runtime_deps.cc b/tools/gn/runtime_deps.cc
index f350cb7..1849117 100644
--- a/tools/gn/runtime_deps.cc
+++ b/tools/gn/runtime_deps.cc
@@ -50,14 +50,6 @@
   AddIfNew(output_file, source, deps, found_file);
 }
 
-// Returns the output file that the runtime deps considers for the given
-// targets. This is weird only for shared libraries.
-const OutputFile& GetMainOutput(const Target* target) {
-  if (target->output_type() == Target::SHARED_LIBRARY)
-    return target->runtime_link_output_file();
-  return target->dependency_output_file();
-}
-
 // To avoid duplicate traversals of targets, or duplicating output files that
 // might be listed by more than one target, the set of targets and output files
 // that have been found so far is passed. The "value" of the seen_targets map
@@ -86,8 +78,10 @@
   // loadable modules.
   if (target->output_type() == Target::EXECUTABLE ||
       target->output_type() == Target::LOADABLE_MODULE ||
-      target->output_type() == Target::SHARED_LIBRARY)
-    AddIfNew(GetMainOutput(target), target, deps, found_files);
+      target->output_type() == Target::SHARED_LIBRARY) {
+    for (const auto& runtime_output : target->runtime_outputs())
+      AddIfNew(runtime_output, target, deps, found_files);
+  }
 
   // Add all data files.
   for (const auto& file : target->data())
@@ -173,8 +167,19 @@
       return false;
     }
 
-    OutputFile output_file =
-        OutputFile(GetMainOutput(target).value() + ".runtime_deps");
+    OutputFile output_file;
+    const char extension[] = ".runtime_deps";
+    if (target->output_type() == Target::SHARED_LIBRARY ||
+        target->output_type() == Target::LOADABLE_MODULE) {
+      // Force the first output for shared-library-type linker outputs since
+      // the dependency output files might not be the main output.
+      CHECK(!target->computed_outputs().empty());
+      output_file =
+          OutputFile(target->computed_outputs()[0].value() + extension);
+    } else {
+      output_file =
+          OutputFile(target->dependency_output_file().value() + extension);
+    }
     files_to_write->push_back(std::make_pair(output_file, target));
   }
   return true;
@@ -262,11 +267,9 @@
     "\n"
     "Multiple outputs\n"
     "\n"
-    "  When a tool produces more than one output, only the first output\n"
-    "  is considered. For example, a shared library target may produce a\n"
-    "  .dll and a .lib file on Windows. Only the .dll file will be considered\n"
-    "  a runtime dependency. This applies only to linker tools. Scripts and\n"
-    "  copy steps with multiple outputs will get all outputs listed.\n";
+    "  Linker tools can specify which of their outputs should be considered\n"
+    "  when computing the runtime deps by setting runtime_outputs. If this\n"
+    "  is unset on the tool, the default will be the first output only.\n";
 
 RuntimeDepsVector ComputeRuntimeDeps(const Target* target) {
   RuntimeDepsVector result;
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 9851035..206db83 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -557,6 +557,14 @@
       dependency_output_file_ =
           SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
               this, tool, tool->outputs().list()[0]);
+
+      if (tool->runtime_outputs().list().empty()) {
+        // Default to the first output for the runtime output.
+        runtime_outputs_.push_back(dependency_output_file_);
+      } else {
+        SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+            this, tool, tool->runtime_outputs(), &runtime_outputs_);
+      }
       break;
     case STATIC_LIBRARY:
       // Static libraries both have dependencies and linking going off of the
@@ -588,12 +596,12 @@
                   this, tool, tool->depend_output());
         }
       }
-      if (tool->runtime_link_output().empty()) {
-        runtime_link_output_file_ = link_output_file_;
+      if (tool->runtime_outputs().list().empty()) {
+        // Default to the link output for the runtime output.
+        runtime_outputs_.push_back(link_output_file_);
       } else {
-          runtime_link_output_file_ =
-              SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
-                  this, tool, tool->runtime_link_output());
+        SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+            this, tool, tool->runtime_outputs(), &runtime_outputs_);
       }
       break;
     case UNKNOWN:
diff --git a/tools/gn/target.h b/tools/gn/target.h
index bd84ec3..6b28f1f 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -292,8 +292,10 @@
   const OutputFile& dependency_output_file() const {
     return dependency_output_file_;
   }
-  const OutputFile& runtime_link_output_file() const {
-    return runtime_link_output_file_;
+
+  // The subset of computed_outputs that are considered runtime outputs.
+  const std::vector<OutputFile>& runtime_outputs() const {
+    return runtime_outputs_;
   }
 
   // Computes the set of output files resulting from compiling the given source
@@ -392,7 +394,7 @@
   std::vector<OutputFile> computed_outputs_;
   OutputFile link_output_file_;
   OutputFile dependency_output_file_;
-  OutputFile runtime_link_output_file_;
+  std::vector<OutputFile> runtime_outputs_;
 
   DISALLOW_COPY_AND_ASSIGN(Target);
 };
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc
index 2e2e63e..9bfeed5 100644
--- a/tools/gn/target_unittest.cc
+++ b/tools/gn/target_unittest.cc
@@ -562,12 +562,14 @@
 
   EXPECT_EQ("./liba.so", target.link_output_file().value());
   EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value());
-  EXPECT_EQ("./liba.so", target.runtime_link_output_file().value());
+
+  ASSERT_EQ(1u, target.runtime_outputs().size());
+  EXPECT_EQ("./liba.so", target.runtime_outputs()[0].value());
 }
 
-// Tests that runtime_link output works without an explicit link_output for
+// Tests that runtime_outputs works without an explicit link_output for
 // solink tools.
-TEST(Target, RuntimeLinkOuput) {
+TEST(Target, RuntimeOuputs) {
   TestWithScope setup;
   Err err;
 
@@ -577,20 +579,23 @@
   solink_tool->set_output_prefix("");
   solink_tool->set_default_output_extension(".dll");
 
+  // Say the linker makes a DLL< an import library, and a symbol file we want
+  // to treat as a runtime output.
   const char kLibPattern[] =
       "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib";
-  SubstitutionPattern lib_output =
-      SubstitutionPattern::MakeForTest(kLibPattern);
-
   const char kDllPattern[] =
       "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
-  SubstitutionPattern dll_output =
-      SubstitutionPattern::MakeForTest(kDllPattern);
+  const char kPdbPattern[] =
+      "{{root_out_dir}}/{{target_output_name}}.pdb";
+  SubstitutionPattern pdb_pattern =
+      SubstitutionPattern::MakeForTest(kPdbPattern);
 
   solink_tool->set_outputs(
-      SubstitutionList::MakeForTest(kLibPattern, kDllPattern));
+      SubstitutionList::MakeForTest(kLibPattern, kDllPattern, kPdbPattern));
 
-  solink_tool->set_runtime_link_output(dll_output);
+  // Say we only want the DLL and symbol file treaded as runtime outputs.
+  solink_tool->set_runtime_outputs(SubstitutionList::MakeForTest(
+      kDllPattern, kPdbPattern));
 
   toolchain.SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
 
@@ -601,7 +606,10 @@
 
   EXPECT_EQ("./a.dll.lib", target.link_output_file().value());
   EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value());
-  EXPECT_EQ("./a.dll", target.runtime_link_output_file().value());
+
+  ASSERT_EQ(2u, target.runtime_outputs().size());
+  EXPECT_EQ("./a.dll", target.runtime_outputs()[0].value());
+  EXPECT_EQ("./a.pdb", target.runtime_outputs()[1].value());
 }
 
 // Shared libraries should be inherited across public shared liobrary
diff --git a/tools/gn/tool.h b/tools/gn/tool.h
index 96ff7c0..f44af2e 100644
--- a/tools/gn/tool.h
+++ b/tools/gn/tool.h
@@ -14,6 +14,7 @@
 #include "tools/gn/substitution_list.h"
 #include "tools/gn/substitution_pattern.h"
 
+class ParseNode;
 class Pool;
 
 class Tool {
@@ -32,6 +33,9 @@
   Tool();
   ~Tool();
 
+  const ParseNode* defined_from() const { return defined_from_; }
+  void set_defined_from(const ParseNode* df) { defined_from_ = df; }
+
   // Getters/setters ----------------------------------------------------------
   //
   // After the tool has had its attributes set, the caller must call
@@ -41,36 +45,36 @@
   const SubstitutionPattern& command() const {
     return command_;
   }
-  void set_command(const SubstitutionPattern& cmd) {
+  void set_command(SubstitutionPattern cmd) {
     DCHECK(!complete_);
-    command_ = cmd;
+    command_ = std::move(cmd);
   }
 
   // Should include a leading "." if nonempty.
   const std::string& default_output_extension() const {
     return default_output_extension_;
   }
-  void set_default_output_extension(const std::string& ext) {
+  void set_default_output_extension(std::string ext) {
     DCHECK(!complete_);
     DCHECK(ext.empty() || ext[0] == '.');
-    default_output_extension_ = ext;
+    default_output_extension_ = std::move(ext);
   }
 
   const SubstitutionPattern& default_output_dir() const {
     return default_output_dir_;
   }
-  void set_default_output_dir(const SubstitutionPattern& dir) {
+  void set_default_output_dir(SubstitutionPattern dir) {
     DCHECK(!complete_);
-    default_output_dir_ = dir;
+    default_output_dir_ = std::move(dir);
   }
 
   // Dependency file (if supported).
   const SubstitutionPattern& depfile() const {
     return depfile_;
   }
-  void set_depfile(const SubstitutionPattern& df) {
+  void set_depfile(SubstitutionPattern df) {
     DCHECK(!complete_);
-    depfile_ = df;
+    depfile_ = std::move(df);
   }
 
   DepsFormat depsformat() const {
@@ -91,66 +95,66 @@
   const SubstitutionPattern& description() const {
     return description_;
   }
-  void set_description(const SubstitutionPattern& desc) {
+  void set_description(SubstitutionPattern desc) {
     DCHECK(!complete_);
-    description_ = desc;
+    description_ = std::move(desc);
   }
 
   const std::string& lib_switch() const {
     return lib_switch_;
   }
-  void set_lib_switch(const std::string& s) {
+  void set_lib_switch(std::string s) {
     DCHECK(!complete_);
-    lib_switch_ = s;
+    lib_switch_ = std::move(s);
   }
 
   const std::string& lib_dir_switch() const {
     return lib_dir_switch_;
   }
-  void set_lib_dir_switch(const std::string& s) {
+  void set_lib_dir_switch(std::string s) {
     DCHECK(!complete_);
-    lib_dir_switch_ = s;
+    lib_dir_switch_ = std::move(s);
   }
 
   const SubstitutionList& outputs() const {
     return outputs_;
   }
-  void set_outputs(const SubstitutionList& out) {
+  void set_outputs(SubstitutionList out) {
     DCHECK(!complete_);
-    outputs_ = out;
+    outputs_ = std::move(out);
   }
 
   // Should match files in the outputs() if nonempty.
   const SubstitutionPattern& link_output() const {
     return link_output_;
   }
-  void set_link_output(const SubstitutionPattern& link_out) {
+  void set_link_output(SubstitutionPattern link_out) {
     DCHECK(!complete_);
-    link_output_ = link_out;
+    link_output_ = std::move(link_out);
   }
 
   const SubstitutionPattern& depend_output() const {
     return depend_output_;
   }
-  void set_depend_output(const SubstitutionPattern& dep_out) {
+  void set_depend_output(SubstitutionPattern dep_out) {
     DCHECK(!complete_);
-    depend_output_ = dep_out;
+    depend_output_ = std::move(dep_out);
   }
 
-  const SubstitutionPattern& runtime_link_output() const {
-    return runtime_link_output_;
+  const SubstitutionList& runtime_outputs() const {
+    return runtime_outputs_;
   }
-  void set_runtime_link_output(const SubstitutionPattern& run_out) {
+  void set_runtime_outputs(SubstitutionList run_out) {
     DCHECK(!complete_);
-    runtime_link_output_ = run_out;
+    runtime_outputs_ = std::move(run_out);
   }
 
   const std::string& output_prefix() const {
     return output_prefix_;
   }
-  void set_output_prefix(const std::string& s) {
+  void set_output_prefix(std::string s) {
     DCHECK(!complete_);
-    output_prefix_ = s;
+    output_prefix_ = std::move(s);
   }
 
   bool restat() const {
@@ -164,21 +168,21 @@
   const SubstitutionPattern& rspfile() const {
     return rspfile_;
   }
-  void set_rspfile(const SubstitutionPattern& rsp) {
+  void set_rspfile(SubstitutionPattern rsp) {
     DCHECK(!complete_);
-    rspfile_ = rsp;
+    rspfile_ = std::move(rsp);
   }
 
   const SubstitutionPattern& rspfile_content() const {
     return rspfile_content_;
   }
-  void set_rspfile_content(const SubstitutionPattern& content) {
+  void set_rspfile_content(SubstitutionPattern content) {
     DCHECK(!complete_);
-    rspfile_content_ = content;
+    rspfile_content_ = std::move(content);
   }
 
   const LabelPtrPair<Pool>& pool() const { return pool_; }
-  void set_pool(const LabelPtrPair<Pool>& pool) { pool_ = pool; }
+  void set_pool(LabelPtrPair<Pool> pool) { pool_ = std::move(pool); }
 
   // Other functions ----------------------------------------------------------
 
@@ -201,6 +205,8 @@
   bool OnResolved(Err* err);
 
  private:
+  const ParseNode* defined_from_;
+
   SubstitutionPattern command_;
   std::string default_output_extension_;
   SubstitutionPattern default_output_dir_;
@@ -213,7 +219,7 @@
   SubstitutionList outputs_;
   SubstitutionPattern link_output_;
   SubstitutionPattern depend_output_;
-  SubstitutionPattern runtime_link_output_;
+  SubstitutionList runtime_outputs_;
   std::string output_prefix_;
   bool restat_;
   SubstitutionPattern rspfile_;