Skip to content

Commit

Permalink
Use long executable path instead of argv[0] in all launchers
Browse files Browse the repository at this point in the history
`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.
  • Loading branch information
fmeum committed Dec 3, 2022
1 parent de7b26a commit 7f80ff5
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 44 deletions.
6 changes: 1 addition & 5 deletions src/tools/launcher/bash_launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,7 @@ ExitCode BashBinaryLauncher::Launch() {

vector<wstring> 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' ';
Expand Down
6 changes: 4 additions & 2 deletions src/tools/launcher/bash_launcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
8 changes: 3 additions & 5 deletions src/tools/launcher/java_launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 "
Expand Down
6 changes: 4 additions & 2 deletions src/tools/launcher/java_launcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down
31 changes: 20 additions & 11 deletions src/tools/launcher/launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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") {
Expand All @@ -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) &&
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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;
}
Expand Down
19 changes: 15 additions & 4 deletions src/tools/launcher/launcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ class BinaryLauncherBase {
typedef std::unordered_map<std::wstring, std::wstring> 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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -127,7 +138,7 @@ class BinaryLauncherBase {
// Expect the manifest file to be at
// 1. <path>/<to>/<binary>/<target_name>.runfiles/MANIFEST
// or 2. <path>/<to>/<binary>/<target_name>.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,
Expand Down
14 changes: 11 additions & 3 deletions src/tools/launcher/launcher_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.");
Expand All @@ -81,9 +87,11 @@ int wmain(int argc, wchar_t* argv[]) {
binary_launcher = make_unique<PythonBinaryLauncher>(
launch_info, executable_file, argc, argv);
} else if (result->second == L"Bash") {
binary_launcher = make_unique<BashBinaryLauncher>(launch_info, argc, argv);
binary_launcher = make_unique<BashBinaryLauncher>(
launch_info, executable_file, argc, argv);
} else if (result->second == L"Java") {
binary_launcher = make_unique<JavaBinaryLauncher>(launch_info, argc, argv);
binary_launcher = make_unique<JavaBinaryLauncher>(
launch_info, executable_file, argc, argv);
} else {
die(L"Unknown binary type, cannot launch anything.");
}
Expand Down
8 changes: 2 additions & 6 deletions src/tools/launcher/python_launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,10 @@ ExitCode PythonBinaryLauncher::Launch() {
vector<wstring> 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
Expand Down
10 changes: 4 additions & 6 deletions src/tools/launcher/python_launcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7f80ff5

Please sign in to comment.