Add bundle_deps_filter to create_bundle targets.

On iOS application extension can share runtime data with the main
application and thus it is not necessary to copy the runtime data
into the application extension bundle.

To implement this add a new bundle_deps_filter property to the
create_bundle target used to filter targets when recursively
resolving the deps. Any target matching one of the labels in
bundle_deps_filter is not added as a dependency.

BUG=615058

Review-Url: https://codereview.chromium.org/2205693005
Cr-Original-Commit-Position: refs/heads/master@{#410281}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 7ac497647684c5e90020f2917b2d849b9056826a
diff --git a/tools/gn/bundle_data.cc b/tools/gn/bundle_data.cc
index 448eac9..c7f3fb8 100644
--- a/tools/gn/bundle_data.cc
+++ b/tools/gn/bundle_data.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "tools/gn/filesystem_utils.h"
+#include "tools/gn/label_pattern.h"
 #include "tools/gn/output_file.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/substitution_writer.h"
@@ -52,6 +53,10 @@
 
 void BundleData::AddBundleData(const Target* target) {
   DCHECK_EQ(target->output_type(), Target::BUNDLE_DATA);
+  for (const auto& pattern : bundle_deps_filter_) {
+    if (pattern.Matches(target->label()))
+      return;
+  }
   bundle_deps_.push_back(target);
 }
 
diff --git a/tools/gn/bundle_data.h b/tools/gn/bundle_data.h
index e35cacb..0281b8b 100644
--- a/tools/gn/bundle_data.h
+++ b/tools/gn/bundle_data.h
@@ -15,6 +15,7 @@
 #include "tools/gn/substitution_list.h"
 #include "tools/gn/unique_vector.h"
 
+class LabelPattern;
 class OutputFile;
 class Settings;
 class Target;
@@ -120,6 +121,13 @@
     return code_signing_args_;
   }
 
+  std::vector<LabelPattern>& bundle_deps_filter() {
+    return bundle_deps_filter_;
+  }
+  const std::vector<LabelPattern>& bundle_deps_filter() const {
+    return bundle_deps_filter_;
+  }
+
   // Recursive collection of all bundle_data that the target depends on.
   const UniqueTargets& bundle_deps() const { return bundle_deps_; }
 
@@ -128,6 +136,7 @@
   std::vector<const Target*> assets_catalog_deps_;
   BundleFileRules file_rules_;
   UniqueTargets bundle_deps_;
+  std::vector<LabelPattern> bundle_deps_filter_;
 
   // 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/create_bundle_target_generator.cc b/tools/gn/create_bundle_target_generator.cc
index 99bc464..6cbd96f 100644
--- a/tools/gn/create_bundle_target_generator.cc
+++ b/tools/gn/create_bundle_target_generator.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "tools/gn/filesystem_utils.h"
+#include "tools/gn/label_pattern.h"
 #include "tools/gn/parse_tree.h"
 #include "tools/gn/scope.h"
 #include "tools/gn/substitution_type.h"
@@ -54,6 +55,9 @@
 
   if (!FillCodeSigningArgs())
     return;
+
+  if (!FillBundleDepsFilter())
+    return;
 }
 
 bool CreateBundleTargetGenerator::FillBundleDir(
@@ -193,3 +197,24 @@
 
   return target_->bundle_data().code_signing_args().Parse(*value, err_);
 }
+
+bool CreateBundleTargetGenerator::FillBundleDepsFilter() {
+  const Value* value = scope_->GetValue(variables::kBundleDepsFilter, true);
+  if (!value)
+    return true;
+
+  if (!value->VerifyTypeIs(Value::LIST, err_))
+    return false;
+
+  const SourceDir& current_dir = scope_->GetSourceDir();
+  std::vector<LabelPattern>& bundle_deps_filter =
+      target_->bundle_data().bundle_deps_filter();
+  for (const auto& item : value->list_value()) {
+    bundle_deps_filter.push_back(
+        LabelPattern::GetPattern(current_dir, item, err_));
+    if (err_->has_error())
+      return false;
+  }
+
+  return true;
+}
diff --git a/tools/gn/create_bundle_target_generator.h b/tools/gn/create_bundle_target_generator.h
index 9527a1a..585abf1 100644
--- a/tools/gn/create_bundle_target_generator.h
+++ b/tools/gn/create_bundle_target_generator.h
@@ -33,6 +33,7 @@
   bool FillCodeSigningSources();
   bool FillCodeSigningOutputs();
   bool FillCodeSigningArgs();
