Bazel client: mock out read/write calls Make blaze::ReadFileDescriptor(int fd, ...) and blaze::WriteFile(int fd, ...) platform-independent by mocking out the read(2) and write(2) calls. Also rename ReadFileDescriptor to ReadFrom and introduce a new WriteTo method that encapsulates WriteFile's prior logic. In particular these functions now take a read_func/write_func function argument instead of a file descriptor, so the read(2)/write(2) calls can be mocked out. This allows us to use these functions on Windows too, where read(2)/write(2) are not implemented, and we can inject a different read_func/write_func. See https://github.com/bazelbuild/bazel/issues/2107 -- MOS_MIGRATED_REVID=140195973
diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc index cfa2b92..0ab9b76 100644 --- a/src/main/cpp/blaze_util.cc +++ b/src/main/cpp/blaze_util.cc
@@ -77,14 +77,15 @@ return cwd + separator + path; } -bool ReadFileDescriptor(int fd, string *content, int max_size) { +bool ReadFrom(const std::function<int(void *, int)> &read_func, string *content, + int max_size) { content->clear(); char buf[4096]; // OPT: This loop generates one spurious read on regular files. - while (int r = read(fd, buf, - max_size > 0 - ? std::min(max_size, static_cast<int>(sizeof buf)) - : sizeof buf)) { + while (int r = read_func( + buf, max_size > 0 + ? std::min(max_size, static_cast<int>(sizeof buf)) + : sizeof buf)) { if (r == -1) { if (errno == EINTR || errno == EAGAIN) continue; return false; @@ -98,40 +99,49 @@ } } } - close(fd); return true; } bool ReadFile(const string &filename, string *content, int max_size) { int fd = open(filename.c_str(), O_RDONLY); if (fd == -1) return false; - return ReadFileDescriptor(fd, content, max_size); + bool result = + ReadFrom([fd](void *buf, int len) { return read(fd, buf, len); }, content, + max_size); + close(fd); + return result; } -// Writes 'content' into file 'filename', and makes it executable. -// Returns false on failure, sets errno. -bool WriteFile(const string &content, const string &filename) { - return WriteFile(content.data(), content.size(), filename); -} - -bool WriteFile(const void *data, size_t size, const std::string &filename) { +bool WriteFile(const void* data, size_t size, const string &filename) { UnlinkPath(filename); // We don't care about the success of this. int fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0755); // chmod +x if (fd == -1) { return false; } - int r = write(fd, data, size); - if (r == -1) { - return false; - } + bool result = WriteTo( + [fd](const void *buf, size_t bufsize) { return write(fd, buf, bufsize); }, + data, size); int saved_errno = errno; if (close(fd)) { return false; // Can fail on NFS. } errno = saved_errno; // Caller should see errno from write(). + return result; +} + +bool WriteTo(const std::function<int(const void *, size_t)> &write_func, + const void *data, size_t size) { + int r = write_func(data, size); + if (r == -1) { + return false; + } return static_cast<uint>(r) == size; } +bool WriteFile(const std::string &content, const std::string &filename) { + return WriteFile(content.c_str(), content.size(), filename); +} + bool UnlinkPath(const string &file_path) { return unlink(file_path.c_str()) == 0; }
diff --git a/src/main/cpp/blaze_util.h b/src/main/cpp/blaze_util.h index 06c75ed..cdef849 100644 --- a/src/main/cpp/blaze_util.h +++ b/src/main/cpp/blaze_util.h
@@ -21,6 +21,7 @@ #include <sys/types.h> +#include <functional> #include <sstream> #include <string> #include <vector> @@ -52,11 +53,12 @@ bool ReadFile(const std::string &filename, std::string *content, int max_size = 0); -// Replaces 'content' with contents of file descriptor 'fd'. -// If `max_size` is positive, the method reads at most that many bytes; if it -// otherwise the method reads the whole file. +// Replaces 'content' with data read from a source using `read_func`. +// If `max_size` is positive, the method reads at most that many bytes; +// otherwise the method reads everything. // Returns false on error. Can be called from a signal handler. -bool ReadFileDescriptor(int fd, std::string *content, int max_size = 0); +bool ReadFrom(const std::function<int(void *, int)> &read_func, + std::string *content, int max_size = 0); // Writes 'content' into file 'filename', and makes it executable. // Returns false on failure, sets errno. @@ -66,6 +68,11 @@ // Returns false on failure, sets errno. bool WriteFile(const void* data, size_t size, const std::string &filename); +// Writes `size` bytes from `data` into a destination using `write_func`. +// Returns false on failure, sets errno. +bool WriteTo(const std::function<int(const void *, size_t)> &write_func, + const void *data, size_t size); + // Unlinks the file given by 'file_path'. // Returns true on success. In case of failure sets errno. bool UnlinkPath(const std::string &file_path);
diff --git a/src/main/cpp/blaze_util_posix.cc b/src/main/cpp/blaze_util_posix.cc index ed0d9b3..5fdf3dd 100644 --- a/src/main/cpp/blaze_util_posix.cc +++ b/src/main/cpp/blaze_util_posix.cc
@@ -261,23 +261,30 @@ if (pipe(fds)) { pdie(blaze_exit_code::INTERNAL_ERROR, "pipe creation failed"); } + int recv_socket = fds[0]; + int send_socket = fds[1]; int child = fork(); if (child == -1) { pdie(blaze_exit_code::INTERNAL_ERROR, "fork() failed"); } else if (child > 0) { // we're the parent - close(fds[1]); // parent keeps only the reading side + close(send_socket); // parent keeps only the reading side string result; - if (!ReadFileDescriptor(fds[0], &result)) { + bool success = ReadFrom( + [recv_socket](void* buf, int size) { + return read(recv_socket, buf, size); + }, + &result); + close(recv_socket); + if (!success) { pdie(blaze_exit_code::INTERNAL_ERROR, "Cannot read subprocess output"); } - return result; - } else { // We're the child - close(fds[0]); // child keeps only the writing side + } else { // We're the child + close(recv_socket); // child keeps only the writing side // Redirect output to the writing side of the dup. - dup2(fds[1], STDOUT_FILENO); - dup2(fds[1], STDERR_FILENO); + dup2(send_socket, STDOUT_FILENO); + dup2(send_socket, STDERR_FILENO); // Execute the binary ExecuteProgram(exe, args_vector); pdie(blaze_exit_code::INTERNAL_ERROR, "Failed to run %s", exe.c_str());