Add support for Resources build phase to generated Xcode project

Xcode complains that the project is missing some default resources
if there is no "Default-568h@2x.png" file listed in a Resources
build phase.

As a first step, add the support for a Resources build phase to the
generated project (in a followup CL, this phase will be filled with
resources as collected from the project definition).

Bug: chromium/1071055
Change-Id: Idc818b273aaaa49b1c0898848865331ae43788a3
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8101
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/xcode_object.cc b/src/gn/xcode_object.cc
index 1b139a8..471a201 100644
--- a/src/gn/xcode_object.cc
+++ b/src/gn/xcode_object.cc
@@ -303,6 +303,8 @@
       return "PBXNativeTarget";
     case PBXProjectClass:
       return "PBXProject";
+    case PBXResourcesBuildPhaseClass:
+      return "PBXResourcesBuildPhase";
     case PBXShellScriptBuildPhaseClass:
       return "PBXShellScriptBuildPhase";
     case PBXSourcesBuildPhaseClass:
@@ -368,6 +370,25 @@
 
 PBXBuildPhase::~PBXBuildPhase() = default;
 
+void PBXBuildPhase::AddBuildFile(std::unique_ptr<PBXBuildFile> build_file) {
+  DCHECK(build_file);
+  files_.push_back(std::move(build_file));
+}
+
+void PBXBuildPhase::Visit(PBXObjectVisitor& visitor) {
+  PBXObject::Visit(visitor);
+  for (const auto& file : files_) {
+    file->Visit(visitor);
+  }
+}
+
+void PBXBuildPhase::Visit(PBXObjectVisitorConst& visitor) const {
+  PBXObject::Visit(visitor);
+  for (const auto& file : files_) {
+    file->Visit(visitor);
+  }
+}
+
 // PBXTarget ------------------------------------------------------------------
 
 PBXTarget::PBXTarget(const std::string& name,
@@ -442,7 +463,7 @@
 // PBXBuildFile ---------------------------------------------------------------
 
 PBXBuildFile::PBXBuildFile(const PBXFileReference* file_reference,
-                           const PBXSourcesBuildPhase* build_phase,
+                           const PBXBuildPhase* build_phase,
                            const CompilerFlags compiler_flag)
     : file_reference_(file_reference),
       build_phase_(build_phase),
@@ -568,7 +589,7 @@
   out << indent_str << Reference() << " = {\n";
   PrintProperty(out, rules, "isa", ToString(Class()));
   PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
-  PrintProperty(out, rules, "files", EmptyPBXObjectVector());
+  PrintProperty(out, rules, "files", files_);
   PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
   out << indent_str << "};\n";
 }
@@ -701,10 +722,19 @@
       static_cast<PBXSourcesBuildPhase*>(build_phases_.back().get());
 
   build_phases_.push_back(std::make_unique<PBXFrameworksBuildPhase>());
+  build_phases_.push_back(std::make_unique<PBXResourcesBuildPhase>());
+  resource_build_phase_ =
+      static_cast<PBXResourcesBuildPhase*>(build_phases_.back().get());
 }
 
 PBXNativeTarget::~PBXNativeTarget() = default;
 
+void PBXNativeTarget::AddResourceFile(const PBXFileReference* file_reference) {
+  DCHECK(file_reference);
+  resource_build_phase_->AddBuildFile(std::make_unique<PBXBuildFile>(
+      file_reference, resource_build_phase_, CompilerFlags::NONE));
+}
+
 void PBXNativeTarget::AddFileForIndexing(const PBXFileReference* file_reference,
                                          const CompilerFlags compiler_flag) {
   DCHECK(file_reference);
@@ -913,6 +943,31 @@
   out << indent_str << "};\n";
 }
 
+// PBXResourcesBuildPhase -----------------------------------------------------
+
+PBXResourcesBuildPhase::PBXResourcesBuildPhase() = default;
+
+PBXResourcesBuildPhase::~PBXResourcesBuildPhase() = default;
+
+PBXObjectClass PBXResourcesBuildPhase::Class() const {
+  return PBXResourcesBuildPhaseClass;
+}
+
+std::string PBXResourcesBuildPhase::Name() const {
+  return "Resources";
+}
+
+void PBXResourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
+  const std::string indent_str(indent, '\t');
+  const IndentRules rules = {false, indent + 1};
+  out << indent_str << Reference() << " = {\n";
+  PrintProperty(out, rules, "isa", ToString(Class()));
+  PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
+  PrintProperty(out, rules, "files", files_);
+  PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
+  out << indent_str << "};\n";
+}
+
 // PBXShellScriptBuildPhase ---------------------------------------------------
 
 PBXShellScriptBuildPhase::PBXShellScriptBuildPhase(
@@ -937,7 +992,7 @@
   out << indent_str << Reference() << " = {\n";
   PrintProperty(out, rules, "isa", ToString(Class()));
   PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
-  PrintProperty(out, rules, "files", EmptyPBXObjectVector());
+  PrintProperty(out, rules, "files", files_);
   PrintProperty(out, rules, "inputPaths", EmptyPBXObjectVector());
   PrintProperty(out, rules, "name", name_);
   PrintProperty(out, rules, "outputPaths", EmptyPBXObjectVector());
@@ -954,11 +1009,6 @@
 
 PBXSourcesBuildPhase::~PBXSourcesBuildPhase() = default;
 
-void PBXSourcesBuildPhase::AddBuildFile(
-    std::unique_ptr<PBXBuildFile> build_file) {
-  files_.push_back(std::move(build_file));
-}
-
 PBXObjectClass PBXSourcesBuildPhase::Class() const {
   return PBXSourcesBuildPhaseClass;
 }
@@ -967,20 +1017,6 @@
   return "Sources";
 }
 
