GN: Make the linker output file depend on the inputs.

This is useful for files that are only read by the linker, such as
orderfiles. We were normally getting this right in the case where
the target contained at least one source file, as the output file
would transitively depend on the inputs via the source files, but if
the target only contained dependencies on other targets, the inputs
wouldn't be depended on at all.

Bug: 835622
Change-Id: Ic7fb39caeacd4698b7ec6cfb0cb583a429a117dd
Reviewed-on: https://chromium-review.googlesource.com/1041274
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Commit-Queue: Peter Collingbourne <pcc@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#555931}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: ae62ba99a1a71db0758616215a557053d5fcf72a
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md
index 6f3682d..8fa85d2 100644
--- a/tools/gn/docs/reference.md
+++ b/tools/gn/docs/reference.md
@@ -4696,10 +4696,10 @@
 #### **Inputs for binary targets**
 
 ```
-  Any input dependencies will be resolved before compiling any sources.
-  Normally, all actions that a target depends on will be run before any files
-  in a target are compiled. So if you depend on generated headers, you do not
-  typically need to list them in the inputs section.
+  Any input dependencies will be resolved before compiling any sources or
+  linking the target. Normally, all actions that a target depends on will be run
+  before any files in a target are compiled. So if you depend on generated
+  headers, you do not typically need to list them in the inputs section.
 
   Inputs for binary targets will be treated as implicit dependencies, meaning
   that changes in any of the inputs will force all sources in the target to be
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc
index 0e2bab0..1711ad2 100644
--- a/tools/gn/ninja_binary_target_writer.cc
+++ b/tools/gn/ninja_binary_target_writer.cc
@@ -348,7 +348,7 @@
       DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj));
 #endif
   } else {
-    WriteLinkerStuff(obj_files, other_files);
+    WriteLinkerStuff(obj_files, other_files, input_dep);
   }
 }
 
@@ -752,7 +752,8 @@
 
 void NinjaBinaryTargetWriter::WriteLinkerStuff(
     const std::vector<OutputFile>& object_files,
-    const std::vector<SourceFile>& other_files) {
+    const std::vector<SourceFile>& other_files,
+    const OutputFile& input_dep) {
   std::vector<OutputFile> output_files;
   SubstitutionWriter::ApplyListToLinkerAsOutputFile(
       target_, tool_, tool_->outputs(), &output_files);
@@ -816,6 +817,11 @@
     }
   }
 
+  // The input dependency is only needed if there are no object files, as the
+  // dependency is normally provided transitively by the source files.
+  if (!input_dep.value().empty() && object_files.empty())
+    implicit_deps.push_back(input_dep);
+
   // Append implicit dependencies collected above.
   if (!implicit_deps.empty()) {
     out_ << " |";
diff --git a/tools/gn/ninja_binary_target_writer.h b/tools/gn/ninja_binary_target_writer.h
index 93e5b2d..91a7a27 100644
--- a/tools/gn/ninja_binary_target_writer.h
+++ b/tools/gn/ninja_binary_target_writer.h
@@ -107,7 +107,8 @@
                               const std::vector<OutputFile>& outputs);
 
   void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
-                        const std::vector<SourceFile>& other_files);
+                        const std::vector<SourceFile>& other_files,
+                        const OutputFile& input_dep);
   void WriteLinkerFlags(const SourceFile* optional_def_file);
   void WriteLibs();
   void WriteOutputSubstitutions();
diff --git a/tools/gn/ninja_binary_target_writer_unittest.cc b/tools/gn/ninja_binary_target_writer_unittest.cc
index 15b6da9..20239b0 100644
--- a/tools/gn/ninja_binary_target_writer_unittest.cc
+++ b/tools/gn/ninja_binary_target_writer_unittest.cc
@@ -1007,6 +1007,36 @@
     EXPECT_EQ(expected, out.str());
   }
 
+  // This target has one input but no source files.
+  {
+    Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+    target.set_output_type(Target::SHARED_LIBRARY);
+    target.visibility().SetPublic();
+    target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
+    target.SetToolchain(setup.toolchain());
+    ASSERT_TRUE(target.OnResolved(&err));
+
+    std::ostringstream out;
+    NinjaBinaryTargetWriter writer(&target, out);
+    writer.Run();
+
+    const char expected[] =
+        "defines =\n"
+        "include_dirs =\n"
+        "root_out_dir = .\n"
+        "target_out_dir = obj/foo\n"
+        "target_output_name = libbar\n"
+        "\n"
+        "\n"
+        "build ./libbar.so: solink | ../../foo/input.data\n"
+        "  ldflags =\n"
+        "  libs =\n"
+        "  output_extension = .so\n"
+        "  output_dir = \n";
+
+    EXPECT_EQ(expected, out.str());
+  }
+
   // This target has multiple inputs.
   {
     Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index b9dec07..42191e3 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -1229,10 +1229,10 @@
 
 Inputs for binary targets
 
-  Any input dependencies will be resolved before compiling any sources.
-  Normally, all actions that a target depends on will be run before any files
-  in a target are compiled. So if you depend on generated headers, you do not
-  typically need to list them in the inputs section.
+  Any input dependencies will be resolved before compiling any sources or
+  linking the target. Normally, all actions that a target depends on will be run
+  before any files in a target are compiled. So if you depend on generated
+  headers, you do not typically need to list them in the inputs section.
 
   Inputs for binary targets will be treated as implicit dependencies, meaning
   that changes in any of the inputs will force all sources in the target to be