aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Laszlo Csomor <laszlocsomor@google.com>2016-12-20 09:38:53 +0000
committerGravatar Klaus Aehlig <aehlig@google.com>2016-12-20 09:41:46 +0000
commitd6297fb8b924dda41111356c0c32d4a5a297a11b (patch)
tree717f4d9ba8e430f7289e3e0dd8fed350f48879c5 /src
parent332b61f416855eb5938a226eed7787473487a268 (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.cc9
-rw-r--r--src/main/cpp/util/errors.h1
-rw-r--r--src/main/cpp/util/file_platform.h20
-rw-r--r--src/main/cpp/util/file_posix.cc5
-rw-r--r--src/main/cpp/util/file_windows.cc89
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java6
-rw-r--r--src/test/cpp/util/file_windows_test.cc41
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