-void PBXSourcesBuildPhase::Visit(PBXObjectVisitor& visitor) {
-  PBXBuildPhase::Visit(visitor);
-  for (const auto& file : files_) {
-    file->Visit(visitor);
-  }
-}
-
-void PBXSourcesBuildPhase::Visit(PBXObjectVisitorConst& visitor) const {
-  PBXBuildPhase::Visit(visitor);
-  for (const auto& file : files_) {
-    file->Visit(visitor);
-  }
-}
-
 void PBXSourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
diff --git a/src/gn/xcode_object.h b/src/gn/xcode_object.h
index aaa494c..78bdc92 100644
--- a/src/gn/xcode_object.h
+++ b/src/gn/xcode_object.h
@@ -40,6 +40,7 @@
   PBXGroupClass,
   PBXNativeTargetClass,
   PBXProjectClass,
+  PBXResourcesBuildPhaseClass,
   PBXShellScriptBuildPhaseClass,
   PBXSourcesBuildPhaseClass,
   PBXTargetDependencyClass,
@@ -61,6 +62,7 @@
 class PBXNativeTarget;
 class PBXObject;
 class PBXProject;
+class PBXResourcesBuildPhase;
 class PBXShellScriptBuildPhase;
 class PBXSourcesBuildPhase;
 class PBXTarget;
@@ -126,6 +128,15 @@
   PBXBuildPhase();
   ~PBXBuildPhase() override;
 
+  void AddBuildFile(std::unique_ptr<PBXBuildFile> build_file);
+
+  // PBXObject implementation.
+  void Visit(PBXObjectVisitor& visitor) override;
+  void Visit(PBXObjectVisitorConst& visitor) const override;
+
+ protected:
+  std::vector<std::unique_ptr<PBXBuildFile>> files_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(PBXBuildPhase);
 };
@@ -152,6 +163,7 @@
   std::vector<std::unique_ptr<PBXBuildPhase>> build_phases_;
   std::vector<std::unique_ptr<PBXTargetDependency>> dependencies_;
   PBXSourcesBuildPhase* source_build_phase_ = nullptr;
+  PBXResourcesBuildPhase* resource_build_phase_ = nullptr;
   std::string name_;
 
  private:
@@ -181,7 +193,7 @@
 class PBXBuildFile : public PBXObject {
  public:
   PBXBuildFile(const PBXFileReference* file_reference,
-               const PBXSourcesBuildPhase* build_phase,
+               const PBXBuildPhase* build_phase,
                const CompilerFlags compiler_flag);
   ~PBXBuildFile() override;
 
@@ -192,7 +204,7 @@
 
  private:
   const PBXFileReference* file_reference_ = nullptr;
-  const PBXSourcesBuildPhase* build_phase_ = nullptr;
+  const PBXBuildPhase* build_phase_ = nullptr;
   const CompilerFlags compiler_flag_;
 
   DISALLOW_COPY_AND_ASSIGN(PBXBuildFile);
@@ -315,6 +327,8 @@
                   const PBXFileReference* product_reference);
   ~PBXNativeTarget() override;
 
+  void AddResourceFile(const PBXFileReference* file_reference);
+
   void AddFileForIndexing(const PBXFileReference* file_reference,
                           const CompilerFlags compiler_flag);
 
@@ -388,6 +402,22 @@
   DISALLOW_COPY_AND_ASSIGN(PBXProject);
 };
 
+// PBXResourcesBuildPhase -----------------------------------------------------
+
+class PBXResourcesBuildPhase : public PBXBuildPhase {
+ public:
+  PBXResourcesBuildPhase();
+  ~PBXResourcesBuildPhase() override;
+
+  // PBXObject implementation.
+  PBXObjectClass Class() const override;
+  std::string Name() const override;
+  void Print(std::ostream& out, unsigned indent) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PBXResourcesBuildPhase);
+};
+
 // PBXShellScriptBuildPhase ---------------------------------------------------
 
 class PBXShellScriptBuildPhase : public PBXBuildPhase {
@@ -415,18 +445,12 @@
   PBXSourcesBuildPhase();
   ~PBXSourcesBuildPhase() override;
 
-  void AddBuildFile(std::unique_ptr<PBXBuildFile> build_file);
-
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Visit(PBXObjectVisitor& visitor) override;
-  void Visit(PBXObjectVisitorConst& visitor) const override;
   void Print(std::ostream& out, unsigned indent) const override;
 
  private:
-  std::vector<std::unique_ptr<PBXBuildFile>> files_;
-
   DISALLOW_COPY_AND_ASSIGN(PBXSourcesBuildPhase);
 };