[xcode] Allow user to choose Xcode build system

Xcode 10 introduced a new build system, and beta versions of
Xcode 12 prints a warning if the legacy build system is used
telling that it will be removed in a future version.

Give the user an option to choose which build system to use.

Bug: chromium:1094890
Change-Id: I681201cc5b1355f6139b96b20d74dc93a4545e63
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/9100
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md
index 7896bec..8d8d30e 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -229,7 +229,7 @@
 
        - "Found dependency"
        - "No dependency"
-       - "Found dependency (all) "
+       - "Found dependency (all)"
 
      In the first case, the lists returned in compile_targets and test_targets
      should be passed to ninja to build. In the second case, nothing was
@@ -768,6 +768,12 @@
       Override defaut Xcode project file name ("all"). The project file is
       written to the root build directory.
 
+  --xcode-build-system=<value>
+      Configure the build system to use for the Xcode project. Supported
+      values are (default to "legacy"):
+      "legacy" - Legacy Build system
+      "new" - New Build System
+
   --ninja-executable=<string>
       Can be used to specify the ninja executable to use when building.
 
@@ -1609,7 +1615,7 @@
           bundle_resources_dir = bundle_contents_dir
           bundle_executable_dir = bundle_contents_dir
 
-          extra_attributes = {
+          xcode_extra_attributes = {
             ONLY_ACTIVE_ARCH = "YES"
             DEBUG_INFORMATION_FORMAT = "dwarf"
           }
diff --git a/src/gn/command_gen.cc b/src/gn/command_gen.cc
index 8424165..0180cd1 100644
--- a/src/gn/command_gen.cc
+++ b/src/gn/command_gen.cc
@@ -49,29 +49,15 @@
 const char kSwitchRootTarget[] = "root-target";
 const char kSwitchSln[] = "sln";
 const char kSwitchXcodeProject[] = "xcode-project";
+const char kSwitchXcodeBuildSystem[] = "xcode-build-system";
+const char kSwitchXcodeBuildsystemValueLegacy[] = "legacy";
+const char kSwitchXcodeBuildsystemValueNew[] = "new";
 const char kSwitchJsonFileName[] = "json-file-name";
 const char kSwitchJsonIdeScript[] = "json-ide-script";
 const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
 const char kSwitchExportCompileCommands[] = "export-compile-commands";
 const char kSwitchExportRustProject[] = "export-rust-project";
 
-// Extracts extra parameters for XcodeWriter from command-line flags.
-XcodeWriter::Options XcodeWriterOptionsFromCommandLine(
-    const base::CommandLine& command_line) {
-  std::string project_name =
-      command_line.GetSwitchValueASCII(kSwitchXcodeProject);
-  if (project_name.empty())
-    project_name = "all";
-
-  return {
-      std::move(project_name),
-      command_line.GetSwitchValueASCII(kSwitchRootTarget),
-      command_line.GetSwitchValueASCII(kSwitchNinjaExecutable),
-      command_line.GetSwitchValueASCII(kSwitchNinjaExtraArgs),
-      command_line.GetSwitchValueASCII(kSwitchFilters),
-  };
-}
-
 // Collects Ninja rules for each toolchain. The lock protectes the rules.
 struct TargetWriteInfo {
   std::mutex lock;
@@ -254,9 +240,34 @@
     }
     return res;
   } else if (ide == kSwitchIdeValueXcode) {
-    bool res = XcodeWriter::RunAndWriteFiles(
-        build_settings, builder,
-        XcodeWriterOptionsFromCommandLine(*command_line), err);
+    XcodeWriter::Options options = {
+        command_line->GetSwitchValueASCII(kSwitchXcodeProject),
+        command_line->GetSwitchValueASCII(kSwitchRootTarget),
+        command_line->GetSwitchValueASCII(kSwitchNinjaExecutable),
+        command_line->GetSwitchValueASCII(kSwitchNinjaExtraArgs),
+        command_line->GetSwitchValueASCII(kSwitchFilters),
+        XcodeBuildSystem::kLegacy,
+    };
+
+    if (options.project_name.empty()) {
+      options.project_name = "all";
+    }
+
+    const std::string build_system =
+        command_line->GetSwitchValueASCII(kSwitchXcodeBuildSystem);
+    if (!build_system.empty()) {
+      if (build_system == kSwitchXcodeBuildsystemValueNew) {
+        options.build_system = XcodeBuildSystem::kNew;
+      } else if (build_system == kSwitchXcodeBuildsystemValueLegacy) {
+        options.build_system = XcodeBuildSystem::kLegacy;
+      } else {
+        *err = Err(Location(), "Unknown build system: " + build_system);
+        return false;
+      }
+    }
+
+    bool res =
+        XcodeWriter::RunAndWriteFiles(build_settings, builder, options, err);
     if (res && !quiet) {
       OutputString("Generating Xcode projects took " +
                    base::Int64ToString(timer.Elapsed().InMilliseconds()) +
@@ -412,6 +423,12 @@
       Override defaut Xcode project file name ("all"). The project file is
       written to the root build directory.
 
+  --xcode-build-system=<value>
+      Configure the build system to use for the Xcode project. Supported
+      values are (default to "legacy"):
+      "legacy" - Legacy Build system
+      "new" - New Build System
+
   --ninja-executable=<string>
       Can be used to specify the ninja executable to use when building.
 
diff --git a/src/gn/functions_target.cc b/src/gn/functions_target.cc
index 0864db5..f74cfa6 100644
--- a/src/gn/functions_target.cc
+++ b/src/gn/functions_target.cc
@@ -443,7 +443,7 @@
           bundle_resources_dir = bundle_contents_dir
           bundle_executable_dir = bundle_contents_dir
 
-          extra_attributes = {
+          xcode_extra_attributes = {
             ONLY_ACTIVE_ARCH = "YES"
             DEBUG_INFORMATION_FORMAT = "dwarf"
           }
diff --git a/src/gn/xcode_object.cc b/src/gn/xcode_object.cc
index 7948b17..eb3bd64 100644
--- a/src/gn/xcode_object.cc
+++ b/src/gn/xcode_object.cc
@@ -812,7 +812,6 @@
                                     const std::string& shell_script) {
   PBXAttributes attributes;
   attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
-  attributes["CODE_SIGN_IDENTITY"] = "";
   attributes["CODE_SIGNING_REQUIRED"] = "NO";
   attributes["CONFIGURATION_BUILD_DIR"] = ".";
   attributes["PRODUCT_NAME"] = name;
@@ -825,7 +824,6 @@
   DCHECK(!target_for_indexing_);
   PBXAttributes attributes;
   attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
-  attributes["CODE_SIGN_IDENTITY"] = "";
   attributes["CODE_SIGNING_REQUIRED"] = "NO";
   attributes["EXECUTABLE_PREFIX"] = "";
   attributes["HEADER_SEARCH_PATHS"] = sources_->path();
@@ -869,7 +867,6 @@
 
   PBXAttributes attributes = extra_attributes;
   attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
-  attributes["CODE_SIGN_IDENTITY"] = "";
   attributes["CODE_SIGNING_REQUIRED"] = "NO";
   attributes["CONFIGURATION_BUILD_DIR"] = output_dir;
   attributes["PRODUCT_NAME"] = product_name;
diff --git a/src/gn/xcode_writer.cc b/src/gn/xcode_writer.cc
index ee8c84d..0daf1c5 100644
--- a/src/gn/xcode_writer.cc
+++ b/src/gn/xcode_writer.cc
@@ -373,6 +373,7 @@
   // not set. Since the generated Xcode project is only used for debugging
   // and the source of truth for build settings is the .gn files themselves,
   // we can safely set them in the project as they won't be used by "ninja".
+  attributes["ALWAYS_SEARCH_USER_PATHS"] = "NO";
   attributes["CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED"] = "YES";
   attributes["CLANG_WARN__DUPLICATE_METHOD_MATCH"] = "YES";
   attributes["CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING"] = "YES";
@@ -387,6 +388,7 @@
   attributes["CLANG_WARN_NON_LITERAL_NULL_CONVERSION"] = "YES";
   attributes["CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF"] = "YES";
   attributes["CLANG_WARN_OBJC_LITERAL_CONVERSION"] = "YES";
+  attributes["CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER"] = "YES";
   attributes["CLANG_WARN_RANGE_LOOP_ANALYSIS"] = "YES";
   attributes["CLANG_WARN_STRICT_PROTOTYPES"] = "YES";
   attributes["CLANG_WARN_SUSPICIOUS_MOVE"] = "YES";
@@ -409,10 +411,11 @@
 
 // Class representing the workspace embedded in an xcodeproj file used to
 // configure the build settings shared by all targets in the project (used
-// to configure the build system to "Legacy build system").
+// to configure the build system).
 class XcodeWorkspace {
  public:
-  XcodeWorkspace(const BuildSettings* settings);
+  XcodeWorkspace(const BuildSettings* build_settings,
+                 XcodeWriter::Options options);
   ~XcodeWorkspace();
 
   XcodeWorkspace(const XcodeWorkspace&) = delete;
@@ -429,10 +432,12 @@
   bool WriteSettingsFile(const std::string& name, Err* err) const;
 
   const BuildSettings* build_settings_ = nullptr;
+  XcodeWriter::Options options_;
 };
 
-XcodeWorkspace::XcodeWorkspace(const BuildSettings* build_settings)
-    : build_settings_(build_settings) {}
+XcodeWorkspace::XcodeWorkspace(const BuildSettings* build_settings,
+                               XcodeWriter::Options options)
+    : build_settings_(build_settings), options_(options) {}
 
 XcodeWorkspace::~XcodeWorkspace() = default;
 
@@ -475,10 +480,18 @@
       << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
       << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
       << "<plist version=\"1.0\">\n"
-      << "<dict>\n"
-      << "\t<key>BuildSystemType</key>\n"
-      << "\t<string>Original</string>\n"
-      << "</dict>\n"
+      << "<dict>\n";
+
+  switch (options_.build_system) {
+    case XcodeBuildSystem::kLegacy:
+      out << "\t<key>BuildSystemType</key>\n"
+          << "\t<string>Original</string>\n";
+      break;
+    case XcodeBuildSystem::kNew:
+      break;
+  }
+
+  out << "</dict>\n"
       << "</plist>\n";
 
   return WriteFileIfChanged(build_settings_->GetFullPath(source_file),
@@ -803,7 +816,7 @@
     return false;
   }
 
-  XcodeWorkspace workspace(build_settings_);
+  XcodeWorkspace workspace(build_settings_, options_);
   return workspace.WriteWorkspace(
       project_.Name() + ".xcodeproj/project.xcworkspace", err);
 }
@@ -909,6 +922,9 @@
 
   PBXAttributes xcode_extra_attributes =
       target->bundle_data().xcode_extra_attributes();
+  if (options_.build_system == XcodeBuildSystem::kLegacy) {
+    xcode_extra_attributes["CODE_SIGN_IDENTITY"] = "";
+  }
 
   const std::string& target_output_name = RebasePath(
       target->bundle_data().GetBundleRootDirOutput(target->settings()).value(),
diff --git a/src/gn/xcode_writer.h b/src/gn/xcode_writer.h
index 62f6ab5..b45b606 100644
--- a/src/gn/xcode_writer.h
+++ b/src/gn/xcode_writer.h
@@ -17,6 +17,11 @@
 class BuildSettings;
 class Err;
 
+enum class XcodeBuildSystem {
+  kLegacy,
+  kNew,
+};
+
 // Writes an Xcode workspace to build and debug code.
 class XcodeWriter {
  public:
@@ -30,7 +35,7 @@
     // try to build all defined targets.
     std::string root_target_name;
 
-    // Name of the ninja executable. Defaults to "ninja" is empty.
+    // Name of the ninja executable. Defaults to "ninja" if empty.
     std::string ninja_executable;
 
     // Extra parameters to pass to ninja. Deprecated.
@@ -41,6 +46,10 @@
     // (in the same way that the other filtering is done, source and header
     // files for those target will still be listed in the generated project).
     std::string dir_filters_string;
+
+    // Control which version of the build system should be used for the
+    // generated Xcode project.
+    XcodeBuildSystem build_system = XcodeBuildSystem::kLegacy;
   };
 
   // Writes an Xcode workspace with a single project file.