gn: Add runtime_link_output to tool("solib")

gn's tool("solink") has two settings for the output of the rule:
link_output and depend_output. link_output is what targets depending
on a shared_library are linked against, and depend_output is what
is used for the ninja dependency.

On POSIX, this is used to implement a component build optimization:
If a shared library is linked but its public interface doesn't change, then
it's not necessary to relink its downstream dependencies. To implement
this, depend_output is set to a text file that contains a description of all
public symbols of the shared library. The link step first links and then
checks if the new public symbols are different from the old contents of
the file, and only then does it overwrite the TOC file. This allows ninja's
restat optimization to work.

However, downstream dependencies need to link against the actual .so
file on the link line `ld -o foo libinput.so`, so link_output needs to be set
to the .so file.

On Windows, link.exe writes a .lib and a .dll file when it creates a .dll file.
The .lib is only written when needed with incremental links, so if
depend_output is set to the .lib then the restat optimization works there
too. And downstream dependencies also need to link to the .lib at
link time, so in Windows both depend_output and link_output must
be set the the .lib file.

So far, so good. However, `gn desc runtime_deps` is used to print what files
need to be copied to swarming bots to run an executable, and that currently
looks at link_output. On POSIX that's ok, but on Windows that ends up
copying the .lib instead of the .dll.

This patch adds a third setting "runtime_link_output" which can be set
to the output of the solink tool that must be present at runtime. It defaults
to link_output (which does the right thing on POSIX), but it can be set to the
.dll file to make `gn desc runtime_deps` do the right thing on Windows.

This is needed to make swarming work in component builds on Windows.

BUG=354261, 498033
R=dpranke@chromium.org, scottmg@chromium.org

Review URL: https://codereview.chromium.org/1690843002 .

Cr-Original-Commit-Position: refs/heads/master@{#374840}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: e3398c19aa9769ad99e97a9fabf29614d645a095
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md
index 77f5700..9ed3b87 100644
--- a/tools/gn/docs/reference.md
+++ b/tools/gn/docs/reference.md
@@ -2486,9 +2486,10 @@
         that actually produces these files.
 
         If you specify more than one output for shared library links,
-        you should consider setting link_output and depend_output.
-        Otherwise, the first entry in the outputs list should always be
-        the main output which will be linked to.
+        you should consider setting link_output, depend_output, and
+        runtime_link_output. Otherwise, the first entry in the
+        outputs list should always be the main output which will be
+        linked to.
 
         Example for a compiler tool that produces .obj files:
           outputs = [
@@ -2507,20 +2508,22 @@
 
     link_output  [string with substitutions]
     depend_output  [string with substitutions]
+    runtime_link_output  [string with substitutions]
         Valid for: "solink" only (optional)
 
-        These two files specify which of the outputs from the solink
+        These three files specify which of the outputs from the solink
         tool should be used for linking and dependency tracking. These
         should match entries in the "outputs". If unspecified, the
-        first item in the "outputs" array will be used for both. See
+        first item in the "outputs" array will be used for all. See
         "Separate linking and dependencies for shared libraries"
-        below for more.
+        below for more.  If link_output is set but runtime_link_output
+        is not set, runtime_link_output defaults to link_output.
 
         On Windows, where the tools produce a .dll shared library and
-        a .lib import library, you will want both of these to be the
-        import library. On Linux, if you're not doing the separate
-        linking/dependency optimization, both of these should be the
-        .so output.
+        a .lib import library, you will want the first two to be the
+        import library and the third one to be the .dll file.
+        On Linux, if you're not doing the separate linking/dependency
+        optimization, all of these should be the .so output.
 
     output_prefix  [string]
         Valid for: Linker tools (optional)
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index 5853255..3e272c9 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -481,9 +481,10 @@
     "        that actually produces these files.\n"
     "\n"
     "        If you specify more than one output for shared library links,\n"
-    "        you should consider setting link_output and depend_output.\n"
-    "        Otherwise, the first entry in the outputs list should always be\n"
-    "        the main output which will be linked to.\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"
     "\n"
     "        Example for a compiler tool that produces .obj files:\n"
     "          outputs = [\n"
@@ -503,20 +504,22 @@
     "\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 two files specify which of the outputs from the solink\n"
+    "        These three 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 both. See\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.\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"
     "\n"
     "        On Windows, where the tools produce a .dll shared library and\n"
-    "        a .lib import library, you will want both of these to be the\n"
-    "        import library. On Linux, if you're not doing the separate\n"
-    "        linking/dependency optimization, both of these should be the\n"
-    "        .so output.\n"
+    "        a .lib import library, you will want the first two to be the\n"
+    "        import library and the third one to be the .dll file.\n"
+    "        On Linux, if you're not doing the separate linking/dependency\n"
+    "        optimization, all of these should be the .so output.\n"
     "\n"
     "    output_prefix  [string]\n"
     "        Valid for: Linker tools (optional)\n"
@@ -827,14 +830,16 @@
       !ReadDepsFormat(&block_scope, tool.get(), err) ||
       !ReadPattern(&block_scope, "description", subst_validator, tool.get(),
                    &Tool::set_description, err) ||
-      !ReadString(&block_scope, "lib_switch", tool.get(),
-                  &Tool::set_lib_switch, err) ||
+      !ReadString(&block_scope, "lib_switch", tool.get(), &Tool::set_lib_switch,
+                  err) ||
       !ReadString(&block_scope, "lib_dir_switch", tool.get(),
                   &Tool::set_lib_dir_switch, err) ||
       !ReadPattern(&block_scope, "link_output", subst_validator, tool.get(),
                    &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) ||
       !ReadString(&block_scope, "output_prefix", tool.get(),
                   &Tool::set_output_prefix, err) ||
       !ReadPrecompiledHeaderType(&block_scope, tool.get(), err) ||
@@ -854,8 +859,9 @@
       return Value();
   }
 
-  // Validate that the link_output and depend_output refer to items in the
-  // outputs and aren't defined for irrelevant tool types.
+  // 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) {
@@ -888,6 +894,19 @@
         "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/runtime_deps.cc b/tools/gn/runtime_deps.cc
index d828311..078bebe 100644
--- a/tools/gn/runtime_deps.cc
+++ b/tools/gn/runtime_deps.cc
@@ -53,7 +53,7 @@
 // targets. This is weird only for shared libraries.
 const OutputFile& GetMainOutput(const Target* target) {
   if (target->output_type() == Target::SHARED_LIBRARY)
-    return target->link_output_file();
+    return target->runtime_link_output_file();
   return target->dependency_output_file();
 }
 
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 7dd1921..594b368 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -531,6 +531,13 @@
                   this, tool, tool->depend_output());
         }
       }
