Bazel client: reduce dependency on POSIX API
We can now compile blaze_util_windows.cc with
MSVC, yay! (when building //src:bazel
--cpu=x64_windows_msvc -k).
There are a lot of #ifdef's and TODOs so this
is a modest victory for now.
In this change:
- change blaze::MakeDirectories to return bool
instead of int, since that's how it was used
anyway, and to expect the permission mask as
unsigned int instead of mode_t, since the
former is good enough and compatible with
mode_t on POSIX while mode_t is not defined on
Windows
- move blaze::MakeDirectories into
blaze_util_<platform>
- implement envvar-handling in
blaze_util_<platform> and use it everywhere
See https://github.com/bazelbuild/bazel/issues/2107
--
MOS_MIGRATED_REVID=139887503
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index 9633565..abb16ec 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -721,7 +721,7 @@
// The server dir has the socket, so we don't allow access by other
// users.
- if (MakeDirectories(server_dir, 0700) == -1) {
+ if (!MakeDirectories(server_dir, 0700)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
"server directory '%s' could not be created", server_dir.c_str());
}
@@ -797,7 +797,7 @@
virtual void Process(const char *filename, const devtools_ijar::u4 attr,
const devtools_ijar::u1 *data, const size_t size) {
string path = blaze_util::JoinPath(embedded_binaries_, filename);
- if (MakeDirectories(blaze_util::Dirname(path), 0777) == -1) {
+ if (!MakeDirectories(blaze_util::Dirname(path), 0777)) {
pdie(blaze_exit_code::INTERNAL_ERROR,
"couldn't create '%s'", path.c_str());
}
@@ -818,7 +818,7 @@
static void ActuallyExtractData(const string &argv0,
const string &embedded_binaries) {
ExtractBlazeZipProcessor processor(embedded_binaries);
- if (MakeDirectories(embedded_binaries, 0777) == -1) {
+ if (!MakeDirectories(embedded_binaries, 0777)) {
pdie(blaze_exit_code::INTERNAL_ERROR, "couldn't create '%s'",
embedded_binaries.c_str());
}
@@ -1254,7 +1254,7 @@
const char *output_base = globals->options->output_base.c_str();
if (!blaze_util::PathExists(globals->options->output_base)) {
- if (MakeDirectories(globals->options->output_base, 0777) == -1) {
+ if (!MakeDirectories(globals->options->output_base, 0777)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
"Output base directory '%s' could not be created",
output_base);
@@ -1280,32 +1280,32 @@
}
static void CheckEnvironment() {
- if (getenv("http_proxy") != NULL) {
+ if (!blaze::GetEnv("http_proxy").empty()) {
fprintf(stderr, "Warning: ignoring http_proxy in environment.\n");
- unsetenv("http_proxy");
+ blaze::UnsetEnv("http_proxy");
}
- if (getenv("LD_ASSUME_KERNEL") != NULL) {
+ if (!blaze::GetEnv("LD_ASSUME_KERNEL").empty()) {
// Fix for bug: if ulimit -s and LD_ASSUME_KERNEL are both
// specified, the JVM fails to create threads. See thread_stack_regtest.
// This is also provoked by LD_LIBRARY_PATH=/usr/lib/debug,
// or anything else that causes the JVM to use LinuxThreads.
fprintf(stderr, "Warning: ignoring LD_ASSUME_KERNEL in environment.\n");
- unsetenv("LD_ASSUME_KERNEL");
+ blaze::UnsetEnv("LD_ASSUME_KERNEL");
}
- if (getenv("LD_PRELOAD") != NULL) {
+ if (!blaze::GetEnv("LD_PRELOAD").empty()) {
fprintf(stderr, "Warning: ignoring LD_PRELOAD in environment.\n");
- unsetenv("LD_PRELOAD");
+ blaze::UnsetEnv("LD_PRELOAD");
}
- if (getenv("_JAVA_OPTIONS") != NULL) {
+ if (!blaze::GetEnv("_JAVA_OPTIONS").empty()) {
// This would override --host_jvm_args
fprintf(stderr, "Warning: ignoring _JAVA_OPTIONS in environment.\n");
- unsetenv("_JAVA_OPTIONS");
+ blaze::UnsetEnv("_JAVA_OPTIONS");
}
- if (getenv("TEST_TMPDIR") != NULL) {
+ if (!blaze::GetEnv("TEST_TMPDIR").empty()) {
fprintf(stderr, "INFO: $TEST_TMPDIR defined: output root default is "
"'%s'.\n", globals->options->output_root.c_str());
}
@@ -1316,10 +1316,10 @@
// Make the JVM use ISO-8859-1 for parsing its command line because "blaze
// run" doesn't handle non-ASCII command line arguments. This is apparently
// the most reliable way to select the platform default encoding.
- setenv("LANG", "en_US.ISO-8859-1", 1);
- setenv("LANGUAGE", "en_US.ISO-8859-1", 1);
- setenv("LC_ALL", "en_US.ISO-8859-1", 1);
- setenv("LC_CTYPE", "en_US.ISO-8859-1", 1);
+ blaze::SetEnv("LANG", "en_US.ISO-8859-1");
+ blaze::SetEnv("LANGUAGE", "en_US.ISO-8859-1");
+ blaze::SetEnv("LC_ALL", "en_US.ISO-8859-1");
+ blaze::SetEnv("LC_CTYPE", "en_US.ISO-8859-1");
}
static void SetupStreams() {
diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc
index b7aa84a..9e1bd2d 100644
--- a/src/main/cpp/blaze_util.cc
+++ b/src/main/cpp/blaze_util.cc
@@ -51,8 +51,10 @@
const char kServerPidSymlink[] = "server.pid";
string GetUserName() {
- const char *user = getenv("USER");
- if (user && user[0] != '\0') return user;
+ string user = GetEnv("USER");
+ if (!user.empty()) {
+ return user;
+ }
errno = 0;
passwd *pwent = getpwuid(getuid()); // NOLINT (single-threaded)
if (pwent == NULL || pwent->pw_name == NULL) {
@@ -75,100 +77,6 @@
return cwd + separator + path;
}
-// Runs "stat" on `path`. Returns -1 and sets errno if stat fails or
-// `path` isn't a directory. If check_perms is true, this will also
-// make sure that `path` is owned by the current user and has `mode`
-// permissions (observing the umask). It attempts to run chmod to
-// correct the mode if necessary. If `path` is a symlink, this will
-// check ownership of the link, not the underlying directory.
-static int GetDirectoryStat(const string& path, mode_t mode, bool check_perms) {
- struct stat filestat = {};
- if (stat(path.c_str(), &filestat) == -1) {
- return -1;
- }
-
- if (!S_ISDIR(filestat.st_mode)) {
- errno = ENOTDIR;
- return -1;
- }
-
- if (check_perms) {
- // If this is a symlink, run checks on the link. (If we did lstat above
- // then it would return false for ISDIR).
- struct stat linkstat = {};
- if (lstat(path.c_str(), &linkstat) != 0) {
- return -1;
- }
- if (linkstat.st_uid != geteuid()) {
- // The directory isn't owned by me.
- errno = EACCES;
- return -1;
- }
-
- mode_t mask = umask(022);
- umask(mask);
- mode = (mode & ~mask);
- if ((filestat.st_mode & 0777) != mode
- && chmod(path.c_str(), mode) == -1) {
- // errno set by chmod.
- return -1;
- }
- }
- return 0;
-}
-
-static int MakeDirectories(const string& path, mode_t mode, bool childmost) {
- if (path.empty() || path == "/") {
- errno = EACCES;
- return -1;
- }
-
- int retval = GetDirectoryStat(path, mode, childmost);
- if (retval == 0) {
- return 0;
- }
-
- if (errno == ENOENT) {
- // Path does not exist, attempt to create its parents, then it.
- string parent = blaze_util::Dirname(path);
- if (MakeDirectories(parent, mode, false) == -1) {
- // errno set by stat.
- return -1;
- }
-
- if (mkdir(path.c_str(), mode) == -1) {
- if (errno == EEXIST) {
- if (childmost) {
- // If there are multiple bazel calls at the same time then the
- // directory could be created between the MakeDirectories and mkdir
- // calls. This is okay, but we still have to check the permissions.
- return GetDirectoryStat(path, mode, childmost);
- } else {
- // If this isn't the childmost directory, we don't care what the
- // permissions were. If it's not even a directory then that error will
- // get caught when we attempt to create the next directory down the
- // chain.
- return 0;
- }
- }
- // errno set by mkdir.
- return -1;
- }
- return 0;
- }
-
- return retval;
-}
-
-// mkdir -p path. Returns 0 if the path was created or already exists and could
-// be chmod-ed to exactly the given permissions. If final part of the path is a
-// symlink, this ensures that the destination of the symlink has the desired
-// permissions. It also checks that the directory or symlink is owned by us.
-// On failure, this returns -1 and sets errno.
-int MakeDirectories(const string& path, mode_t mode) {
- return MakeDirectories(path, mode, true);
-}
-
// Replaces 'contents' with contents of 'fd' file descriptor.
// If `max_size` is positive, the method reads at most that many bytes; if it
// is 0, the method reads the whole file.
@@ -185,7 +93,7 @@
}
content->append(buf, r);
if (max_size > 0) {
- if (max_size > r) {
+ if (max_size > static_cast<size_t>(r)) {
max_size -= r;
} else {
break;
@@ -235,14 +143,13 @@
}
bool IsEmacsTerminal() {
- string emacs = getenv("EMACS") == nullptr ? "" : getenv("EMACS");
- string inside_emacs =
- getenv("INSIDE_EMACS") == nullptr ? "" : getenv("INSIDE_EMACS");
+ string emacs = GetEnv("EMACS");
+ string inside_emacs = GetEnv("INSIDE_EMACS");
// GNU Emacs <25.1 (and ~all non-GNU emacsen) set EMACS=t, but >=25.1 doesn't
// do that and instead sets INSIDE_EMACS=<stuff> (where <stuff> can look like
// e.g. "25.1.1,comint"). So we check both variables for maximum
// compatibility.
- return emacs == "t" || inside_emacs != "";
+ return emacs == "t" || !inside_emacs.empty();
}
// Returns true iff both stdout and stderr are connected to a
@@ -250,9 +157,10 @@
// (this is computed heuristically based on the values of
// environment variables).
bool IsStandardTerminal() {
- string term = getenv("TERM") == nullptr ? "" : getenv("TERM");
- if (term == "" || term == "dumb" || term == "emacs" || term == "xterm-mono" ||
- term == "symbolics" || term == "9term" || IsEmacsTerminal()) {
+ string term = GetEnv("TERM");
+ if (term.empty() || term == "dumb" || term == "emacs" ||
+ term == "xterm-mono" || term == "symbolics" || term == "9term" ||
+ IsEmacsTerminal()) {
return false;
}
return isatty(STDOUT_FILENO) && isatty(STDERR_FILENO);
@@ -265,10 +173,10 @@
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
return ws.ws_col;
}
- const char* columns_env = getenv("COLUMNS");
- if (columns_env != NULL && columns_env[0] != '\0') {
+ string columns_env = GetEnv("COLUMNS");
+ if (!columns_env.empty()) {
char* endptr;
- int columns = blaze_util::strto32(columns_env, &endptr, 10);
+ int columns = blaze_util::strto32(columns_env.c_str(), &endptr, 10);
if (*endptr == '\0') { // $COLUMNS is a valid number
return columns;
}
@@ -305,9 +213,7 @@
return true;
}
-bool VerboseLogging() {
- return getenv("VERBOSE_BLAZE_CLIENT") != NULL;
-}
+bool VerboseLogging() { return !GetEnv("VERBOSE_BLAZE_CLIENT").empty(); }
// Read the Jvm version from a file descriptor. The read fd
// should contains a similar output as the java -version output.
diff --git a/src/main/cpp/blaze_util.h b/src/main/cpp/blaze_util.h
index 666b06e..7241682 100644
--- a/src/main/cpp/blaze_util.h
+++ b/src/main/cpp/blaze_util.h
@@ -41,10 +41,6 @@
// MakeAbsolute("C:/foo") ---> "C:/foo"
std::string MakeAbsolute(const std::string &path);
-// mkdir -p path. All newly created directories use the given mode.
-// Returns -1 on failure, sets errno.
-int MakeDirectories(const std::string &path, mode_t mode);
-
// Replaces 'content' with contents of file 'filename'.
// If `max_size` is positive, the method reads at most that many bytes; if it
// is 0, the method reads the whole file.
diff --git a/src/main/cpp/blaze_util_darwin.cc b/src/main/cpp/blaze_util_darwin.cc
index c4a35c0..63bfcec 100644
--- a/src/main/cpp/blaze_util_darwin.cc
+++ b/src/main/cpp/blaze_util_darwin.cc
@@ -153,9 +153,9 @@
}
string GetDefaultHostJavabase() {
- const char *java_home = getenv("JAVA_HOME");
- if (java_home) {
- return std::string(java_home);
+ string java_home = GetEnv("JAVA_HOME");
+ if (!java_home.empty()) {
+ return java_home;
}
FILE *output = popen("/usr/libexec/java_home -v 1.7+", "r");
diff --git a/src/main/cpp/blaze_util_freebsd.cc b/src/main/cpp/blaze_util_freebsd.cc
index c4ac11a..f1177b21 100644
--- a/src/main/cpp/blaze_util_freebsd.cc
+++ b/src/main/cpp/blaze_util_freebsd.cc
@@ -145,11 +145,8 @@
string GetDefaultHostJavabase() {
// if JAVA_HOME is defined, then use it as default.
- const char *javahome = getenv("JAVA_HOME");
- if (javahome != NULL) {
- return string(javahome);
- }
- return "/usr/local/openjdk8";
+ string javahome = getenv("JAVA_HOME");
+ return !javahome.empty() ? javahome : "/usr/local/openjdk8";
}
void WriteSystemSpecificProcessIdentifier(const string& server_dir) {
diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h
index 6d68e73..a4c9c20 100644
--- a/src/main/cpp/blaze_util_platform.h
+++ b/src/main/cpp/blaze_util_platform.h
@@ -148,6 +148,17 @@
// user, and not accessible to anyone else.
void CreateSecureOutputRoot(const std::string& path);
+// mkdir -p path. All newly created directories use the given mode.
+// `mode` should be an octal permission mask, e.g. 0755
+// Returns false on failure, sets errno.
+bool MakeDirectories(const std::string &path, unsigned int mode);
+
+std::string GetEnv(const std::string& name);
+
+void SetEnv(const std::string& name, const std::string& value);
+
+void UnsetEnv(const std::string& name);
+
} // namespace blaze
#endif // BAZEL_SRC_MAIN_CPP_BLAZE_UTIL_PLATFORM_H_
diff --git a/src/main/cpp/blaze_util_posix.cc b/src/main/cpp/blaze_util_posix.cc
index b70d95c..55cb59b 100644
--- a/src/main/cpp/blaze_util_posix.cc
+++ b/src/main/cpp/blaze_util_posix.cc
@@ -236,7 +236,7 @@
const char* root = path.c_str();
struct stat fileinfo = {};
- if (MakeDirectories(root, 0755) == -1) {
+ if (!MakeDirectories(root, 0755)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "mkdir('%s')", root);
}
@@ -273,4 +273,112 @@
ExcludePathFromBackup(root);
}
+// Runs "stat" on `path`. Returns -1 and sets errno if stat fails or
+// `path` isn't a directory. If check_perms is true, this will also
+// make sure that `path` is owned by the current user and has `mode`
+// permissions (observing the umask). It attempts to run chmod to
+// correct the mode if necessary. If `path` is a symlink, this will
+// check ownership of the link, not the underlying directory.
+static bool GetDirectoryStat(const string& path, mode_t mode,
+ bool check_perms) {
+ struct stat filestat = {};
+ if (stat(path.c_str(), &filestat) == -1) {
+ return false;
+ }
+
+ if (!S_ISDIR(filestat.st_mode)) {
+ errno = ENOTDIR;
+ return false;
+ }
+
+ if (check_perms) {
+ // If this is a symlink, run checks on the link. (If we did lstat above
+ // then it would return false for ISDIR).
+ struct stat linkstat = {};
+ if (lstat(path.c_str(), &linkstat) != 0) {
+ return false;
+ }
+ if (linkstat.st_uid != geteuid()) {
+ // The directory isn't owned by me.
+ errno = EACCES;
+ return false;
+ }
+
+ mode_t mask = umask(022);
+ umask(mask);
+ mode = (mode & ~mask);
+ if ((filestat.st_mode & 0777) != mode
+ && chmod(path.c_str(), mode) == -1) {
+ // errno set by chmod.
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool MakeDirectories(const string& path, mode_t mode, bool childmost) {
+ if (path.empty() || path == "/") {
+ errno = EACCES;
+ return false;
+ }
+
+ bool stat_succeeded = GetDirectoryStat(path, mode, childmost);
+ if (stat_succeeded) {
+ return true;
+ }
+
+ if (errno == ENOENT) {
+ // Path does not exist, attempt to create its parents, then it.
+ string parent = blaze_util::Dirname(path);
+ if (!MakeDirectories(parent, mode, false)) {
+ // errno set by stat.
+ return false;
+ }
+
+ if (mkdir(path.c_str(), mode) == -1) {
+ if (errno == EEXIST) {
+ if (childmost) {
+ // If there are multiple bazel calls at the same time then the
+ // directory could be created between the MakeDirectories and mkdir
+ // calls. This is okay, but we still have to check the permissions.
+ return GetDirectoryStat(path, mode, childmost);
+ } else {
+ // If this isn't the childmost directory, we don't care what the
+ // permissions were. If it's not even a directory then that error will
+ // get caught when we attempt to create the next directory down the
+ // chain.
+ return true;
+ }
+ }
+ // errno set by mkdir.
+ return false;
+ }
+ return true;
+ }
+
+ return stat_succeeded;
+}
+
+// mkdir -p path. Returns 0 if the path was created or already exists and could
+// be chmod-ed to exactly the given permissions. If final part of the path is a
+// symlink, this ensures that the destination of the symlink has the desired
+// permissions. It also checks that the directory or symlink is owned by us.
+// On failure, this returns -1 and sets errno.
+bool MakeDirectories(const string& path, unsigned int mode) {
+ return MakeDirectories(path, mode, true);
+}
+
+string GetEnv(const string& name) {
+ char* result = getenv(name.c_str());
+ return result != NULL ? string(result) : "";
+}
+
+void SetEnv(const string& name, const string& value) {
+ setenv(name.c_str(), value.c_str(), 1);
+}
+
+void UnsetEnv(const string& name) {
+ unsetenv(name.c_str());
+}
+
} // namespace blaze.
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index 2905673..87406f9 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -14,7 +14,6 @@
#include <errno.h> // errno, ENAMETOOLONG
#include <limits.h>
-#include <string.h> // strerror
#ifndef COMPILER_MSVC
#include <sys/cygwin.h>
@@ -100,6 +99,9 @@
}
string GetSelfPath() {
+#ifdef COMPILER_MSVC
+ const size_t PATH_MAX = 4096;
+#endif // COMPILER_MSVC
char buffer[PATH_MAX] = {};
if (!GetModuleFileName(0, buffer, sizeof(buffer))) {
pdie(255, "Error %u getting executable file name\n", GetLastError());
@@ -153,6 +155,11 @@
}
string GetProcessCWD(int pid) {
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team) 2016-11-18: decide whether we need this on Windows and
+ // implement or delete.
+ return "";
+#else // not COMPILER_MSVC
char server_cwd[PATH_MAX] = {};
if (readlink(
("/proc/" + ToString(pid) + "/cwd").c_str(),
@@ -161,6 +168,7 @@
}
return string(server_cwd);
+#endif // COMPILER_MSVC
}
bool IsSharedLibrary(const string &filename) {
@@ -340,6 +348,10 @@
// So, we first pretend to be a POSIX daemon so that msys2 knows about our
// intentions and *then* we call CreateProcess(). Life ain't easy.
static bool DaemonizeOnWindows() {
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team) 2016-11-18: implement this.
+ return false;
+#else // not COMPILER_MSVC
if (fork() > 0) {
// We are the original client process.
return true;
@@ -356,6 +368,7 @@
// descriptors here. CreateProcess() will take care of that and it's useful
// to see the error messages in ExecuteDaemon() on the console of the client.
return false;
+#endif // COMPILER_MSVC
}
// Keeping an eye on the server process on Windows is not implemented yet.
@@ -463,6 +476,9 @@
WaitForSingleObject(java_handle, INFINITE);
}
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team): implement signal handling.
+#else // not COMPILER_MSVC
static void MingwSignalHandler(int signum) {
// Java process will be terminated because we set the job to terminate if its
// handle is closed.
@@ -476,6 +492,7 @@
// allow breakaway processes.
exit(blaze_exit_code::ExitCode::INTERRUPTED);
}
+#endif // COMPILER_MSVC
// Returns whether assigning the given process to a job failed because nested
// jobs are not available on the current system.
@@ -570,7 +587,12 @@
// msys doesn't deliver signals while a Win32 call is pending so we need to
// do the blocking call in another thread
+
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team): implement signal handling.
+#else // not COMPILER_MSVC
signal(SIGINT, MingwSignalHandler);
+#endif // COMPILER_MSVC
std::thread batch_waiter_thread([=]() {
BatchWaiterThread(processInfo.hProcess);
});
@@ -587,6 +609,11 @@
string ListSeparator() { return ";"; }
string ConvertPath(const string& path) {
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team): implement this.
+ pdie(255, "blaze::ConvertPath is not implemented on Windows");
+ return "";
+#else // not COMPILER_MSVC
// If the path looks like %USERPROFILE%/foo/bar, don't convert.
if (path.empty() || path[0] == '%') {
return path;
@@ -596,6 +623,7 @@
string result(wpath);
free(wpath);
return result;
+#endif // COMPILER_MSVC
}
// Convert a Unix path list to Windows path list
@@ -613,12 +641,18 @@
return w_list;
}
-string ConvertPathToPosix(const string& win_path) {
+static string ConvertPathToPosix(const string& win_path) {
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team) 2016-11-18: verify that this function is not needed on
+ // Windows.
+ return win_path;
+#else // not COMPILER_MSVC
char* posix_path = static_cast<char*>(cygwin_create_path(
CCP_WIN_A_TO_POSIX, static_cast<const void*>(win_path.c_str())));
string result(posix_path);
free(posix_path);
return result;
+#endif // COMPILER_MSVC
}
// Cribbed from ntifs.h, not present in windows.h
@@ -867,7 +901,7 @@
const char* root = path.c_str();
struct stat fileinfo = {};
- if (MakeDirectories(root, 0755) == -1) {
+ if (!MakeDirectories(root, 0755)) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "mkdir('%s')", root);
}
@@ -905,6 +939,137 @@
#endif // COMPILER_MSVC
}
+#ifdef COMPILER_MSVC
+bool MakeDirectories(const string& path, unsigned int mode) {
+ // TODO(bazel-team): implement this.
+ pdie(255, "blaze::MakeDirectories is not implemented on Windows");
+ return false;
+}
+#else // not COMPILER_MSVC
+// Runs "stat" on `path`. Returns -1 and sets errno if stat fails or
+// `path` isn't a directory. If check_perms is true, this will also
+// make sure that `path` is owned by the current user and has `mode`
+// permissions (observing the umask). It attempts to run chmod to
+// correct the mode if necessary. If `path` is a symlink, this will
+// check ownership of the link, not the underlying directory.
+static bool GetDirectoryStat(const string& path, mode_t mode,
+ bool check_perms) {
+ struct stat filestat = {};
+ if (stat(path.c_str(), &filestat) == -1) {
+ return false;
+ }
+
+ if (!S_ISDIR(filestat.st_mode)) {
+ errno = ENOTDIR;
+ return false;
+ }
+
+ if (check_perms) {
+ // If this is a symlink, run checks on the link. (If we did lstat above
+ // then it would return false for ISDIR).
+ struct stat linkstat = {};
+ if (lstat(path.c_str(), &linkstat) != 0) {
+ return false;
+ }
+ if (linkstat.st_uid != geteuid()) {
+ // The directory isn't owned by me.
+ errno = EACCES;
+ return false;
+ }
+
+ mode_t mask = umask(022);
+ umask(mask);
+ mode = (mode & ~mask);
+ if ((filestat.st_mode & 0777) != mode && chmod(path.c_str(), mode) == -1) {
+ // errno set by chmod.
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool MakeDirectories(const string& path, mode_t mode, bool childmost) {
+ if (path.empty() || path == "/") {
+ errno = EACCES;
+ return false;
+ }
+
+ bool stat_succeeded = GetDirectoryStat(path, mode, childmost);
+ if (stat_succeeded) {
+ return true;
+ }
+
+ if (errno == ENOENT) {
+ // Path does not exist, attempt to create its parents, then it.
+ string parent = blaze_util::Dirname(path);
+ if (!MakeDirectories(parent, mode, false)) {
+ // errno set by stat.
+ return false;
+ }
+
+ if (mkdir(path.c_str(), mode) == -1) {
+ if (errno == EEXIST) {
+ if (childmost) {
+ // If there are multiple bazel calls at the same time then the
+ // directory could be created between the MakeDirectories and mkdir
+ // calls. This is okay, but we still have to check the permissions.
+ return GetDirectoryStat(path, mode, childmost);
+ } else {
+ // If this isn't the childmost directory, we don't care what the
+ // permissions were. If it's not even a directory then that error will
+ // get caught when we attempt to create the next directory down the
+ // chain.
+ return true;
+ }
+ }
+ // errno set by mkdir.
+ return false;
+ }
+ return true;
+ }
+
+ return stat_succeeded;
+}
+
+// mkdir -p path. Returns 0 if the path was created or already exists and could
+// be chmod-ed to exactly the given permissions. If final part of the path is a
+// symlink, this ensures that the destination of the symlink has the desired
+// permissions. It also checks that the directory or symlink is owned by us.
+// On failure, this returns -1 and sets errno.
+bool MakeDirectories(const string& path, mode_t mode) {
+ return MakeDirectories(path, mode, true);
+}
+#endif // COMPILER_MSVC
+
+string GetEnv(const string& name) {
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team): implement this.
+ pdie(255, "blaze::GetEnv is not implemented on Windows");
+ return "";
+#else // not COMPILER_MSVC
+ char* result = getenv(name.c_str());
+ return result != NULL ? string(result) : "";
+#endif // COMPILER_MSVC
+}
+
+void SetEnv(const string& name, const string& value) {
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team): implement this.
+ pdie(255, "blaze::SetEnv is not implemented on Windows");
+#else // not COMPILER_MSVC
+ setenv(name.c_str(), value.c_str(), 1);
+#endif // COMPILER_MSVC
+}
+
+void UnsetEnv(const string& name) {
+#ifdef COMPILER_MSVC
+ // TODO(bazel-team): implement this.
+ pdie(255, "blaze::UnsetEnv is not implemented on Windows");
+#else // not COMPILER_MSVC
+ unsetenv(name.c_str());
+#endif // COMPILER_MSVC
+}
+
LARGE_INTEGER WindowsClock::GetFrequency() {
LARGE_INTEGER result;
if (!QueryPerformanceFrequency(&result)) {
diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc
index 6be8823..f7340db 100644
--- a/src/main/cpp/option_processor.cc
+++ b/src/main/cpp/option_processor.cc
@@ -274,8 +274,8 @@
return blaze_exit_code::SUCCESS;
}
- const char* home = getenv("HOME");
- if (home == NULL) {
+ string home = blaze::GetEnv("HOME");
+ if (home.empty()) {
*blaze_rc_file = "";
return blaze_exit_code::SUCCESS;
}
diff --git a/src/main/cpp/startup_options.cc b/src/main/cpp/startup_options.cc
index fd02628..06f5126 100644
--- a/src/main/cpp/startup_options.cc
+++ b/src/main/cpp/startup_options.cc
@@ -54,9 +54,9 @@
connect_timeout_secs(10),
invocation_policy(NULL),
client_debug(false) {
- bool testing = getenv("TEST_TMPDIR") != NULL;
+ bool testing = !blaze::GetEnv("TEST_TMPDIR").empty();
if (testing) {
- output_root = MakeAbsolute(getenv("TEST_TMPDIR"));
+ output_root = MakeAbsolute(blaze::GetEnv("TEST_TMPDIR"));
} else {
output_root = WorkspaceLayout::GetOutputRoot();
}
diff --git a/src/test/cpp/blaze_util_test.cc b/src/test/cpp/blaze_util_test.cc
index e8b5b28..bb0faf0 100644
--- a/src/test/cpp/blaze_util_test.cc
+++ b/src/test/cpp/blaze_util_test.cc
@@ -21,6 +21,7 @@
#include <vector>
#include "src/main/cpp/blaze_util.h"
+#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/util/file.h"
#include "gtest/gtest.h"
@@ -182,12 +183,12 @@
ASSERT_STRNE(NULL, test_src_dir);
string dir = blaze_util::JoinPath(tmp_dir, "x/y/z");
- int ok = MakeDirectories(dir, 0755);
- ASSERT_EQ(0, ok);
+ bool ok = MakeDirectories(dir, 0755);
+ ASSERT_TRUE(ok);
// Changing permissions on an existing dir should work.
ok = MakeDirectories(dir, 0750);
- ASSERT_EQ(0, ok);
+ ASSERT_TRUE(ok);
struct stat filestat = {};
ASSERT_EQ(0, stat(dir.c_str(), &filestat));
ASSERT_EQ(0750, filestat.st_mode & 0777);
@@ -196,27 +197,27 @@
// TODO(ulfjack): Fix this!
// string srcdir = blaze_util::JoinPath(test_src_dir, "x/y/z");
// ok = MakeDirectories(srcdir, 0755);
-// ASSERT_EQ(-1, ok);
+// ASSERT_FALSE(ok);
// ASSERT_EQ(EACCES, errno);
// Can't make a dir out of a file.
string non_dir = blaze_util::JoinPath(dir, "w");
ASSERT_TRUE(CreateEmptyFile(non_dir));
ok = MakeDirectories(non_dir, 0755);
- ASSERT_EQ(-1, ok);
+ ASSERT_FALSE(ok);
ASSERT_EQ(ENOTDIR, errno);
// Valid symlink should work.
string symlink = blaze_util::JoinPath(tmp_dir, "z");
ASSERT_TRUE(Symlink(dir, symlink));
ok = MakeDirectories(symlink, 0755);
- ASSERT_EQ(0, ok);
+ ASSERT_TRUE(ok);
// Error: Symlink to a file.
symlink = blaze_util::JoinPath(tmp_dir, "w");
ASSERT_TRUE(Symlink(non_dir, symlink));
ok = MakeDirectories(symlink, 0755);
- ASSERT_EQ(-1, ok);
+ ASSERT_FALSE(ok);
ASSERT_EQ(ENOTDIR, errno);
// Error: Symlink to a dir with wrong perms.
@@ -226,13 +227,13 @@
// These perms will force a chmod()
// TODO(ulfjack): Fix this!
// ok = MakeDirectories(symlink, 0000);
-// ASSERT_EQ(-1, ok);
+// ASSERTFALSE(ok);
// ASSERT_EQ(EPERM, errno);
// Edge cases.
- ASSERT_EQ(-1, MakeDirectories("", 0755));
+ ASSERT_FALSE(MakeDirectories("", 0755));
ASSERT_EQ(EACCES, errno);
- ASSERT_EQ(-1, MakeDirectories("/", 0755));
+ ASSERT_FALSE(MakeDirectories("/", 0755));
ASSERT_EQ(EACCES, errno);
}
@@ -243,7 +244,7 @@
string path = blaze_util::JoinPath(tmp_dir, "x/y/z");
// TODO(ulfjack): Fix this!
// ASSERT_LE(0, fork());
-// ASSERT_EQ(0, MakeDirectories(path, 0755));
+// ASSERT_TRUE(MakeDirectories(path, 0755));
}
} // namespace blaze
diff --git a/src/tools/singlejar/test_util.cc b/src/tools/singlejar/test_util.cc
index f5cbca9..d026388 100644
--- a/src/tools/singlejar/test_util.cc
+++ b/src/tools/singlejar/test_util.cc
@@ -21,6 +21,7 @@
#include <string>
#include "src/main/cpp/blaze_util.h"
+#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/strings.h"