Apply path search logic more generally

The --script-executable option becomes much more portable if "python"
(or "python3") can be specified across platforms. On Windows this
requires adding a .bat or .exe extension and doing a search of the path.
The logic to do this already existed and just needed to be generalized.

Running clang-format on setup.cc adjusted a few unrelated lines.

Change-Id: If0b44449fa4f4672b5408c3c26dc21b2388dd184
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/10600
Commit-Queue: Dirk Pranke <dpranke@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/setup.cc b/src/gn/setup.cc
index 081bc7e..8c402f1 100644
--- a/src/gn/setup.cc
+++ b/src/gn/setup.cc
@@ -286,16 +286,14 @@
   return base::FilePath();
 }
 
-const char16_t kPythonExeName[] = u"python.exe";
-const char16_t kPythonBatName[] = u"python.bat";
-
-base::FilePath FindWindowsPython() {
+base::FilePath FindWindowsPython(const base::FilePath& python_exe_name,
+                                 const base::FilePath& python_bat_name) {
   char16_t current_directory[MAX_PATH];
   ::GetCurrentDirectory(MAX_PATH, reinterpret_cast<LPWSTR>(current_directory));
 
   // First search for python.exe in the current directory.
   base::FilePath cur_dir_candidate_exe =
-      base::FilePath(current_directory).Append(kPythonExeName);
+      base::FilePath(current_directory).Append(python_exe_name);
   if (base::PathExists(cur_dir_candidate_exe))
     return cur_dir_candidate_exe;
 
@@ -316,13 +314,13 @@
            std::u16string_view(full_path.get(), path_length), u";",
            base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
     base::FilePath candidate_exe =
-        base::FilePath(component).Append(kPythonExeName);
+        base::FilePath(component).Append(python_exe_name);
     if (base::PathExists(candidate_exe))
       return candidate_exe;
 
     // Also allow python.bat, but convert into the .exe.
     base::FilePath candidate_bat =
-        base::FilePath(component).Append(kPythonBatName);
+        base::FilePath(component).Append(python_bat_name);
     if (base::PathExists(candidate_bat)) {
       base::FilePath python_exe = PythonBatToExe(candidate_bat);
       if (!python_exe.empty())
@@ -652,7 +650,8 @@
     base::GetCurrentDirectory(&cur_dir);
     dotfile_name_ = FindDotFile(cur_dir);
     if (dotfile_name_.empty()) {
-      *err = Err(Location(), "Can't find source root.",
+      *err = Err(
+          Location(), "Can't find source root.",
           "I could not find a \".gn\" file in the current directory or any "
           "parent,\nand the --root command-line argument was not specified.");
       return false;
@@ -727,10 +726,20 @@
   ScopedTrace setup_trace(TraceItem::TRACE_SETUP, "Fill Python Path");
   const Value* value = dotfile_scope_.GetValue("script_executable", true);
   if (cmdline.HasSwitch(switches::kScriptExecutable)) {
-    auto script_executable = cmdline.GetSwitchValuePath(switches::kScriptExecutable);
+    auto script_executable =
+        cmdline.GetSwitchValuePath(switches::kScriptExecutable);
 #if defined(OS_WIN)
-    if (script_executable.FinalExtension() == u".bat")
-      script_executable = PythonBatToExe(script_executable);
+    // If we have a relative path with no extension such as "python" or
+    // "python3" then do a path search on the name with .exe and .bat appended.
+    if (!script_executable.IsAbsolute() &&
+        script_executable.FinalExtension() == u"") {
+      script_executable =
+          FindWindowsPython(script_executable.ReplaceExtension(u".exe"),
+                            script_executable.ReplaceExtension(u".bat"));
+    } else {
+      if (script_executable.FinalExtension() == u".bat")
+        script_executable = PythonBatToExe(script_executable);
+    }
 #endif
     build_settings_.set_python_path(script_executable);
   } else if (value) {
@@ -741,12 +750,15 @@
         base::FilePath(UTF8ToFilePath(value->string_value())));
   } else {
 #if defined(OS_WIN)
-    base::FilePath python_path = FindWindowsPython();
+    const base::FilePath python_exe_name(u"python.exe");
+    const base::FilePath python_bat_name(u"python.bat");
+    base::FilePath python_path =
+        FindWindowsPython(python_exe_name, python_bat_name);
     if (python_path.empty()) {
       scheduler_.Log("WARNING",
                      "Could not find python on path, using "
                      "just \"python.exe\"");
-      python_path = base::FilePath(kPythonExeName);
+      python_path = python_exe_name;
     }
     build_settings_.set_python_path(python_path.NormalizePathSeparatorsTo('/'));
 #else
@@ -848,8 +860,7 @@
 
   // Root build file.
   if (cmdline.HasSwitch(switches::kRootTarget)) {
-    auto switch_value =
-        cmdline.GetSwitchValueASCII(switches::kRootTarget);
+    auto switch_value = cmdline.GetSwitchValueASCII(switches::kRootTarget);
     Value root_value(nullptr, switch_value);
     root_target_label = Label::Resolve(current_dir, std::string_view(), Label(),
                                        root_value, err);