diff options
-rw-r--r-- | src/main/cpp/util/file_posix.cc | 2 | ||||
-rw-r--r-- | src/main/cpp/util/file_windows.cc | 26 | ||||
-rw-r--r-- | src/test/cpp/util/file_windows_test.cc | 36 |
3 files changed, 57 insertions, 7 deletions
diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc index f4890a0294..466bae499d 100644 --- a/src/main/cpp/util/file_posix.cc +++ b/src/main/cpp/util/file_posix.cc @@ -248,12 +248,12 @@ bool CanAccess(const string& path, bool read, bool write, bool exec) { return access(path.c_str(), mode) == 0; } +#ifndef __CYGWIN__ bool IsDirectory(const string& path) { struct stat buf; return stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode); } -#ifndef __CYGWIN__ bool IsRootDirectory(const string &path) { return path.size() == 1 && path[0] == '/'; } diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc index 2b4a105f05..3a5b5ac8e4 100644 --- a/src/main/cpp/util/file_windows.cc +++ b/src/main/cpp/util/file_windows.cc @@ -38,6 +38,11 @@ using std::wstring; // The result may have a UNC prefix. static unique_ptr<WCHAR[]> GetCwdW(); +// Returns true if `path` refers to a directory or (non-dangling) junction. +// `path` must be a normalized Windows path, with UNC prefix (and absolute) if +// necessary. +static bool IsDirectoryW(const wstring& path); + // Like `AsWindowsPath` but the result is absolute and has UNC prefix if needed. static bool AsWindowsPathWithUncPrefix(const string& path, wstring* wpath); @@ -551,14 +556,23 @@ bool CanAccess(const string& path, bool read, bool write, bool exec) { #else // not COMPILER_MSVC #endif // COMPILER_MSVC -#ifdef COMPILER_MSVC +static bool IsDirectoryW(const wstring& path) { + DWORD attrs = ::GetFileAttributesW(path.c_str()); + return (attrs != INVALID_FILE_ATTRIBUTES) && + (attrs & FILE_ATTRIBUTE_DIRECTORY) && + JunctionResolver().Resolve(path.c_str(), nullptr); +} + bool IsDirectory(const string& path) { - // TODO(bazel-team): implement this. - pdie(255, "blaze_util::IsDirectory is not implemented on Windows"); - return false; + if (path.empty()) { + return false; + } + wstring wpath; + if (!AsWindowsPathWithUncPrefix(path, &wpath)) { + return false; + } + return IsDirectoryW(wpath); } -#else // not COMPILER_MSVC -#endif // COMPILER_MSVC bool IsRootDirectory(const string& path) { return IsRootOrAbsolute(path, true); diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc index 1cf49d88c5..5c2f68ee27 100644 --- a/src/test/cpp/util/file_windows_test.cc +++ b/src/test/cpp/util/file_windows_test.cc @@ -290,4 +290,40 @@ TEST(FileTest, TestPathExistsWindows) { ASSERT_FALSE(PathExists(JoinPath(tmpdir, "junc2"))); } +TEST(FileTest, TestIsDirectory) { + ASSERT_FALSE(IsDirectory("")); + + string tmpdir(GetTestTmpDir()); + ASSERT_LT(0, tmpdir.size()); + ASSERT_TRUE(IsDirectory(tmpdir)); + ASSERT_TRUE(IsDirectory("C:\\")); + ASSERT_TRUE(IsDirectory("C:/")); + ASSERT_TRUE(IsDirectory("/c")); + + ASSERT_FALSE(IsDirectory("non.existent")); + // Create a directory under `tempdir`, verify that IsDirectory reports true. + // Call it msys_dir1 so we can also use it as a mock msys root. + string dir1(JoinPath(tmpdir, "msys_dir1")); + ASSERT_EQ(0, mkdir(dir1.c_str())); + ASSERT_TRUE(IsDirectory(dir1)); + + // Use dir1 as the mock msys root, verify that IsDirectory works for a MSYS + // path. + SetEnvironmentVariableA("BAZEL_SH", JoinPath(dir1, "bash.exe").c_str()); + ResetMsysRootForTesting(); + ASSERT_TRUE(IsDirectory("/")); + + // Verify that IsDirectory works for a junction. + string junc1(JoinPath(tmpdir, "junc1")); + RunCommand(string("cmd.exe /C mklink /J \"") + junc1 + "\" \"" + dir1 + + "\" >NUL 2>NUL"); + ASSERT_TRUE(IsDirectory(junc1)); + + ASSERT_EQ(0, rmdir(dir1.c_str())); + ASSERT_FALSE(IsDirectory(dir1)); + ASSERT_FALSE(IsDirectory(junc1)); + + ASSERT_EQ(0, rmdir(junc1.c_str())); +} + } // namespace blaze_util |