Divide sources between targets and add include paths to them

Autocomplete in Xcode not works if we have all sources in one target,
at least in big projects.
So we add a few types of targets: static and dynamic libraries, with
 them, generated project better represent the internal structure of
the project. Also, we do not add all sources to one target, we spread
them between targets, where they should be.
Also, we add include paths to targets.

Change-Id: I3c81aaff5f62eff2d7a6ba1c1b14c62da6e26c27
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8380
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/xcode_object.cc b/src/gn/xcode_object.cc
index 471a201..d498271 100644
--- a/src/gn/xcode_object.cc
+++ b/src/gn/xcode_object.cc
@@ -394,9 +394,12 @@
 PBXTarget::PBXTarget(const std::string& name,
                      const std::string& shell_script,
                      const std::string& config_name,
-                     const PBXAttributes& attributes)
-    : configurations_(
-          std::make_unique<XCConfigurationList>(config_name, attributes, this)),
+                     const PBXAttributes& attributes,
+                     const std::vector<std::string>& include_path)
+    : configurations_(std::make_unique<XCConfigurationList>(config_name,
+                                                            attributes,
+                                                            this,
+                                                            include_path)),
       name_(name) {
   if (!shell_script.empty()) {
     build_phases_.push_back(
@@ -439,7 +442,7 @@
                                        const std::string& shell_script,
                                        const std::string& config_name,
                                        const PBXAttributes& attributes)
-    : PBXTarget(name, shell_script, config_name, attributes) {}
+    : PBXTarget(name, shell_script, config_name, attributes, {}) {}
 
 PBXAggregateTarget::~PBXAggregateTarget() = default;
 
@@ -711,8 +714,9 @@
                                  const PBXAttributes& attributes,
                                  const std::string& product_type,
                                  const std::string& product_name,
-                                 const PBXFileReference* product_reference)
-    : PBXTarget(name, shell_script, config_name, attributes),
+                                 const PBXFileReference* product_reference,
+                                 const std::vector<std::string>& include_paths)
+    : PBXTarget(name, shell_script, config_name, attributes, include_paths),
       product_reference_(product_reference),
       product_type_(product_type),
       product_name_(product_name) {
@@ -777,21 +781,33 @@
 
   products_ = main_group_->CreateChild<PBXGroup>(std::string(), "Products");
 
-  configurations_ =
-      std::make_unique<XCConfigurationList>(config_name, attributes, this);
+  configurations_ = std::make_unique<XCConfigurationList>(
+      config_name, attributes, this, std::vector<std::string>());
 }
 
 PBXProject::~PBXProject() = default;
 
-void PBXProject::AddSourceFileToIndexingTarget(
+void PBXProject::AddSourceFileToTargetForIndexing(
+    const std::string& target_name,
     const std::string& navigator_path,
     const std::string& source_path,
     const CompilerFlags compiler_flag) {
   if (!target_for_indexing_) {
     AddIndexingTarget();
   }
-  AddSourceFile(navigator_path, source_path, compiler_flag,
-                target_for_indexing_);
+
+  // If target with this name don't exists, we add source file to special
+  // target.
+  PBXNativeTarget* target = target_for_indexing_;
+  auto iter = std::find_if(targets_.begin(), targets_.end(),
+                           [&target_name](std::unique_ptr<PBXTarget>& target) {
+                             return target->Name() == target_name;
+                           });
+  if (iter != targets_.end() &&
+      iter->get()->Class() == PBXObjectClass::PBXNativeTargetClass) {
+    target = static_cast<PBXNativeTarget*>(iter->get());
+  }
+  AddSourceFile(navigator_path, source_path, compiler_flag, target);
 }
 
 void PBXProject::AddSourceFile(const std::string& navigator_path,
@@ -838,7 +854,7 @@
   const char product_type[] = "com.apple.product-type.tool";
   targets_.push_back(std::make_unique<PBXNativeTarget>(
       "sources", std::string(), config_name_, attributes, product_type,
-      "sources", product_reference));
+      "sources", product_reference, std::vector<std::string>()));
   target_for_indexing_ = static_cast<PBXNativeTarget*>(targets_.back().get());
 }
 
