Fix support for empty `script_executable` value in .gn file.

A recent CL [1] broke the Fuchsia build because the latter sets
the `script_executable` value to the empty script in its `.gn`
file. This use case is documented to let GN invoke action scripts
directly.

This fixes the issue and adds a unit-test for this use case.

[1] https://gn-review.googlesource.com/c/gn/+/13260

Bug: None
Change-Id: I2660c6de0e8f41790c60bbb072149f1d7360e7f0
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13460
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: David Turner <digit@google.com>
diff --git a/src/gn/setup.cc b/src/gn/setup.cc
index 8f1bd7a..68d42b7 100644
--- a/src/gn/setup.cc
+++ b/src/gn/setup.cc
@@ -126,12 +126,14 @@
       help --root-target").
 
   script_executable [optional]
-      Path to specific Python executable or other interpreter to use in
-      action targets and exec_script calls. By default GN searches the
-      PATH for Python to execute these scripts.
+      By default, GN runs the scripts used in action targets and exec_script
+      calls using the Python interpreter found in PATH. This value specifies the
+      Python executable or other interpreter to use instead.
 
-      If set to the empty string, the path specified in action targets
-      and exec_script calls will be executed directly.
+      If set to the empty string, the scripts will be executed directly.
+
+      The command-line switch --script-executable will override this value (see
+      "gn help --script-executable")
 
   secondary_source [optional]
       Label of an alternate directory tree to find input files. When searching
@@ -781,12 +783,17 @@
     if (!value->VerifyTypeIs(Value::STRING, err)) {
       return false;
     }
-    base::FilePath python_path =
-        ProcessFileExtensions(UTF8ToFilePath(value->string_value()));
-    if (python_path.empty()) {
-      *err = Err(Location(), "Could not find \"" + value->string_value() +
-                                 "\" from dotfile in PATH.");
-      return false;
+    // Note that an empty string value is valid, and means that the scripts
+    // invoked by actions will be run directly.
+    base::FilePath python_path;
+    if (!value->string_value().empty()) {
+      python_path =
+          ProcessFileExtensions(UTF8ToFilePath(value->string_value()));
+      if (python_path.empty()) {
+        *err = Err(Location(), "Could not find \"" + value->string_value() +
+                                   "\" from dotfile in PATH.");
+        return false;
+      }
     }
     build_settings_.set_python_path(python_path);
   } else {
diff --git a/src/gn/setup_unittest.cc b/src/gn/setup_unittest.cc
index d905355..8239589 100644
--- a/src/gn/setup_unittest.cc
+++ b/src/gn/setup_unittest.cc
@@ -46,6 +46,33 @@
   EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name));
 }
 
+TEST_F(SetupTest, EmptyScriptExecutableDoesNotGenerateError) {
+  base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+  // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
+  // pass it as --root.
+  base::ScopedTempDir in_temp_dir;
+  ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+  base::FilePath in_path = in_temp_dir.GetPath();
+  base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+  WriteFile(dot_gn_name,
+            "buildconfig = \"//BUILDCONFIG.gn\"\n"
+            "script_executable = \"\"\n");
+
+  WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+  cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
+
+  // Create another temp dir for writing the generated files to.
+  base::ScopedTempDir build_temp_dir;
+  ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+  // Run setup and check that the .gn file is in the scheduler's gen deps.
+  Setup setup;
+  Err err;
+  EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
+                                   true, cmdline, &err));
+}
+
 #if defined(OS_WIN)
 TEST_F(SetupTest, MissingScriptExeGeneratesSetupErrorOnWindows) {
   base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
diff --git a/src/gn/switches.cc b/src/gn/switches.cc
index 6caec3a..e670517 100644
--- a/src/gn/switches.cc
+++ b/src/gn/switches.cc
@@ -119,8 +119,8 @@
   action targets and exec_script calls. By default GN searches the
   PATH for Python to execute these scripts.
 
-  If set to the empty string, the path specified in action targets
-  and exec_script calls will be executed directly.
+  If set to the empty string, the path of scripts specified in action
+  targets and exec_script calls will be executed directly.
 )";
 
 const char kQuiet[] = "q";