Bazel client, Windows: support /dev/null Support passing /dev/null and NUL as flag values for flags like --bazelrc. In particular, special-case these paths in methods like blaze_util::ReadFile, blaze_util::IsDirectory, etc. Fixes https://github.com/bazelbuild/bazel/issues/2354 RELNOTES[NEW]: Windows: "/dev/null" is now a supported path, e.g. --bazelrc=/dev/null now works -- PiperOrigin-RevId: 144195994 MOS_MIGRATED_REVID=144195994
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc index 22ab715..63d6d66 100644 --- a/src/main/cpp/util/file_windows.cc +++ b/src/main/cpp/util/file_windows.cc
@@ -38,6 +38,8 @@ // The result may have a UNC prefix. static unique_ptr<WCHAR[]> GetCwdW(); +static bool IsDevNull(const string& path); + // 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. @@ -287,6 +289,10 @@ result->clear(); return true; } + if (IsDevNull(path)) { + result->assign(L"NUL"); + return true; + } string mutable_path = path; if (path[0] == '/') { @@ -318,6 +324,11 @@ } static bool AsWindowsPathWithUncPrefix(const string& path, wstring* wpath) { + if (IsDevNull(path)) { + wpath->assign(L"NUL"); + return true; + } + if (!AsWindowsPath(path, wpath)) { PrintError("AsWindowsPathWithUncPrefix(%s): AsWindowsPath failed, err=%d\n", path.c_str(), GetLastError()); @@ -331,6 +342,11 @@ } bool AsShortWindowsPath(const string& path, string* result) { + if (IsDevNull(path)) { + result->assign("NUL"); + return true; + } + result->clear(); wstring wpath; if (!AsWindowsPathWithUncPrefix(path, &wpath)) { @@ -359,6 +375,10 @@ if (filename.empty()) { return false; } + if (IsDevNull(filename)) { + content->clear(); + return true; + } wstring wfilename; if (!AsWindowsPathWithUncPrefix(filename, &wfilename)) { return false; @@ -415,6 +435,10 @@ } bool UnlinkPath(const string& file_path) { + if (IsDevNull(file_path)) { + return false; + } + wstring wpath; if (!AsWindowsPathWithUncPrefix(file_path, &wpath)) { return false; @@ -562,6 +586,9 @@ if (path.empty()) { return false; } + if (IsDevNull(path)) { + return true; + } wstring wpath; if (!AsWindowsPathWithUncPrefix(path, &wpath)) { PrintError("PathExists(%s): AsWindowsPathWithUncPrefix failed, err=%d\n", @@ -591,6 +618,10 @@ } #endif // COMPILER_MSVC +static bool IsDevNull(const string& path) { + return path == "/dev/null" || AsLower(path) == "nul"; +} + static bool IsDirectoryW(const wstring& path) { DWORD attrs = ::GetFileAttributesW(path.c_str()); return (attrs != INVALID_FILE_ATTRIBUTES) && @@ -599,7 +630,7 @@ } bool IsDirectory(const string& path) { - if (path.empty()) { + if (path.empty() || IsDevNull(path)) { return false; } wstring wpath;
diff --git a/src/main/cpp/util/strings.cc b/src/main/cpp/util/strings.cc index 3fed561..383ffa1 100644 --- a/src/main/cpp/util/strings.cc +++ b/src/main/cpp/util/strings.cc
@@ -299,15 +299,20 @@ void ToLower(string *str) { assert(str); - if (str->empty()) { - return; - } + *str = AsLower(*str); +} - string temp = ""; - for (auto ch : *str) { - temp += tolower(ch); +string AsLower(const string &str) { + if (str.empty()) { + return ""; } - *str = temp; + unique_ptr<char[]> result(new char[str.size() + 1]); + char *result_ptr = result.get(); + for (const auto &ch : str) { + *result_ptr++ = tolower(ch); + } + result.get()[str.size()] = 0; + return string(result.get()); } template <typename U, typename V>
diff --git a/src/main/cpp/util/strings.h b/src/main/cpp/util/strings.h index 6650226..61187b7 100644 --- a/src/main/cpp/util/strings.h +++ b/src/main/cpp/util/strings.h
@@ -106,6 +106,8 @@ // Convert str to lower case. No locale handling, this is just for ASCII. void ToLower(std::string *str); +std::string AsLower(const std::string &str); + // Convert a wchar_t string to a char string. Useful when consuming results of // widechar Windows API functions. std::unique_ptr<char[]> WstringToCstring(const wchar_t *input);
diff --git a/src/test/cpp/util/file_test.cc b/src/test/cpp/util/file_test.cc index aa654cc..52d7f8d 100644 --- a/src/test/cpp/util/file_test.cc +++ b/src/test/cpp/util/file_test.cc
@@ -70,6 +70,9 @@ ASSERT_TRUE(ReadFile(filename, &actual, 5)); ASSERT_EQ(std::string("hello"), actual); + + ASSERT_TRUE(ReadFile("/dev/null", &actual, 42)); + ASSERT_EQ(std::string(""), actual); } } // namespace blaze_util
diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc index 9546055..49c69bc 100644 --- a/src/test/cpp/util/file_windows_test.cc +++ b/src/test/cpp/util/file_windows_test.cc
@@ -151,6 +151,12 @@ ASSERT_TRUE(AsWindowsPath("\\\\?\\c://../foo", &actual)); ASSERT_EQ(wstring(L"c:\\foo"), actual); + ASSERT_TRUE(AsWindowsPath("/dev/null", &actual)); + ASSERT_EQ(wstring(L"NUL"), actual); + + ASSERT_TRUE(AsWindowsPath("Nul", &actual)); + ASSERT_EQ(wstring(L"NUL"), actual); + ASSERT_TRUE(AsWindowsPath("/c", &actual)); ASSERT_EQ(wstring(L"c:\\"), actual); @@ -179,6 +185,13 @@ } TEST(FileTest, TestAsShortWindowsPath) { + string actual; + ASSERT_TRUE(AsShortWindowsPath("/dev/null", &actual)); + ASSERT_EQ(string("NUL"), actual); + + ASSERT_TRUE(AsShortWindowsPath("nul", &actual)); + ASSERT_EQ(string("NUL"), actual); + string tmpdir(GetTestTmpDir()); ASSERT_LT(0, tmpdir.size()); @@ -191,7 +204,6 @@ 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())); @@ -251,6 +263,8 @@ ASSERT_FALSE(PathExists("")); ASSERT_TRUE(PathExists(".")); ASSERT_FALSE(PathExists("non.existent")); + ASSERT_TRUE(PathExists("/dev/null")); + ASSERT_TRUE(PathExists("Nul")); string tmpdir(GetTestTmpDir()); ASSERT_LT(0, tmpdir.size()); @@ -292,6 +306,8 @@ TEST(FileTest, TestIsDirectory) { ASSERT_FALSE(IsDirectory("")); + ASSERT_FALSE(IsDirectory("/dev/null")); + ASSERT_FALSE(IsDirectory("Nul")); string tmpdir(GetTestTmpDir()); ASSERT_LT(0, tmpdir.size()); @@ -327,6 +343,9 @@ } TEST(FileTest, TestUnlinkPath) { + ASSERT_FALSE(UnlinkPath("/dev/null")); + ASSERT_FALSE(UnlinkPath("Nul")); + string tmpdir(GetTestTmpDir()); ASSERT_LT(0, tmpdir.size()); ASSERT_TRUE(PathExists(tmpdir));