Windows: Native launcher now works with unicode. The native launcher can now launch Java and Bash binary in directory with non-English characters. Unfortunately, python doesn't support running python zip file under directory with non-English characters. eg. python ./??/bin.zip will still fail. See https://github.com/bazelbuild/bazel/issues/4473 Change-Id: I77fe9cdaabffc2e0d25c7097da5c0c9333a9c4a3 PiperOrigin-RevId: 201939391
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h index d7538fd..76e94d1 100644 --- a/src/main/cpp/util/file_platform.h +++ b/src/main/cpp/util/file_platform.h
@@ -19,6 +19,7 @@ #include <cinttypes> #include <string> +#include <vector> namespace blaze_util { @@ -206,6 +207,38 @@ #if defined(_WIN32) || defined(__CYGWIN__) std::wstring GetCwdW(); +bool MakeDirectoriesW(const std::wstring &path, unsigned int mode); + +// Interface to be implemented by ForEachDirectoryEntryW clients. +class DirectoryEntryConsumerW { + public: + virtual ~DirectoryEntryConsumerW() {} + + // This method is called for each entry in a directory. + // `name` is the full path of the entry. + // `is_directory` is true if this entry is a directory (but false if this is a + // symlink pointing to a directory). + virtual void Consume(const std::wstring &name, bool is_directory) = 0; +}; + +// Lists all files in `path` and all of its subdirectories. +// +// Does not follow symlinks / junctions. +// +// Populates `result` with the full paths of the files. Every entry will have +// `path` as its prefix. If `path` is a file, `result` contains just this +// file. +void GetAllFilesUnderW(const std::wstring &path, + std::vector<std::wstring> *result); + +// Visible for testing only. +typedef void (*_ForEachDirectoryEntryW)(const std::wstring &path, + DirectoryEntryConsumerW *consume); + +// Visible for testing only. +void _GetAllFilesUnderW(const std::wstring &path, + std::vector<std::wstring> *result, + _ForEachDirectoryEntryW walk_entries); #endif // defined(_WIN32) || defined(__CYGWIN__) } // namespace blaze_util
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc index d81ada1..d266f96 100644 --- a/src/main/cpp/util/file_windows.cc +++ b/src/main/cpp/util/file_windows.cc
@@ -32,17 +32,16 @@ namespace blaze_util { -using std::basic_string; -using std::pair; -using std::string; -using std::unique_ptr; -using std::wstring; using bazel::windows::AutoHandle; using bazel::windows::GetLongPath; using bazel::windows::HasUncPrefix; using bazel::windows::OpenDirectory; - - +using std::basic_string; +using std::pair; +using std::string; +using std::unique_ptr; +using std::vector; +using std::wstring; // 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 @@ -55,8 +54,6 @@ // necessary. static bool UnlinkPathW(const wstring& path); -static bool MakeDirectoriesW(const wstring& path); - static bool CanReadFileW(const wstring& path); template <typename char_type> @@ -806,7 +803,7 @@ // fsync always fails on Cygwin with "Permission denied" for some reason. } -static bool MakeDirectoriesW(const wstring& path) { +bool MakeDirectoriesW(const wstring& path, unsigned int mode) { if (path.empty()) { return false; } @@ -821,7 +818,7 @@ << "MakeDirectoriesW(" << blaze_util::WstringToString(path) << ") could not find dirname: " << GetLastErrorString(); } - return MakeDirectoriesW(parent) && + return MakeDirectoriesW(parent, mode) && ::CreateDirectoryW(path.c_str(), NULL) == TRUE; } @@ -841,7 +838,7 @@ << "): AsAbsoluteWindowsPath failed: " << error; return false; } - return MakeDirectoriesW(wpath); + return MakeDirectoriesW(wpath, mode); } std::wstring GetCwdW() { @@ -872,6 +869,79 @@ return ::SetCurrentDirectoryA(spath.c_str()) == TRUE; } +class DirectoryTreeWalkerW : public DirectoryEntryConsumerW { + public: + DirectoryTreeWalkerW(vector<wstring>* files, + _ForEachDirectoryEntryW walk_entries) + : _files(files), _walk_entries(walk_entries) {} + + void Consume(const wstring& path, bool follow_directory) override { + if (follow_directory) { + Walk(path); + } else { + _files->push_back(path); + } + } + + void Walk(const wstring& path) { _walk_entries(path, this); } + + private: + vector<wstring>* _files; + _ForEachDirectoryEntryW _walk_entries; +}; + +void ForEachDirectoryEntryW(const wstring& path, + DirectoryEntryConsumerW* consume) { + wstring wpath; + if (path.empty() || IsDevNull(path.c_str())) { + return; + } + string error; + if (!AsWindowsPath(path, &wpath, &error)) { + BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) + << "ForEachDirectoryEntryW(" << path + << "): AsWindowsPath failed: " << GetLastErrorString(); + } + + static const wstring kUncPrefix(L"\\\\?\\"); + static const wstring kDot(L"."); + static const wstring kDotDot(L".."); + // Always add an UNC prefix to ensure we can work with long paths. + if (!HasUncPrefix(wpath.c_str())) { + wpath = kUncPrefix + wpath; + } + // Unconditionally add a trailing backslash. We know `wpath` has no trailing + // backslash because it comes from AsWindowsPath whose output is always + // normalized (see NormalizeWindowsPath). + wpath.append(L"\\"); + WIN32_FIND_DATAW metadata; + HANDLE handle = ::FindFirstFileW((wpath + L"*").c_str(), &metadata); + if (handle == INVALID_HANDLE_VALUE) { + return; // directory does not exist or is empty + } + + do { + if (kDot != metadata.cFileName && kDotDot != metadata.cFileName) { + wstring wname = wpath + metadata.cFileName; + wstring name(/* omit prefix */ 4 + wname.c_str()); + bool is_dir = (metadata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + bool is_junc = + (metadata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; + consume->Consume(name, is_dir && !is_junc); + } + } while (::FindNextFileW(handle, &metadata)); + ::FindClose(handle); +} + +void GetAllFilesUnderW(const wstring& path, vector<wstring>* result) { + _GetAllFilesUnderW(path, result, &ForEachDirectoryEntryW); +} + +void _GetAllFilesUnderW(const wstring& path, vector<wstring>* result, + _ForEachDirectoryEntryW walk_entries) { + DirectoryTreeWalkerW(result, walk_entries).Walk(path); +} + void ForEachDirectoryEntry(const string &path, DirectoryEntryConsumer *consume) { wstring wpath;
diff --git a/src/main/cpp/util/path_platform.h b/src/main/cpp/util/path_platform.h index e2efe09..b0fb88d 100644 --- a/src/main/cpp/util/path_platform.h +++ b/src/main/cpp/util/path_platform.h
@@ -70,7 +70,12 @@ // is that this would add more complexity to the implementation file(s)? of // path.h, which would have to have the platform-specific implementations. #if defined(_WIN32) || defined(__CYGWIN__) +bool IsDevNull(const wchar_t *path); + +bool IsAbsolute(const std::wstring &path); + const wchar_t *RemoveUncPrefixMaybe(const wchar_t *ptr); + void AddUncPrefixMaybe(std::wstring *path); std::pair<std::wstring, std::wstring> SplitPathW(const std::wstring &path);
diff --git a/src/main/cpp/util/path_windows.cc b/src/main/cpp/util/path_windows.cc index d115b6e..c7afe40 100644 --- a/src/main/cpp/util/path_windows.cc +++ b/src/main/cpp/util/path_windows.cc
@@ -396,6 +396,10 @@ return IsRootOrAbsolute(path, false); } +bool IsAbsolute(const std::wstring& path) { + return IsRootOrAbsolute(path, false); +} + bool IsRootDirectoryW(const std::wstring& path) { return IsRootOrAbsolute(path, true); }
diff --git a/src/main/cpp/util/strings.cc b/src/main/cpp/util/strings.cc index e98b5d2..649156a 100644 --- a/src/main/cpp/util/strings.cc +++ b/src/main/cpp/util/strings.cc
@@ -308,4 +308,8 @@ return UstringToVstring<char, wchar_t>(input, mbstowcs, "%s"); } +std::wstring CstringToWstring(const std::string &input) { + return wstring(CstringToWstring(input.c_str()).get()); +} + } // namespace blaze_util
diff --git a/src/main/cpp/util/strings.h b/src/main/cpp/util/strings.h index 0a761e1..cd54181 100644 --- a/src/main/cpp/util/strings.h +++ b/src/main/cpp/util/strings.h
@@ -115,6 +115,7 @@ // Convert a char string to a wchar_t string. Useful when passing arguments to // widechar Windows API functions. std::unique_ptr<wchar_t[]> CstringToWstring(const char *input); +std::wstring CstringToWstring(const std::string &input); } // namespace blaze_util