Consolidate Xcode Project Setup

1. Remove 'sources.xcodeproj'
2. Add a 'sources' target under products project for files indexing.
3. Delete xctests/ folder to fix duplicate xctest file copies.

BUG=679110

Review-Url: https://codereview.chromium.org/2623203004
Cr-Original-Commit-Position: refs/heads/master@{#444469}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: de19976ca12bb4c42acd5bfb5a83a69047d6e7b4
diff --git a/tools/gn/xcode_object.cc b/tools/gn/xcode_object.cc
index 9854095..9d574a5 100644
--- a/tools/gn/xcode_object.cc
+++ b/tools/gn/xcode_object.cc
@@ -675,16 +675,16 @@
   PBXAttributes attributes;
   attributes["EXECUTABLE_PREFIX"] = "";
   attributes["HEADER_SEARCH_PATHS"] = sources_->path();
-  attributes["PRODUCT_NAME"] = name_;
+  attributes["PRODUCT_NAME"] = "sources";
 
   PBXFileReference* product_reference = static_cast<PBXFileReference*>(
       products_->AddChild(base::MakeUnique<PBXFileReference>(
-          std::string(), name_, "compiled.mach-o.executable")));
+          std::string(), "sources", "compiled.mach-o.executable")));
 
   const char product_type[] = "com.apple.product-type.tool";
   targets_.push_back(base::MakeUnique<PBXNativeTarget>(
-      name_, std::string(), config_name_, attributes, product_type, name_,
-      product_reference));
+      "sources", std::string(), config_name_, attributes, product_type,
+      "sources", product_reference));
   target_for_indexing_ = static_cast<PBXNativeTarget*>(targets_.back().get());
 }
 