@@ -849,7 +865,8 @@
     const std::string& output_type,
     const std::string& output_dir,
     const std::string& shell_script,
-    const PBXAttributes& extra_attributes) {
+    const PBXAttributes& extra_attributes,
+    const std::vector<std::string>& include_paths) {
   std::string_view ext = FindExtension(&output_name);
   PBXFileReference* product = products_->CreateChild<PBXFileReference>(
       std::string(), output_name, type.empty() ? GetSourceType(ext) : type);
@@ -876,7 +893,7 @@
 
   targets_.push_back(std::make_unique<PBXNativeTarget>(
       name, shell_script, config_name_, attributes, output_type, product_name,
-      product));
+      product, include_paths));
   return static_cast<PBXNativeTarget*>(targets_.back().get());
 }
 
@@ -1065,9 +1082,11 @@
 
 // XCBuildConfiguration -------------------------------------------------------
 
-XCBuildConfiguration::XCBuildConfiguration(const std::string& name,
-                                           const PBXAttributes& attributes)
-    : attributes_(attributes), name_(name) {}
+XCBuildConfiguration::XCBuildConfiguration(
+    const std::string& name,
+    const PBXAttributes& attributes,
+    const std::vector<std::string>& include_path)
+    : attributes_(attributes), name_(name), include_paths_(include_path) {}
 
 XCBuildConfiguration::~XCBuildConfiguration() = default;
 
@@ -1084,20 +1103,38 @@
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
   PrintProperty(out, rules, "isa", ToString(Class()));
-  PrintProperty(out, rules, "buildSettings", attributes_);
+
+  out << indent_str << "buildSettings"
+      << " = \n";
+  out << indent_str << "{" << (rules.one_line ? " " : "\n");
+
+  for (const auto& pair : attributes_) {
+    PrintProperty(out, rules, pair.first.c_str(), pair.second);
+  }
+
+  out << indent_str << "HEADER_SEARCH_PATHS"
+      << " = ";
+  PrintValue(out, rules, include_paths_);
+  out << ";" << (rules.one_line ? " " : "\n");
+
+  out << std::string(rules.level, '\t');
+  out << "};\n";
+
   PrintProperty(out, rules, "name", name_);
   out << indent_str << "};\n";
 }
 
 // XCConfigurationList --------------------------------------------------------
 
-XCConfigurationList::XCConfigurationList(const std::string& name,
-                                         const PBXAttributes& attributes,
-                                         const PBXObject* owner_reference)
+XCConfigurationList::XCConfigurationList(
+    const std::string& name,
+    const PBXAttributes& attributes,
+    const PBXObject* owner_reference,
+    const std::vector<std::string>& include_paths)
     : owner_reference_(owner_reference) {
   DCHECK(owner_reference_);
   configurations_.push_back(
-      std::make_unique<XCBuildConfiguration>(name, attributes));
+      std::make_unique<XCBuildConfiguration>(name, attributes, include_paths));
 }
 
 XCConfigurationList::~XCConfigurationList() = default;
diff --git a/src/gn/xcode_object.h b/src/gn/xcode_object.h
index 78bdc92..2b7f2d3 100644
--- a/src/gn/xcode_object.h
+++ b/src/gn/xcode_object.h
@@ -148,7 +148,8 @@
   PBXTarget(const std::string& name,
             const std::string& shell_script,
             const std::string& config_name,
-            const PBXAttributes& attributes);
+            const PBXAttributes& attributes,
+            const std::vector<std::string>& include_paths);
   ~PBXTarget() override;
 
   void AddDependency(std::unique_ptr<PBXTargetDependency> dependency);
@@ -324,7 +325,8 @@
                   const PBXAttributes& attributes,
                   const std::string& product_type,
                   const std::string& product_name,
-                  const PBXFileReference* product_reference);
+                  const PBXFileReference* product_reference,
+                  const std::vector<std::string>& include_paths);
   ~PBXNativeTarget() override;
 
   void AddResourceFile(const PBXFileReference* file_reference);
