diff options
author | 2016-12-20 09:38:53 +0000 | |
---|---|---|
committer | 2016-12-20 09:41:46 +0000 | |
commit | d6297fb8b924dda41111356c0c32d4a5a297a11b (patch) | |
tree | 717f4d9ba8e430f7289e3e0dd8fed350f48879c5 /src | |
parent | 332b61f416855eb5938a226eed7787473487a268 (diff) |
Bazel client, Windows: implement AsWindowsPath
This method converts MSYS paths to Windows path.
It uses the BAZEL_SH envvar to obtain the MSYS
root directory, to which all Unix paths (except
for mounts) are relative.
We cannot handle mounts because we don't want to
read /etc/mtab every time there's a file operation
so we simply apply a heuristic similar to
https://github.com/bazelbuild/bazel/blob/cd4cc09fa6ef96380a3d0888f825dfd1dbada651/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java#L52-L63
Also clean up the #ifdefs surrounding SyncFile.
See https://github.com/bazelbuild/bazel/issues/2107
--
PiperOrigin-RevId: 142531986
MOS_MIGRATED_REVID=142531986
Diffstat (limited to 'src')
-rw-r--r-- | src/main/cpp/util/errors.cc | 9 | ||||
-rw-r--r-- | src/main/cpp/util/errors.h | 1 | ||||
-rw-r--r-- | src/main/cpp/util/file_platform.h | 20 | ||||
-rw-r--r-- | src/main/cpp/util/file_posix.cc | 5 | ||||
-rw-r--r-- | src/main/cpp/util/file_windows.cc | 89 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java | 6 | ||||
-rw-r--r-- | src/test/cpp/util/file_windows_test.cc | 41 |
7 files changed, 163 insertions, 8 deletions
diff --git a/src/main/cpp/util/errors.cc b/src/main/cpp/util/errors.cc index 49248a15fc..8497545e0b 100644 --- a/src/main/cpp/util/errors.cc +++ b/src/main/cpp/util/errors.cc @@ -41,4 +41,13 @@ void pdie(const int exit_status, const char *format, ...) { exit(exit_status); } +void PrintError(const char *format, ...) { + fprintf(stderr, "Error: "); + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, ": %s\n", strerror(errno)); +} + } // namespace blaze_util diff --git a/src/main/cpp/util/errors.h b/src/main/cpp/util/errors.h index 84de39d715..833a6ee473 100644 --- a/src/main/cpp/util/errors.h +++ b/src/main/cpp/util/errors.h @@ -28,6 +28,7 @@ void die(const int exit_status, const char *format, ...) ATTRIBUTE_NORETURN // Prints "Error: <formatted-message>: <strerror(errno)>\n", and exits nonzero. void pdie(const int exit_status, const char *format, ...) ATTRIBUTE_NORETURN PRINTF_ATTRIBUTE(2, 3); +void PrintError(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); } // namespace blaze_util diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h index e6df884ea0..15bfd5d737 100644 --- a/src/main/cpp/util/file_platform.h +++ b/src/main/cpp/util/file_platform.h @@ -120,6 +120,26 @@ class DirectoryEntryConsumer { void ForEachDirectoryEntry(const std::string &path, DirectoryEntryConsumer *consume); +#if defined(COMPILER_MSVC) || defined(__CYGWIN__) +// Converts a UTF8-encoded `path` to a widechar Windows path. +// +// Returns true if conversion succeeded and sets the contents of `result` to it. +// +// The `path` may be absolute or relative, and may be a Windows or MSYS path. +// In every case, this method replaces forward slashes with backslashes if +// necessary. +// +// Recognizes the drive letter in MSYS paths, so e.g. "/c/windows" becomes +// "c:\windows". Prepends the MSYS root (computed from the BAZEL_SH envvar) to +// absolute MSYS paths, so e.g. "/usr" becomes "c:\tools\msys64\usr". +// +// The result may be longer than MAX_PATH. It's the caller's responsibility to +// prepend the long path prefix ("\\?\") in case they need to pass it to a +// Windows API function (some require the prefix, some don't), or to quote the +// path if necessary. +bool AsWindowsPath(const std::string &path, std::wstring *result); +#endif // defined(COMPILER_MSVC) || defined(__CYGWIN__) + } // namespace blaze_util #endif // BAZEL_SRC_MAIN_CPP_UTIL_FILE_PLATFORM_H_ diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc index b256a68eef..81e9bd65c3 100644 --- a/src/main/cpp/util/file_posix.cc +++ b/src/main/cpp/util/file_posix.cc @@ -278,11 +278,8 @@ bool IsRootDirectory(const string &path) { } bool IsAbsolute(const string &path) { return !path.empty() && path[0] == '/'; } -#endif // not __CYGWIN__ void SyncFile(const string& path) { -// fsync always fails on Cygwin with "Permission denied" for some reason. -#ifndef __CYGWIN__ const char* file_path = path.c_str(); int fd = open(file_path, O_RDONLY); if (fd < 0) { @@ -294,8 +291,8 @@ void SyncFile(const string& path) { file_path); } close(fd); -#endif // not __CYGWIN__ } +#endif // not __CYGWIN__ time_t GetMtimeMillisec(const string& path) { struct stat buf; diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc index bf6e79cf1e..021e96d9a9 100644 --- a/src/main/cpp/util/file_windows.cc +++ b/src/main/cpp/util/file_windows.cc @@ -16,14 +16,19 @@ #include <ctype.h> // isalpha #include <windows.h> +#include <memory> // unique_ptr + #include "src/main/cpp/util/errors.h" #include "src/main/cpp/util/exit_code.h" #include "src/main/cpp/util/file.h" +#include "src/main/cpp/util/strings.h" namespace blaze_util { using std::pair; using std::string; +using std::unique_ptr; +using std::wstring; class WindowsPipe : public IPipe { public: @@ -132,6 +137,86 @@ pair<string, string> SplitPath(const string& path) { return std::make_pair("", path); } +class MsysRoot { + public: + MsysRoot() : data_(Get()) {} + bool IsValid() const { return data_.first; } + const string& GetPath() const { return data_.second; } + + private: + const std::pair<bool, string> data_; + static std::pair<bool, string> Get(); +}; + +std::pair<bool, string> MsysRoot::Get() { + string result; + char value[MAX_PATH]; + DWORD len = GetEnvironmentVariableA("BAZEL_SH", value, MAX_PATH); + if (len > 0) { + result = value; + } else { + const char* value2 = getenv("BAZEL_SH"); + if (value2 == nullptr || value2[0] == '\0') { + PrintError( + "BAZEL_SH environment variable is not defined, cannot convert MSYS " + "paths to Windows paths"); + return std::make_pair(false, ""); + } + result = value2; + } + // BAZEL_SH is usually "c:\tools\msys64\bin\bash.exe", we need to return + // "c:\tools\msys64". + return std::make_pair(true, std::move(Dirname(Dirname(result)))); +} + +bool AsWindowsPath(const string& path, wstring* result) { + if (path.empty()) { + result->clear(); + return true; + } + + string mutable_path = path; + if (path[0] == '/') { + // This is an absolute MSYS path. + if (path.size() == 2 || (path.size() > 2 && path[2] == '/')) { + // The path is either "/x" or "/x/" or "/x/something". In all three cases + // "x" is the drive letter. + // TODO(laszlocsomor): use GetLogicalDrives to retrieve the list of drives + // and only apply this heuristic for the valid drives. It's possible that + // the user has a directory "/a" but no "A:\" drive, so in that case we + // should prepend the MSYS root. + mutable_path = path.substr(1, 1) + ":\\"; + if (path.size() > 2) { + mutable_path += path.substr(3); + } + } 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()) { + return false; + } + mutable_path = JoinPath(kMsysRoot.GetPath(), path); + } + } // otherwise this is a relative path, or absolute Windows path. + + unique_ptr<WCHAR[]> mutable_wpath(CstringToWstring(mutable_path.c_str())); + WCHAR* p = mutable_wpath.get(); + // Replace forward slashes with backslashes. + while (*p != L'\0') { + if (*p == L'/') { + *p = L'\\'; + } + ++p; + } + result->assign(mutable_wpath.get()); + return true; +} + #ifdef COMPILER_MSVC bool ReadFile(const string& filename, string* content, int max_size) { // TODO(bazel-team): implement this. @@ -209,12 +294,10 @@ bool IsRootDirectory(const string& path) { bool IsAbsolute(const string& path) { return IsRootOrAbsolute(path, false); } -#ifdef COMPILER_MSVC void SyncFile(const string& path) { // No-op on Windows native; unsupported by Cygwin. + // fsync always fails on Cygwin with "Permission denied" for some reason. } -#else // not COMPILER_MSVC -#endif // COMPILER_MSVC #ifdef COMPILER_MSVC time_t GetMtimeMillisec(const string& path) { diff --git a/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java index 6ac62c0ea0..9b8ba02ca9 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java @@ -62,7 +62,11 @@ public class WindowsFileSystem extends JavaIoFileSystem { // // This heuristic ignores other mount points as well as procfs. - // TODO(bazel-team): get rid of this heuristic and translate paths using /etc/mtab. + // TODO(laszlocsomor): use GetLogicalDrives to retrieve the list of drives and only apply + // this heuristic for the valid drives. It's possible that the user has a directory "/a" + // but no "A:\" drive, so in that case we should prepend the MSYS root. + + // TODO(laszlocsomor): get rid of this heuristic and translate paths using /etc/mtab. // Figure out how to handle non-top-level mount points (e.g. "/usr/bin" is mounted to // "/bin"), which is problematic because Paths are created segment by segment, so // individual Path objects don't know they are parts of a mount point path. diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc index 00486d4fa9..048d13b510 100644 --- a/src/test/cpp/util/file_windows_test.cc +++ b/src/test/cpp/util/file_windows_test.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #include <string.h> +#include <windows.h> // SetEnvironmentVariableA #include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" @@ -93,4 +94,44 @@ TEST(FileTest, IsRootDirectory) { ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\foo")); } +TEST(FileTest, TestAsWindowsPath) { + SetEnvironmentVariableA("BAZEL_SH", "c:\\dummy\\msys\\bin\\bash.exe"); + std::wstring actual; + + ASSERT_TRUE(AsWindowsPath("", &actual)); + ASSERT_EQ(std::wstring(L""), actual); + + ASSERT_TRUE(AsWindowsPath("", &actual)); + ASSERT_EQ(std::wstring(L""), actual); + + ASSERT_TRUE(AsWindowsPath("foo/bar", &actual)); + ASSERT_EQ(std::wstring(L"foo\\bar"), actual); + + ASSERT_TRUE(AsWindowsPath("/c", &actual)); + ASSERT_EQ(std::wstring(L"c:\\"), actual); + + ASSERT_TRUE(AsWindowsPath("/c/", &actual)); + ASSERT_EQ(std::wstring(L"c:\\"), actual); + + ASSERT_TRUE(AsWindowsPath("/c/blah", &actual)); + ASSERT_EQ(std::wstring(L"c:\\blah"), actual); + + ASSERT_TRUE(AsWindowsPath("/d/progra~1/micros~1", &actual)); + ASSERT_EQ(std::wstring(L"d:\\progra~1\\micros~1"), actual); + + ASSERT_TRUE(AsWindowsPath("/foo", &actual)); + ASSERT_EQ(std::wstring(L"c:\\dummy\\msys\\foo"), actual); + + std::wstring wlongpath(L"dummy_long_path\\"); + std::string longpath("dummy_long_path/"); + while (longpath.size() <= MAX_PATH) { + wlongpath += wlongpath; + longpath += longpath; + } + wlongpath = std::wstring(L"c:\\") + wlongpath; + longpath = std::string("/c/") + longpath; + ASSERT_TRUE(AsWindowsPath(longpath, &actual)); + ASSERT_EQ(wlongpath, actual); +} + } // namespace blaze_util |