diff --git a/tools/gn/xcode_writer.cc b/tools/gn/xcode_writer.cc
index a780d80..41af628 100644
--- a/tools/gn/xcode_writer.cc
+++ b/tools/gn/xcode_writer.cc
@@ -32,16 +32,11 @@
 namespace {
 
 using TargetToFileList = std::unordered_map<const Target*, Target::FileList>;
-using TargetToNativeTarget =
-    std::unordered_map<const Target*, PBXNativeTarget*>;
-using FileToTargets = std::map<SourceFile,
-                               std::vector<const Target*>,
-                               bool (*)(const SourceFile&, const SourceFile&)>;
+using TargetToTarget = std::unordered_map<const Target*, const Target*>;
 
 const char kEarlGreyFileNameIdentifier[] = "egtest.mm";
 const char kXCTestFileNameIdentifier[] = "xctest.mm";
 const char kXCTestModuleTargetNamePostfix[] = "_module";
-const char kXCTestFileReferenceFolder[] = "xctests/";
 
 struct SafeEnvironmentVariableInfo {
   const char* name;
@@ -53,15 +48,6 @@
     {"USER", true}, {"TMPDIR", false},
 };
 
-bool CompareSourceFiles(const SourceFile& lhs, const SourceFile& rhs) {
-  if (lhs.GetName() < rhs.GetName())
-    return true;
-  else if (lhs.GetName() > rhs.GetName())
-    return false;
-  else
-    return lhs.value() < rhs.value();
-}
-
 XcodeWriter::TargetOsType GetTargetOs(const Args& args) {
   const Value* target_os_value = args.GetArgOverride(variables::kTargetOs);
   if (target_os_value) {
@@ -121,6 +107,13 @@
                         base::CompareCase::SENSITIVE);
 }
 
+bool IsXCTestFile(const SourceFile& file) {
+  return base::EndsWith(file.GetName(), kEarlGreyFileNameIdentifier,
+                        base::CompareCase::SENSITIVE) ||
+         base::EndsWith(file.GetName(), kXCTestFileNameIdentifier,
+                        base::CompareCase::SENSITIVE);
+}
+
 const Target* FindXCTestApplicationTarget(
     const Target* xctest_module_target,
     const std::vector<const Target*>& targets) {
@@ -141,14 +134,18 @@
   return nullptr;
 }
 
-// Returns the corresponding application targets given XCTest module targets.
+// Given XCTest module targets, find the corresponding application targets and
+// the mappings between them.
 void FindXCTestApplicationTargets(
     const std::vector<const Target*>& xctest_module_targets,
     const std::vector<const Target*>& targets,
-    std::vector<const Target*>* xctest_application_targets) {
+    std::vector<const Target*>* xctest_application_targets,
+    TargetToTarget* xctest_module_to_application_target) {
   for (const Target* xctest_module_target : xctest_module_targets) {
     xctest_application_targets->push_back(
         FindXCTestApplicationTarget(xctest_module_target, targets));
+    xctest_module_to_application_target->insert(std::make_pair(
+        xctest_module_target, xctest_application_targets->back()));
   }
 }
 
@@ -161,10 +158,7 @@
 
   Target::FileList xctest_files;
   for (const SourceFile& file : target->sources()) {
-    if (base::EndsWith(file.GetName(), kEarlGreyFileNameIdentifier,
-                       base::CompareCase::SENSITIVE) ||
-        base::EndsWith(file.GetName(), kXCTestFileNameIdentifier,
-                       base::CompareCase::SENSITIVE)) {
+    if (IsXCTestFile(file)) {
       xctest_files.push_back(file);
     }
   }
@@ -196,40 +190,15 @@
 
 // Finds the list of xctest files recursively under each of the application
 // targets.
-void FindXCTestFilesForTargets(
+void FindXCTestFilesForApplicationTargets(
     const std::vector<const Target*>& application_targets,
-    std::vector<Target::FileList>* file_lists) {
+    TargetToFileList* xctest_files_per_application_target) {
   TargetToFileList xctest_files_per_target;
-
   for (const Target* target : application_targets) {
     DCHECK(IsApplicationTarget(target));
     SearchXCTestFiles(target, &xctest_files_per_target);
-    file_lists->push_back(xctest_files_per_target[target]);
-  }
-}
-
-// Maps each xctest file to a list of xctest application targets that contains
-// the file.
-void MapXCTestFileToApplicationTargets(
-    const std::vector<const Target*>& xctest_application_targets,
-    const std::vector<Target::FileList>& xctest_file_lists,
-    FileToTargets* xctest_file_to_application_targets) {
-  DCHECK_EQ(xctest_application_targets.size(), xctest_file_lists.size());
-
-  for (size_t i = 0; i < xctest_application_targets.size(); ++i) {
-    const Target* xctest_application_target = xctest_application_targets[i];
-    DCHECK(IsApplicationTarget(xctest_application_target));
-
-    for (const SourceFile& source : xctest_file_lists[i]) {
-      auto iter = xctest_file_to_application_targets->find(source);
-      if (iter == xctest_file_to_application_targets->end()) {
-        iter =
-            xctest_file_to_application_targets
-                ->insert(std::make_pair(source, std::vector<const Target*>()))
-                .first;
-      }
-      iter->second.push_back(xctest_application_target);
-    }
+    xctest_files_per_application_target->insert(
+        std::make_pair(target, xctest_files_per_target[target]));
   }
 }
 
@@ -341,13 +310,9 @@
   }
 
   XcodeWriter workspace(workspace_name);
-  workspace.CreateProductsProject(targets, attributes, source_path, config_name,
-                                  root_target_name, ninja_extra_args,
-                                  build_settings, target_os);
-
-  workspace.CreateSourcesProject(
-      all_targets, build_settings->build_dir(), attributes, source_path,
-      build_settings->root_path_utf8(), config_name, target_os);
+  workspace.CreateProductsProject(targets, all_targets, attributes, source_path,
+                                  config_name, root_target_name,
+                                  ninja_extra_args, build_settings, target_os);
 
   return workspace.WriteFiles(build_settings, err);
 }
@@ -427,6 +392,7 @@
 
 void XcodeWriter::CreateProductsProject(
     const std::vector<const Target*>& targets,
+    const std::vector<const Target*>& all_targets,
     const PBXAttributes& attributes,
     const std::string& source_path,
     const std::string& config_name,
@@ -436,20 +402,53 @@
     TargetOsType target_os) {
   std::unique_ptr<PBXProject> main_project(
       new PBXProject("products", config_name, source_path, attributes));
+  SourceDir source_dir("//");
+
+  // Add all source files for indexing.
+  std::vector<SourceFile> sources;
+  for (const Target* target : all_targets) {
+    for (const SourceFile& source : target->sources()) {
+      if (IsStringInOutputDir(build_settings->build_dir(), source.value()))
+        continue;
+
+      sources.push_back(source);
+    }
+  }
+
+  // Sort sources to ensure determinisn of the project file generation and
+  // remove duplicate reference to the source files (can happen due to the
+  // bundle_data targets).
+  std::sort(sources.begin(), sources.end());
+  sources.erase(std::unique(sources.begin(), sources.end()), sources.end());
+
+  for (const SourceFile& source : sources) {
+    std::string source_file = RebasePath(source.value(), source_dir,
+                                         build_settings->root_path_utf8());
+    main_project->AddSourceFileToIndexingTarget(source_file, source_file,
+                                                CompilerFlags::NONE);
+  }
 
   // Filter xctest module and application targets and find list of xctest files
   // recursively under them.
   std::vector<const Target*> xctest_module_targets;
   FilterXCTestModuleTargets(targets, &xctest_module_targets);
 
+  // There is a 1 on 1 mapping between |xctest_module_targets| and
+  // |xctest_application_targets|.
   std::vector<const Target*> xctest_application_targets;
+  TargetToTarget xctest_module_to_application_target;
   FindXCTestApplicationTargets(xctest_module_targets, targets,
-                               &xctest_application_targets);
+                               &xctest_application_targets,
+                               &xctest_module_to_application_target);
   DCHECK_EQ(xctest_module_targets.size(), xctest_application_targets.size());
+  DCHECK_EQ(xctest_module_targets.size(),
+            xctest_module_to_application_target.size());
 
-  std::vector<Target::FileList> xctest_file_lists;
-  FindXCTestFilesForTargets(xctest_application_targets, &xctest_file_lists);
-  DCHECK_EQ(xctest_application_targets.size(), xctest_file_lists.size());
+  TargetToFileList xctest_files_per_application_target;
+  FindXCTestFilesForApplicationTargets(xctest_application_targets,
+                                       &xctest_files_per_application_target);
+  DCHECK_EQ(xctest_application_targets.size(),
+            xctest_files_per_application_target.size());
 
   std::string build_path;
   std::unique_ptr<base::Environment> env(base::Environment::Create());
@@ -457,8 +456,6 @@
   main_project->AddAggregateTarget(
       "All", GetBuildScript(root_target, ninja_extra_args, env.get()));
 
-  TargetToNativeTarget xctest_application_to_module_native_target;
-
   for (const Target* target : targets) {
     switch (target->output_type()) {
       case Target::EXECUTABLE:
@@ -502,12 +499,23 @@
         if (!IsXCTestModuleTarget(target))
           continue;
 
-        // Populate |xctest_application_to_module_native_target| for XCTest
-        // module targets.
-        const Target* application_target =
-            FindXCTestApplicationTarget(target, xctest_application_targets);
-        xctest_application_to_module_native_target.insert(
-            std::make_pair(application_target, native_target));
+        // Add xctest files to the "Compiler Sources" of corresponding xctest
+        // native targets.
+        const Target::FileList& xctest_file_list =
+            xctest_files_per_application_target
+                [xctest_module_to_application_target[target]];
+
+        for (const SourceFile& source : xctest_file_list) {
+          std::string source_path = RebasePath(
+              source.value(), source_dir, build_settings->root_path_utf8());
+
+          // Test files need to be known to Xcode for proper indexing and for
+          // discovery of tests function for XCTest, but the compilation is done
+          // via ninja and thus must prevent Xcode from compiling the files by
+          // adding '-help' as per file compiler flag.
+          main_project->AddSourceFile(source_path, source_path,
+                                      CompilerFlags::HELP, native_target);
+        }
 
         break;
       }
@@ -517,75 +525,9 @@
     }
   }
 
