Bazel client, Windows: case-insensitive MSYS root

Make the MSYS root retrieval case-insensitive.
While there, make the MSYS root resetable so we
can test it.

See https://github.com/bazelbuild/bazel/issues/2323

--
PiperOrigin-RevId: 143377137
MOS_MIGRATED_REVID=143377137
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 608ed99..1eef9fc 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -139,15 +139,22 @@
 
 class MsysRoot {
  public:
-  MsysRoot() : data_(Get()) {}
-  bool IsValid() const { return data_.first; }
-  const string& GetPath() const { return data_.second; }
+  static bool IsValid() { return instance_.data_.first; }
+  static const string& GetPath() { return instance_.data_.second; }
+  static void ReInitForTesting() { instance_.data_ = Get(); }
 
  private:
-  const std::pair<bool, string> data_;
+  std::pair<bool, string> data_;
+  static MsysRoot instance_;
+
   static std::pair<bool, string> Get();
+  MsysRoot() : data_(Get()) {}
 };
 
+MsysRoot MsysRoot::instance_;
+
+void ReinitMsysRootForTesting() { MsysRoot::ReInitForTesting(); }
+
 std::pair<bool, string> MsysRoot::Get() {
   string result;
   char value[MAX_PATH];
@@ -165,6 +172,8 @@
     result = value2;
   }
 
+  ToLower(&result);
+
   // BAZEL_SH is usually "c:\tools\msys64\usr\bin\bash.exe", we need to return
   // "c:\tools\msys64". Look for the rightmost msys-looking component.
   while (!IsRootDirectory(result) &&
@@ -200,15 +209,10 @@
     } else {
       // The path is a normal MSYS path e.g. "/usr". Prefix it with the MSYS
       // root.
-      // Define kMsysRoot only in this scope. This way we only initialize it
-      // and thus check for BAZEL_SH if we really need to, i.e. the caller
-      // passed an MSYS path and we have to convert it. If all paths ever passed
-      // are Windows paths, we don't need to check whether BAZEL_SH is defined.
-      static const MsysRoot kMsysRoot;
-      if (!kMsysRoot.IsValid()) {
+      if (!MsysRoot::IsValid()) {
         return false;
       }
-      mutable_path = JoinPath(kMsysRoot.GetPath(), path);
+      mutable_path = JoinPath(MsysRoot::GetPath(), path);
     }
   }  // otherwise this is a relative path, or absolute Windows path.
 
diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc
index 231e21d..fe23a92 100644
--- a/src/test/cpp/util/file_windows_test.cc
+++ b/src/test/cpp/util/file_windows_test.cc
@@ -24,6 +24,8 @@
 
 namespace blaze_util {
 
+void ReinitMsysRootForTesting();  // defined in file_windows.cc
+
 TEST(FileTest, TestDirname) {
   ASSERT_EQ("", Dirname(""));
   ASSERT_EQ("/", Dirname("/"));
@@ -96,6 +98,7 @@
 
 TEST(FileTest, TestAsWindowsPath) {
   SetEnvironmentVariableA("BAZEL_SH", "c:\\msys\\some\\long\\path\\bash.exe");
+  ReinitMsysRootForTesting();
   std::wstring actual;
 
   ASSERT_TRUE(AsWindowsPath("", &actual));
@@ -134,4 +137,25 @@
   ASSERT_EQ(wlongpath, actual);
 }
 
+TEST(FileTest, TestMsysRootRetrieval) {
+  std::wstring actual;
+
+  SetEnvironmentVariableA("BAZEL_SH", "c:/foo/msys/bar/qux.exe");
+  ReinitMsysRootForTesting();
+  ASSERT_TRUE(AsWindowsPath("/blah", &actual));
+  ASSERT_EQ(std::wstring(L"c:\\foo\\msys\\blah"), actual);
+
+  SetEnvironmentVariableA("BAZEL_SH", "c:/foo/MSYS64/bar/qux.exe");
+  ReinitMsysRootForTesting();
+  ASSERT_TRUE(AsWindowsPath("/blah", &actual));
+  ASSERT_EQ(std::wstring(L"c:\\foo\\msys64\\blah"), actual);
+
+  SetEnvironmentVariableA("BAZEL_SH", "c:/qux.exe");
+  ReinitMsysRootForTesting();
+  ASSERT_FALSE(AsWindowsPath("/blah", &actual));
+
+  SetEnvironmentVariableA("BAZEL_SH", nullptr);
+  ReinitMsysRootForTesting();
+}
+
 }  // namespace blaze_util