[Xcode] Implement custom sort of the elements in Xcode project

Add way to sort the elements when adding them to PBXGroup and
implement a custom sorting rule that keep folder before files.

Also add helper method to create a child, add it to PBXGroup
and return the value as a pointer to the correct type.

Remove always empty group "Build" from the project.

Bug: none
Change-Id: I7bc1ead9e724879603beb47da9970662ffbc8171
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/7641
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 30ddffe..fe8b6e1 100644
--- a/src/gn/xcode_object.cc
+++ b/src/gn/xcode_object.cc
@@ -235,6 +235,25 @@
   PrintValue(out, rules, std::forward<ValueType>(value));
   out << ";" << (rules.one_line ? " " : "\n");
 }
+
+struct PBXGroupComparator {
+  using PBXObjectPtr = std::unique_ptr<PBXObject>;
+  bool operator()(const PBXObjectPtr& lhs, const PBXObjectPtr& rhs) {
+    if (lhs->Class() != rhs->Class())
+      return rhs->Class() < lhs->Class();
+
+    if (lhs->Class() == PBXGroupClass) {
+      PBXGroup* lhs_group = static_cast<PBXGroup*>(lhs.get());
+      PBXGroup* rhs_group = static_cast<PBXGroup*>(rhs.get());
+      return lhs_group->name() < rhs_group->name();
+    }
+
+    DCHECK_EQ(lhs->Class(), PBXFileReferenceClass);
+    PBXFileReference* lhs_file = static_cast<PBXFileReference*>(lhs.get());
+    PBXFileReference* rhs_file = static_cast<PBXFileReference*>(rhs.get());
+    return lhs_file->Name() < rhs_file->Name();
+  }
+};
 }  // namespace
 
 // PBXObjectClass -------------------------------------------------------------
@@ -532,12 +551,6 @@
 
 PBXGroup::~PBXGroup() = default;
 
-PBXObject* PBXGroup::AddChild(std::unique_ptr<PBXObject> child) {
-  DCHECK(child);
-  children_.push_back(std::move(child));
-  return children_.back().get();
-}
-
 PBXFileReference* PBXGroup::AddSourceFile(const std::string& navigator_path,
                                           const std::string& source_path) {
   DCHECK(!navigator_path.empty());
@@ -557,9 +570,8 @@
       }
     }
 
-    children_.push_back(std::make_unique<PBXFileReference>(
-        navigator_path, source_path, std::string()));
-    return static_cast<PBXFileReference*>(children_.back().get());
+    return CreateChild<PBXFileReference>(navigator_path, source_path,
+                                         std::string());
   }
 
   PBXGroup* group = nullptr;
@@ -576,9 +588,8 @@
   }
 
   if (!group) {
-    children_.push_back(std::make_unique<PBXGroup>(std::string(component),
-                                                   std::string(component)));
-    group = static_cast<PBXGroup*>(children_.back().get());
+    group =
+        CreateChild<PBXGroup>(std::string(component), std::string(component));
   }
 
   DCHECK(group);
@@ -626,6 +637,22 @@
   out << indent_str << "};\n";
 }
 