-  FileToTargets xctest_file_to_application_targets(CompareSourceFiles);
-  MapXCTestFileToApplicationTargets(xctest_application_targets,
-                                    xctest_file_lists,
-                                    &xctest_file_to_application_targets);
-
-  // Add xctest files to the "Compiler Sources" of corresponding xctest native
-  // targets.
-  SourceDir source_dir("//");
-  for (const auto& item : xctest_file_to_application_targets) {
-    const SourceFile& source = item.first;
-    for (const Target* xctest_application_target : item.second) {
-      std::string navigator_path =
-          kXCTestFileReferenceFolder + source.GetName();
-      std::string source_path = RebasePath(source.value(), source_dir,
-                                           build_settings->root_path_utf8());
-      PBXNativeTarget* xctest_module_native_target =
-          xctest_application_to_module_native_target[xctest_application_target];
-
-      // Test files need to be known to Xcode for proper indexing and for
-      // discovery of tests function for XCTest, but the compilation is done
-      // via ninja and thus must prevent Xcode from compiling the files by
-      // adding '-help' as per file compiler flag.
-      main_project->AddSourceFile(navigator_path, source_path,
-                                  CompilerFlags::HELP,
-                                  xctest_module_native_target);
-    }
-  }
-
   projects_.push_back(std::move(main_project));
 }
 