@@ -354,9 +356,10 @@
              const PBXAttributes& attributes);
   ~PBXProject() override;
 
-  void AddSourceFileToIndexingTarget(const std::string& navigator_path,
-                                     const std::string& source_path,
-                                     const CompilerFlags compiler_flag);
+  void AddSourceFileToTargetForIndexing(const std::string& target_name,
+                                        const std::string& navigator_path,
+                                        const std::string& source_path,
+                                        const CompilerFlags compiler_flag);
   void AddSourceFile(const std::string& navigator_path,
                      const std::string& source_path,
                      const CompilerFlags compiler_flag,
@@ -371,7 +374,9 @@
       const std::string& output_name,
       const std::string& output_type,
       const std::string& shell_script,
-      const PBXAttributes& extra_attributes = PBXAttributes());
+      const PBXAttributes& extra_attributes = PBXAttributes(),
+      const std::vector<std::string>& include_paths =
+          std::vector<std::string>());
 
   void SetProjectDirPath(const std::string& project_dir_path);
   void SetProjectRoot(const std::string& project_root);
@@ -481,7 +486,8 @@
 class XCBuildConfiguration : public PBXObject {
  public:
   XCBuildConfiguration(const std::string& name,
-                       const PBXAttributes& attributes);
+                       const PBXAttributes& attributes,
+                       const std::vector<std::string>& include_paths);
   ~XCBuildConfiguration() override;
 
   // PBXObject implementation.
@@ -492,6 +498,7 @@
  private:
   PBXAttributes attributes_;
   std::string name_;
+  std::vector<std::string> include_paths_;
 
   DISALLOW_COPY_AND_ASSIGN(XCBuildConfiguration);
 };
@@ -502,7 +509,8 @@
  public:
   XCConfigurationList(const std::string& name,
                       const PBXAttributes& attributes,
-                      const PBXObject* owner_reference);
+                      const PBXObject* owner_reference,
+                      const std::vector<std::string>& include_paths);
   ~XCConfigurationList() override;
 
   // PBXObject implementation.
diff --git a/src/gn/xcode_object_unittest.cc b/src/gn/xcode_object_unittest.cc
index 70a14df..1f77091 100644
--- a/src/gn/xcode_object_unittest.cc
+++ b/src/gn/xcode_object_unittest.cc
@@ -69,9 +69,10 @@
 // Instantiate a PBXNativeTarget object with arbitrary names.
 std::unique_ptr<PBXNativeTarget> GetPBXNativeTargetObject(
     const PBXFileReference* product_reference) {
-  std::unique_ptr<PBXNativeTarget> pbx_native_target(new PBXNativeTarget(
-      "target_name", "ninja gn_unittests", "config_name", PBXAttributes(),
-      "com.apple.product-type.application", "product_name", product_reference));
+  std::unique_ptr<PBXNativeTarget> pbx_native_target(
+      new PBXNativeTarget("target_name", "ninja gn_unittests", "config_name",
+                          PBXAttributes(), "com.apple.product-type.application",
+                          "product_name", product_reference, {}));
   return pbx_native_target;
 }
 
@@ -96,7 +97,7 @@
 // Instantiate a XCBuildConfiguration object with arbitrary names.
 std::unique_ptr<XCBuildConfiguration> GetXCBuildConfigurationObject() {
   std::unique_ptr<XCBuildConfiguration> xc_build_configuration(
-      new XCBuildConfiguration("config_name", PBXAttributes()));
+      new XCBuildConfiguration("config_name", PBXAttributes(), {}));
   return xc_build_configuration;
 }
 
@@ -105,7 +106,7 @@
     const PBXObject* owner_reference) {
   std::unique_ptr<XCConfigurationList> xc_configuration_list(
       new XCConfigurationList("config_list_name", PBXAttributes(),
-                              owner_reference));
+                              owner_reference, {}));
   return xc_configuration_list;
 }
 
