From 7f80ff51746c4b7736ab76761f043c113faded2a Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Sat, 3 Dec 2022 21:16:23 +0100 Subject: [PATCH] Use long executable path instead of `argv[0]` in all launchers `argv[0]` can differ from the path of the launcher executable and can contain 8.3 style filenames, which need to be resolved to long paths before path manipulation (e.g. appending ".runfiles") can succeed. The Python launcher handled this correctly, but other launchers didn't use the long executable path consistently. --- src/tools/launcher/bash_launcher.cc | 6 +----- src/tools/launcher/bash_launcher.h | 6 ++++-- src/tools/launcher/java_launcher.cc | 8 +++---- src/tools/launcher/java_launcher.h | 6 ++++-- src/tools/launcher/launcher.cc | 31 +++++++++++++++++---------- src/tools/launcher/launcher.h | 19 ++++++++++++---- src/tools/launcher/launcher_main.cc | 14 +++++++++--- src/tools/launcher/python_launcher.cc | 8 ++----- src/tools/launcher/python_launcher.h | 10 ++++----- 9 files changed, 64 insertions(+), 44 deletions(-) diff --git a/src/tools/launcher/bash_launcher.cc b/src/tools/launcher/bash_launcher.cc index 56b77275c1f152..3a9b6849b429f0 100644 --- a/src/tools/launcher/bash_launcher.cc +++ b/src/tools/launcher/bash_launcher.cc @@ -52,11 +52,7 @@ ExitCode BashBinaryLauncher::Launch() { vector origin_args = this->GetCommandlineArguments(); wostringstream bash_command; - // In case the given binary path is a shortened Windows 8dot3 path, we need to - // convert it back to its long path form before using it to find the bash main - // file. - wstring full_binary_path = GetWindowsLongPath(origin_args[0]); - wstring bash_main_file = GetBinaryPathWithoutExtension(full_binary_path); + wstring bash_main_file = GetBinaryPathWithoutExtension(GetExecutableFile()); bash_command << BashEscapeArg(bash_main_file); for (int i = 1; i < origin_args.size(); i++) { bash_command << L' '; diff --git a/src/tools/launcher/bash_launcher.h b/src/tools/launcher/bash_launcher.h index b00828109f81b9..bb4d97d7fc9640 100644 --- a/src/tools/launcher/bash_launcher.h +++ b/src/tools/launcher/bash_launcher.h @@ -22,9 +22,11 @@ namespace launcher { class BashBinaryLauncher : public BinaryLauncherBase { public: - BashBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc, + BashBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, + const std::wstring& executable_file, + int argc, wchar_t* argv[]) - : BinaryLauncherBase(launch_info, argc, argv) {} + : BinaryLauncherBase(launch_info, executable_file, argc, argv) {} ~BashBinaryLauncher() override = default; ExitCode Launch() override; }; diff --git a/src/tools/launcher/java_launcher.cc b/src/tools/launcher/java_launcher.cc index 12cb6a82b97c14..1d90e2514d0f4c 100644 --- a/src/tools/launcher/java_launcher.cc +++ b/src/tools/launcher/java_launcher.cc @@ -162,8 +162,7 @@ static void WriteJarClasspath(const wstring& jar_path, } wstring JavaBinaryLauncher::GetJunctionBaseDir() { - wstring binary_base_path = - GetBinaryPathWithExtension(this->GetCommandlineArguments()[0]); + wstring binary_base_path = GetBinaryPathWithExtension(GetExecutableFile()); wstring result; if (!NormalizePath(binary_base_path + L".j", &result)) { die(L"Failed to get normalized junction base directory."); @@ -191,8 +190,7 @@ void JavaBinaryLauncher::DeleteJunctionBaseDir() { } wstring JavaBinaryLauncher::CreateClasspathJar(const wstring& classpath) { - wstring binary_base_path = - GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]); + wstring binary_base_path = GetBinaryPathWithoutExtension(GetExecutableFile()); wstring abs_manifest_jar_dir_norm = GetManifestJarDir(binary_base_path); wostringstream manifest_classpath; @@ -312,7 +310,7 @@ ExitCode JavaBinaryLauncher::Launch() { // Run deploy jar if needed, otherwise generate the CLASSPATH by rlocation. if (this->singlejar) { wstring deploy_jar = - GetBinaryPathWithoutExtension(this->GetCommandlineArguments()[0]) + + GetBinaryPathWithoutExtension(GetExecutableFile()) + L"_deploy.jar"; if (!DoesFilePathExist(deploy_jar.c_str())) { die(L"Option --singlejar was passed, but %s does not exist.\n (You may " diff --git a/src/tools/launcher/java_launcher.h b/src/tools/launcher/java_launcher.h index 1881d587d998d8..e0e4ff62b16b8b 100644 --- a/src/tools/launcher/java_launcher.h +++ b/src/tools/launcher/java_launcher.h @@ -29,9 +29,11 @@ static const int MAX_ARG_STRLEN = 7000; class JavaBinaryLauncher : public BinaryLauncherBase { public: - JavaBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, int argc, + JavaBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, + const std::wstring& executable_file, + int argc, wchar_t* argv[]) - : BinaryLauncherBase(launch_info, argc, argv), + : BinaryLauncherBase(launch_info, executable_file, argc, argv), singlejar(false), print_javabin(false), classpath_limit(MAX_ARG_STRLEN) {} diff --git a/src/tools/launcher/launcher.cc b/src/tools/launcher/launcher.cc index 2d30bb4ed047ec..6406162c31f292 100644 --- a/src/tools/launcher/launcher.cc +++ b/src/tools/launcher/launcher.cc @@ -42,14 +42,14 @@ using std::vector; using std::wostringstream; using std::wstring; -static wstring GetRunfilesDir(const wchar_t* argv0) { +static wstring GetRunfilesDir(const wchar_t* executable_file) { wstring runfiles_dir; // If RUNFILES_DIR is already set (probably we are either in a test or in a // data dependency) then use it. if (!GetEnv(L"RUNFILES_DIR", &runfiles_dir)) { // Otherwise this is probably a top-level non-test binary (e.g. a genrule // tool) and should look for its runfiles beside the executable. - runfiles_dir = GetBinaryPathWithExtension(argv0) + L".runfiles"; + runfiles_dir = GetBinaryPathWithExtension(executable_file) + L".runfiles"; } // Make sure we return a normalized absolute path. if (!blaze_util::IsAbsolute(runfiles_dir)) { @@ -63,10 +63,14 @@ static wstring GetRunfilesDir(const wchar_t* argv0) { } BinaryLauncherBase::BinaryLauncherBase( - const LaunchDataParser::LaunchInfo& _launch_info, int argc, wchar_t* argv[]) - : launch_info(_launch_info), - manifest_file(FindManifestFile(argv[0])), - runfiles_dir(GetRunfilesDir(argv[0])), + const LaunchDataParser::LaunchInfo& _launch_info, + const std::wstring& executable_file, + int argc, + wchar_t* argv[]) + : executable_file(executable_file), + launch_info(_launch_info), + manifest_file(FindManifestFile(executable_file.c_str())), + runfiles_dir(GetRunfilesDir(executable_file.c_str())), workspace_name(GetLaunchInfoByKey(WORKSPACE_NAME)), symlink_runfiles_enabled(GetLaunchInfoByKey(SYMLINK_RUNFILES_ENABLED) == L"1") { @@ -81,7 +85,8 @@ BinaryLauncherBase::BinaryLauncherBase( } } -static bool FindManifestFileImpl(const wchar_t* argv0, wstring* result) { +static bool FindManifestFileImpl(const wchar_t* executable_file, + wstring* result) { // If this binary X runs as the data-dependency of some other binary Y, then // X has no runfiles manifest/directory and should use Y's. if (GetEnv(L"RUNFILES_MANIFEST_FILE", result) && @@ -100,7 +105,7 @@ static bool FindManifestFileImpl(const wchar_t* argv0, wstring* result) { // If this binary X runs by itself (not as a data-dependency of another // binary), then look for the manifest in a runfiles directory next to the // main binary, then look for it (the manifest) next to the main binary. - directory = GetBinaryPathWithExtension(argv0) + L".runfiles"; + directory = GetBinaryPathWithExtension(executable_file) + L".runfiles"; *result = directory + L"/MANIFEST"; if (DoesFilePathExist(result->c_str())) { return true; @@ -114,9 +119,9 @@ static bool FindManifestFileImpl(const wchar_t* argv0, wstring* result) { return false; } -wstring BinaryLauncherBase::FindManifestFile(const wchar_t* argv0) { +wstring BinaryLauncherBase::FindManifestFile(const wchar_t* executable_file) { wstring manifest_file; - if (!FindManifestFileImpl(argv0, &manifest_file)) { + if (!FindManifestFileImpl(executable_file, &manifest_file)) { return L""; } // The path will be set as the RUNFILES_MANIFEST_FILE envvar and used by the @@ -125,9 +130,13 @@ wstring BinaryLauncherBase::FindManifestFile(const wchar_t* argv0) { return manifest_file; } +wstring BinaryLauncherBase::GetExecutableFile() const { + return executable_file; +} + wstring BinaryLauncherBase::GetRunfilesPath() const { wstring runfiles_path = - GetBinaryPathWithExtension(this->commandline_arguments[0]) + L".runfiles"; + GetBinaryPathWithExtension(executable_file) + L".runfiles"; std::replace(runfiles_path.begin(), runfiles_path.end(), L'/', L'\\'); return runfiles_path; } diff --git a/src/tools/launcher/launcher.h b/src/tools/launcher/launcher.h index c2e8cf16417986..510582407bd612 100644 --- a/src/tools/launcher/launcher.h +++ b/src/tools/launcher/launcher.h @@ -41,7 +41,9 @@ class BinaryLauncherBase { typedef std::unordered_map ManifestFileMap; public: - BinaryLauncherBase(const LaunchDataParser::LaunchInfo& launch_info, int argc, + BinaryLauncherBase(const LaunchDataParser::LaunchInfo& launch_info, + const std::wstring& executable_file, + int argc, wchar_t* argv[]); virtual ~BinaryLauncherBase() = default; @@ -77,13 +79,22 @@ class BinaryLauncherBase { // A launch function to be implemented for a specific language. virtual ExitCode Launch() = 0; + // Returns the path of the launcher's executable file. + // + // The returned path is always a long path, that is, it never contains 8.3 + // style filenames. + std::wstring GetExecutableFile() const; + // Return the runfiles directory of this binary. // - // The method appends ".exe.runfiles" to the first command line argument, - // converts forward slashes to back slashes, then returns that. + // The method appends ".exe.runfiles" to the path of the launcher executable, + // converts forward slashes to backslashes, then returns that. std::wstring GetRunfilesPath() const; private: + // The path of the launcher binary. + const std::wstring executable_file; + // A map to store all the launch information. const LaunchDataParser::LaunchInfo& launch_info; @@ -127,7 +138,7 @@ class BinaryLauncherBase { // Expect the manifest file to be at // 1. ///.runfiles/MANIFEST // or 2. ///.runfiles_manifest - static std::wstring FindManifestFile(const wchar_t* argv0); + static std::wstring FindManifestFile(const wchar_t* executable_file); // Parse manifest file into a map static void ParseManifestFile(ManifestFileMap* manifest_file_map, diff --git a/src/tools/launcher/launcher_main.cc b/src/tools/launcher/launcher_main.cc index e2fc4735e47276..6a1af63bad8743 100644 --- a/src/tools/launcher/launcher_main.cc +++ b/src/tools/launcher/launcher_main.cc @@ -45,6 +45,7 @@ static constexpr const char* BINARY_TYPE = "binary_type"; using bazel::launcher::BashBinaryLauncher; using bazel::launcher::BinaryLauncherBase; using bazel::launcher::GetBinaryPathWithExtension; +using bazel::launcher::GetWindowsLongPath; using bazel::launcher::JavaBinaryLauncher; using bazel::launcher::LaunchDataParser; using bazel::launcher::PythonBinaryLauncher; @@ -64,7 +65,12 @@ static std::wstring GetExecutableFileName() { } int wmain(int argc, wchar_t* argv[]) { - const std::wstring executable_file = GetExecutableFileName(); + // In case the given binary path is a shortened Windows 8dot3 path, we convert + // it back to its long path form so that path manipulations (e.g. appending + // ".runfiles") work as expected. Note that GetExecutableFileName may return a + // path different from argv[0]. + const std::wstring executable_file = + GetWindowsLongPath(GetExecutableFileName()); LaunchDataParser::LaunchInfo launch_info; if (!LaunchDataParser::GetLaunchInfo(executable_file, &launch_info)) { die(L"Failed to parse launch info."); @@ -81,9 +87,11 @@ int wmain(int argc, wchar_t* argv[]) { binary_launcher = make_unique( launch_info, executable_file, argc, argv); } else if (result->second == L"Bash") { - binary_launcher = make_unique(launch_info, argc, argv); + binary_launcher = make_unique( + launch_info, executable_file, argc, argv); } else if (result->second == L"Java") { - binary_launcher = make_unique(launch_info, argc, argv); + binary_launcher = make_unique( + launch_info, executable_file, argc, argv); } else { die(L"Unknown binary type, cannot launch anything."); } diff --git a/src/tools/launcher/python_launcher.cc b/src/tools/launcher/python_launcher.cc index 6a0b487f1a3623..e37cbaba98c3bf 100644 --- a/src/tools/launcher/python_launcher.cc +++ b/src/tools/launcher/python_launcher.cc @@ -56,14 +56,10 @@ ExitCode PythonBinaryLauncher::Launch() { vector args = this->GetCommandlineArguments(); wstring use_zip_file = this->GetLaunchInfoByKey(USE_ZIP_FILE); wstring python_file; - // In case the given binary path is a shortened Windows 8dot3 path, we need to - // convert it back to its long path form before using it to find the python - // file. - wstring full_binary_path = GetWindowsLongPath(executable_file_); if (use_zip_file == L"1") { - python_file = GetBinaryPathWithoutExtension(full_binary_path) + L".zip"; + python_file = GetBinaryPathWithoutExtension(GetExecutableFile()) + L".zip"; } else { - python_file = GetBinaryPathWithoutExtension(full_binary_path); + python_file = GetBinaryPathWithoutExtension(GetExecutableFile()); } // Replace the first argument with python file path diff --git a/src/tools/launcher/python_launcher.h b/src/tools/launcher/python_launcher.h index eb1ca6d2a17ce8..266e4b4a8ceaee 100644 --- a/src/tools/launcher/python_launcher.h +++ b/src/tools/launcher/python_launcher.h @@ -26,14 +26,12 @@ namespace launcher { class PythonBinaryLauncher : public BinaryLauncherBase { public: PythonBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info, - std::wstring executable_file, int argc, wchar_t* argv[]) - : BinaryLauncherBase(launch_info, argc, argv), - executable_file_(std::move(executable_file)) {} + const std::wstring& executable_file, + int argc, + wchar_t* argv[]) + : BinaryLauncherBase(launch_info, executable_file, argc, argv) {} ~PythonBinaryLauncher() override = default; ExitCode Launch() override; - - private: - std::wstring executable_file_; }; } // namespace launcher