| 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);
|
| +}
|
|
|