diff --git a/src/gn/xcode_writer.cc b/src/gn/xcode_writer.cc
index 34a3170..3bc0e95 100644
--- a/src/gn/xcode_writer.cc
+++ b/src/gn/xcode_writer.cc
@@ -23,6 +23,7 @@
 #include "gn/build_settings.h"
 #include "gn/builder.h"
 #include "gn/commands.h"
+#include "gn/config_values_extractors.h"
 #include "gn/deps_iterator.h"
 #include "gn/filesystem_utils.h"
 #include "gn/item.h"
@@ -404,6 +405,22 @@
   return attributes;
 }
 
+std::vector<std::string> GetIncludeDirs(const Target* target,
+                                        const BuildSettings* build_settings_) {
+  std::vector<std::string> paths;
+  if (!target || !build_settings_)
+    return paths;
+
+  for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+    for (const SourceDir& include_dir : iter.cur().include_dirs()) {
+      paths.push_back(
+          FilePathToUTF8(build_settings_->GetFullPath(include_dir)));
+    }
+  }
+
+  return paths;
+}
+
 }  // namespace
 
 // Class corresponding to the "Products" project in the generated workspace.
@@ -450,6 +467,16 @@
                                    base::Environment* env,
                                    Err* err);
 
+  // Adds a target of type STATIC_LIBRARY to the project.
+  PBXNativeTarget* AddStaticLibraryTarget(const Target* target,
+                                          base::Environment* env,
+                                          Err* err);
+
+  // Adds a target of type SHARED_LIBRARY to the project.
+  PBXNativeTarget* AddSharedLibraryTarget(const Target* target,
+                                          base::Environment* env,
+                                          Err* err);
+
   // Adds the XCTest source files for all test xctest or xcuitest module target
   // to allow Xcode to index the list of tests (thus allowing to run individual
   // tests from Xcode UI).
@@ -469,6 +496,10 @@
   // Returns whether the file should be added to the project.
   bool ShouldIncludeFileInProject(const SourceFile& source) const;
 
+  // Adds the file to specified target if it has the suitable extension
+  void TryToAddFileForIndexing(std::string& target_name,
+                               const SourceFile& source);
+
   const BuildSettings* build_settings_;
   XcodeWriter::Options options_;
   PBXProject project_;
@@ -496,30 +527,40 @@
   return true;
 }
 
+void XcodeProject::TryToAddFileForIndexing(std::string& target_name,
+                                           const SourceFile& source) {
+  if (!ShouldIncludeFileInProject(source))
+    return;
+  const std::string source_file = RebasePath(source.value(), SourceDir("//"),
+                                             build_settings_->root_path_utf8());
+  project_.AddSourceFileToTargetForIndexing(target_name, source_file,
+                                            source_file, CompilerFlags::NONE);
+}
+
 bool XcodeProject::AddSourcesFromBuilder(const Builder& builder, Err* err) {
   SourceFileSet sources;
 
-  // Add sources from all targets.
-  for (const Target* target : builder.GetAllResolvedTargets()) {
+  const std::optional<std::vector<const Target*>> targets =
+      GetTargetsFromBuilder(builder, err);
+
+  for (const Target* target : *targets) {
+    std::string target_name = target->label().name();
+
     for (const SourceFile& source : target->sources()) {
-      if (ShouldIncludeFileInProject(source))
-        sources.insert(source);
+      TryToAddFileForIndexing(target_name, source);
     }
 
     for (const SourceFile& source : target->config_values().inputs()) {
-      if (ShouldIncludeFileInProject(source))
-        sources.insert(source);
+      TryToAddFileForIndexing(target_name, source);
     }
 
     for (const SourceFile& source : target->public_headers()) {
-      if (ShouldIncludeFileInProject(source))
-        sources.insert(source);
+      TryToAddFileForIndexing(target_name, source);
     }
 
     if (target->output_type() == Target::ACTION ||
         target->output_type() == Target::ACTION_FOREACH) {
-      if (ShouldIncludeFileInProject(target->action_values().script()))
-        sources.insert(target->action_values().script());
+      TryToAddFileForIndexing(target_name, target->action_values().script());
     }
   }
 
@@ -561,8 +602,8 @@
   for (const SourceFile& source : sorted_sources) {
     const std::string source_file = RebasePath(
         source.value(), source_dir, build_settings_->root_path_utf8());
-    project_.AddSourceFileToIndexingTarget(source_file, source_file,
-                                           CompilerFlags::NONE);
+    project_.AddSourceFileToTargetForIndexing(std::string(), source_file,
+                                              source_file, CompilerFlags::NONE);
   }
 
   return true;
@@ -615,7 +656,12 @@
         bundle_targets.insert(std::make_pair(target, native_target));
         break;
       }
-
+      case Target::STATIC_LIBRARY:
+        native_target = AddStaticLibraryTarget(target, env.get(), err);
+        break;
+      case Target::SHARED_LIBRARY:
+        native_target = AddSharedLibraryTarget(target, env.get(), err);
+        break;
       default:
         break;
     }