+  bool FillBundleDepsFilter();
 
   DISALLOW_COPY_AND_ASSIGN(CreateBundleTargetGenerator);
 };
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md
index f06b517..9d62a5e 100644
--- a/tools/gn/docs/reference.md
+++ b/tools/gn/docs/reference.md
@@ -1334,7 +1334,7 @@
   }
 
   bundle_data("base_unittests_bundle_data]") {
-    sources = [ "test/data" ]
+    sources = [ "test/data" ]
     outputs = [
       "{{bundle_resources_dir}}/{{source_root_relative_dir}}/" +
           "{{source_file_part}}"
@@ -1500,8 +1500,8 @@
 
 ```
   bundle_root_dir*, bundle_resources_dir*, bundle_executable_dir*,
-  bundle_plugins_dir*, deps, data_deps, public_deps, visibility,
-  product_type, code_signing_args, code_signing_script,
+  bundle_plugins_dir*, bundle_deps_filter, deps, data_deps, public_deps,
+  visibility, product_type, code_signing_args, code_signing_script,
   code_signing_sources, code_signing_outputs
   * = required
 
@@ -4830,6 +4830,40 @@
 
 
 ```
+## **bundle_deps_filter**: [label list] A list of labels that are filtered out.
+
+```
+  A list of target labels.
+
+  This list contains target label patterns that should be filtered out
+  when creating the bundle. Any target matching one of those label will
+  be removed from the dependencies of the create_bundle target.
+
+  This is mostly useful when creating application extension bundle as
+  the application extension has access to runtime resources from the
+  application bundle and thus do not require a second copy.
+
+  See "gn help create_bundle" for more information.
+
+```
+
+### **Example**
+
+```
+  create_bundle("today_extension") {
+    deps = [
+      "//base"
+    ]
+    bundle_root_dir = "$root_out_dir/today_extension.appex"
+    bundle_deps_filter = [
+      # The extension uses //base but does not use any function calling
+      # into third_party/icu and thus does not need the icudtl.dat file.
+      "//third_party/icu:icudata",
+    ]
+  }
+
+
+```
 ## **include_dirs**: Additional include directories.
 
 ```
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
index 8aa7f93..b7ee6fb 100644
--- a/tools/gn/functions_target.cc
+++ b/tools/gn/functions_target.cc
@@ -279,7 +279,7 @@
     "  }\n"
     "\n"
     "  bundle_data(\"base_unittests_bundle_data]\") {\n"
-    "    sources = [ \"test/data\" ]\n"
+    "    sources = [ \"test/data\" ]\n"
     "    outputs = [\n"
     "      \"{{bundle_resources_dir}}/{{source_root_relative_dir}}/\" +\n"
     "          \"{{source_file_part}}\"\n"
@@ -353,8 +353,8 @@
     "Variables\n"
     "\n"
     "  bundle_root_dir*, bundle_resources_dir*, bundle_executable_dir*,\n"
-    "  bundle_plugins_dir*, deps, data_deps, public_deps, visibility,\n"
-    "  product_type, code_signing_args, code_signing_script,\n"
+    "  bundle_plugins_dir*, bundle_deps_filter, deps, data_deps, public_deps,\n"
+    "  visibility, product_type, code_signing_args, code_signing_script,\n"
     "  code_signing_sources, code_signing_outputs\n"
     "  * = required\n"
     "\n"
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index 948495c..f374934 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -608,6 +608,38 @@
     "\n"
     "  See \"gn help bundle_root_dir\" for examples.\n";
 
+const char kBundleDepsFilter[] = "bundle_deps_filter";
+const char kBundleDepsFilter_HelpShort[] =
+    "bundle_deps_filter: [label list] A list of labels that are filtered out.";
+const char kBundleDepsFilter_Help[] =
+    "bundle_deps_filter: [label list] A list of labels that are filtered out.\n"
+    "\n"
+    "  A list of target labels.\n"
+    "\n"
+    "  This list contains target label patterns that should be filtered out\n"
+    "  when creating the bundle. Any target matching one of those label will\n"
+    "  be removed from the dependencies of the create_bundle target.\n"
+    "\n"
+    "  This is mostly useful when creating application extension bundle as\n"
+    "  the application extension has access to runtime resources from the\n"
+    "  application bundle and thus do not require a second copy.\n"
+    "\n"
+    "  See \"gn help create_bundle\" for more information.\n"
+    "\n"
+    "Example\n"
+    "\n"
+    "  create_bundle(\"today_extension\") {\n"
+    "    deps = [\n"
+    "      \"//base\"\n"
+    "    ]\n"
+    "    bundle_root_dir = \"$root_out_dir/today_extension.appex\"\n"
+    "    bundle_deps_filter = [\n"
+    "      # The extension uses //base but does not use any function calling\n"
+    "      # into third_party/icu and thus does not need the icudtl.dat file.\n"
+    "      \"//third_party/icu:icudata\",\n"
+    "    ]\n"
+    "  }\n";
+
 const char kBundleExecutableDir[] = "bundle_executable_dir";
 const char kBundleExecutableDir_HelpShort[] =
     "bundle_executable_dir: "
@@ -1811,6 +1843,7 @@
     INSERT_VARIABLE(AssertNoDeps)
     INSERT_VARIABLE(BundleRootDir)
     INSERT_VARIABLE(BundleResourcesDir)
+    INSERT_VARIABLE(BundleDepsFilter)
     INSERT_VARIABLE(BundleExecutableDir)
     INSERT_VARIABLE(BundlePlugInsDir)
     INSERT_VARIABLE(Cflags)
diff --git a/tools/gn/variables.h b/tools/gn/variables.h
index 0e092e4..df94b65 100644
--- a/tools/gn/variables.h
+++ b/tools/gn/variables.h
@@ -111,6 +111,10 @@
 extern const char kBundleResourcesDir_HelpShort[];
 extern const char kBundleResourcesDir_Help[];
 
+extern const char kBundleDepsFilter[];
+extern const char kBundleDepsFilter_HelpShort[];
+extern const char kBundleDepsFilter_Help[];
+
 extern const char kBundleExecutableDir[];
 extern const char kBundleExecutableDir_HelpShort[];
 extern const char kBundleExecutableDir_Help[];