Bazel client, Windows: fix AsShortWindowsPath
This method now works for non-existent paths too.
See https://github.com/bazelbuild/bazel/issues/2107
--
PiperOrigin-RevId: 149284633
MOS_MIGRATED_REVID=149284633
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index 02f5b87..7733e7a 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -208,7 +208,9 @@
bool AsWindowsPathWithUncPrefix(const std::string &path, std::wstring *wpath);
// Same as `AsWindowsPath`, but returns a lowercase 8dot3 style shortened path.
-// Result will never have a UNC prefix.
+// Result will never have a UNC prefix, nor a trailing "/" or "\".
+// Works also for non-existent paths; shortens as much of them as it can.
+// Also works for non-existent drives.
bool AsShortWindowsPath(const std::string &path, std::string *result);
#endif // defined(COMPILER_MSVC) || defined(__CYGWIN__)
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 23dc219..35d8242 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -107,6 +107,10 @@
}
}
+static const wchar_t* RemoveUncPrefixMaybe(const wchar_t* ptr) {
+ return ptr + (windows_util::HasUncPrefix(ptr) ? 4 : 0);
+}
+
class WindowsPipe : public IPipe {
public:
WindowsPipe(const HANDLE& read_handle, const HANDLE& write_handle)
@@ -505,24 +509,57 @@
result->clear();
wstring wpath;
+ wstring wsuffix;
if (!AsWindowsPathWithUncPrefix(path, &wpath)) {
return false;
}
DWORD size = ::GetShortPathNameW(wpath.c_str(), nullptr, 0);
if (size == 0) {
- return false;
+ // GetShortPathNameW can fail if `wpath` does not exist. This is expected
+ // when we are about to create a file at that path, so instead of failing,
+ // walk up in the path until we find a prefix that exists and can be
+ // shortened, or is a root directory. Save the non-existent tail in
+ // `wsuffix`, we'll add it back later.
+ std::vector<wstring> segments;
+ while (size == 0 && !IsRootDirectoryW(wpath)) {
+ pair<wstring, wstring> split = SplitPathW(wpath);
+ wpath = split.first;
+ segments.push_back(split.second);
+ size = ::GetShortPathNameW(wpath.c_str(), nullptr, 0);
+ }
+
+ // Join all segments.
+ std::wostringstream builder;
+ bool first = true;
+ for (std::vector<wstring>::const_reverse_iterator& it = segments.crbegin();
+ it != segments.crend(); ++it) {
+ if (!first || !IsRootDirectoryW(wpath)) {
+ builder << L'\\' << *it;
+ } else {
+ builder << *it;
+ }
+ first = false;
+ }
+ wsuffix = builder.str();
}
- 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());
+ wstring wresult;
+ if (IsRootDirectoryW(wpath)) {
+ // Strip the UNC prefix from `wpath`, and the leading "\" from `wsuffix`.
+ wresult = wstring(RemoveUncPrefixMaybe(wpath.c_str())) + wsuffix;
+ } else {
+ 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.
+ wresult = wstring(RemoveUncPrefixMaybe(wshort.get())) + wsuffix;
}
- // 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());
+ result->assign(WstringToCstring(wresult.c_str()).get());
ToLower(result);
return true;
}
@@ -911,9 +948,7 @@
size_t size = wcslen(long_realpath.get()) -
(windows_util::HasUncPrefix(long_realpath.get()) ? 4 : 0);
unique_ptr<WCHAR[]> lcase_realpath(new WCHAR[size + 1]);
- const WCHAR* p_from =
- long_realpath.get() +
- (windows_util::HasUncPrefix(long_realpath.get()) ? 4 : 0);
+ const WCHAR* p_from = RemoveUncPrefixMaybe(long_realpath.get());
WCHAR* p_to = lcase_realpath.get();
while (size-- > 0) {
*p_to++ = towlower(*p_from++);
@@ -1093,9 +1128,7 @@
}
string GetCwd() {
- unique_ptr<WCHAR[]> cwd(GetCwdW());
- return string(
- WstringToCstring(cwd.get() + (HasUncPrefix(cwd.get()) ? 4 : 0)).get());
+ return string(WstringToCstring(RemoveUncPrefixMaybe(GetCwdW().get())).get());
}
bool ChangeDirectory(const string& path) {