@@ -787,10 +833,46 @@
       target->label().name(), "compiled.mach-o.executable",
       target->output_name().empty() ? target->label().name()
                                     : target->output_name(),
-      "com.apple.product-type.tool",
-      output_dir,
+      "com.apple.product-type.tool", output_dir,
       GetBuildScript(target->label().name(), options_.ninja_executable,
-                     options_.ninja_extra_args, env));
+                     options_.ninja_extra_args, env),
+      PBXAttributes(), GetIncludeDirs(target, build_settings_));
+}
+
+PBXNativeTarget* XcodeProject::AddStaticLibraryTarget(const Target* target,
+                                                      base::Environment* env,
+                                                      Err* err) {
+  DCHECK_EQ(target->output_type(), Target::STATIC_LIBRARY);
+
+  const std::string output_dir =
+      RebasePath(target->output_dir().value(), build_settings_->build_dir());
+
+  return project_.AddNativeTarget(
+      target->label().name(), std::string(),
+      target->output_name().empty() ? target->label().name()
+                                    : target->output_name(),
+      "com.apple.product-type.library.static", output_dir,
+      GetBuildScript(target->label().name(), options_.ninja_executable,
+                     options_.ninja_extra_args, env),
+      PBXAttributes(), GetIncludeDirs(target, build_settings_));
+}
+
+PBXNativeTarget* XcodeProject::AddSharedLibraryTarget(const Target* target,
+                                                      base::Environment* env,
+                                                      Err* err) {
+  DCHECK_EQ(target->output_type(), Target::SHARED_LIBRARY);
+
+  const std::string output_dir =
+      RebasePath(target->output_dir().value(), build_settings_->build_dir());
+
+  return project_.AddNativeTarget(
+      target->label().name(), std::string(),
+      target->output_name().empty() ? target->label().name()
+                                    : target->output_name(),
+      "com.apple.product-type.library.dynamic", output_dir,
+      GetBuildScript(target->label().name(), options_.ninja_executable,
+                     options_.ninja_extra_args, env),
+      PBXAttributes(), GetIncludeDirs(target, build_settings_));
 }
 
 PBXNativeTarget* XcodeProject::AddBundleTarget(const Target* target,
@@ -817,11 +899,10 @@
       build_settings_->build_dir());
   return project_.AddNativeTarget(
       pbxtarget_name, std::string(), target_output_name,
-      target->bundle_data().product_type(),
-      output_dir,
+      target->bundle_data().product_type(), output_dir,
       GetBuildScript(pbxtarget_name, options_.ninja_executable,
                      options_.ninja_extra_args, env),
-      xcode_extra_attributes);
+      xcode_extra_attributes, GetIncludeDirs(target, build_settings_));
 }
 
 void XcodeProject::WriteFileContent(std::ostream& out) const {
@@ -931,10 +1012,10 @@
   XcodeWorkspace workspace(build_settings, options);
 
   XcodeProject* products = workspace.CreateProject("products");
-  if (!products->AddSourcesFromBuilder(builder, err))
+  if (!products->AddTargetsFromBuilder(builder, err))
     return false;
 
-  if (!products->AddTargetsFromBuilder(builder, err))
+  if (!products->AddSourcesFromBuilder(builder, err))
     return false;
 
   if (!products->AssignIds(err))