[xcode] Simplify the build script generated

Recent version of Xcode correctly support relative paths in error
message, so convert the build script from python to a simpler sh
script.

Bug: none
Change-Id: I2b2cbcab3976840c1346d5c33880dbfc410257b5
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13020
Reviewed-by: Petr Hosek <phosek@google.com>
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
diff --git a/src/gn/xcode_object.cc b/src/gn/xcode_object.cc
index f3c5a54..a957c97 100644
--- a/src/gn/xcode_object.cc
+++ b/src/gn/xcode_object.cc
@@ -973,7 +973,7 @@
   PrintProperty(out, rules, "name", name_);
   PrintProperty(out, rules, "outputPaths", EmptyPBXObjectVector());
   PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
-  PrintProperty(out, rules, "shellPath", "/usr/bin/python3");
+  PrintProperty(out, rules, "shellPath", "/bin/sh");
   PrintProperty(out, rules, "shellScript", shell_script_);
   PrintProperty(out, rules, "showEnvVarsInLog", 0u);
   out << indent_str << "};\n";
diff --git a/src/gn/xcode_writer.cc b/src/gn/xcode_writer.cc
index c2d8857..6dda1ac 100644
--- a/src/gn/xcode_writer.cc
+++ b/src/gn/xcode_writer.cc
@@ -19,7 +19,6 @@
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
 #include "gn/args.h"
 #include "gn/build_settings.h"
 #include "gn/builder.h"
@@ -40,60 +39,6 @@
 
 namespace {
 
-// This is the template of the script used to build the target. It invokes
-// ninja (supporting --ninja-executable parameter), parsing ninja's output
-// using a regular expression looking for relative path to the source root
-// from root_build_dir that are at the start of a path and converting them
-// to absolute paths (use str.replace(rel_root_src, abs_root_src) would be
-// simpler but would fail if rel_root_src is present multiple time in the
-// path).
-const char kBuildScriptTemplate[] = R"(
-import re
-import os
-import subprocess
-import sys
-
-rel_root_src = '%s'
-abs_root_src = os.path.abspath(rel_root_src) + '/'
-
-build_target = '%s'
-ninja_binary = '%s'
-ninja_params = [ '-C', '.' ]
-
-%s
-
-if build_target:
-  ninja_params.append(build_target)
-  print('Compile "' + build_target + '" via ninja')
-else:
-  print('Compile "all" via ninja')
-
-process = subprocess.Popen(
-    [ ninja_binary ] + ninja_params,
-    stdout=subprocess.PIPE,
-    stderr=subprocess.STDOUT,
-    universal_newlines=True,
-    encoding='utf-8',
-    env=environ)
-
-pattern = re.compile('(?<!/)' + re.escape(rel_root_src))
-
-for line in iter(process.stdout.readline, ''):
-  while True:
-    match = pattern.search(line)
-    if not match:
-      break
-    span = match.span()
-    print(line[:span[0]], end='')
-    print(abs_root_src, end='')
-    line = line[span[1]:]
-  print(line, flush=True, end='')
-
-process.wait()
-
-sys.exit(process.returncode)
-)";
-
 enum TargetOsType {
   WRITER_TARGET_OS_IOS,
   WRITER_TARGET_OS_MACOS,
@@ -138,29 +83,30 @@
 
 std::string ComputeScriptEnviron(base::Environment* environment) {
   std::stringstream buffer;
-  buffer << "environ = {}";
   for (const auto& variable : kSafeEnvironmentVariables) {
-    buffer << "\nenviron['" << variable.name << "'] = ";
+    buffer << variable.name << "=";
     if (variable.capture_at_generation) {
       std::string value;
       environment->GetVar(variable.name, &value);
       buffer << "'" << value << "'";
     } else {
-      buffer << "os.environ.get('" << variable.name << "', '')";
+      buffer << "\"${" << variable.name << "}\"";
     }
+    buffer << " ";
   }
   return buffer.str();
 }
 
-std::string GetBuildScript(const std::string& target_name,
-                           const std::string& ninja_executable,
-                           const std::string& root_src_dir,
+std::string GetBuildScript(const std::string& ninja_executable,
                            base::Environment* environment) {
-  std::string environ_script = ComputeScriptEnviron(environment);
-  std::string ninja = GetNinjaExecutable(ninja_executable);
-  return base::StringPrintf(kBuildScriptTemplate, root_src_dir.c_str(),
-                            target_name.c_str(), ninja.c_str(),
-                            environ_script.c_str());
+  // Launch ninja with a sanitized environment (Xcode sets many environment
+  // variables overridding settings, including the SDK, thus breaking hermetic
+  // build).
+  std::stringstream buffer;
+  buffer << "exec env -i " << ComputeScriptEnviron(environment);
+  buffer << GetNinjaExecutable(ninja_executable) << " -C .\n";
+  buffer << "exit 1\n";
+  return buffer.str();
 }
 
 bool IsApplicationTarget(const Target* target) {
@@ -712,11 +658,8 @@
 bool XcodeProject::AddTargetsFromBuilder(const Builder& builder, Err* err) {
   std::unique_ptr<base::Environment> env(base::Environment::Create());
 
-  const std::string root_src_dir =
-      RebasePath("//", build_settings_->build_dir());
-  project_.AddAggregateTarget("All", GetBuildScript(options_.root_target_name,
-                                                    options_.ninja_executable,
-                                                    root_src_dir, env.get()));
+  project_.AddAggregateTarget(
+      "All", GetBuildScript(options_.ninja_executable, env.get()));
 
   const std::optional<std::vector<const Target*>> targets =
       GetTargetsFromBuilder(builder, err);
@@ -947,15 +890,12 @@
     output_dir = RebasePath(output_dir, build_settings_->build_dir());
   }
 
-  const std::string root_src_dir =
-      RebasePath("//", build_settings_->build_dir());
   return project_.AddNativeTarget(
       target->label().name(), "compiled.mach-o.executable",
       target->output_name().empty() ? target->label().name()
                                     : target->output_name(),
       "com.apple.product-type.tool", output_dir,
-      GetBuildScript(target->label().name(), options_.ninja_executable,
-                     root_src_dir, env));
+      GetBuildScript(options_.ninja_executable, env));
 }
 
 PBXNativeTarget* XcodeProject::AddBundleTarget(const Target* target,
@@ -982,14 +922,10 @@
   const std::string output_dir =
       RebasePath(target->bundle_data().GetBundleDir(target->settings()).value(),
                  build_settings_->build_dir());
-  const std::string root_src_dir =
-      RebasePath("//", build_settings_->build_dir());
   return project_.AddNativeTarget(
       pbxtarget_name, std::string(), target_output_name,
       target->bundle_data().product_type(), output_dir,
-      GetBuildScript(pbxtarget_name, options_.ninja_executable, root_src_dir,
-                     env),
-      xcode_extra_attributes);
+      GetBuildScript(options_.ninja_executable, env), xcode_extra_attributes);
 }
 
 void XcodeProject::WriteFileContent(std::ostream& out) const {