Allow .o files for GN generated inputs. GN checks inputs and sources for files that are in the build directory, and requires those files be generated by a dependency of the target. Previously, only the final outputs of a target were considered. But NaCl wants to compile some CRT code and take one of the intermediate object files as inputs to a subsequent action. The current code was not aware of the generated object files. This patch will check for generated object files when the normal checks for generated inputs fails. BUG=579629 Review URL: https://codereview.chromium.org/1607423002 Cr-Original-Commit-Position: refs/heads/master@{#370539} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 65e89854f57ac34a51e4c60ac663ce21110e6944
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc index 593d769..fa8849a 100644 --- a/tools/gn/ninja_binary_target_writer.cc +++ b/tools/gn/ninja_binary_target_writer.cc
@@ -92,49 +92,6 @@ PathOutput& path_output_; }; -// 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 in -// and writes the tool type to computed_tool_type. If the file is not -// compilable, returns false. -// -// The target that the source belongs to is passed as an argument. In the case -// of linking to source sets, this can be different than the target this class -// is currently writing. -// -// The function can succeed with a "NONE" tool type for object files which are -// just passed to the output. The output will always be overwritten, not -// appended to. -bool GetOutputFilesForSource(const Target* target, - const SourceFile& source, - Toolchain::ToolType* computed_tool_type, - std::vector<OutputFile>* outputs) { - outputs->clear(); - *computed_tool_type = Toolchain::TYPE_NONE; - - SourceFileType file_type = GetSourceFileType(source); - if (file_type == SOURCE_UNKNOWN) - return false; - if (file_type == SOURCE_O) { - // Object files just get passed to the output and not compiled. - outputs->push_back( - OutputFile(target->settings()->build_settings(), source)); - return true; - } - - *computed_tool_type = - target->toolchain()->GetToolTypeForSourceType(file_type); - if (*computed_tool_type == Toolchain::TYPE_NONE) - return false; // No tool for this file (it's a header file or something). - const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); - if (!tool) - return false; // Tool does not apply for this toolchain.file. - - // Figure out what output(s) this compiler produces. - SubstitutionWriter::ApplyListToCompilerAsOutputFile( - target, source, tool->outputs(), outputs); - return !outputs->empty(); -} - // Returns the language-specific suffix for precompiled header files. const char* GetPCHLangSuffixForToolType(Toolchain::ToolType type) { switch (type) { @@ -256,7 +213,7 @@ // the tool if there are more than one. for (const auto& source : source_set->sources()) { Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; - if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs)) + if (source_set->GetOutputFilesForSource(source, &tool_type, &tool_outputs)) obj_files->push_back(tool_outputs[0]); used_types.Set(GetSourceFileType(source)); @@ -662,7 +619,7 @@ // Clear the vector but maintain the max capacity to prevent reallocations. deps.resize(0); Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; - if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { + if (!target_->GetOutputFilesForSource(source, &tool_type, &tool_outputs)) { if (GetSourceFileType(source) == SOURCE_DEF) other_files->push_back(source); continue; // No output for this source.
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc index d3902c5..6c5d12b 100644 --- a/tools/gn/ninja_target_writer.cc +++ b/tools/gn/ninja_target_writer.cc
@@ -67,11 +67,7 @@ } else if (target->output_type() == Target::GROUP) { NinjaGroupTargetWriter writer(target, file); writer.Run(); - } else if (target->output_type() == Target::EXECUTABLE || - target->output_type() == Target::LOADABLE_MODULE || - target->output_type() == Target::STATIC_LIBRARY || - target->output_type() == Target::SHARED_LIBRARY || - target->output_type() == Target::SOURCE_SET) { + } else if (target->IsBinary()) { NinjaBinaryTargetWriter writer(target, file); writer.Run(); } else {
diff --git a/tools/gn/target.cc b/tools/gn/target.cc index 49a22fc..905ee4f 100644 --- a/tools/gn/target.cc +++ b/tools/gn/target.cc
@@ -6,6 +6,8 @@ #include <stddef.h> +#include <algorithm> + #include "base/bind.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -13,7 +15,10 @@ #include "tools/gn/deps_iterator.h" #include "tools/gn/filesystem_utils.h" #include "tools/gn/scheduler.h" +#include "tools/gn/source_file_type.h" #include "tools/gn/substitution_writer.h" +#include "tools/gn/tool.h" +#include "tools/gn/toolchain.h" #include "tools/gn/trace.h" namespace { @@ -69,9 +74,15 @@ // // Pass a pointer to an empty set for the first invocation. This will be used // to avoid duplicate checking. +// +// Checking of object files is optional because it is much slower. This allows +// us to check targets for normal outputs, and then as a second pass check +// object files (since we know it will be an error otherwise). This allows +// us to avoid computing all object file names in the common case. bool EnsureFileIsGeneratedByDependency(const Target* target, const OutputFile& file, bool check_private_deps, + bool consider_object_files, std::set<const Target*>* seen_targets) { if (seen_targets->find(target) != seen_targets->end()) return false; // Already checked this one and it's not found. @@ -85,11 +96,24 @@ return true; } + // Check binary target intermediate files if requested. + if (consider_object_files && target->IsBinary()) { + std::vector<OutputFile> source_outputs; + for (const SourceFile& source : target->sources()) { + Toolchain::ToolType tool_type; + if (!target->GetOutputFilesForSource(source, &tool_type, &source_outputs)) + continue; + if (std::find(source_outputs.begin(), source_outputs.end(), file) != + source_outputs.end()) + return true; + } + } + // Check all public dependencies (don't do data ones since those are // runtime-only). for (const auto& pair : target->public_deps()) { if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false, - seen_targets)) + consider_object_files, seen_targets)) return true; // Found a path. } @@ -97,6 +121,7 @@ if (check_private_deps) { for (const auto& pair : target->private_deps()) { if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false, + consider_object_files, seen_targets)) return true; // Found a path. } @@ -215,6 +240,14 @@ return true; } +bool Target::IsBinary() const { + return output_type_ == EXECUTABLE || + output_type_ == SHARED_LIBRARY || + output_type_ == LOADABLE_MODULE || + output_type_ == STATIC_LIBRARY || + output_type_ == SOURCE_SET; +} + bool Target::IsLinkable() const { return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY; } @@ -287,6 +320,34 @@ return false; } +bool Target::GetOutputFilesForSource(const SourceFile& source, + Toolchain::ToolType* computed_tool_type, + std::vector<OutputFile>* outputs) const { + outputs->clear(); + *computed_tool_type = Toolchain::TYPE_NONE; + + SourceFileType file_type = GetSourceFileType(source); + if (file_type == SOURCE_UNKNOWN) + return false; + if (file_type == SOURCE_O) { + // Object files just get passed to the output and not compiled. + outputs->push_back(OutputFile(settings()->build_settings(), source)); + return true; + } + + *computed_tool_type = toolchain_->GetToolTypeForSourceType(file_type); + if (*computed_tool_type == Toolchain::TYPE_NONE) + return false; // No tool for this file (it's a header file or something). + const Tool* tool = toolchain_->GetTool(*computed_tool_type); + if (!tool) + return false; // Tool does not apply for this toolchain.file. + + // Figure out what output(s) this compiler produces. + SubstitutionWriter::ApplyListToCompilerAsOutputFile( + this, source, tool->outputs(), outputs); + return !outputs->empty(); +} + void Target::PullDependentTargetConfigsFrom(const Target* dep) { MergeAllDependentConfigsFrom(dep, &configs_, &all_dependent_configs_); MergePublicConfigsFrom(dep, &configs_); @@ -568,6 +629,13 @@ // can be filtered out of this list. OutputFile out_file(settings()->build_settings(), source); std::set<const Target*> seen_targets; - if (!EnsureFileIsGeneratedByDependency(this, out_file, true, &seen_targets)) - g_scheduler->AddUnknownGeneratedInput(this, source); + if (!EnsureFileIsGeneratedByDependency(this, out_file, true, false, + &seen_targets)) { + // Check object files (much slower and very rare) only if the "normal" + // output check failed. + seen_targets.clear(); + if (!EnsureFileIsGeneratedByDependency(this, out_file, true, true, + &seen_targets)) + g_scheduler->AddUnknownGeneratedInput(this, source); + } }
diff --git a/tools/gn/target.h b/tools/gn/target.h index eff4d15..cbdab35 100644 --- a/tools/gn/target.h +++ b/tools/gn/target.h
@@ -21,6 +21,7 @@ #include "tools/gn/ordered_set.h" #include "tools/gn/output_file.h" #include "tools/gn/source_file.h" +#include "tools/gn/toolchain.h" #include "tools/gn/unique_vector.h" class DepsIteratorRange; @@ -66,6 +67,10 @@ OutputType output_type() const { return output_type_; } void set_output_type(OutputType t) { output_type_ = t; } + // True for targets that compile source code (all types of libaries and + // executables). + bool IsBinary() const; + // Can be linked into other targets. bool IsLinkable() const; @@ -245,6 +250,18 @@ return dependency_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 + // in and writes the tool type to computed_tool_type. If the file is not + // compilable, returns false. + // + // The function can succeed with a "NONE" tool type for object files which + // are just passed to the output. The output will always be overwritten, not + // appended to. + bool GetOutputFilesForSource(const SourceFile& source, + Toolchain::ToolType* computed_tool_type, + std::vector<OutputFile>* outputs) const; + private: FRIEND_TEST_ALL_PREFIXES(Target, ResolvePrecompiledHeaders);
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc index 4d9b1da..c59f44c 100644 --- a/tools/gn/target_unittest.cc +++ b/tools/gn/target_unittest.cc
@@ -587,6 +587,31 @@ EXPECT_TRUE(scheduler.GetUnknownGeneratedInputs().empty()); } +// Tests that intermediate object files generated by binary targets are also +// considered generated for the purposes of input checking. Above, we tested +// the failure cases for generated inputs, so here only test .o files that are +// present. +TEST(Target, ObjectGeneratedInputs) { + Scheduler scheduler; + TestWithScope setup; + Err err; + + // This target compiles the source. + SourceFile source_file("//source.cc"); + TestTarget source_generator(setup, "//:source_target", Target::SOURCE_SET); + source_generator.sources().push_back(source_file); + EXPECT_TRUE(source_generator.OnResolved(&err)); + + // This is the object file that the test toolchain generates for the source. + SourceFile object_file("//out/Debug/obj/source_target.source.o"); + + TestTarget final_target(setup, "//:final", Target::ACTION); + final_target.inputs().push_back(object_file); + EXPECT_TRUE(final_target.OnResolved(&err)); + + AssertSchedulerHasOneUnknownFileMatching(&final_target, object_file); +} + TEST(Target, ResolvePrecompiledHeaders) { TestWithScope setup; Err err;