+PBXObject* PBXGroup::AddChildImpl(std::unique_ptr<PBXObject> child) {
+  DCHECK(child);
+  DCHECK(child->Class() == PBXGroupClass ||
+         child->Class() == PBXFileReferenceClass);
+
+  PBXObject* child_ptr = child.get();
+  if (autosorted()) {
+    auto iter = std::lower_bound(children_.begin(), children_.end(), child,
+                                 PBXGroupComparator());
+    children_.insert(iter, std::move(child));
+  } else {
+    children_.push_back(std::move(child));
+  }
+  return child_ptr;
+}
+
 // PBXNativeTarget ------------------------------------------------------------
 
 PBXNativeTarget::PBXNativeTarget(const std::string& name,
@@ -686,12 +713,12 @@
   attributes_["BuildIndependentTargetsInParallel"] = "YES";
 
   main_group_.reset(new PBXGroup);
-  sources_ = static_cast<PBXGroup*>(
-      main_group_->AddChild(std::make_unique<PBXGroup>(source_path, "Source")));
+  main_group_->set_autosorted(false);
+
+  sources_ = main_group_->CreateChild<PBXGroup>(source_path, "Source");
   sources_->set_is_source(true);
-  products_ = static_cast<PBXGroup*>(main_group_->AddChild(
-      std::make_unique<PBXGroup>(std::string(), "Product")));
-  main_group_->AddChild(std::make_unique<PBXGroup>(std::string(), "Build"));
+
+  products_ = main_group_->CreateChild<PBXGroup>(std::string(), "Products");
 
   configurations_.reset(new XCConfigurationList(config_name, attributes, this));
 }
@@ -741,9 +768,9 @@
   attributes["HEADER_SEARCH_PATHS"] = sources_->path();
   attributes["PRODUCT_NAME"] = "sources";
 
-  PBXFileReference* product_reference = static_cast<PBXFileReference*>(
-      products_->AddChild(std::make_unique<PBXFileReference>(
-          std::string(), "sources", "compiled.mach-o.executable")));
+  PBXFileReference* product_reference =
+      products_->CreateChild<PBXFileReference>(std::string(), "sources",
+                                               "compiled.mach-o.executable");
 
   const char product_type[] = "com.apple.product-type.tool";
   targets_.push_back(std::make_unique<PBXNativeTarget>(
@@ -760,10 +787,8 @@
     const std::string& shell_script,
     const PBXAttributes& extra_attributes) {
   std::string_view ext = FindExtension(&output_name);
-  PBXFileReference* product = static_cast<PBXFileReference*>(
-      products_->AddChild(std::make_unique<PBXFileReference>(
-          std::string(), output_name,
-          type.empty() ? GetSourceType(ext) : type)));
+  PBXFileReference* product = products_->CreateChild<PBXFileReference>(
+      std::string(), output_name, type.empty() ? GetSourceType(ext) : type);
 
   // Per Xcode build settings documentation: Product Name (PRODUCT_NAME) should
   // the basename of the product generated by the target.
diff --git a/src/gn/xcode_object.h b/src/gn/xcode_object.h
index e2b00e0..1901366 100644
--- a/src/gn/xcode_object.h
+++ b/src/gn/xcode_object.h
@@ -265,12 +265,22 @@
   ~PBXGroup() override;
 
   const std::string& path() const { return path_; }
+  const std::string& name() const { return name_; }
 
-  PBXObject* AddChild(std::unique_ptr<PBXObject> child);
   PBXFileReference* AddSourceFile(const std::string& navigator_path,
                                   const std::string& source_path);
-  bool is_source() { return is_source_; }
-  void set_is_source(const bool is_source) { is_source_ = is_source; }
+
+  bool is_source() const { return is_source_; }
+  void set_is_source(bool is_source) { is_source_ = is_source; }
+
+  bool autosorted() const { return autosorted_; }
+  void set_autosorted(bool autosorted) { autosorted_ = autosorted; }
+
+  template <typename T, typename... Args>
+  T* CreateChild(Args&&... args) {
+    return static_cast<T*>(
+        AddChildImpl(std::make_unique<T>(std::forward<Args>(args)...)));
+  }
 
   // PBXObject implementation.
   PBXObjectClass Class() const override;
@@ -280,10 +290,13 @@
   void Print(std::ostream& out, unsigned indent) const override;
 
  private:
+  PBXObject* AddChildImpl(std::unique_ptr<PBXObject> child);
+
   std::vector<std::unique_ptr<PBXObject>> children_;
   std::string name_;
   std::string path_;
   bool is_source_ = false;
+  bool autosorted_ = true;
 
   DISALLOW_COPY_AND_ASSIGN(PBXGroup);
 };