Windows: implement and use AsShortWindowsPath
Because CreateProcessW doesn't support long paths,
not even with the "\\?\" prefix [1], we need to
convert long paths to short ones to spawn
processes. This change implements the
corresponding function and uses it in
blaze_util_windows.
[1] https://github.com/bazelbuild/bazel/issues/2181#issuecomment-270696173
See https://github.com/bazelbuild/bazel/issues/2107
See https://github.com/bazelbuild/bazel/issues/2181
--
PiperOrigin-RevId: 144062404
MOS_MIGRATED_REVID=144062404
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index 12aa3e3..0595de6 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -373,6 +373,12 @@
static void CreateCommandLine(CmdLine* result, const string& exe,
const vector<string>& args_vector) {
std::ostringstream cmdline;
+ string short_exe;
+ if (!blaze_util::AsShortWindowsPath(exe + ".exe", &short_exe)) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
+ "CreateCommandLine: AsShortWindowsPath(%s.exe) failed, err=%d",
+ exe.c_str(), GetLastError());
+ }
bool first = true;
for (const auto& s : args_vector) {
if (first) {
@@ -456,23 +462,23 @@
}
PROCESS_INFORMATION processInfo = {0};
- STARTUPINFOW startupInfo = {0};
+ STARTUPINFOA startupInfo = {0};
startupInfo.hStdError = pipe_write;
startupInfo.hStdOutput = pipe_write;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
- wstring wjava_exe;
- if (!blaze_util::AsWindowsPath(java_exe, &wjava_exe)) {
+ string win_java_exe;
+ if (!blaze_util::AsShortWindowsPath(java_exe + ".exe", &win_java_exe)) {
CloseHandle(pipe_read);
CloseHandle(pipe_write);
- pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "AsWindowsPath(%s)",
- java_exe);
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
+ "GetJvmVersion: AsWindowsPath(%s.exe)", java_exe.c_str());
}
- wjava_exe = wstring(L"\"") + wjava_exe + L".exe\" -version";
+ win_java_exe = string("\"") + win_java_exe + "\" -version";
- WCHAR cmdline[MAX_CMDLINE_LENGTH];
- wcscpy(cmdline, wjava_exe.c_str());
- BOOL ok = CreateProcessW(
+ char cmdline[MAX_CMDLINE_LENGTH];
+ strncpy(cmdline, win_java_exe.c_str(), win_java_exe.size() + 1);
+ BOOL ok = CreateProcessA(
/* lpApplicationName */ NULL,
/* lpCommandLine */ cmdline,
/* lpProcessAttributes */ NULL,
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index 9c95468..91fc0866 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -133,6 +133,10 @@
// prepend the UNC prefix in case they need to pass it to a WinAPI function
// (some require the prefix, some don't), or to quote the path if necessary.
bool AsWindowsPath(const std::string &path, std::wstring *result);
+
+// Same as `AsWindowsPath`, but returns a lowercase 8dot3 style shortened path.
+// Result will never have a UNC prefix.
+bool AsShortWindowsPath(const std::string &path, std::string *result);
#endif // defined(COMPILER_MSVC) || defined(__CYGWIN__)
} // namespace blaze_util
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 75555bb..3fa01a4 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -308,6 +308,8 @@
static bool AsWindowsPathWithUncPrefix(const string& path, wstring* wpath) {
if (!AsWindowsPath(path, wpath)) {
+ PrintError("AsWindowsPathWithUncPrefix(%s): AsWindowsPath failed, err=%d\n",
+ path.c_str(), GetLastError());
return false;
}
if (!IsAbsolute(path)) {
@@ -317,6 +319,31 @@
return true;
}
+bool AsShortWindowsPath(const string& path, string* result) {
+ result->clear();
+ wstring wpath;
+ if (!AsWindowsPathWithUncPrefix(path, &wpath)) {
+ return false;
+ }
+ DWORD size = ::GetShortPathNameW(wpath.c_str(), nullptr, 0);
+ if (size == 0) {
+ return false;
+ }
+
+ unique_ptr<WCHAR[]> wshort(new WCHAR[size]); // size includes null-terminator
+ if (size - 1 != ::GetShortPathNameW(wpath.c_str(), wshort.get(), size)) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
+ "AsShortWindowsPath(%s): GetShortPathNameW(%S) failed, err=%d",
+ path.c_str(), wpath.c_str(), GetLastError());
+ }
+ // GetShortPathNameW may preserve the UNC prefix in the result, so strip it.
+ WCHAR* result_ptr = wshort.get() + (HasUncPrefix(wshort.get()) ? 4 : 0);
+
+ result->assign(WstringToCstring(result_ptr).get());
+ ToLower(result);
+ return true;
+}
+
bool ReadFile(const string& filename, string* content, int max_size) {
if (filename.empty()) {
return false;
diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc
index 983763a..1cf49d8 100644
--- a/src/test/cpp/util/file_windows_test.cc
+++ b/src/test/cpp/util/file_windows_test.cc
@@ -178,6 +178,25 @@
ASSERT_EQ(wlongpath, actual);
}
+TEST(FileTest, TestAsShortWindowsPath) {
+ string tmpdir(GetTestTmpDir());
+ ASSERT_LT(0, tmpdir.size());
+
+ string short_tmpdir;
+ ASSERT_TRUE(AsShortWindowsPath(tmpdir, &short_tmpdir));
+ ASSERT_LT(0, short_tmpdir.size());
+ ASSERT_TRUE(PathExists(short_tmpdir));
+
+ string dirname(JoinPath(short_tmpdir, "LONGpathNAME"));
+ ASSERT_EQ(0, mkdir(dirname.c_str()));
+ ASSERT_TRUE(PathExists(dirname));
+
+ string actual;
+ ASSERT_TRUE(AsShortWindowsPath(dirname, &actual));
+ ASSERT_EQ(short_tmpdir + "\\longpa~1", actual);
+ ASSERT_EQ(0, rmdir(dirname.c_str()));
+}
+
TEST(FileTest, TestMsysRootRetrieval) {
wstring actual;