Add xcasset_compiler_flags variable to create_bundle target

And corresponding {{xcasset_compiler_flags}} substitution to
compile_xcassets tool. Those flags can be used to achive more
control over asset compilation. Examples of usage could be:
on demand resources, errors suppression (see more in `man actool`).

Change-Id: Ia2fbbd6ab214e349309a018963d5a19a73e6ab8c
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8401
Commit-Queue: Brett Wilson <brettw@chromium.org>
Reviewed-by: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/bundle_data.h b/src/gn/bundle_data.h
index 47e3d25..3c4feb1 100644
--- a/src/gn/bundle_data.h
+++ b/src/gn/bundle_data.h
@@ -154,6 +154,13 @@
     return bundle_deps_filter_;
   }
 
+  SubstitutionList& xcasset_compiler_flags() {
+    return xcasset_compiler_flags_;
+  }
+  const SubstitutionList& xcasset_compiler_flags() const {
+    return xcasset_compiler_flags_;
+  }
+
   // Recursive collection of all bundle_data that the target depends on.
   const UniqueTargets& bundle_deps() const { return bundle_deps_; }
 
@@ -199,6 +206,7 @@
   std::vector<SourceFile> code_signing_sources_;
   SubstitutionList code_signing_outputs_;
   SubstitutionList code_signing_args_;
+  SubstitutionList xcasset_compiler_flags_;
 
   DISALLOW_COPY_AND_ASSIGN(BundleData);
 };
diff --git a/src/gn/create_bundle_target_generator.cc b/src/gn/create_bundle_target_generator.cc
index dae8683..b614a9c 100644
--- a/src/gn/create_bundle_target_generator.cc
+++ b/src/gn/create_bundle_target_generator.cc
@@ -69,6 +69,9 @@
 
   if (!FillBundleDepsFilter())
     return;
+
+  if (!FillXcassetCompilerFlags())
+    return;
 }
 
 bool CreateBundleTargetGenerator::FillBundleDir(
@@ -302,3 +305,14 @@
 
   return true;
 }
+
+bool CreateBundleTargetGenerator::FillXcassetCompilerFlags() {
+  const Value* value = scope_->GetValue(variables::kXcassetCompilerFlags, true);
+  if (!value)
+    return true;
+
+  if (!value->VerifyTypeIs(Value::LIST, err_))
+    return false;
+
+  return target_->bundle_data().xcasset_compiler_flags().Parse(*value, err_);
+}
diff --git a/src/gn/create_bundle_target_generator.h b/src/gn/create_bundle_target_generator.h
index 310bf6a..6f13fb4 100644
--- a/src/gn/create_bundle_target_generator.h
+++ b/src/gn/create_bundle_target_generator.h
@@ -38,6 +38,7 @@
   bool FillCodeSigningOutputs();
   bool FillCodeSigningArgs();
   bool FillBundleDepsFilter();
+  bool FillXcassetCompilerFlags();
 
   DISALLOW_COPY_AND_ASSIGN(CreateBundleTargetGenerator);
 };
diff --git a/src/gn/function_toolchain.cc b/src/gn/function_toolchain.cc
index 8175c8d..9efff8e 100644
--- a/src/gn/function_toolchain.cc
+++ b/src/gn/function_toolchain.cc
@@ -738,6 +738,10 @@
         assets catalog compiler. Usually based on the target_name of
         the create_bundle target.
 
+    {{xcasset_compiler_flags}}
+        Expands to the list of flags specified in corresponding
+        create_bundle target.
+
   Rust tools have the notion of a single input and a single output, along
   with a set of compiler-specific flags. The following expansions are
   available:
diff --git a/src/gn/ninja_create_bundle_target_writer.cc b/src/gn/ninja_create_bundle_target_writer.cc
index efb896a..9f000f3 100644
--- a/src/gn/ninja_create_bundle_target_writer.cc
+++ b/src/gn/ninja_create_bundle_target_writer.cc
@@ -261,6 +261,20 @@
     path_output_.WriteFile(out_, partial_info_plist);
     out_ << std::endl;
   }
+
+  const std::vector<SubstitutionPattern>& flags =
+      target_->bundle_data().xcasset_compiler_flags().list();
+  if (!flags.empty()) {
+    out_ << "  " << SubstitutionXcassetsCompilerFlags.ninja_name << " =";
+    EscapeOptions args_escape_options;
+    args_escape_options.mode = ESCAPE_NINJA_COMMAND;
+    for (const auto& flag : flags) {
+      out_ << " ";
+      SubstitutionWriter::WriteWithNinjaVariables(
+          flag, args_escape_options, out_);
+    }
+    out_ << std::endl;
+  }
 }
 
 OutputFile
diff --git a/src/gn/ninja_create_bundle_target_writer_unittest.cc b/src/gn/ninja_create_bundle_target_writer_unittest.cc
index 9a966d9..d2c5c24 100644
--- a/src/gn/ninja_create_bundle_target_writer_unittest.cc
+++ b/src/gn/ninja_create_bundle_target_writer_unittest.cc
@@ -214,6 +214,9 @@
   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
   create_bundle.bundle_data().product_type().assign("com.apple.product-type");
