[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);
};