+      if (tool->runtime_link_output().empty()) {
+        runtime_link_output_file_ = link_output_file_;
+      } else {
+          runtime_link_output_file_ =
+              SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+                  this, tool, tool->runtime_link_output());
+      }
       break;
     case UNKNOWN:
     default:
diff --git a/tools/gn/target.h b/tools/gn/target.h
index 18e5fb8..f49fdba 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -257,6 +257,9 @@
   const OutputFile& dependency_output_file() const {
     return dependency_output_file_;
   }
+  const OutputFile& runtime_link_output_file() const {
+    return runtime_link_output_file_;
+  }
 
   // Computes the set of output files resulting from compiling the given source
   // file. If the file can be compiled and the tool exists, fills the outputs
@@ -350,6 +353,7 @@
   std::vector<OutputFile> computed_outputs_;
   OutputFile link_output_file_;
   OutputFile dependency_output_file_;
+  OutputFile runtime_link_output_file_;
 
   DISALLOW_COPY_AND_ASSIGN(Target);
 };
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc
index c99ebe8..8376fff 100644
--- a/tools/gn/target_unittest.cc
+++ b/tools/gn/target_unittest.cc
@@ -457,6 +457,46 @@
 
   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());
+}
+
+// Tests that runtime_link output works without an explicit link_output for
+// solink tools.
+TEST(Target, RuntimeLinkOuput) {
+  TestWithScope setup;
+  Err err;
+
+  Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
+
+  scoped_ptr<Tool> solink_tool(new Tool());
+  solink_tool->set_output_prefix("");
+  solink_tool->set_default_output_extension(".dll");
+
+  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);
+
+  solink_tool->set_outputs(
+      SubstitutionList::MakeForTest(kLibPattern, kDllPattern));
+
+  solink_tool->set_runtime_link_output(dll_output);
+
+  toolchain.SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+
+  Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
+  target.set_output_type(Target::SHARED_LIBRARY);
+  target.SetToolchain(&toolchain);
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  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());
 }
 
 // Shared libraries should be inherited across public shared liobrary
diff --git a/tools/gn/tool.h b/tools/gn/tool.h
index 8cd0c7e..9360ce6 100644
--- a/tools/gn/tool.h
+++ b/tools/gn/tool.h
@@ -125,6 +125,14 @@
     depend_output_ = dep_out;
   }
 
+  const SubstitutionPattern& runtime_link_output() const {
+    return runtime_link_output_;
+  }
+  void set_runtime_link_output(const SubstitutionPattern& run_out) {
+    DCHECK(!complete_);
+    runtime_link_output_ = run_out;
+  }
+
   const std::string& output_prefix() const {
     return output_prefix_;
   }
@@ -187,6 +195,7 @@
   SubstitutionList outputs_;
   SubstitutionPattern link_output_;
   SubstitutionPattern depend_output_;
+  SubstitutionPattern runtime_link_output_;
   std::string output_prefix_;
   bool restat_;
   SubstitutionPattern rspfile_;