-void XcodeWriter::CreateSourcesProject(
-    const std::vector<const Target*>& targets,
-    const SourceDir& root_build_dir,
-    const PBXAttributes& attributes,
-    const std::string& source_path,
-    const std::string& absolute_source_path,
-    const std::string& config_name,
-    TargetOsType target_os) {
-  std::vector<SourceFile> sources;
-  for (const Target* target : targets) {
-    for (const SourceFile& source : target->sources()) {
-      if (IsStringInOutputDir(root_build_dir, source.value()))
-        continue;
-
-      sources.push_back(source);
-    }
-  }
-
-  std::unique_ptr<PBXProject> sources_for_indexing(
-      new PBXProject("sources", config_name, source_path, attributes));
-
-  // Sort sources to ensure determinisn of the project file generation and
-  // remove duplicate reference to the source files (can happen due to the
-  // bundle_data targets).
-  std::sort(sources.begin(), sources.end());
-  sources.erase(std::unique(sources.begin(), sources.end()), sources.end());
-
-  SourceDir source_dir("//");
-  for (const SourceFile& source : sources) {
-    std::string source_file =
-        RebasePath(source.value(), source_dir, absolute_source_path);
-    sources_for_indexing->AddSourceFileToIndexingTarget(
-        source_file, source_file, CompilerFlags::NONE);
-  }
-
-  projects_.push_back(std::move(sources_for_indexing));
-}
-
 bool XcodeWriter::WriteFiles(const BuildSettings* build_settings, Err* err) {
   for (const auto& project : projects_) {
     if (!WriteProjectFile(build_settings, project.get(), err))
diff --git a/tools/gn/xcode_writer.h b/tools/gn/xcode_writer.h
index 37f49d6..4973135 100644
--- a/tools/gn/xcode_writer.h
+++ b/tools/gn/xcode_writer.h
@@ -16,7 +16,6 @@
 class Builder;
 class BuildSettings;
 class Err;
-class SourceDir;
 class Target;
 
 using PBXAttributes = std::map<std::string, std::string>;
@@ -69,6 +68,7 @@
   // (i.e. targets that have a build artefact usable from Xcode, mostly
   // application bundles).
   void CreateProductsProject(const std::vector<const Target*>& targets,
+                             const std::vector<const Target*>& all_targets,
                              const PBXAttributes& attributes,
                              const std::string& source_path,
                              const std::string& config_name,
@@ -77,16 +77,6 @@
                              const BuildSettings* build_settings,
                              TargetOsType target_os);
 
-  // Generates the "sources.xcodeproj" project that reference all source
-  // files to allow Xcode to index them.
-  void CreateSourcesProject(const std::vector<const Target*>& targets,
-                            const SourceDir& root_build_dir,
-                            const PBXAttributes& attributes,
-                            const std::string& source_path,
-                            const std::string& absolute_source_path,
-                            const std::string& config_name,
-                            TargetOsType target_os);
-
   bool WriteFiles(const BuildSettings* build_settings, Err* err);
   bool WriteProjectFile(const BuildSettings* build_settings,
                         PBXProject* project,