Client: introduce Path class, convert server_dir
Benefits of a Path abstraction instead of raw
strings:
- type safety
- no need to convert paths on Windows all the time
- no need to check if paths are absolute
Closes #9232.
Change-Id: Id81da64c2d9c2881e0a57c6d886d7e2e62ecb8af
PiperOrigin-RevId: 265455526
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index 2b80b82..c88e136 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -622,11 +622,11 @@
return result;
}
-static void EnsureServerDir(const string &server_dir) {
+static void EnsureServerDir(const blaze_util::Path &server_dir) {
// The server dir has the connection info - don't allow access by other users.
if (!blaze_util::MakeDirectories(server_dir, 0700)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "server directory '" << server_dir
+ << "server directory '" << server_dir.AsPrintablePath()
<< "' could not be created: " << GetLastErrorString();
}
}
@@ -644,13 +644,10 @@
// Replace this process with the blaze server. Does not exit.
static void RunServerMode(
- const string &server_exe,
- const vector<string> &server_exe_args,
- const WorkspaceLayout &workspace_layout,
- const string &workspace,
- const OptionProcessor &option_processor,
- const StartupOptions &startup_options,
- BlazeServer *server) {
+ const string &server_exe, const vector<string> &server_exe_args,
+ const blaze_util::Path &server_dir, const WorkspaceLayout &workspace_layout,
+ const string &workspace, const OptionProcessor &option_processor,
+ const StartupOptions &startup_options, BlazeServer *server) {
if (startup_options.batch) {
BAZEL_DIE(blaze_exit_code::BAD_ARGV)
<< "exec-server command is not compatible with --batch";
@@ -663,15 +660,12 @@
server->KillRunningServer();
}
- const string server_dir =
- blaze_util::JoinPath(startup_options.output_base, "server");
-
EnsureServerDir(server_dir);
blaze_util::WriteFile(blaze::GetProcessIdAsString(),
- blaze_util::JoinPath(server_dir, "server.pid.txt"));
+ server_dir.GetRelative("server.pid.txt"));
blaze_util::WriteFile(GetArgumentString(server_exe_args),
- blaze_util::JoinPath(server_dir, "cmdline"));
+ server_dir.GetRelative("cmdline"));
GoToWorkspace(workspace_layout, workspace);
@@ -768,10 +762,10 @@
// After connecting to the Blaze server, return its PID, or -1 if there was an
// error.
-static int GetServerPid(const string &server_dir) {
+static int GetServerPid(const blaze_util::Path &server_dir) {
// Note: there is no race here on startup since the server creates
// the pid file strictly before it binds the socket.
- string pid_file = blaze_util::JoinPath(server_dir, kServerPidFile);
+ blaze_util::Path pid_file = server_dir.GetRelative(kServerPidFile);
string bufstr;
int result;
if (!blaze_util::ReadFile(pid_file, &bufstr, 32) ||
@@ -838,7 +832,7 @@
// Ensures that any server previously associated with `server_dir` is no longer
// running.
static void EnsurePreviousServerProcessTerminated(
- const string &server_dir, const StartupOptions &startup_options,
+ const blaze_util::Path &server_dir, const StartupOptions &startup_options,
LoggingInfo *logging_info) {
int server_pid = GetServerPid(server_dir);
if (server_pid > 0) {
@@ -858,23 +852,16 @@
// Starts up a new server and connects to it. Exits if it didn't work out.
static void StartServerAndConnect(
- const string &server_exe,
- const vector<string> &server_exe_args,
- const WorkspaceLayout &workspace_layout,
- const string &workspace,
- const OptionProcessor &option_processor,
- const StartupOptions &startup_options,
- LoggingInfo *logging_info,
+ const string &server_exe, const vector<string> &server_exe_args,
+ const blaze_util::Path &server_dir, const WorkspaceLayout &workspace_layout,
+ const string &workspace, const OptionProcessor &option_processor,
+ const StartupOptions &startup_options, LoggingInfo *logging_info,
BlazeServer *server) {
- const string server_dir =
- blaze_util::JoinPath(startup_options.output_base, "server");
-
// Delete the old command_port file if it already exists. Otherwise we might
// run into the race condition that we read the old command_port file before
// the new server has written the new file and we try to connect to the old
// port, run into a timeout and try again.
- (void)blaze_util::UnlinkPath(
- blaze_util::JoinPath(server_dir, "command_port"));
+ (void)blaze_util::UnlinkPath(server_dir.GetRelative("command_port"));
EnsureServerDir(server_dir);
@@ -886,7 +873,7 @@
// cmdline file is used to validate the server running in this server_dir.
// There's no server running now so we're safe to unconditionally write this.
blaze_util::WriteFile(GetArgumentString(server_exe_args),
- blaze_util::JoinPath(server_dir, "cmdline"));
+ server_dir.GetRelative("cmdline"));
// Do this here instead of in the daemon so the user can see if it fails.
GoToWorkspace(workspace_layout, workspace);
@@ -1217,22 +1204,16 @@
// signal.
static ATTRIBUTE_NORETURN void RunClientServerMode(
const string &server_exe, const vector<string> &server_exe_args,
- const WorkspaceLayout &workspace_layout, const string &workspace,
- const OptionProcessor &option_processor,
+ const blaze_util::Path &server_dir, const WorkspaceLayout &workspace_layout,
+ const string &workspace, const OptionProcessor &option_processor,
const StartupOptions &startup_options, LoggingInfo *logging_info,
const DurationMillis extract_data_duration,
const DurationMillis command_wait_duration_ms, BlazeServer *server) {
while (true) {
if (!server->Connected()) {
- StartServerAndConnect(
- server_exe,
- server_exe_args,
- workspace_layout,
- workspace,
- option_processor,
- startup_options,
- logging_info,
- server);
+ StartServerAndConnect(server_exe, server_exe_args, server_dir,
+ workspace_layout, workspace, option_processor,
+ startup_options, logging_info, server);
}
// Check for the case when the workspace directory deleted and then gets
@@ -1520,23 +1501,19 @@
const string server_exe = startup_options.GetExe(jvm_path, server_jar_path);
+ const blaze_util::Path server_dir =
+ blaze_util::Path(startup_options.output_base).GetRelative("server");
if ("exec-server" == option_processor.GetCommand()) {
- RunServerMode(
- server_exe,
- server_exe_args,
- workspace_layout,
- workspace,
- option_processor,
- startup_options,
- blaze_server);
+ RunServerMode(server_exe, server_exe_args, server_dir, workspace_layout,
+ workspace, option_processor, startup_options, blaze_server);
} else if (startup_options.batch) {
RunBatchMode(server_exe, server_exe_args, workspace_layout, workspace,
option_processor, startup_options, logging_info,
extract_data_duration, command_wait_duration_ms, blaze_server);
} else {
- RunClientServerMode(server_exe, server_exe_args, workspace_layout,
- workspace, option_processor, startup_options,
- logging_info, extract_data_duration,
+ RunClientServerMode(server_exe, server_exe_args, server_dir,
+ workspace_layout, workspace, option_processor,
+ startup_options, logging_info, extract_data_duration,
command_wait_duration_ms, blaze_server);
}
}
@@ -1708,7 +1685,7 @@
return false;
}
- pid_t server_pid = GetServerPid(server_dir);
+ pid_t server_pid = GetServerPid(blaze_util::Path(server_dir));
if (server_pid < 0) {
return false;
}
diff --git a/src/main/cpp/blaze_util_darwin.cc b/src/main/cpp/blaze_util_darwin.cc
index 699407e..fbeb0a1 100644
--- a/src/main/cpp/blaze_util_darwin.cc
+++ b/src/main/cpp/blaze_util_darwin.cc
@@ -201,9 +201,8 @@
return posix_spawnattr_set_qos_class_np(attrp, options.macos_qos_class);
}
-void WriteSystemSpecificProcessIdentifier(
- const string& server_dir, pid_t server_pid) {
-}
+void WriteSystemSpecificProcessIdentifier(const blaze_util::Path &server_dir,
+ pid_t server_pid) {}
bool VerifyServerProcess(int pid, const string &output_base) {
// TODO(lberki): This only checks for the process's existence, not whether
diff --git a/src/main/cpp/blaze_util_freebsd.cc b/src/main/cpp/blaze_util_freebsd.cc
index 9d873d0..b731d38 100644
--- a/src/main/cpp/blaze_util_freebsd.cc
+++ b/src/main/cpp/blaze_util_freebsd.cc
@@ -161,9 +161,8 @@
return 0;
}
-void WriteSystemSpecificProcessIdentifier(
- const string& server_dir, pid_t server_pid) {
-}
+void WriteSystemSpecificProcessIdentifier(const blaze_util::Path &server_dir,
+ pid_t server_pid) {}
bool VerifyServerProcess(int pid, const string &output_base) {
// TODO(lberki): This only checks for the process's existence, not whether
diff --git a/src/main/cpp/blaze_util_linux.cc b/src/main/cpp/blaze_util_linux.cc
index 731f478..b23898e 100644
--- a/src/main/cpp/blaze_util_linux.cc
+++ b/src/main/cpp/blaze_util_linux.cc
@@ -210,8 +210,8 @@
return 0;
}
-void WriteSystemSpecificProcessIdentifier(
- const string& server_dir, pid_t server_pid) {
+void WriteSystemSpecificProcessIdentifier(const blaze_util::Path &server_dir,
+ pid_t server_pid) {
string pid_string = ToString(server_pid);
string start_time;
@@ -221,11 +221,11 @@
<< GetLastErrorString();
}
- string start_time_file = blaze_util::JoinPath(server_dir, "server.starttime");
+ blaze_util::Path start_time_file = server_dir.GetRelative("server.starttime");
if (!blaze_util::WriteFile(start_time, start_time_file)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "Cannot write start time in server dir " << server_dir << ": "
- << GetLastErrorString();
+ << "Cannot write start time in server dir "
+ << server_dir.AsPrintablePath() << ": " << GetLastErrorString();
}
}
diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h
index c7c3aee..d001306 100644
--- a/src/main/cpp/blaze_util_platform.h
+++ b/src/main/cpp/blaze_util_platform.h
@@ -22,6 +22,7 @@
#include "src/main/cpp/blaze_util.h"
#include "src/main/cpp/server_process_info.h"
+#include "src/main/cpp/util/path.h"
#include "src/main/cpp/util/port.h"
namespace blaze {
@@ -171,15 +172,12 @@
// still alive. The PID of the daemon started is written into server_dir,
// both as a symlink (for legacy reasons) and as a file, and returned to the
// caller.
-int ExecuteDaemon(const std::string& exe,
- const std::vector<std::string>& args_vector,
- const std::map<std::string, EnvVarValue>& env,
- const std::string& daemon_output,
- const bool daemon_output_append,
- const std::string& binaries_dir,
- const std::string& server_dir,
- const StartupOptions &options,
- BlazeServerStartup** server_startup);
+int ExecuteDaemon(
+ const std::string& exe, const std::vector<std::string>& args_vector,
+ const std::map<std::string, EnvVarValue>& env,
+ const std::string& daemon_output, const bool daemon_output_append,
+ const std::string& binaries_dir, const blaze_util::Path& server_dir,
+ const StartupOptions& options, BlazeServerStartup** server_startup);
// A character used to separate paths in a list.
extern const char kListSeparator;
diff --git a/src/main/cpp/blaze_util_posix.cc b/src/main/cpp/blaze_util_posix.cc
index 87ba899..23c2c8c 100644
--- a/src/main/cpp/blaze_util_posix.cc
+++ b/src/main/cpp/blaze_util_posix.cc
@@ -352,23 +352,21 @@
int ConfigureDaemonProcess(posix_spawnattr_t* attrp,
const StartupOptions &options);
-void WriteSystemSpecificProcessIdentifier(
- const string& server_dir, pid_t server_pid);
+void WriteSystemSpecificProcessIdentifier(const blaze_util::Path& server_dir,
+ pid_t server_pid);
-int ExecuteDaemon(const string& exe,
- const std::vector<string>& args_vector,
+int ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
const std::map<string, EnvVarValue>& env,
- const string& daemon_output,
- const bool daemon_output_append,
+ const string& daemon_output, const bool daemon_output_append,
const string& binaries_dir,
- const string& server_dir,
- const StartupOptions &options,
+ const blaze_util::Path& server_dir,
+ const StartupOptions& options,
BlazeServerStartup** server_startup) {
- const string pid_file = blaze_util::JoinPath(server_dir, kServerPidFile);
+ const blaze_util::Path pid_file = server_dir.GetRelative(kServerPidFile);
const string daemonize = blaze_util::JoinPath(binaries_dir, "daemonize");
- std::vector<string> daemonize_args =
- {"daemonize", "-l", daemon_output, "-p", pid_file};
+ std::vector<string> daemonize_args = {"daemonize", "-l", daemon_output, "-p",
+ pid_file.AsNativePath()};
if (daemon_output_append) {
daemonize_args.push_back("-a");
}
@@ -427,10 +425,11 @@
<< "daemonize didn't exit cleanly: " << GetLastErrorString();
}
- std::ifstream pid_reader(pid_file);
+ std::ifstream pid_reader(pid_file.AsNativePath());
if (!pid_reader) {
+ string err = GetLastErrorString();
BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
- << "Failed to open " << pid_file << ": " << GetLastErrorString();
+ << "Failed to open " << pid_file.AsPrintablePath() << ": " << err;
}
pid_t server_pid;
pid_reader >> server_pid;
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index cc14a4a..dae4855 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -537,19 +537,21 @@
return true;
}
-static void WriteProcessStartupTime(const string& server_dir, HANDLE process) {
+static void WriteProcessStartupTime(const blaze_util::Path& server_dir,
+ HANDLE process) {
uint64_t start_time = 0;
if (!GetProcessStartupTime(process, &start_time)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "WriteProcessStartupTime(" << server_dir
+ << "WriteProcessStartupTime(" << server_dir.AsPrintablePath()
<< "): GetProcessStartupTime failed: " << GetLastErrorString();
}
- string start_time_file = blaze_util::JoinPath(server_dir, "server.starttime");
+ blaze_util::Path start_time_file = server_dir.GetRelative("server.starttime");
if (!blaze_util::WriteFile(ToString(start_time), start_time_file)) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "WriteProcessStartupTime(" << server_dir << "): WriteFile("
- << start_time_file << ") failed: " << GetLastErrorString();
+ << "WriteProcessStartupTime(" << server_dir.AsPrintablePath()
+ << "): WriteFile(" << start_time_file.AsPrintablePath()
+ << ") failed: " << GetLastErrorString();
}
}
@@ -612,14 +614,12 @@
AutoHandle proc;
};
-int ExecuteDaemon(const string& exe,
- const std::vector<string>& args_vector,
+int ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
const std::map<string, EnvVarValue>& env,
- const string& daemon_output,
- const bool daemon_out_append,
+ const string& daemon_output, const bool daemon_out_append,
const string& binaries_dir,
- const string& server_dir,
- const StartupOptions &options,
+ const blaze_util::Path& server_dir,
+ const StartupOptions& options,
BlazeServerStartup** server_startup) {
wstring wdaemon_output;
string error;
@@ -725,10 +725,11 @@
*server_startup = new ProcessHandleBlazeServerStartup(processInfo.hProcess);
string pid_string = ToString(processInfo.dwProcessId);
- string pid_file = blaze_util::JoinPath(server_dir, kServerPidFile);
+ blaze_util::Path pid_file = server_dir.GetRelative(kServerPidFile);
if (!blaze_util::WriteFile(pid_string, pid_file)) {
// Not a lot we can do if this fails
- fprintf(stderr, "Cannot write PID file %s\n", pid_file.c_str());
+ fprintf(stderr, "Cannot write PID file %s\n",
+ pid_file.AsPrintablePath().c_str());
}
// Don't close processInfo.hProcess here, it's now owned by the
diff --git a/src/main/cpp/util/file.cc b/src/main/cpp/util/file.cc
index 041d779..87f2f02 100644
--- a/src/main/cpp/util/file.cc
+++ b/src/main/cpp/util/file.cc
@@ -88,6 +88,11 @@
return WriteFile(content.c_str(), content.size(), filename, perm);
}
+bool WriteFile(const std::string &content, const Path &path,
+ unsigned int perm) {
+ return WriteFile(content.c_str(), content.size(), path, perm);
+}
+
class DirectoryTreeWalker : public DirectoryEntryConsumer {
public:
DirectoryTreeWalker(vector<string> *files,
diff --git a/src/main/cpp/util/file.h b/src/main/cpp/util/file.h
index 235ec87..be9917b 100644
--- a/src/main/cpp/util/file.h
+++ b/src/main/cpp/util/file.h
@@ -63,6 +63,9 @@
bool WriteFile(const std::string &content, const std::string &filename,
unsigned int perm = 0644);
+bool WriteFile(const std::string &content, const Path &path,
+ unsigned int perm = 0644);
+
// Lists all files in `path` and all of its subdirectories.
//
// Does not follow symlinks / junctions.
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index 7457e5e..25dc856 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -23,6 +23,8 @@
namespace blaze_util {
+class Path;
+
class IPipe;
IPipe* CreatePipe();
@@ -91,17 +93,22 @@
// Returns false on error. Can be called from a signal handler.
bool ReadFile(const std::string &filename, std::string *content,
int max_size = 0);
+bool ReadFile(const Path &path, std::string *content, int max_size = 0);
// Reads up to `size` bytes from the file `filename` into `data`.
// There must be enough memory allocated at `data`.
// Returns true on success, false on error.
bool ReadFile(const std::string &filename, void *data, size_t size);
+bool ReadFile(const Path &filename, void *data, size_t size);
// Writes `size` bytes from `data` into file `filename` and chmods it to `perm`.
// Returns false on failure, sets errno.
bool WriteFile(const void *data, size_t size, const std::string &filename,
unsigned int perm = 0644);
+bool WriteFile(const void *data, size_t size, const Path &path,
+ unsigned int perm = 0644);
+
// Result of a `WriteToStdOutErr` operation.
//
// This is a platform-independent abstraction of `errno`. If you need to handle
@@ -141,6 +148,7 @@
// Unlinks the file given by 'file_path'.
// Returns true on success. In case of failure sets errno.
bool UnlinkPath(const std::string &file_path);
+bool UnlinkPath(const Path &file_path);
// Returns true if this path exists, following symlinks.
bool PathExists(const std::string& path);
@@ -176,6 +184,7 @@
// `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);
+bool MakeDirectories(const Path &path, unsigned int mode);
// Returns the current working directory.
// The path is platform-specific (e.g. Windows path of Windows) and absolute.
diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc
index 8032e80..3133345 100644
--- a/src/main/cpp/util/file_posix.cc
+++ b/src/main/cpp/util/file_posix.cc
@@ -207,6 +207,10 @@
return result;
}
+bool ReadFile(const Path &path, std::string *content, int max_size) {
+ return ReadFile(path.AsNativePath(), content, max_size);
+}
+
bool ReadFile(const string &filename, void *data, size_t size) {
int fd = open(filename.c_str(), O_RDONLY);
if (fd == -1) return false;
@@ -215,6 +219,10 @@
return result;
}
+bool ReadFile(const Path &filename, void *data, size_t size) {
+ return ReadFile(filename.AsNativePath(), data, size);
+}
+
bool WriteFile(const void *data, size_t size, const string &filename,
unsigned int perm) {
UnlinkPath(filename); // We don't care about the success of this.
@@ -229,6 +237,11 @@
return result == static_cast<int>(size);
}
+bool WriteFile(const void *data, size_t size, const Path &path,
+ unsigned int perm) {
+ return WriteFile(data, size, path.AsNativePath(), perm);
+}
+
int WriteToStdOutErr(const void *data, size_t size, bool to_stdout) {
size_t r = fwrite(data, 1, size, to_stdout ? stdout : stderr);
return (r == size) ? WriteResult::SUCCESS
@@ -264,6 +277,10 @@
return unlink(file_path.c_str()) == 0;
}
+bool UnlinkPath(const Path &file_path) {
+ return UnlinkPath(file_path.AsNativePath());
+}
+
bool PathExists(const string& path) {
return access(path.c_str(), F_OK) == 0;
}
@@ -399,6 +416,10 @@
return MakeDirectories(path, mode, true);
}
+bool MakeDirectories(const Path &path, unsigned int mode) {
+ return MakeDirectories(path.AsNativePath(), mode);
+}
+
string GetCwd() {
char cwdbuf[PATH_MAX];
if (getcwd(cwdbuf, sizeof cwdbuf) == NULL) {
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index b47e222..9a387bd 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -247,23 +247,9 @@
IFileMtime* CreateFileMtime() { return new WindowsFileMtime(); }
-static bool OpenFileForReading(const string& filename, HANDLE* result) {
- if (filename.empty()) {
- return false;
- }
- // TODO(laszlocsomor): remove the following check; it won't allow opening NUL.
- if (IsDevNull(filename.c_str())) {
- return true;
- }
- wstring wfilename;
- string error;
- if (!AsAbsoluteWindowsPath(filename, &wfilename, &error)) {
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "OpenFileForReading(" << filename
- << "): AsAbsoluteWindowsPath failed: " << error;
- }
+static bool OpenFileForReading(const Path& path, HANDLE* result) {
*result = ::CreateFileW(
- /* lpFileName */ wfilename.c_str(),
+ /* lpFileName */ path.AsNativePath().c_str(),
/* dwDesiredAccess */ GENERIC_READ,
/* dwShareMode */ kAllShare,
/* lpSecurityAttributes */ NULL,
@@ -291,8 +277,20 @@
content->clear();
return true;
}
+ return ReadFile(Path(filename), content, max_size);
+}
+
+bool ReadFile(const Path& path, std::string* content, int max_size) {
+ if (path.IsEmpty()) {
+ return false;
+ }
+ // TODO(laszlocsomor): remove the following check; it won't allow opening NUL.
+ if (path.IsNull()) {
+ return true;
+ }
+
HANDLE handle;
- if (!OpenFileForReading(filename, &handle)) {
+ if (!OpenFileForReading(path, &handle)) {
return false;
}
@@ -305,12 +303,19 @@
}
bool ReadFile(const string& filename, void* data, size_t size) {
- if (IsDevNull(filename.c_str())) {
+ return ReadFile(Path(filename), data, size);
+}
+
+bool ReadFile(const Path& path, void* data, size_t size) {
+ if (path.IsEmpty()) {
+ return false;
+ }
+ if (path.IsNull()) {
// mimic read(2) behavior: we can always read 0 bytes from /dev/null
return true;
}
HANDLE handle;
- if (!OpenFileForReading(filename, &handle)) {
+ if (!OpenFileForReading(path, &handle)) {
return false;
}
@@ -326,18 +331,14 @@
if (IsDevNull(filename.c_str())) {
return true; // mimic write(2) behavior with /dev/null
}
- wstring wpath;
- string error;
- if (!AsAbsoluteWindowsPath(filename, &wpath, &error)) {
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "WriteFile(" << filename
- << "): AsAbsoluteWindowsPath failed: " << error;
- return false;
- }
+ return WriteFile(data, size, Path(filename), perm);
+}
- UnlinkPathW(wpath); // We don't care about the success of this.
+bool WriteFile(const void* data, size_t size, const Path& path,
+ unsigned int perm) {
+ UnlinkPathW(path.AsNativePath()); // We don't care about the success of this.
AutoHandle handle(::CreateFileW(
- /* lpFileName */ wpath.c_str(),
+ /* lpFileName */ path.AsNativePath().c_str(),
/* dwDesiredAccess */ GENERIC_WRITE,
/* dwShareMode */ FILE_SHARE_READ,
/* lpSecurityAttributes */ NULL,
@@ -420,18 +421,11 @@
if (IsDevNull(file_path.c_str())) {
return false;
}
-
- wstring wpath;
- string error;
- if (!AsAbsoluteWindowsPath(file_path, &wpath, &error)) {
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "UnlinkPath(" << file_path
- << "): AsAbsoluteWindowsPath failed: " << error;
- return false;
- }
- return UnlinkPathW(wpath);
+ return UnlinkPath(Path(file_path));
}
+bool UnlinkPath(const Path& path) { return UnlinkPathW(path.AsNativePath()); }
+
static bool RealPath(const WCHAR* path, unique_ptr<WCHAR[]>* result = nullptr) {
// Attempt opening the path, which may be anything -- a file, a directory, a
// symlink, even a dangling symlink is fine.
@@ -694,6 +688,10 @@
return MakeDirectoriesW(wpath, mode);
}
+bool MakeDirectories(const Path& path, unsigned int mode) {
+ return MakeDirectoriesW(path.AsNativePath(), mode);
+}
+
static inline void ToLowerW(WCHAR* p) {
while (*p) {
*p++ = towlower(*p);
diff --git a/src/main/cpp/util/path_platform.h b/src/main/cpp/util/path_platform.h
index da5bf4b..27656f7 100644
--- a/src/main/cpp/util/path_platform.h
+++ b/src/main/cpp/util/path_platform.h
@@ -18,6 +18,31 @@
namespace blaze_util {
+// Platform-native, absolute, normalized path.
+class Path {
+ public:
+ Path() {}
+ explicit Path(const std::string &path);
+ bool IsEmpty() const { return path_.empty(); }
+ bool IsNull() const;
+ Path GetRelative(const std::string &r) const;
+ std::string AsPrintablePath() const;
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+ const std::wstring &AsNativePath() const { return path_; }
+#else
+ const std::string &AsNativePath() const { return path_; }
+#endif
+
+ private:
+#if defined(_WIN32) || defined(__CYGWIN__)
+ explicit Path(const std::wstring &wpath) : path_(wpath) {}
+ std::wstring path_;
+#else
+ std::string path_;
+#endif
+};
+
// Convert a path from Bazel internal form to underlying OS form.
// On Unixes this is an identity operation.
// On Windows, Bazel internal form is cygwin path, and underlying OS form
@@ -138,6 +163,10 @@
bool AsShortWindowsPath(const std::string &path, std::string *result,
std::string *error);
+#else
+
+std::string TestOnly_NormalizeAbsPath(const std::string &s);
+
#endif // defined(_WIN32) || defined(__CYGWIN__)
} // namespace blaze_util
diff --git a/src/main/cpp/util/path_posix.cc b/src/main/cpp/util/path_posix.cc
index 11577c3..eed7f64 100644
--- a/src/main/cpp/util/path_posix.cc
+++ b/src/main/cpp/util/path_posix.cc
@@ -94,4 +94,55 @@
return MakeAbsolute(ResolveEnvvars(path));
}
+static std::string NormalizeAbsPath(const std::string &p) {
+ if (p.empty() || p[0] != '/') {
+ return "";
+ }
+ typedef std::string::size_type index;
+ std::vector<std::pair<index, index> > segments;
+ for (index s = 0; s < p.size();) {
+ index e = p.find_first_of('/', s);
+ if (e == std::string::npos) {
+ e = p.size();
+ }
+ if (e > s) {
+ if (p.compare(s, e - s, "..") == 0) {
+ if (!segments.empty()) {
+ segments.pop_back();
+ }
+ } else if (p.compare(s, e - s, ".") != 0) {
+ segments.push_back(std::make_pair(s, e - s));
+ }
+ }
+ s = e + 1;
+ }
+ if (segments.empty()) {
+ return "/";
+ } else {
+ std::stringstream r;
+ for (const auto &s : segments) {
+ r << "/" << p.substr(s.first, s.second);
+ }
+ if (p[p.size() - 1] == '/') {
+ r << "/";
+ }
+ return r.str();
+ }
+}
+
+std::string TestOnly_NormalizeAbsPath(const std::string &s) {
+ return NormalizeAbsPath(s);
+}
+
+Path::Path(const std::string &path)
+ : path_(NormalizeAbsPath(MakeAbsolute(path))) {}
+
+bool Path::IsNull() const { return path_ == "/dev/null"; }
+
+Path Path::GetRelative(const std::string &r) const {
+ return Path(JoinPath(path_, r));
+}
+
+std::string Path::AsPrintablePath() const { return path_; }
+
} // namespace blaze_util
diff --git a/src/main/cpp/util/path_windows.cc b/src/main/cpp/util/path_windows.cc
index bb4da57..8621dff 100644
--- a/src/main/cpp/util/path_windows.cc
+++ b/src/main/cpp/util/path_windows.cc
@@ -455,4 +455,45 @@
return 'a' + wdrive - offset;
}
+Path::Path(const std::string& path) {
+ if (path.empty()) {
+ return;
+ } else if (IsDevNull(path.c_str())) {
+ path_ = L"NUL";
+ } else {
+ std::string error;
+ if (!AsAbsoluteWindowsPath(path, &path_, &error)) {
+ BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+ << "Path::Path(" << path
+ << "): AsAbsoluteWindowsPath failed: " << error;
+ }
+ }
+}
+
+Path Path::GetRelative(const std::string& r) const {
+ if (r.empty()) {
+ return *this;
+ } else if (IsDevNull(r.c_str())) {
+ return Path(L"NUL");
+ } else if (IsAbsolute(r)) {
+ return Path(r);
+ } else {
+ std::string error;
+ std::wstring new_path;
+ if (!AsAbsoluteWindowsPath(
+ path_ + L"\\" + CstringToWstring(r.c_str()).get(), &new_path,
+ &error)) {
+ BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+ << "Path::GetRelative failed: " << error;
+ }
+ return Path(new_path);
+ }
+}
+
+bool Path::IsNull() const { return path_ == L"NUL"; }
+
+std::string Path::AsPrintablePath() const {
+ return WstringToCstring(RemoveUncPrefixMaybe(path_.c_str())).get();
+}
+
} // namespace blaze_util
diff --git a/src/test/cpp/util/path_posix_test.cc b/src/test/cpp/util/path_posix_test.cc
index 7c08490..5bf1826 100644
--- a/src/test/cpp/util/path_posix_test.cc
+++ b/src/test/cpp/util/path_posix_test.cc
@@ -184,4 +184,54 @@
JoinPath(GetCwd(), "%PATH%"));
}
+TEST(PathPosixTest, NormalizeAbsPath) {
+ EXPECT_EQ(TestOnly_NormalizeAbsPath(""), "");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("not_absolute"), "");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("../x/y"), "");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("./"), "");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/."), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/./"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//.//"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/."), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//."), "/");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/../"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//..//"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/.."), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//.."), "/");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x"), "/x");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/"), "/x/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//x//"), "/x/");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/.."), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/../"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//x//..//"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//x//y//"), "/x/y/");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/./y/"), "/x/y/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/../y/"), "/y/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/../../y/"), "/y/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/../y/.."), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/x/../y/../"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/./x/../y/../"), "/");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo"), "/foo");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/"), "/foo/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//foo//"), "/foo/");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/.."), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/../"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//foo//..//"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("//foo//bar//"), "/foo/bar/");
+
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/./bar/"), "/foo/bar/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/../bar/"), "/bar/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/../../bar/"), "/bar/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/../bar/.."), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/foo/../bar/../"), "/");
+ EXPECT_EQ(TestOnly_NormalizeAbsPath("/./foo/../bar/../"), "/");
+}
+
} // namespace blaze_util