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) {