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;