Consider bundle_data as public_deps of create_bundle targets.

The create_bundle and bundle_data targets works as a pair to copy
files into iOS/OS X bundles. The bundle_data target defines the
files to copy and the destination in the bundle, the create_bundle
target generate the copy rule (as the destination cannot be resolved
earlier).

This cause a problem with generated files, as gn prevents a target
using as input a generated file unless there is a direct dependency
or a chain of public dependencies. Since bundle_data/create_bundle
are designed to be resolved recursively, update this check to consider
all bundle_data as public_deps of create_bundle targets.

Convert Target::PullRecursiveBundleData() to O(n) algorithm instead
of a O(n^2) by percolating the dependencies up.

BUG=297668

Review URL: https://codereview.chromium.org/1804263003

Cr-Original-Commit-Position: refs/heads/master@{#383049}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 1cc4b1ca2085776730f92ab41173c6afd9d51d12
diff --git a/tools/gn/bundle_data.cc b/tools/gn/bundle_data.cc
index e22612c..7faccbd 100644
--- a/tools/gn/bundle_data.cc
+++ b/tools/gn/bundle_data.cc
@@ -43,26 +43,38 @@
 
 BundleData::~BundleData() {}
 
-void BundleData::AddFileRuleFromTarget(const Target* target) {
+void BundleData::AddBundleData(const Target* target) {
   DCHECK_EQ(target->output_type(), Target::BUNDLE_DATA);
+  bundle_deps_.push_back(target);
+}
 
-  std::vector<SourceFile> file_rule_sources;
-  for (const SourceFile& source_file : target->sources()) {
-    if (IsSourceFileFromAssetCatalog(source_file, nullptr)) {
-      asset_catalog_sources_.push_back(source_file);
-    } else {
-      file_rule_sources.push_back(source_file);
+void BundleData::OnTargetResolved(Target* owning_target) {
+  // Only initialize file_rules_ and asset_catalog_sources for "create_bundle"
+  // target (properties are only used by those targets).
+  if (owning_target->output_type() != Target::CREATE_BUNDLE)
+    return;
+
+  for (const Target* target : bundle_deps_) {
+    SourceFiles file_rule_sources;
+    for (const SourceFile& source_file : target->sources()) {
+      if (IsSourceFileFromAssetCatalog(source_file, nullptr)) {
+        asset_catalog_sources_.push_back(source_file);
+      } else {
+        file_rule_sources.push_back(source_file);
+      }
+    }
+
+    if (!file_rule_sources.empty()) {
+      DCHECK_EQ(target->action_values().outputs().list().size(), 1u);
+      file_rules_.push_back(BundleFileRule(
+          file_rule_sources, target->action_values().outputs().list()[0]));
     }
   }
 
-  if (!file_rule_sources.empty()) {
-    DCHECK_EQ(target->action_values().outputs().list().size(), 1u);
-    file_rules_.push_back(BundleFileRule(
-        file_rule_sources, target->action_values().outputs().list()[0]));
-  }
+  GetSourceFiles(&owning_target->sources());
 }
 
-void BundleData::GetSourceFiles(std::vector<SourceFile>* sources) const {
+void BundleData::GetSourceFiles(SourceFiles* sources) const {
   for (const BundleFileRule& file_rule : file_rules_) {
     sources->insert(sources->end(), file_rule.sources().begin(),
                     file_rule.sources().end());
@@ -72,8 +84,8 @@
 }
 
 void BundleData::GetOutputFiles(const Settings* settings,
-                                std::vector<OutputFile>* outputs) const {
-  std::vector<SourceFile> outputs_as_sources;
+                                OutputFiles* outputs) const {
+  SourceFiles outputs_as_sources;
   GetOutputsAsSourceFiles(settings, &outputs_as_sources);
   for (const SourceFile& source_file : outputs_as_sources)
     outputs->push_back(OutputFile(settings->build_settings(), source_file));
@@ -81,7 +93,7 @@
 
 void BundleData::GetOutputsAsSourceFiles(
     const Settings* settings,
-    std::vector<SourceFile>* outputs_as_source) const {
+    SourceFiles* outputs_as_source) const {
   for (const BundleFileRule& file_rule : file_rules_) {
     for (const SourceFile& source : file_rule.sources()) {
       outputs_as_source->push_back(
diff --git a/tools/gn/bundle_data.h b/tools/gn/bundle_data.h
index 9d1df13..e78589e 100644
--- a/tools/gn/bundle_data.h
+++ b/tools/gn/bundle_data.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "tools/gn/bundle_file_rule.h"
+#include "tools/gn/unique_vector.h"
 
 class OutputFile;
 class SourceFile;
@@ -31,38 +32,46 @@
 // BundleData holds the information required by "create_bundle" target.
 class BundleData {
  public:
+  using UniqueTargets = UniqueVector<const Target*>;
+  using SourceFiles = std::vector<SourceFile>;
+  using OutputFiles = std::vector<OutputFile>;
+  using BundleFileRules = std::vector<BundleFileRule>;
+
   BundleData();
   ~BundleData();
 
-  // Extracts the information required from a "bundle_data" target.
-  void AddFileRuleFromTarget(const Target* target);
+  // Adds a bundle_data target to the recursive collection of all bundle_data
+  // that the target depends on.
+  void AddBundleData(const Target* target);
+
+  // Called upon resolution of the target owning this instance of BundleData.
+  // |owning_target| is the owning target.
+  void OnTargetResolved(Target* owning_target);
 
   // Returns the list of inputs.
-  void GetSourceFiles(std::vector<SourceFile>* sources) const;
+  void GetSourceFiles(SourceFiles* sources) const;
 
   // Returns the list of outputs.
   void GetOutputFiles(const Settings* settings,
-                      std::vector<OutputFile>* outputs) const;
+                      OutputFiles* outputs) const;
 
   // Returns the list of outputs as SourceFile.
   void GetOutputsAsSourceFiles(
       const Settings* settings,
-      std::vector<SourceFile>* outputs_as_source) const;
+      SourceFiles* outputs_as_source) const;
 
   // Returns the path to the compiled asset catalog. Only valid if
   // asset_catalog_sources() is not empty.
   SourceFile GetCompiledAssetCatalogPath() const;
 
   // Returns the list of inputs for the compilation of the asset catalog.
-  std::vector<SourceFile>& asset_catalog_sources() {
-    return asset_catalog_sources_;
-  }
-  const std::vector<SourceFile>& asset_catalog_sources() const {
+  SourceFiles& asset_catalog_sources() { return asset_catalog_sources_; }
+  const SourceFiles& asset_catalog_sources() const {
     return asset_catalog_sources_;
   }
 
-  std::vector<BundleFileRule>& file_rules() { return file_rules_; }
-  const std::vector<BundleFileRule>& file_rules() const { return file_rules_; }
+  BundleFileRules& file_rules() { return file_rules_; }
+  const BundleFileRules& file_rules() const { return file_rules_; }
 
   std::string& root_dir() { return root_dir_; }
   const std::string& root_dir() const { return root_dir_; }
@@ -76,9 +85,13 @@
   std::string& plugins_dir() { return plugins_dir_; }
   const std::string& plugins_dir() const { return plugins_dir_; }
 
+  // Recursive collection of all bundle_data that the target depends on.
+  const UniqueTargets& bundle_deps() const { return bundle_deps_; }
+
  private:
-  std::vector<SourceFile> asset_catalog_sources_;
-  std::vector<BundleFileRule> file_rules_;
+  SourceFiles asset_catalog_sources_;
+  BundleFileRules file_rules_;
+  UniqueTargets bundle_deps_;
 
   // All those values are subdirectories relative to root_build_dir, and apart
   // from root_dir, they are either equal to root_dir_ or subdirectories of it.
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 714fd91..2386870 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -125,6 +125,14 @@
                                             seen_targets))
         return true;  // Found a path.
     }
+    if (target->output_type() == Target::CREATE_BUNDLE) {
+      for (const auto& dep : target->bundle_data().bundle_deps()) {
+        if (EnsureFileIsGeneratedByDependency(dep, file, false,
+                                              consider_object_files,
+                                              seen_targets))
+          return true;  // Found a path.
+      }
+    }
   }
   return false;
 }
@@ -481,38 +489,21 @@
 }
 
 void Target::PullRecursiveBundleData() {
-  if (output_type_ != CREATE_BUNDLE)
-    return;
-
-  std::set<const Target*> visited;
-  std::vector<const Target*> deps;
-  deps.push_back(this);
-
-  while (!deps.empty()) {
-    const Target* current = deps.back();
-    deps.pop_back();
-
-    if (visited.find(current) != visited.end())
+  for (const auto& pair : GetDeps(DEPS_LINKED)) {
+    // Don't propagate bundle_data once they are added to a bundle.
+    if (pair.ptr->output_type() == CREATE_BUNDLE)
       continue;
-    visited.insert(current);
 
-    if (current->output_type_ == BUNDLE_DATA)
-      bundle_data_.AddFileRuleFromTarget(current);
+    // Direct dependency on a bundle_data target.
+    if (pair.ptr->output_type() == BUNDLE_DATA)
+      bundle_data_.AddBundleData(pair.ptr);
 
-    for (const LabelTargetPair& pair : current->GetDeps(DEPS_ALL)) {
-      DCHECK(pair.ptr);
-      DCHECK(pair.ptr->toolchain_);
-      if (visited.find(pair.ptr) != visited.end())
-        continue;
-
-      if (pair.ptr->output_type() == CREATE_BUNDLE)
-        continue;
-
-      deps.push_back(pair.ptr);
-    }
+    // Recursive bundle_data informations from all dependencies.
+    for (const auto& target : pair.ptr->bundle_data().bundle_deps())
+      bundle_data_.AddBundleData(target);
   }
 
-  bundle_data_.GetSourceFiles(&sources_);
+  bundle_data_.OnTargetResolved(this);
 }
 
 void Target::FillOutputFiles() {
diff --git a/tools/gn/target_unittest.cc b/tools/gn/target_unittest.cc
index 8f6684f..876c2e3 100644
--- a/tools/gn/target_unittest.cc
+++ b/tools/gn/target_unittest.cc
@@ -821,8 +821,16 @@
   ASSERT_EQ(a.bundle_data().file_rules()[0].sources().size(), 2u);
   ASSERT_EQ(a.bundle_data().file_rules()[1].sources().size(), 3u);
   ASSERT_EQ(a.bundle_data().asset_catalog_sources().size(), 4u);
+  ASSERT_EQ(a.bundle_data().bundle_deps().size(), 2u);
 
   // C gets its data from D.
   ASSERT_EQ(c.bundle_data().file_rules().size(), 1u);
   ASSERT_EQ(c.bundle_data().file_rules()[0].sources().size(), 1u);
+  ASSERT_EQ(c.bundle_data().bundle_deps().size(), 1u);
+
+  // E does not have any bundle_data information but gets a list of
+  // bundle_deps to propagate them during target resolution.
+  ASSERT_TRUE(e.bundle_data().file_rules().empty());
+  ASSERT_TRUE(e.bundle_data().asset_catalog_sources().empty());
+  ASSERT_EQ(e.bundle_data().bundle_deps().size(), 2u);
 }