Index: tools/gn/target.cc |
diff --git a/tools/gn/target.cc b/tools/gn/target.cc |
index 40c8a21e2adf743d0b0de24edcfee564f6559b3e..2ea285943248ffdef6ddabf0044738351d791d5b 100644 |
--- a/tools/gn/target.cc |
+++ b/tools/gn/target.cc |
@@ -60,6 +60,47 @@ Err MakeStaticLibDepsError(const Target* from, const Target* to) { |
"Use source sets for intermediate targets instead."); |
} |
+// Set check_private_deps to true for the first invocation since a target |
+// can see all of its dependencies. For recursive invocations this will be set |
+// to false to follow only public dependency paths. |
+// |
+// Pass a pointer to an empty set for the first invocation. This will be used |
+// to avoid duplicate checking. |
+bool EnsureFileIsGeneratedByDependency(const Target* target, |
+ const OutputFile& file, |
+ bool check_private_deps, |
+ 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. |
+ seen_targets->insert(target); |
+ |
+ // Assume that we have relatively few generated inputs so brute-force |
+ // searching here is OK. If this becomes a bottleneck, consider storing |
+ // computed_outputs as a hash set. |
+ for (const OutputFile& cur : target->computed_outputs()) { |
+ if (file == cur) |
+ 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)) |
+ return true; // Found a path. |
+ } |
+ |
+ // Only check private deps if requested. |
+ if (check_private_deps) { |
+ for (const auto& pair : target->private_deps()) { |
+ if (EnsureFileIsGeneratedByDependency(pair.ptr, file, false, |
+ seen_targets)) |
+ return true; // Found a path. |
+ } |
+ } |
+ return false; |
+} |
+ |
} // namespace |
Target::Target(const Settings* settings, const Label& label) |
@@ -132,12 +173,15 @@ bool Target::OnResolved(Err* err) { |
FillOutputFiles(); |
- if (!CheckVisibility(err)) |
- return false; |
- if (!CheckTestonly(err)) |
- return false; |
- if (!CheckNoNestedStaticLibs(err)) |
- return false; |
+ if (settings()->build_settings()->check_for_bad_items()) { |
+ if (!CheckVisibility(err)) |
+ return false; |
+ if (!CheckTestonly(err)) |
+ return false; |
+ if (!CheckNoNestedStaticLibs(err)) |
+ return false; |
+ CheckSourcesGenerated(); |
+ } |
return true; |
} |
@@ -304,6 +348,7 @@ void Target::PullRecursiveHardDeps() { |
void Target::FillOutputFiles() { |
const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this); |
+ bool check_tool_outputs = false; |
switch (output_type_) { |
case GROUP: |
case SOURCE_SET: |
@@ -322,6 +367,7 @@ void Target::FillOutputFiles() { |
// Executables don't get linked to, but the first output is used for |
// dependency management. |
CHECK_GE(tool->outputs().list().size(), 1u); |
+ check_tool_outputs = true; |
dependency_output_file_ = |
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( |
this, tool, tool->outputs().list()[0]); |
@@ -330,12 +376,14 @@ void Target::FillOutputFiles() { |
// Static libraries both have dependencies and linking going off of the |
// first output. |
CHECK(tool->outputs().list().size() >= 1); |
+ check_tool_outputs = true; |
link_output_file_ = dependency_output_file_ = |
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile( |
this, tool, tool->outputs().list()[0]); |
break; |
case SHARED_LIBRARY: |
CHECK(tool->outputs().list().size() >= 1); |
+ check_tool_outputs = true; |
if (tool->link_output().empty() && tool->depend_output().empty()) { |
// Default behavior, use the first output file for both. |
link_output_file_ = dependency_output_file_ = |
@@ -359,6 +407,26 @@ void Target::FillOutputFiles() { |
default: |
NOTREACHED(); |
} |
+ |
+ // Count all outputs from this tool as something generated by this target. |
+ if (check_tool_outputs) { |
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile( |
+ this, tool, tool->outputs(), &computed_outputs_); |
+ |
+ // Output names aren't canonicalized in the same way that source files |
+ // are. For example, the tool outputs often use |
+ // {{some_var}}/{{output_name}} which expands to "./foo", but this won't |
+ // match "foo" which is what we'll compute when converting a SourceFile to |
+ // an OutputFile. |
+ for (auto& out : computed_outputs_) |
+ NormalizePath(&out.value()); |
+ } |
+ |
+ // Also count anything the target has declared to be an output. |
+ std::vector<SourceFile> outputs_as_sources; |
+ action_values_.GetOutputsAsSourceFiles(this, &outputs_as_sources); |
+ for (const SourceFile& out : outputs_as_sources) |
+ computed_outputs_.push_back(OutputFile(settings()->build_settings(), out)); |
} |
bool Target::CheckVisibility(Err* err) const { |
@@ -409,3 +477,30 @@ bool Target::CheckNoNestedStaticLibs(Err* err) const { |
} |
return true; |
} |
+ |
+void Target::CheckSourcesGenerated() const { |
+ // Checks that any inputs or sources to this target that are in the build |
+ // directory are generated by a target that this one transitively depends on |
+ // in some way. We already guarantee that all generated files are written |
+ // to the build dir. |
+ // |
+ // See Scheduler::AddUnknownGeneratedInput's declaration for more. |
+ for (const SourceFile& file : sources_) |
+ CheckSourceGenerated(file); |
+ for (const SourceFile& file : inputs_) |
+ CheckSourceGenerated(file); |
+} |
+ |
+void Target::CheckSourceGenerated(const SourceFile& source) const { |
+ if (!IsStringInOutputDir(settings()->build_settings()->build_dir(), |
+ source.value())) |
+ return; // Not in output dir, this is OK. |
+ |
+ // Tell the scheduler about unknown files. This will be noted for later so |
+ // the list of files written by the GN build itself (often response files) |
+ // 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); |
+} |