From 4e578e6f7205a352630720ed482967b6edb4afca Mon Sep 17 00:00:00 2001 From: Yun Peng Date: Wed, 20 Jun 2018 07:56:11 -0700 Subject: Make blaze_util::AsAbsoluteWindowsPath support wstring as input Now we have: bool AsAbsoluteWindowsPath(const std::wstring& path, std::wstring* result, std::string* error); This change helps making the C++ native launcher work with UTF-16. See https://github.com/bazelbuild/bazel/issues/4473 Closes #5406. Change-Id: I7eaf55f9fe5a4d41e3dd09edc2a21e9b3cc9277c PiperOrigin-RevId: 201352866 --- src/main/cpp/util/BUILD | 5 +++- src/main/cpp/util/logging.cc | 7 +++++ src/main/cpp/util/logging.h | 1 + src/main/cpp/util/path_platform.h | 40 ++++++++++++++++++++++---- src/main/cpp/util/path_windows.cc | 51 +++++++++++++++++++++++----------- src/test/cpp/util/path_windows_test.cc | 26 +++++++++++++++-- 6 files changed, 104 insertions(+), 26 deletions(-) diff --git a/src/main/cpp/util/BUILD b/src/main/cpp/util/BUILD index 6f53b4d590..eed2a60a12 100644 --- a/src/main/cpp/util/BUILD +++ b/src/main/cpp/util/BUILD @@ -107,7 +107,10 @@ cc_library( srcs = ["logging.cc"], hdrs = ["logging.h"], visibility = ["//visibility:public"], - deps = [":blaze_exit_code"], + deps = [ + ":blaze_exit_code", + ":strings", + ], ) cc_library( diff --git a/src/main/cpp/util/logging.cc b/src/main/cpp/util/logging.cc index 40a54aed17..c2e7565bd8 100644 --- a/src/main/cpp/util/logging.cc +++ b/src/main/cpp/util/logging.cc @@ -21,6 +21,7 @@ #include #include "src/main/cpp/util/exit_code.h" +#include "src/main/cpp/util/strings.h" namespace blaze_util { @@ -68,6 +69,12 @@ DECLARE_STREAM_OPERATOR(float) DECLARE_STREAM_OPERATOR(double) DECLARE_STREAM_OPERATOR(long double) DECLARE_STREAM_OPERATOR(void*) + +LogMessage& LogMessage::operator<<(const std::wstring& wstr) { + message_ << WstringToString(wstr); + return *this; +} + #undef DECLARE_STREAM_OPERATOR void LogMessage::Finish() { diff --git a/src/main/cpp/util/logging.h b/src/main/cpp/util/logging.h index b2cd459d06..7346c18176 100644 --- a/src/main/cpp/util/logging.h +++ b/src/main/cpp/util/logging.h @@ -53,6 +53,7 @@ class LogMessage { int exit_code); LogMessage& operator<<(const std::string& value); + LogMessage& operator<<(const std::wstring& wstr); LogMessage& operator<<(const char* value); LogMessage& operator<<(char value); LogMessage& operator<<(bool value); diff --git a/src/main/cpp/util/path_platform.h b/src/main/cpp/util/path_platform.h index 3c4671f004..e2efe097fe 100644 --- a/src/main/cpp/util/path_platform.h +++ b/src/main/cpp/util/path_platform.h @@ -77,9 +77,6 @@ std::pair SplitPathW(const std::wstring &path); bool IsRootDirectoryW(const std::wstring &path); -bool AsWindowsPath(const std::string &path, std::string *result, - std::string *error); - // Returns a normalized form of the input `path`. // // `path` must be a relative or absolute Windows path, it may use "/" instead of @@ -95,7 +92,14 @@ bool AsWindowsPath(const std::string &path, std::string *result, // "foo". // // Visible for testing, would be static otherwise. -std::string NormalizeWindowsPath(std::string path); +template +std::basic_string NormalizeWindowsPath( + std::basic_string path); + +template +std::basic_string NormalizeWindowsPath(const char_type *path) { + return NormalizeWindowsPath(std::basic_string(path)); +} // Converts a UTF8-encoded `path` to a normalized, widechar Windows path. // @@ -116,8 +120,32 @@ std::string NormalizeWindowsPath(std::string path); bool AsWindowsPath(const std::string &path, std::wstring *result, std::string *error); -bool AsAbsoluteWindowsPath(const std::string &path, std::wstring *wpath, - std::string *error); +template +bool AsWindowsPath(const std::basic_string &path, + std::basic_string *result, std::string *error); + +template +bool AsWindowsPath(const char_type *path, std::basic_string *result, + std::string *error) { + return AsWindowsPath(std::basic_string(path), result, error); +} + +template +bool AsAbsoluteWindowsPath(const std::basic_string &path, + std::wstring *result, std::string *error); + +template +bool AsAbsoluteWindowsPath(const char_type *path, std::wstring *result, + std::string *error) { + return AsAbsoluteWindowsPath(std::basic_string(path), result, + error); +} + +// Explicit instantiate AsAbsoluteWindowsPath for char and wchar_t. +template bool AsAbsoluteWindowsPath(const char *, std::wstring *, + std::string *); +template bool AsAbsoluteWindowsPath(const wchar_t *, std::wstring *, + std::string *); // Same as `AsWindowsPath`, but returns a lowercase 8dot3 style shortened path. // Result will never have a UNC prefix, nor a trailing "/" or "\". diff --git a/src/main/cpp/util/path_windows.cc b/src/main/cpp/util/path_windows.cc index 15dd0ddce6..d115b6e6d9 100644 --- a/src/main/cpp/util/path_windows.cc +++ b/src/main/cpp/util/path_windows.cc @@ -215,14 +215,19 @@ std::pair SplitPathW(const std::wstring& path) { return SplitPathImpl(path); } -bool AsWindowsPath(const std::string& path, std::string* result, - std::string* error) { +void assignNUL(std::string* s) { s->assign("NUL"); } + +void assignNUL(std::wstring* s) { s->assign(L"NUL"); } + +template +bool AsWindowsPath(const std::basic_string& path, + std::basic_string* result, std::string* error) { if (path.empty()) { result->clear(); return true; } if (IsDevNull(path.c_str())) { - result->assign("NUL"); + assignNUL(result); return true; } if (HasUncPrefix(path.c_str())) { @@ -247,7 +252,7 @@ bool AsWindowsPath(const std::string& path, std::string* result, return false; } - std::string mutable_path = path; + std::basic_string mutable_path = path; if (path[0] == '/') { if (error) { *error = "Unix-style paths are unsupported"; @@ -257,9 +262,10 @@ bool AsWindowsPath(const std::string& path, std::string* result, if (path[0] == '\\') { // This is an absolute Windows path on the current drive, e.g. "\foo\bar". - mutable_path = std::string(1, GetCurrentDrive()) + ":" + path; + std::basic_string drive(1, GetCurrentDrive()); + drive.push_back(':'); + mutable_path = drive + path; } // otherwise this is a relative path, or absolute Windows path. - result->assign(NormalizeWindowsPath(mutable_path)); return true; } @@ -275,8 +281,9 @@ bool AsWindowsPath(const std::string& path, std::wstring* result, return true; } -bool AsAbsoluteWindowsPath(const std::string& path, std::wstring* result, - std::string* error) { +template +bool AsAbsoluteWindowsPath(const std::basic_string& path, + std::wstring* result, std::string* error) { if (path.empty()) { result->clear(); return true; @@ -373,6 +380,14 @@ bool IsDevNull(const char* path) { (path[2] == 'L' || path[2] == 'l') && path[3] == 0)); } +bool IsDevNull(const wchar_t* path) { + return path != NULL && *path != 0 && + (wcsncmp(L"/dev/null\0", path, 10) == 0 || + ((path[0] == L'N' || path[0] == L'n') && + (path[1] == L'U' || path[1] == L'u') && + (path[2] == L'L' || path[2] == L'l') && path[3] == 0)); +} + bool IsRootDirectory(const std::string& path) { return IsRootOrAbsolute(path, true); } @@ -392,9 +407,11 @@ static char GetCurrentDrive() { return 'a' + wdrive - offset; } -std::string NormalizeWindowsPath(std::string path) { +template +std::basic_string NormalizeWindowsPath( + std::basic_string path) { if (path.empty()) { - return ""; + return std::basic_string(); } if (path[0] == '/') { // This is an absolute MSYS path, error out. @@ -405,10 +422,10 @@ std::string NormalizeWindowsPath(std::string path) { path = path.substr(4); } - static const std::string dot("."); - static const std::string dotdot(".."); + static const std::basic_string dot(1, '.'); + static const std::basic_string dotdot(2, '.'); - std::vector segments; + std::vector> segments; int segment_start = -1; // Find the path segments in `path` (separated by "/"). for (int i = 0;; ++i) { @@ -421,7 +438,8 @@ std::string NormalizeWindowsPath(std::string path) { } else if (segment_start >= 0 && i > segment_start) { // The current character is "/" or "\0", so this ends a segment. // Add that to `segments` if there's anything to add; handle "." and "..". - std::string segment(path, segment_start, i - segment_start); + std::basic_string segment(path, segment_start, + i - segment_start); segment_start = -1; if (segment == dotdot) { if (!segments.empty() && @@ -441,12 +459,13 @@ std::string NormalizeWindowsPath(std::string path) { // form of it, e.g. "c:\.."). if (segments.size() == 1 && segments[0].size() == 2 && HasDriveSpecifierPrefix(segments[0].c_str())) { - return segments[0] + '\\'; + segments[0].push_back('\\'); + return segments[0]; } // Join all segments. bool first = true; - std::ostringstream result; + std::basic_ostringstream result; for (const auto& s : segments) { if (!first) { result << '\\'; diff --git a/src/test/cpp/util/path_windows_test.cc b/src/test/cpp/util/path_windows_test.cc index c78c4ab012..bcf62622d5 100644 --- a/src/test/cpp/util/path_windows_test.cc +++ b/src/test/cpp/util/path_windows_test.cc @@ -41,9 +41,6 @@ using std::string; using std::unique_ptr; using std::wstring; -// Methods defined in path_windows.cc that are only visible for testing. -string NormalizeWindowsPath(string path); - TEST(PathWindowsTest, TestNormalizeWindowsPath) { ASSERT_EQ(string(""), NormalizeWindowsPath("")); ASSERT_EQ(string(""), NormalizeWindowsPath(".")); @@ -56,6 +53,19 @@ TEST(PathWindowsTest, TestNormalizeWindowsPath) { ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:/")); ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:\\")); ASSERT_EQ(string("c:\\foo\\bar"), NormalizeWindowsPath("c:\\..//foo/./bar/")); + + ASSERT_EQ(wstring(L""), NormalizeWindowsPath(L"")); + ASSERT_EQ(wstring(L""), NormalizeWindowsPath(L".")); + ASSERT_EQ(wstring(L"foo"), NormalizeWindowsPath(L"foo")); + ASSERT_EQ(wstring(L"foo"), NormalizeWindowsPath(L"foo/")); + ASSERT_EQ(wstring(L"foo\\bar"), NormalizeWindowsPath(L"foo//bar")); + ASSERT_EQ(wstring(L"foo\\bar"), NormalizeWindowsPath(L"../..//foo/./bar")); + ASSERT_EQ(wstring(L"foo\\bar"), NormalizeWindowsPath(L"../foo/baz/../bar")); + ASSERT_EQ(wstring(L"c:\\"), NormalizeWindowsPath(L"c:")); + ASSERT_EQ(wstring(L"c:\\"), NormalizeWindowsPath(L"c:/")); + ASSERT_EQ(wstring(L"c:\\"), NormalizeWindowsPath(L"c:\\")); + ASSERT_EQ(wstring(L"c:\\foo\\bar"), + NormalizeWindowsPath(L"c:\\..//foo/./bar/")); } TEST(PathWindowsTest, TestDirname) { @@ -201,9 +211,16 @@ TEST(PathWindowsTest, TestAsAbsoluteWindowsPath) { ASSERT_TRUE(AsAbsoluteWindowsPath("c:/", &actual, nullptr)); ASSERT_EQ(L"\\\\?\\c:\\", actual); + ASSERT_TRUE(AsAbsoluteWindowsPath(L"c:/", &actual, nullptr)); + ASSERT_EQ(L"\\\\?\\c:\\", actual); + ASSERT_TRUE(AsAbsoluteWindowsPath("c:/..\\non-existent//", &actual, nullptr)); ASSERT_EQ(L"\\\\?\\c:\\non-existent", actual); + ASSERT_TRUE( + AsAbsoluteWindowsPath(L"c:/..\\non-existent//", &actual, nullptr)); + ASSERT_EQ(L"\\\\?\\c:\\non-existent", actual); + WCHAR cwd[MAX_PATH]; wstring cwdw(CstringToWstring(GetCwd().c_str()).get()); wstring expected = @@ -211,6 +228,9 @@ TEST(PathWindowsTest, TestAsAbsoluteWindowsPath) { ((cwdw.back() == L'\\') ? L"non-existent" : L"\\non-existent"); ASSERT_TRUE(AsAbsoluteWindowsPath("non-existent", &actual, nullptr)); ASSERT_EQ(actual, expected); + + ASSERT_TRUE(AsAbsoluteWindowsPath(L"non-existent", &actual, nullptr)); + ASSERT_EQ(actual, expected); } TEST(PathWindowsTest, TestAsShortWindowsPath) { -- cgit v1.2.3