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