GN: Throw an error for duplicate object files. Detect duplicate object files in the same target and throw an error. This provides a stronger barrier and a more actionable message than the Ninja "multiple rules" error that will result. Review URL: https://codereview.chromium.org/1210143003 Cr-Original-Commit-Position: refs/heads/master@{#336853} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 874d7ad541925ea79c94d9a8000407c7d5bb2c91
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc index 7b81bfa..a0a4e9f 100644 --- a/tools/gn/ninja_binary_target_writer.cc +++ b/tools/gn/ninja_binary_target_writer.cc
@@ -8,6 +8,7 @@ #include <set> #include <sstream> +#include "base/containers/hash_tables.h" #include "base/strings/string_util.h" #include "tools/gn/config_values_extractors.h" #include "tools/gn/deps_iterator.h" @@ -15,6 +16,7 @@ #include "tools/gn/escape.h" #include "tools/gn/filesystem_utils.h" #include "tools/gn/ninja_utils.h" +#include "tools/gn/scheduler.h" #include "tools/gn/settings.h" #include "tools/gn/source_file_type.h" #include "tools/gn/string_utils.h" @@ -294,6 +296,9 @@ // Also link all pch object files. obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end()); + if (!CheckForDuplicateObjectFiles(obj_files)) + return; + if (target_->output_type() == Target::SOURCE_SET) { WriteSourceSetStamp(obj_files); #ifndef NDEBUG @@ -791,3 +796,30 @@ return ret; } + +bool NinjaBinaryTargetWriter::CheckForDuplicateObjectFiles( + const std::vector<OutputFile>& files) const { + base::hash_set<std::string> set; + for (const auto& file : files) { + if (!set.insert(file.value()).second) { + Err err( + target_->defined_from(), + "Duplicate object file", + "The target " + target_->label().GetUserVisibleName(false) + + "\ngenerates two object files with the same name:\n " + + file.value() + "\n" + "\n" + "It could be you accidentally have a file listed twice in the\n" + "sources. Or, depending on how your toolchain maps sources to\n" + "object files, two source files with the same name in different\n" + "directories could map to the same object file.\n" + "\n" + "In the latter case, either rename one of the files or move one of\n" + "the sources to a separate source_set to avoid them both being in\n" + "the same target."); + g_scheduler->FailWithError(err); + return false; + } + } + return true; +}
diff --git a/tools/gn/ninja_binary_target_writer.h b/tools/gn/ninja_binary_target_writer.h index fe7a132..5888ac5 100644 --- a/tools/gn/ninja_binary_target_writer.h +++ b/tools/gn/ninja_binary_target_writer.h
@@ -116,6 +116,10 @@ // tool type. The tool must support precompiled headers. OutputFile GetWindowsPCHFile(Toolchain::ToolType tool_type) const; + // Checks for duplicates in the given list of output files. If any duplicates + // are found, throws an error and return false. + bool CheckForDuplicateObjectFiles(const std::vector<OutputFile>& files) const; + const Tool* tool_; // Cached version of the prefix used for rule types for this toolchain.
diff --git a/tools/gn/ninja_binary_target_writer_unittest.cc b/tools/gn/ninja_binary_target_writer_unittest.cc index 2ad816a..5438c4d 100644 --- a/tools/gn/ninja_binary_target_writer_unittest.cc +++ b/tools/gn/ninja_binary_target_writer_unittest.cc
@@ -6,6 +6,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "tools/gn/ninja_binary_target_writer.h" +#include "tools/gn/scheduler.h" #include "tools/gn/target.h" #include "tools/gn/test_with_scope.h" @@ -459,3 +460,23 @@ EXPECT_EQ(pch_win_expected, out.str()); } } + +// Should throw an error with the scheduler if a duplicate object file exists. +// This is dependent on the toolchain's object file mapping. +TEST(NinjaBinaryTargetWriter, DupeObjFileError) { + Scheduler scheduler; + + TestWithScope setup; + TestTarget target(setup, "//foo:bar", Target::EXECUTABLE); + target.sources().push_back(SourceFile("//a.cc")); + target.sources().push_back(SourceFile("//a.cc")); + + EXPECT_FALSE(scheduler.is_failed()); + + std::ostringstream out; + NinjaBinaryTargetWriter writer(&target, out); + writer.Run(); + + // Should have issued an error. + EXPECT_TRUE(scheduler.is_failed()); +}