+  create_bundle.bundle_data().xcasset_compiler_flags() =
+      SubstitutionList::MakeForTest("--app-icon", "foo");
+
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
@@ -228,6 +231,7 @@
       "../../foo/Foo.xcassets | obj/foo/data.stamp || "
       "obj/baz/bar.inputdeps.stamp\n"
       "  product_type = com.apple.product-type\n"
+      "  xcasset_compiler_flags = --app-icon foo\n"
       "build obj/baz/bar.stamp: stamp "
       "bar.bundle/Contents/Resources/Assets.car || "
       "obj/baz/bar.inputdeps.stamp\n"
diff --git a/src/gn/substitution_type.cc b/src/gn/substitution_type.cc
index d052c36..a4d1384 100644
--- a/src/gn/substitution_type.cc
+++ b/src/gn/substitution_type.cc
@@ -44,6 +44,7 @@
 
     &SubstitutionBundleProductType,
     &SubstitutionBundlePartialInfoPlist,
+    &SubstitutionXcassetsCompilerFlags,
 
     &SubstitutionRspFileName,
 };
@@ -100,6 +101,8 @@
                                                     "product_type"};
 const Substitution SubstitutionBundlePartialInfoPlist = {
     "{{bundle_partial_info_plist}}", "partial_info_plist"};
+const Substitution SubstitutionXcassetsCompilerFlags = {
+    "{{xcasset_compiler_flags}}", "xcasset_compiler_flags"};
 
 // Used only for the args of actions.
 const Substitution SubstitutionRspFileName = {"{{response_file_name}}",
@@ -176,7 +179,8 @@
 bool IsValidCompileXCassetsSubstitution(const Substitution* type) {
   return IsValidToolSubstitution(type) || type == &CSubstitutionLinkerInputs ||
          type == &SubstitutionBundleProductType ||
-         type == &SubstitutionBundlePartialInfoPlist;
+         type == &SubstitutionBundlePartialInfoPlist ||
+         type == &SubstitutionXcassetsCompilerFlags;
 }
 
 bool EnsureValidSubstitutions(const std::vector<const Substitution*>& types,
diff --git a/src/gn/substitution_type.h b/src/gn/substitution_type.h
index 5be6e0d..7ec929f 100644
--- a/src/gn/substitution_type.h
+++ b/src/gn/substitution_type.h
@@ -64,6 +64,7 @@
 // Valid for compile_xcassets tool.
 extern const Substitution SubstitutionBundleProductType;
 extern const Substitution SubstitutionBundlePartialInfoPlist;
+extern const Substitution SubstitutionXcassetsCompilerFlags;
 
 // Used only for the args of actions.
 extern const Substitution SubstitutionRspFileName;
diff --git a/src/gn/variables.cc b/src/gn/variables.cc
index 0d56301..8a1ec2a 100644
--- a/src/gn/variables.cc
+++ b/src/gn/variables.cc
@@ -698,6 +698,19 @@
   See "gn help bundle_root_dir" for examples.
 )";
 
+const char kXcassetCompilerFlags[] = "xcasset_compiler_flags";
+const char kXcassetCompilerFlags_HelpShort[] =
+    "xcasset_compiler_flags: [string list] Flags passed to xcassets compiler";
+const char kXcassetCompilerFlags_Help[] =
+    R"(xcasset_compiler_flags: Flags passed to xcassets compiler.
+
+  A list of strings.
+
+  Valid for create_bundle target. Those flags are directly passed to
+  xcassets compiler, corresponding to {{xcasset_compiler_flags}} substitution
+  in compile_xcassets tool.
+)";
+
 const char kCflags[] = "cflags";
 const char kCflags_HelpShort[] =
     "cflags: [string list] Flags passed to all C compiler variants.";
@@ -2232,6 +2245,7 @@
     INSERT_VARIABLE(BundleResourcesDir)
     INSERT_VARIABLE(BundleDepsFilter)
     INSERT_VARIABLE(BundleExecutableDir)
+    INSERT_VARIABLE(XcassetCompilerFlags)
     INSERT_VARIABLE(Cflags)
     INSERT_VARIABLE(CflagsC)
     INSERT_VARIABLE(CflagsCC)
diff --git a/src/gn/variables.h b/src/gn/variables.h
index df30632..dca4e34 100644
--- a/src/gn/variables.h
+++ b/src/gn/variables.h
@@ -126,6 +126,10 @@
 extern const char kBundleExecutableDir_HelpShort[];
 extern const char kBundleExecutableDir_Help[];
 
+extern const char kXcassetCompilerFlags[];
+extern const char kXcassetCompilerFlags_HelpShort[];
+extern const char kXcassetCompilerFlags_Help[];
+
 extern const char kCflags[];
 extern const char kCflags_HelpShort[];
 extern const char* kCflags_Help;