Rewriting python search logic The various rewrites to properly search for Python on Windows whether specified on the command-line, dotfile, or implicitly were done a bit hurriedly and accumulated some inconsistencies and unhandled cases. This cleans up the code to make it more obviously correct. The initial goal was to have set_python_path call ProcessFileExtensions but in the end that seemed to cause more problems that it solves. Change-Id: I46133b6f015bfa5bfa77a3aa405ff906497c4045 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/10920 Reviewed-by: Brett Wilson <brettw@chromium.org> Reviewed-by: Dirk Pranke <dpranke@google.com> Commit-Queue: Bruce Dawson <brucedawson@chromium.org>
diff --git a/src/gn/setup.cc b/src/gn/setup.cc index 0454fd8..5ef4346 100644 --- a/src/gn/setup.cc +++ b/src/gn/setup.cc
@@ -286,16 +286,22 @@ return base::FilePath(); } +// python_exe_name and python_bat_name can be empty but cannot be absolute +// paths. They should be "python.exe" or "", etc., and "python.bat" or "", etc. 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(python_exe_name); - if (base::PathExists(cur_dir_candidate_exe)) - return cur_dir_candidate_exe; + if (!python_exe_name.empty()) { + CHECK(python_exe_name.FinalExtension() == u".exe"); + CHECK_EQ(python_exe_name.IsAbsolute(), false); + base::FilePath cur_dir_candidate_exe = + base::FilePath(current_directory).Append(python_exe_name); + if (base::PathExists(cur_dir_candidate_exe)) + return cur_dir_candidate_exe; + } // Get the path. const char16_t kPathEnvVarName[] = u"Path"; @@ -313,18 +319,24 @@ for (const auto& component : base::SplitStringPiece( std::u16string_view(full_path.get(), path_length), u";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { - base::FilePath candidate_exe = - base::FilePath(component).Append(python_exe_name); - if (base::PathExists(candidate_exe)) - return candidate_exe; + if (!python_exe_name.empty()) { + base::FilePath candidate_exe = + 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(python_bat_name); - if (base::PathExists(candidate_bat)) { - base::FilePath python_exe = PythonBatToExe(candidate_bat); - if (!python_exe.empty()) - return python_exe; + if (!python_bat_name.empty()) { + CHECK(python_bat_name.FinalExtension() == u".bat"); + CHECK_EQ(python_bat_name.IsAbsolute(), false); + base::FilePath candidate_bat = + base::FilePath(component).Append(python_bat_name); + if (base::PathExists(candidate_bat)) { + base::FilePath python_exe = PythonBatToExe(candidate_bat); + if (!python_exe.empty()) + return python_exe; + } } } return base::FilePath(); @@ -730,15 +742,29 @@ #if defined(OS_WIN) // 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") + auto extension = script_executable.FinalExtension(); + if (script_executable.IsAbsolute()) { + // Do translation from .bat to .exe but otherwise just pass through. + if (extension == u".bat") script_executable = PythonBatToExe(script_executable); + } else { + if (extension == u"") { + // If no extension is specified then search the path for .exe and .bat + // variants. + script_executable = + FindWindowsPython(script_executable.ReplaceExtension(u".exe"), + script_executable.ReplaceExtension(u".bat")); + } else if (extension == u".bat") { + // Search the path just for the specified .bat. + script_executable = + FindWindowsPython(base::FilePath(), script_executable); + } else if (extension == u".exe") { + // Search the path just for the specified .exe. + script_executable = + FindWindowsPython(script_executable, base::FilePath()); + } } + script_executable = script_executable.NormalizePathSeparatorsTo('/'); #endif return script_executable; } @@ -750,8 +776,7 @@ if (cmdline.HasSwitch(switches::kScriptExecutable)) { auto script_executable = cmdline.GetSwitchValuePath(switches::kScriptExecutable); - script_executable = ProcessFileExtensions(script_executable); - build_settings_.set_python_path(script_executable); + build_settings_.set_python_path(ProcessFileExtensions(script_executable)); } else if (value) { if (!value->VerifyTypeIs(Value::STRING, err)) { return false; @@ -760,17 +785,15 @@ ProcessFileExtensions(UTF8ToFilePath(value->string_value()))); } else { #if defined(OS_WIN) - 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()) { + ProcessFileExtensions(base::FilePath(u"python")); + if (!python_path.IsAbsolute()) { scheduler_.Log("WARNING", "Could not find python on path, using " "just \"python.exe\""); - python_path = python_exe_name; + python_path = base::FilePath(u"python.exe"); } - build_settings_.set_python_path(python_path.NormalizePathSeparatorsTo('/')); + build_settings_.set_python_path(python_path); #else build_settings_.set_python_path(base::FilePath("python")); #endif