aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/cpp/blaze.cc19
-rw-r--r--src/main/cpp/blaze_util.cc12
-rw-r--r--src/main/cpp/blaze_util.h9
-rw-r--r--src/main/cpp/blaze_util_linux.cc1
-rw-r--r--src/main/cpp/blaze_util_platform.h16
-rw-r--r--src/main/cpp/blaze_util_posix.cc13
-rw-r--r--src/main/cpp/blaze_util_windows.cc38
-rw-r--r--src/main/cpp/option_processor.cc10
-rw-r--r--src/main/cpp/startup_options.cc20
-rw-r--r--src/main/cpp/util/BUILD17
-rw-r--r--src/main/cpp/util/file.cc38
-rw-r--r--src/main/cpp/util/file.h11
-rw-r--r--src/main/cpp/util/file_platform.h26
-rw-r--r--src/main/cpp/util/file_posix.cc26
-rw-r--r--src/main/cpp/util/file_windows.cc385
-rw-r--r--src/main/cpp/util/path.cc64
-rw-r--r--src/main/cpp/util/path.h43
-rw-r--r--src/main/cpp/util/path_platform.h119
-rw-r--r--src/main/cpp/util/path_posix.cc60
-rw-r--r--src/main/cpp/util/path_windows.cc420
-rw-r--r--src/main/cpp/workspace_layout.cc2
-rw-r--r--src/test/cpp/blaze_util_test.cc16
-rw-r--r--src/test/cpp/blaze_util_windows_test.cc23
-rw-r--r--src/test/cpp/option_processor_test.cc1
-rw-r--r--src/test/cpp/rc_file_test.cc1
-rw-r--r--src/test/cpp/rc_options_test.cc1
-rw-r--r--src/test/cpp/util/BUILD28
-rw-r--r--src/test/cpp/util/file_posix_test.cc115
-rw-r--r--src/test/cpp/util/file_test.cc2
-rw-r--r--src/test/cpp/util/file_windows_test.cc234
-rw-r--r--src/test/cpp/util/logging_test.cc1
-rw-r--r--src/test/cpp/util/path_posix_test.cc162
-rw-r--r--src/test/cpp/util/path_windows_test.cc318
-rw-r--r--src/test/cpp/workspace_layout_test.cc1
-rw-r--r--src/test/shell/bazel/testdata/embedded_tools_srcs_deps2
-rw-r--r--src/tools/launcher/BUILD2
-rw-r--r--src/tools/launcher/java_launcher.cc1
-rw-r--r--src/tools/launcher/launcher.cc2
-rw-r--r--src/tools/launcher/util/BUILD2
-rw-r--r--src/tools/launcher/util/launcher_util.cc2
-rw-r--r--src/tools/singlejar/test_util.cc1
-rw-r--r--third_party/ijar/BUILD4
-rw-r--r--third_party/ijar/mapped_file_windows.cc2
-rw-r--r--third_party/ijar/platform_utils.cc2
-rw-r--r--tools/cpp/runfiles/BUILD2
45 files changed, 1303 insertions, 971 deletions
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index d023f6491a..7487c76c90 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -64,6 +64,8 @@
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
#include "src/main/cpp/util/numbers.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/port.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/cpp/workspace_layout.h"
@@ -412,7 +414,8 @@ static vector<string> GetArgumentArray(
result.push_back("-XX:+HeapDumpOnOutOfMemoryError");
string heap_crash_path = globals->options->output_base;
- result.push_back("-XX:HeapDumpPath=" + blaze::PathAsJvmFlag(heap_crash_path));
+ result.push_back("-XX:HeapDumpPath=" +
+ blaze_util::PathAsJvmFlag(heap_crash_path));
result.push_back("-Xverify:none");
@@ -442,7 +445,7 @@ static vector<string> GetArgumentArray(
bool first = true;
for (const auto &it : globals->extracted_binaries) {
if (IsSharedLibrary(it)) {
- string libpath(blaze::PathAsJvmFlag(
+ string libpath(blaze_util::PathAsJvmFlag(
blaze_util::JoinPath(real_install_dir, blaze_util::Dirname(it))));
// Only add the library path if it's not added yet.
if (java_library_paths.find(libpath) == java_library_paths.end()) {
@@ -497,14 +500,14 @@ static vector<string> GetArgumentArray(
ToString(globals->options->connect_timeout_secs));
result.push_back("--output_user_root=" +
- blaze::ConvertPath(globals->options->output_user_root));
+ blaze_util::ConvertPath(globals->options->output_user_root));
result.push_back("--install_base=" +
- blaze::ConvertPath(globals->options->install_base));
+ blaze_util::ConvertPath(globals->options->install_base));
result.push_back("--install_md5=" + globals->install_md5);
result.push_back("--output_base=" +
- blaze::ConvertPath(globals->options->output_base));
+ blaze_util::ConvertPath(globals->options->output_base));
result.push_back("--workspace_directory=" +
- blaze::ConvertPath(globals->workspace));
+ blaze_util::ConvertPath(globals->workspace));
result.push_back("--default_system_javabase=" + GetSystemJavabase());
if (!globals->options->server_jvm_out.empty()) {
@@ -1170,8 +1173,8 @@ static void EnsureCorrectRunningVersion(BlazeServer *server) {
string prev_installation;
bool ok =
blaze_util::ReadDirectorySymlink(installation_path, &prev_installation);
- if (!ok || !CompareAbsolutePaths(prev_installation,
- globals->options->install_base)) {
+ if (!ok || !blaze_util::CompareAbsolutePaths(
+ prev_installation, globals->options->install_base)) {
if (server->Connected()) {
BAZEL_LOG(INFO)
<< "Killing running server because it is using another version of "
diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc
index d7ab44ca01..10c4e1a38f 100644
--- a/src/main/cpp/blaze_util.cc
+++ b/src/main/cpp/blaze_util.cc
@@ -43,18 +43,6 @@ const unsigned int kPostShutdownGracePeriodSeconds = 60;
const unsigned int kPostKillGracePeriodSeconds = 10;
-string MakeAbsolute(const string &p) {
- string path = ConvertPath(p);
- if (path.empty()) {
- return blaze_util::GetCwd();
- }
- if (blaze_util::IsDevNull(path.c_str()) || blaze_util::IsAbsolute(path)) {
- return path;
- }
-
- return blaze_util::JoinPath(blaze_util::GetCwd(), path);
-}
-
const char* GetUnaryOption(const char *arg,
const char *next_arg,
const char *key) {
diff --git a/src/main/cpp/blaze_util.h b/src/main/cpp/blaze_util.h
index e7f8ba1c46..084e8d6653 100644
--- a/src/main/cpp/blaze_util.h
+++ b/src/main/cpp/blaze_util.h
@@ -30,15 +30,6 @@ namespace blaze {
extern const char kServerPidFile[];
-// Returns the given path in absolute form. Does not change paths that are
-// already absolute.
-//
-// If called from working directory "/bar":
-// MakeAbsolute("foo") --> "/bar/foo"
-// MakeAbsolute("/foo") ---> "/foo"
-// MakeAbsolute("C:/foo") ---> "C:/foo"
-std::string MakeAbsolute(const std::string &path);
-
// If 'arg' matches 'key=value', returns address of 'value'.
// If it matches 'key' alone, returns address of next_arg.
// Returns NULL otherwise.
diff --git a/src/main/cpp/blaze_util_linux.cc b/src/main/cpp/blaze_util_linux.cc
index 4f01ba833b..dee5463f72 100644
--- a/src/main/cpp/blaze_util_linux.cc
+++ b/src/main/cpp/blaze_util_linux.cc
@@ -32,6 +32,7 @@
#include "src/main/cpp/util/exit_code.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/path.h"
#include "src/main/cpp/util/port.h"
#include "src/main/cpp/util/strings.h"
diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h
index 6de5eb154a..f08aa567e4 100644
--- a/src/main/cpp/blaze_util_platform.h
+++ b/src/main/cpp/blaze_util_platform.h
@@ -119,16 +119,6 @@ int ExecuteDaemon(const std::string& exe,
const std::string& server_dir,
BlazeServerStartup** server_startup);
-// 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
-// is Windows path.
-std::string ConvertPath(const std::string& path);
-
-// Converts `path` to a string that's safe to pass as path in a JVM flag.
-// See https://github.com/bazelbuild/bazel/issues/2576
-std::string PathAsJvmFlag(const std::string& path);
-
// A character used to separate paths in a list.
extern const char kListSeparator;
@@ -137,12 +127,6 @@ extern const char kListSeparator;
// Implemented via junctions on Windows.
bool SymlinkDirectories(const std::string& target, const std::string& link);
-// Compares two absolute paths. Necessary because the same path can have
-// multiple different names under msys2: "C:\foo\bar" or "C:/foo/bar"
-// (Windows-style) and "/c/foo/bar" (msys2 style). Returns if the paths are
-// equal.
-bool CompareAbsolutePaths(const std::string& a, const std::string& b);
-
struct BlazeLock {
#if defined(COMPILER_MSVC) || defined(__CYGWIN__)
/* HANDLE */ void* handle;
diff --git a/src/main/cpp/blaze_util_posix.cc b/src/main/cpp/blaze_util_posix.cc
index 4f8d9c766c..156b021529 100644
--- a/src/main/cpp/blaze_util_posix.cc
+++ b/src/main/cpp/blaze_util_posix.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/main/cpp/blaze_util_platform.h"
+
#define _WITH_DPRINTF
#include <dirent.h>
#include <errno.h>
@@ -36,7 +38,6 @@
#include <cinttypes>
#include "src/main/cpp/blaze_util.h"
-#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/global_variables.h"
#include "src/main/cpp/startup_options.h"
#include "src/main/cpp/util/errors.h"
@@ -45,6 +46,8 @@
#include "src/main/cpp/util/logging.h"
#include "src/main/cpp/util/md5.h"
#include "src/main/cpp/util/numbers.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
namespace blaze {
@@ -173,10 +176,6 @@ void ExecuteProgram(const string& exe, const vector<string>& args_vector) {
execv(exe.c_str(), const_cast<char**>(argv));
}
-std::string ConvertPath(const std::string &path) { return path; }
-
-std::string PathAsJvmFlag(const std::string& path) { return path; }
-
const char kListSeparator = ':';
bool SymlinkDirectories(const string &target, const string &link) {
@@ -403,10 +402,6 @@ int ExecuteDaemon(const string& exe,
}
}
-bool CompareAbsolutePaths(const string& a, const string& b) {
- return a == b;
-}
-
string GetHashedBaseDir(const string& root, const string& hashable) {
unsigned char buf[blaze_util::Md5Digest::kDigestLength];
blaze_util::Md5Digest digest;
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index 565e8876c2..d41a2f4493 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/main/cpp/blaze_util_platform.h"
+
#include <fcntl.h>
#include <stdarg.h> // va_start, va_end, va_list
@@ -33,7 +35,6 @@
#include <vector>
#include "src/main/cpp/blaze_util.h"
-#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/global_variables.h"
#include "src/main/cpp/startup_options.h"
#include "src/main/cpp/util/errors.h"
@@ -43,6 +44,8 @@
#include "src/main/cpp/util/logging.h"
#include "src/main/cpp/util/md5.h"
#include "src/main/cpp/util/numbers.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/native/windows/file.h"
#include "src/main/native/windows/util.h"
@@ -652,36 +655,6 @@ void ExecuteProgram(const string& exe, const std::vector<string>& args_vector) {
const char kListSeparator = ';';
-string PathAsJvmFlag(const string& path) {
- string spath;
- string error;
- if (!blaze_util::AsShortWindowsPath(path, &spath, &error)) {
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "PathAsJvmFlag(" << path
- << "): AsShortWindowsPath failed: " << error;
- }
- // Convert backslashes to forward slashes, in order to avoid the JVM parsing
- // Windows paths as if they contained escaped characters.
- // See https://github.com/bazelbuild/bazel/issues/2576
- std::replace(spath.begin(), spath.end(), '\\', '/');
- return spath;
-}
-
-string ConvertPath(const string& path) {
- // The path may not be Windows-style and may not be normalized, so convert it.
- wstring wpath;
- string error;
- if (!blaze_util::AsAbsoluteWindowsPath(path, &wpath, &error)) {
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "ConvertPath(" << path
- << "): AsAbsoluteWindowsPath failed: " << error;
- }
- std::transform(wpath.begin(), wpath.end(), wpath.begin(), ::towlower);
- return string(blaze_util::WstringToCstring(
- blaze_util::RemoveUncPrefixMaybe(wpath.c_str()))
- .get());
-}
-
bool SymlinkDirectories(const string &posix_target, const string &posix_name) {
wstring name;
wstring target;
@@ -708,9 +681,6 @@ bool SymlinkDirectories(const string &posix_target, const string &posix_name) {
return true;
}
-bool CompareAbsolutePaths(const string& a, const string& b) {
- return ConvertPath(a) == ConvertPath(b);
-}
#ifndef STILL_ACTIVE
#define STILL_ACTIVE (259) // From MSDN about GetExitCodeProcess.
diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc
index 7df0af7203..a1d7f9d976 100644
--- a/src/main/cpp/option_processor.cc
+++ b/src/main/cpp/option_processor.cc
@@ -27,6 +27,8 @@
#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/cpp/workspace_layout.h"
@@ -136,7 +138,7 @@ blaze_exit_code::ExitCode OptionProcessor::FindUserBlazerc(
"." + parsed_startup_options_->GetLowercaseProductName() + "rc";
if (cmd_line_rc_file != nullptr) {
- string rcFile = MakeAbsolute(cmd_line_rc_file);
+ string rcFile = blaze_util::MakeAbsolute(cmd_line_rc_file);
if (!blaze_util::CanReadFile(rcFile)) {
blaze_util::StringPrintf(error,
"Error: Unable to read %s file '%s'.", rc_basename.c_str(),
@@ -424,7 +426,7 @@ static void PreprocessEnvString(string* env_str) {
} else if (name == "TMP") {
// A valid Windows path "c:/foo" is also a valid Unix path list of
// ["c", "/foo"] so must use ConvertPath here. See GitHub issue #1684.
- env_str->assign("TMP=" + ConvertPath(env_str->substr(pos + 1)));
+ env_str->assign("TMP=" + blaze_util::ConvertPath(env_str->substr(pos + 1)));
}
}
@@ -477,7 +479,7 @@ std::vector<std::string> OptionProcessor::GetBlazercAndEnvCommandArgs(
// from multiple places.
if (rcfile_indexes.find(source_path) != rcfile_indexes.end()) continue;
- result.push_back("--rc_source=" + blaze::ConvertPath(source_path));
+ result.push_back("--rc_source=" + blaze_util::ConvertPath(source_path));
rcfile_indexes[source_path] = cur_index;
cur_index++;
}
@@ -503,7 +505,7 @@ std::vector<std::string> OptionProcessor::GetBlazercAndEnvCommandArgs(
for (const string& env_var : env) {
result.push_back("--client_env=" + env_var);
}
- result.push_back("--client_cwd=" + blaze::ConvertPath(cwd));
+ result.push_back("--client_cwd=" + blaze_util::ConvertPath(cwd));
return result;
}
diff --git a/src/main/cpp/startup_options.cc b/src/main/cpp/startup_options.cc
index 5ccec1d635..3699d40498 100644
--- a/src/main/cpp/startup_options.cc
+++ b/src/main/cpp/startup_options.cc
@@ -25,6 +25,8 @@
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
#include "src/main/cpp/util/numbers.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/cpp/workspace_layout.h"
@@ -93,7 +95,7 @@ StartupOptions::StartupOptions(const string &product_name,
original_startup_options_(std::vector<RcStartupFlag>()) {
bool testing = !blaze::GetEnv("TEST_TMPDIR").empty();
if (testing) {
- output_root = MakeAbsolute(blaze::GetEnv("TEST_TMPDIR"));
+ output_root = blaze_util::MakeAbsolute(blaze::GetEnv("TEST_TMPDIR"));
max_idle_secs = 15;
BAZEL_LOG(USER) << "$TEST_TMPDIR defined: output root default is '"
<< output_root << "' and max_idle_secs default is '"
@@ -187,19 +189,19 @@ blaze_exit_code::ExitCode StartupOptions::ProcessArg(
const char* value = NULL;
if ((value = GetUnaryOption(arg, next_arg, "--output_base")) != NULL) {
- output_base = MakeAbsolute(value);
+ output_base = blaze_util::MakeAbsolute(value);
option_sources["output_base"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--install_base")) != NULL) {
- install_base = MakeAbsolute(value);
+ install_base = blaze_util::MakeAbsolute(value);
option_sources["install_base"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--output_user_root")) != NULL) {
- output_user_root = MakeAbsolute(value);
+ output_user_root = blaze_util::MakeAbsolute(value);
option_sources["output_user_root"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg,
"--server_jvm_out")) != NULL) {
- server_jvm_out = MakeAbsolute(value);
+ server_jvm_out = blaze_util::MakeAbsolute(value);
option_sources["server_jvm_out"] = rcfile;
} else if (GetNullaryOption(arg, "--deep_execroot")) {
deep_execroot = true;
@@ -221,7 +223,7 @@ blaze_exit_code::ExitCode StartupOptions::ProcessArg(
"--host_javabase")) != NULL) {
// TODO(bazel-team): Consider examining the javabase and re-execing in case
// of architecture mismatch.
- host_javabase = MakeAbsolute(value);
+ host_javabase = blaze_util::MakeAbsolute(value);
option_sources["host_javabase"] = rcfile;
} else if ((value = GetUnaryOption(arg, next_arg, "--host_jvm_args")) !=
NULL) {
@@ -486,8 +488,8 @@ void StartupOptions::AddJVMArgumentSuffix(const string &real_install_dir,
const string &jar_path,
std::vector<string> *result) const {
result->push_back("-jar");
- result->push_back(
- blaze::PathAsJvmFlag(blaze_util::JoinPath(real_install_dir, jar_path)));
+ result->push_back(blaze_util::PathAsJvmFlag(
+ blaze_util::JoinPath(real_install_dir, jar_path)));
}
blaze_exit_code::ExitCode StartupOptions::AddJVMArguments(
@@ -502,7 +504,7 @@ void StartupOptions::AddJVMLoggingArguments(std::vector<string> *result) const {
const string propFile =
blaze_util::JoinPath(output_base, "javalog.properties");
string java_log(
- blaze::PathAsJvmFlag(blaze_util::JoinPath(output_base, "java.log")));
+ blaze_util::PathAsJvmFlag(blaze_util::JoinPath(output_base, "java.log")));
if (!blaze_util::WriteFile("handlers=java.util.logging.FileHandler\n"
".level=INFO\n"
"java.util.logging.FileHandler.level=INFO\n"
diff --git a/src/main/cpp/util/BUILD b/src/main/cpp/util/BUILD
index 254b6f8f1f..6f53b4d590 100644
--- a/src/main/cpp/util/BUILD
+++ b/src/main/cpp/util/BUILD
@@ -15,13 +15,15 @@ cc_library(
"file_platform.h",
"md5.h",
"numbers.h",
+ "path.h",
+ "path_platform.h",
"port.h",
],
visibility = ["//visibility:public"],
deps = [
":blaze_exit_code",
":errors",
- ":file",
+ ":filesystem",
":md5",
":numbers",
":port",
@@ -30,18 +32,25 @@ cc_library(
)
cc_library(
- name = "file",
- srcs = ["file.cc"] + select({
+ name = "filesystem",
+ srcs = [
+ "file.cc",
+ "path.cc",
+ ] + select({
"//src/conditions:windows": [
"file_windows.cc",
+ "path_windows.cc",
],
"//conditions:default": [
"file_posix.cc",
+ "path_posix.cc",
],
}),
hdrs = [
"file.h",
"file_platform.h",
+ "path.h",
+ "path_platform.h",
],
visibility = [
":ijar",
@@ -108,7 +117,7 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
":blaze_exit_code",
- ":file",
+ ":filesystem",
":logging",
],
)
diff --git a/src/main/cpp/util/file.cc b/src/main/cpp/util/file.cc
index 3eb614c0dc..041d7798dc 100644
--- a/src/main/cpp/util/file.cc
+++ b/src/main/cpp/util/file.cc
@@ -11,6 +11,9 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+
+#include "src/main/cpp/util/file.h"
+
#include <limits.h> // PATH_MAX
#include <algorithm>
@@ -19,7 +22,7 @@
#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/path.h"
#include "src/main/cpp/util/strings.h"
namespace blaze_util {
@@ -85,39 +88,6 @@ bool WriteFile(const std::string &content, const std::string &filename,
return WriteFile(content.c_str(), content.size(), filename, perm);
}
-string Dirname(const string &path) {
- return SplitPath(path).first;
-}
-
-string Basename(const string &path) {
- return SplitPath(path).second;
-}
-
-string JoinPath(const string &path1, const string &path2) {
- if (path1.empty()) {
- // "" + "/bar"
- return path2;
- }
-
- if (path1[path1.size() - 1] == '/') {
- if (path2.find('/') == 0) {
- // foo/ + /bar
- return path1 + path2.substr(1);
- } else {
- // foo/ + bar
- return path1 + path2;
- }
- } else {
- if (path2.find('/') == 0) {
- // foo + /bar
- return path1 + path2;
- } else {
- // foo + bar
- return path1 + "/" + path2;
- }
- }
-}
-
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 4bc165110e..235ec87527 100644
--- a/src/main/cpp/util/file.h
+++ b/src/main/cpp/util/file.h
@@ -63,17 +63,6 @@ bool ReadFrom(file_handle_type handle, void *data, size_t size);
bool WriteFile(const std::string &content, const std::string &filename,
unsigned int perm = 0644);
-// Returns the part of the path before the final "/". If there is a single
-// leading "/" in the path, the result will be the leading "/". If there is
-// no "/" in the path, the result is the empty prefix of the input (i.e., "").
-std::string Dirname(const std::string &path);
-
-// Returns the part of the path after the final "/". If there is no
-// "/" in the path, the result is the same as the input.
-std::string Basename(const std::string &path);
-
-std::string JoinPath(const std::string &path1, const std::string &path2);
-
// 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 5b96133d3a..ac4fc3ab00 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -50,9 +50,6 @@ class IFileMtime {
// Creates a platform-specific implementation of `IFileMtime`.
IFileMtime *CreateFileMtime();
-// Split a path to dirname and basename parts.
-std::pair<std::string, std::string> SplitPath(const std::string &path);
-
#if defined(COMPILER_MSVC) || defined(__CYGWIN__)
// We cannot include <windows.h> because it #defines many symbols that conflict
// with our function names, e.g. GetUserName, SendMessage.
@@ -162,17 +159,9 @@ bool CanExecuteFile(const std::string &path);
// Follows symlinks/junctions.
bool CanAccessDirectory(const std::string &path);
-bool IsDevNull(const char *path);
-
// Returns true if `path` refers to a directory or a symlink/junction to one.
bool IsDirectory(const std::string& path);
-// Returns true if `path` is the root directory or a Windows drive root.
-bool IsRootDirectory(const std::string &path);
-
-// Returns true if `path` is absolute.
-bool IsAbsolute(const std::string &path);
-
// Calls fsync() on the file (or directory) specified in 'file_path'.
// pdie() if syncing fails.
void SyncFile(const std::string& path);
@@ -211,20 +200,7 @@ void ForEachDirectoryEntry(const std::string &path,
DirectoryEntryConsumer *consume);
#if defined(COMPILER_MSVC) || defined(__CYGWIN__)
-const wchar_t *RemoveUncPrefixMaybe(const wchar_t *ptr);
-
-bool AsWindowsPath(const std::string &path, std::string *result,
- std::string *error);
-
-bool AsAbsoluteWindowsPath(const std::string &path, std::wstring *wpath,
- std::string *error);
-
-// Same as `AsWindowsPath`, but returns a lowercase 8dot3 style shortened path.
-// Result will never have a UNC prefix, nor a trailing "/" or "\".
-// Works also for non-existent paths; shortens as much of them as it can.
-// Also works for non-existent drives.
-bool AsShortWindowsPath(const std::string &path, std::string *result,
- std::string *error);
+std::wstring GetCwdW();
#endif // defined(COMPILER_MSVC) || defined(__CYGWIN__)
} // namespace blaze_util
diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc
index 5136df0655..df05b54a45 100644
--- a/src/main/cpp/util/file_posix.cc
+++ b/src/main/cpp/util/file_posix.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/main/cpp/util/file_platform.h"
+
#include <dirent.h> // DIR, dirent, opendir, closedir
#include <errno.h>
#include <fcntl.h> // O_RDONLY
@@ -29,6 +31,8 @@
#include "src/main/cpp/util/exit_code.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
namespace blaze_util {
@@ -178,18 +182,6 @@ IPipe* CreatePipe() {
return new PosixPipe(fd[0], fd[1]);
}
-pair<string, string> SplitPath(const string &path) {
- size_t pos = path.rfind('/');
-
- // Handle the case with no '/' in 'path'.
- if (pos == string::npos) return std::make_pair("", path);
-
- // Handle the case with a single leading '/' in 'path'.
- if (pos == 0) return std::make_pair(string(path, 0, 1), string(path, 1));
-
- return std::make_pair(string(path, 0, pos), string(path, pos + 1));
-}
-
int ReadFromHandle(file_handle_type fd, void *data, size_t size, int *error) {
int result = read(fd, data, size);
if (error != nullptr) {
@@ -299,10 +291,6 @@ static bool CanAccess(const string &path, bool read, bool write, bool exec) {
return access(path.c_str(), mode) == 0;
}
-bool IsDevNull(const char *path) {
- return path != NULL && *path != 0 && strncmp("/dev/null\0", path, 10) == 0;
-}
-
bool CanReadFile(const std::string &path) {
return !IsDirectory(path) && CanAccess(path, true, false, false);
}
@@ -320,12 +308,6 @@ bool IsDirectory(const string& path) {
return stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode);
}
-bool IsRootDirectory(const string &path) {
- return path.size() == 1 && path[0] == '/';
-}
-
-bool IsAbsolute(const string &path) { return !path.empty() && path[0] == '/'; }
-
void SyncFile(const string& path) {
const char* file_path = path.c_str();
int fd = open(file_path, O_RDONLY);
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 39160c9c22..537852eb70 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -24,6 +24,8 @@
#include "src/main/cpp/util/exit_code.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/native/windows/file.h"
#include "src/main/native/windows/util.h"
@@ -40,11 +42,7 @@ using bazel::windows::GetLongPath;
using bazel::windows::HasUncPrefix;
using bazel::windows::OpenDirectory;
-// Returns the current working directory as a Windows path.
-// The result may have a UNC prefix.
-static unique_ptr<WCHAR[]> GetCwdW();
-static char GetCurrentDrive();
// Returns true if `path` refers to a directory or (non-dangling) junction.
// `path` must be a normalized Windows path, with UNC prefix (and absolute) if
@@ -57,64 +55,15 @@ static bool IsDirectoryW(const wstring& path);
// necessary.
static bool UnlinkPathW(const wstring& path);
-static bool IsRootDirectoryW(const wstring& path);
-
static bool MakeDirectoriesW(const wstring& path);
static bool CanReadFileW(const wstring& path);
-// Returns a normalized form of the input `path`.
-//
-// `path` must be a relative or absolute Windows path, it may use "/" instead of
-// "\" but must not be a Unix-style (MSYS) path.
-// The result won't have a UNC prefix, even if `path` did.
-//
-// Normalization means removing "." references, resolving ".." references, and
-// deduplicating "/" characters while converting them to "\".
-// For example if `path` is "foo/../bar/.//qux", the result is "bar\qux".
-//
-// Uplevel references that cannot go any higher in the directory tree are simply
-// ignored, e.g. "c:/.." is normalized to "c:\" and "../../foo" is normalized to
-// "foo".
-//
-// Visible for testing, would be static otherwise.
-string NormalizeWindowsPath(string path);
-
-template <typename char_type>
-struct CharTraits {
- static bool IsAlpha(char_type ch);
-};
-
-template <>
-struct CharTraits<char> {
- static bool IsAlpha(char ch) { return isalpha(ch); }
-};
-
-template <>
-struct CharTraits<wchar_t> {
- static bool IsAlpha(wchar_t ch) { return iswalpha(ch); }
-};
-
template <typename char_type>
static bool IsPathSeparator(char_type ch) {
return ch == '/' || ch == '\\';
}
-template <typename char_type>
-static bool HasDriveSpecifierPrefix(const char_type* ch) {
- return CharTraits<char_type>::IsAlpha(ch[0]) && ch[1] == ':';
-}
-
-static void AddUncPrefixMaybe(wstring* path, size_t max_path = MAX_PATH) {
- if (path->size() >= max_path && !HasUncPrefix(path->c_str())) {
- *path = wstring(L"\\\\?\\") + *path;
- }
-}
-
-const wchar_t* RemoveUncPrefixMaybe(const wchar_t* ptr) {
- return ptr + (HasUncPrefix(ptr) ? 4 : 0);
-}
-
class WindowsPipe : public IPipe {
public:
WindowsPipe(const HANDLE& read_handle, const HANDLE& write_handle)
@@ -300,239 +249,6 @@ FILETIME WindowsFileMtime::GetFuture(WORD years) {
IFileMtime* CreateFileMtime() { return new WindowsFileMtime(); }
-// Checks if the path is absolute and/or is a root path.
-//
-// If `must_be_root` is true, then in addition to being absolute, the path must
-// also be just the root part, no other components, e.g. "c:\" is both absolute
-// and root, but "c:\foo" is just absolute.
-template <typename char_type>
-static bool IsRootOrAbsolute(const basic_string<char_type>& path,
- bool must_be_root) {
- // An absolute path is one that starts with "/", "\", "c:/", "c:\",
- // "\\?\c:\", or rarely "\??\c:\" or "\\.\c:\".
- //
- // It is unclear whether the UNC prefix is just "\\?\" or is "\??\" also
- // valid (in some cases it seems to be, though MSDN doesn't mention it).
- return
- // path is (or starts with) "/" or "\"
- ((must_be_root ? path.size() == 1 : !path.empty()) &&
- IsPathSeparator(path[0])) ||
- // path is (or starts with) "c:/" or "c:\" or similar
- ((must_be_root ? path.size() == 3 : path.size() >= 3) &&
- HasDriveSpecifierPrefix(path.c_str()) && IsPathSeparator(path[2])) ||
- // path is (or starts with) "\\?\c:\" or "\??\c:\" or similar
- ((must_be_root ? path.size() == 7 : path.size() >= 7) &&
- HasUncPrefix(path.c_str()) &&
- HasDriveSpecifierPrefix(path.c_str() + 4) && IsPathSeparator(path[6]));
-}
-
-template <typename char_type>
-static pair<basic_string<char_type>, basic_string<char_type> > SplitPathImpl(
- const basic_string<char_type>& path) {
- if (path.empty()) {
- return std::make_pair(basic_string<char_type>(), basic_string<char_type>());
- }
-
- size_t pos = path.size() - 1;
- for (auto it = path.crbegin(); it != path.crend(); ++it, --pos) {
- if (IsPathSeparator(*it)) {
- if ((pos == 2 || pos == 6) &&
- IsRootOrAbsolute(path.substr(0, pos + 1), /* must_be_root */ true)) {
- // Windows path, top-level directory, e.g. "c:\foo",
- // result is ("c:\", "foo").
- // Or UNC path, top-level directory, e.g. "\\?\c:\foo"
- // result is ("\\?\c:\", "foo").
- return std::make_pair(
- // Include the "/" or "\" in the drive specifier.
- path.substr(0, pos + 1), path.substr(pos + 1));
- } else {
- // Windows path (neither top-level nor drive root), Unix path, or
- // relative path.
- return std::make_pair(
- // If the only "/" is the leading one, then that shall be the first
- // pair element, otherwise the substring up to the rightmost "/".
- pos == 0 ? path.substr(0, 1) : path.substr(0, pos),
- // If the rightmost "/" is the tail, then the second pair element
- // should be empty.
- pos == path.size() - 1 ? basic_string<char_type>()
- : path.substr(pos + 1));
- }
- }
- }
- // Handle the case with no '/' or '\' in `path`.
- return std::make_pair(basic_string<char_type>(), path);
-}
-
-pair<string, string> SplitPath(const string& path) {
- return SplitPathImpl(path);
-}
-
-pair<wstring, wstring> SplitPathW(const wstring& path) {
- return SplitPathImpl(path);
-}
-
-bool AsWindowsPath(const string& path, string* result, string* error) {
- if (path.empty()) {
- result->clear();
- return true;
- }
- if (IsDevNull(path.c_str())) {
- result->assign("NUL");
- return true;
- }
- if (HasUncPrefix(path.c_str())) {
- // Path has "\\?\" prefix --> assume it's already Windows-style.
- *result = path.c_str();
- return true;
- }
- if (IsPathSeparator(path[0]) && path.size() > 1 && IsPathSeparator(path[1])) {
- // Unsupported path: "\\" or "\\server\path", or some degenerate form of
- // these, such as "//foo".
- if (error) {
- *error = "network paths are unsupported";
- }
- return false;
- }
- if (HasDriveSpecifierPrefix(path.c_str()) &&
- (path.size() < 3 || !IsPathSeparator(path[2]))) {
- // Unsupported path: "c:" or "c:foo"
- if (error) {
- *error = "working-directory relative paths are unsupported";
- }
- return false;
- }
-
- string mutable_path = path;
- if (path[0] == '/') {
- if (error) {
- *error = "Unix-style paths are unsupported";
- }
- return false;
- }
-
- if (path[0] == '\\') {
- // This is an absolute Windows path on the current drive, e.g. "\foo\bar".
- mutable_path = string(1, GetCurrentDrive()) + ":" + path;
- } // otherwise this is a relative path, or absolute Windows path.
-
- result->assign(NormalizeWindowsPath(mutable_path));
- return true;
-}
-
-// Converts a UTF8-encoded `path` to a normalized, widechar Windows path.
-//
-// Returns true if conversion succeeded and sets the contents of `result` to it.
-//
-// The input `path` may be an absolute or relative Windows path.
-//
-// The returned path is normalized (see NormalizeWindowsPath).
-//
-// If `path` had a "\\?\" prefix then the function assumes it's already Windows
-// style and converts it to wstring without any alterations.
-// Otherwise `path` is normalized and converted to a Windows path and the result
-// won't have a "\\?\" prefix even if it's longer than MAX_PATH (adding the
-// prefix is the caller's responsibility).
-//
-// The method recognizes current-drive-relative Windows paths ("\foo") turning
-// them into absolute paths ("c:\foo").
-bool AsWindowsPath(const string& path, wstring* result, string* error) {
- string normalized_win_path;
- if (!AsWindowsPath(path, &normalized_win_path, error)) {
- return false;
- }
-
- result->assign(CstringToWstring(normalized_win_path.c_str()).get());
- return true;
-}
-
-bool AsAbsoluteWindowsPath(const string& path, wstring* result, string* error) {
- if (path.empty()) {
- result->clear();
- return true;
- }
- if (IsDevNull(path.c_str())) {
- result->assign(L"NUL");
- return true;
- }
- if (!AsWindowsPath(path, result, error)) {
- return false;
- }
- if (!IsRootOrAbsolute(*result, /* must_be_root */ false)) {
- *result = wstring(GetCwdW().get()) + L"\\" + *result;
- }
- if (!HasUncPrefix(result->c_str())) {
- *result = wstring(L"\\\\?\\") + *result;
- }
- return true;
-}
-
-bool AsShortWindowsPath(const string& path, string* result, string* error) {
- if (IsDevNull(path.c_str())) {
- result->assign("NUL");
- return true;
- }
-
- result->clear();
- wstring wpath;
- wstring wsuffix;
- if (!AsAbsoluteWindowsPath(path, &wpath, error)) {
- return false;
- }
- DWORD size = ::GetShortPathNameW(wpath.c_str(), nullptr, 0);
- if (size == 0) {
- // GetShortPathNameW can fail if `wpath` does not exist. This is expected
- // when we are about to create a file at that path, so instead of failing,
- // walk up in the path until we find a prefix that exists and can be
- // shortened, or is a root directory. Save the non-existent tail in
- // `wsuffix`, we'll add it back later.
- std::vector<wstring> segments;
- while (size == 0 && !IsRootDirectoryW(wpath)) {
- pair<wstring, wstring> split = SplitPathW(wpath);
- wpath = split.first;
- segments.push_back(split.second);
- size = ::GetShortPathNameW(wpath.c_str(), nullptr, 0);
- }
-
- // Join all segments.
- std::wostringstream builder;
- bool first = true;
- for (auto it = segments.crbegin(); it != segments.crend(); ++it) {
- if (!first || !IsRootDirectoryW(wpath)) {
- builder << L'\\' << *it;
- } else {
- builder << *it;
- }
- first = false;
- }
- wsuffix = builder.str();
- }
-
- wstring wresult;
- if (IsRootDirectoryW(wpath)) {
- // Strip the UNC prefix from `wpath`, and the leading "\" from `wsuffix`.
- wresult = wstring(RemoveUncPrefixMaybe(wpath.c_str())) + wsuffix;
- } else {
- unique_ptr<WCHAR[]> wshort(
- new WCHAR[size]); // size includes null-terminator
- if (size - 1 != ::GetShortPathNameW(wpath.c_str(), wshort.get(), size)) {
- if (error) {
- string last_error = GetLastErrorString();
- std::stringstream msg;
- msg << "AsShortWindowsPath(" << path << "): GetShortPathNameW("
- << blaze_util::WstringToString(wpath) << ") failed: " << last_error;
- *error = msg.str();
- }
- return false;
- }
- // GetShortPathNameW may preserve the UNC prefix in the result, so strip it.
- wresult = wstring(RemoveUncPrefixMaybe(wshort.get())) + wsuffix;
- }
-
- result->assign(WstringToCstring(wresult.c_str()).get());
- ToLower(result);
- return true;
-}
-
static bool OpenFileForReading(const string& filename, HANDLE* result) {
if (filename.empty()) {
return false;
@@ -1067,14 +783,6 @@ bool CanAccessDirectory(const std::string& path) {
return true;
}
-bool IsDevNull(const char* path) {
- return path != NULL && *path != 0 &&
- (strncmp("/dev/null\0", path, 10) == 0 ||
- ((path[0] == 'N' || path[0] == 'n') &&
- (path[1] == 'U' || path[1] == 'u') &&
- (path[2] == 'L' || path[2] == 'l') && path[3] == 0));
-}
-
static bool IsDirectoryW(const wstring& path) {
DWORD attrs = ::GetFileAttributesW(path.c_str());
return (attrs != INVALID_FILE_ATTRIBUTES) &&
@@ -1097,21 +805,11 @@ bool IsDirectory(const string& path) {
return IsDirectoryW(wpath);
}
-bool IsRootDirectory(const string& path) {
- return IsRootOrAbsolute(path, true);
-}
-
-bool IsAbsolute(const string& path) { return IsRootOrAbsolute(path, false); }
-
void SyncFile(const string& path) {
// No-op on Windows native; unsupported by Cygwin.
// fsync always fails on Cygwin with "Permission denied" for some reason.
}
-static bool IsRootDirectoryW(const wstring& path) {
- return IsRootOrAbsolute(path, true);
-}
-
static bool MakeDirectoriesW(const wstring& path) {
if (path.empty()) {
return false;
@@ -1150,7 +848,7 @@ bool MakeDirectories(const string& path, unsigned int mode) {
return MakeDirectoriesW(wpath);
}
-static unique_ptr<WCHAR[]> GetCwdW() {
+std::wstring GetCwdW() {
DWORD len = ::GetCurrentDirectoryW(0, nullptr);
unique_ptr<WCHAR[]> cwd(new WCHAR[len]);
if (!::GetCurrentDirectoryW(len, cwd.get())) {
@@ -1160,18 +858,12 @@ static unique_ptr<WCHAR[]> GetCwdW() {
for (WCHAR* p = cwd.get(); *p != 0; ++p) {
*p = towlower(*p);
}
- return std::move(cwd);
+ return std::wstring(cwd.get());
}
string GetCwd() {
- return string(WstringToCstring(RemoveUncPrefixMaybe(GetCwdW().get())).get());
-}
-
-static char GetCurrentDrive() {
- unique_ptr<wchar_t[]> cwd = GetCwdW();
- wchar_t wdrive = RemoveUncPrefixMaybe(cwd.get())[0];
- wchar_t offset = wdrive >= L'A' && wdrive <= L'Z' ? L'A' : L'a';
- return 'a' + wdrive - offset;
+ return string(
+ WstringToCstring(RemoveUncPrefixMaybe(GetCwdW().c_str())).get());
}
bool ChangeDirectory(const string& path) {
@@ -1227,69 +919,4 @@ void ForEachDirectoryEntry(const string &path,
::FindClose(handle);
}
-string NormalizeWindowsPath(string path) {
- if (path.empty()) {
- return "";
- }
- if (path[0] == '/') {
- // This is an absolute MSYS path, error out.
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "NormalizeWindowsPath(" << path << "): expected a Windows path";
- }
- if (path.size() >= 4 && HasUncPrefix(path.c_str())) {
- path = path.substr(4);
- }
-
- static const string dot(".");
- static const string dotdot("..");
-
- std::vector<string> segments;
- int segment_start = -1;
- // Find the path segments in `path` (separated by "/").
- for (int i = 0;; ++i) {
- if (!IsPathSeparator(path[i]) && path[i] != '\0') {
- // The current character does not end a segment, so start one unless it's
- // already started.
- if (segment_start < 0) {
- segment_start = i;
- }
- } else if (segment_start >= 0 && i > segment_start) {
- // The current character is "/" or "\0", so this ends a segment.
- // Add that to `segments` if there's anything to add; handle "." and "..".
- string segment(path, segment_start, i - segment_start);
- segment_start = -1;
- if (segment == dotdot) {
- if (!segments.empty() &&
- !HasDriveSpecifierPrefix(segments[0].c_str())) {
- segments.pop_back();
- }
- } else if (segment != dot) {
- segments.push_back(segment);
- }
- }
- if (path[i] == '\0') {
- break;
- }
- }
-
- // Handle the case when `path` is just a drive specifier (or some degenerate
- // form of it, e.g. "c:\..").
- if (segments.size() == 1 && segments[0].size() == 2 &&
- HasDriveSpecifierPrefix(segments[0].c_str())) {
- return segments[0] + '\\';
- }
-
- // Join all segments.
- bool first = true;
- std::ostringstream result;
- for (const auto& s : segments) {
- if (!first) {
- result << '\\';
- }
- first = false;
- result << s;
- }
- return result.str();
-}
-
} // namespace blaze_util
diff --git a/src/main/cpp/util/path.cc b/src/main/cpp/util/path.cc
new file mode 100644
index 0000000000..efa10b81d4
--- /dev/null
+++ b/src/main/cpp/util/path.cc
@@ -0,0 +1,64 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/main/cpp/util/path.h"
+
+#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path_platform.h"
+
+namespace blaze_util {
+
+std::string Dirname(const std::string &path) { return SplitPath(path).first; }
+
+std::string Basename(const std::string &path) { return SplitPath(path).second; }
+
+std::string JoinPath(const std::string &path1, const std::string &path2) {
+ if (path1.empty()) {
+ // "" + "/bar"
+ return path2;
+ }
+
+ if (path1[path1.size() - 1] == '/') {
+ if (path2.find('/') == 0) {
+ // foo/ + /bar
+ return path1 + path2.substr(1);
+ } else {
+ // foo/ + bar
+ return path1 + path2;
+ }
+ } else {
+ if (path2.find('/') == 0) {
+ // foo + /bar
+ return path1 + path2;
+ } else {
+ // foo + bar
+ return path1 + "/" + path2;
+ }
+ }
+}
+
+std::string MakeAbsolute(const std::string &path) {
+ std::string converted_path = ConvertPath(path);
+ if (converted_path.empty()) {
+ return GetCwd();
+ }
+ if (IsDevNull(converted_path.c_str()) ||
+ blaze_util::IsAbsolute(converted_path)) {
+ return converted_path;
+ }
+
+ return JoinPath(blaze_util::GetCwd(), converted_path);
+}
+
+} // namespace blaze_util
diff --git a/src/main/cpp/util/path.h b/src/main/cpp/util/path.h
new file mode 100644
index 0000000000..38e735b7af
--- /dev/null
+++ b/src/main/cpp/util/path.h
@@ -0,0 +1,43 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef BAZEL_SRC_MAIN_CPP_UTIL_PATH_H_
+#define BAZEL_SRC_MAIN_CPP_UTIL_PATH_H_
+
+#include <string>
+
+namespace blaze_util {
+
+// Returns the part of the path before the final "/". If there is a single
+// leading "/" in the path, the result will be the leading "/". If there is
+// no "/" in the path, the result is the empty prefix of the input (i.e., "").
+std::string Dirname(const std::string &path);
+
+// Returns the part of the path after the final "/". If there is no
+// "/" in the path, the result is the same as the input.
+std::string Basename(const std::string &path);
+
+std::string JoinPath(const std::string &path1, const std::string &path2);
+
+// Returns the given path in absolute form. Does not change paths that are
+// already absolute.
+//
+// If called from working directory "/bar":
+// MakeAbsolute("foo") --> "/bar/foo"
+// MakeAbsolute("/foo") ---> "/foo"
+// MakeAbsolute("C:/foo") ---> "C:/foo"
+std::string MakeAbsolute(const std::string &path);
+
+} // namespace blaze_util
+
+#endif // BAZEL_SRC_MAIN_CPP_UTIL_PATH_H_
diff --git a/src/main/cpp/util/path_platform.h b/src/main/cpp/util/path_platform.h
new file mode 100644
index 0000000000..abb25dcb75
--- /dev/null
+++ b/src/main/cpp/util/path_platform.h
@@ -0,0 +1,119 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef BAZEL_SRC_MAIN_CPP_UTIL_PATH_PLATFORM_H_
+#define BAZEL_SRC_MAIN_CPP_UTIL_PATH_PLATFORM_H_
+
+#include <string>
+
+namespace blaze_util {
+
+// 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
+// is Windows path.
+std::string ConvertPath(const std::string &path);
+
+// Converts `path` to a string that's safe to pass as path in a JVM flag.
+// See https://github.com/bazelbuild/bazel/issues/2576
+std::string PathAsJvmFlag(const std::string &path);
+
+// Compares two absolute paths. Necessary because the same path can have
+// multiple different names under msys2: "C:\foo\bar" or "C:/foo/bar"
+// (Windows-style) and "/c/foo/bar" (msys2 style). Returns if the paths are
+// equal.
+bool CompareAbsolutePaths(const std::string &a, const std::string &b);
+
+// Split a path to dirname and basename parts.
+std::pair<std::string, std::string> SplitPath(const std::string &path);
+
+bool IsDevNull(const char *path);
+
+// Returns true if `path` is the root directory or a Windows drive root.
+bool IsRootDirectory(const std::string &path);
+
+// Returns true if `path` is absolute.
+bool IsAbsolute(const std::string &path);
+
+// TODO(bazel-team) consider changing the path(_platform) header split to be a
+// path.h and path_windows.h split, which would make it clearer what functions
+// are included by an import statement. The downside to this gain in clarity
+// is that this would add more complexity to the implementation file(s)? of
+// path.h, which would have to have the platform-specific implementations.
+#if defined(COMPILER_MSVC) || defined(__CYGWIN__)
+const wchar_t *RemoveUncPrefixMaybe(const wchar_t *ptr);
+void AddUncPrefixMaybe(std::wstring *path);
+
+std::pair<std::wstring, std::wstring> SplitPathW(const std::wstring &path);
+
+bool IsRootDirectoryW(const std::wstring &path);
+
+bool AsWindowsPath(const std::string &path, std::string *result,
+ std::string *error);
+
+// Returns a normalized form of the input `path`.
+//
+// `path` must be a relative or absolute Windows path, it may use "/" instead of
+// "\" but must not be a Unix-style (MSYS) path.
+// The result won't have a UNC prefix, even if `path` did.
+//
+// Normalization means removing "." references, resolving ".." references, and
+// deduplicating "/" characters while converting them to "\".
+// For example if `path` is "foo/../bar/.//qux", the result is "bar\qux".
+//
+// Uplevel references that cannot go any higher in the directory tree are simply
+// ignored, e.g. "c:/.." is normalized to "c:\" and "../../foo" is normalized to
+// "foo".
+//
+// Visible for testing, would be static otherwise.
+std::string NormalizeWindowsPath(std::string path);
+
+// Converts a UTF8-encoded `path` to a normalized, widechar Windows path.
+//
+// Returns true if conversion succeeded and sets the contents of `result` to it.
+//
+// The input `path` may be an absolute or relative Windows path.
+//
+// The returned path is normalized (see NormalizeWindowsPath).
+//
+// If `path` had a "\\?\" prefix then the function assumes it's already Windows
+// style and converts it to wstring without any alterations.
+// Otherwise `path` is normalized and converted to a Windows path and the result
+// won't have a "\\?\" prefix even if it's longer than MAX_PATH (adding the
+// prefix is the caller's responsibility).
+//
+// The method recognizes current-drive-relative Windows paths ("\foo") turning
+// them into absolute paths ("c:\foo").
+bool AsWindowsPath(const std::string &path, std::wstring *result,
+ std::string *error);
+
+bool AsAbsoluteWindowsPath(const std::string &path, std::wstring *wpath,
+ std::string *error);
+
+// Same as `AsWindowsPath`, but returns a lowercase 8dot3 style shortened path.
+// Result will never have a UNC prefix, nor a trailing "/" or "\".
+// Works also for non-existent paths; shortens as much of them as it can.
+// Also works for non-existent drives.
+bool AsShortWindowsPath(const std::string &path, std::string *result,
+ std::string *error);
+
+template <typename char_type>
+bool IsPathSeparator(char_type ch);
+
+template <typename char_type>
+bool HasDriveSpecifierPrefix(const char_type *ch);
+
+#endif // defined(COMPILER_MSVC) || defined(__CYGWIN__)
+} // namespace blaze_util
+
+#endif // BAZEL_SRC_MAIN_CPP_UTIL_PATH_PLATFORM_H_
diff --git a/src/main/cpp/util/path_posix.cc b/src/main/cpp/util/path_posix.cc
new file mode 100644
index 0000000000..bbeffca30c
--- /dev/null
+++ b/src/main/cpp/util/path_posix.cc
@@ -0,0 +1,60 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/main/cpp/util/path_platform.h"
+
+#include <limits.h> // PATH_MAX
+
+#include <string.h> // strncmp
+#include <unistd.h> // access, open, close, fsync
+#include "src/main/cpp/util/errors.h"
+#include "src/main/cpp/util/exit_code.h"
+#include "src/main/cpp/util/logging.h"
+
+namespace blaze_util {
+
+std::string ConvertPath(const std::string &path) { return path; }
+
+std::string PathAsJvmFlag(const std::string &path) { return path; }
+
+bool CompareAbsolutePaths(const std::string &a, const std::string &b) {
+ return a == b;
+}
+
+std::pair<std::string, std::string> SplitPath(const std::string &path) {
+ size_t pos = path.rfind('/');
+
+ // Handle the case with no '/' in 'path'.
+ if (pos == std::string::npos) return std::make_pair("", path);
+
+ // Handle the case with a single leading '/' in 'path'.
+ if (pos == 0)
+ return std::make_pair(std::string(path, 0, 1), std::string(path, 1));
+
+ return std::make_pair(std::string(path, 0, pos), std::string(path, pos + 1));
+}
+
+bool IsDevNull(const char *path) {
+ return path != NULL && *path != 0 && strncmp("/dev/null\0", path, 10) == 0;
+}
+
+bool IsRootDirectory(const std::string &path) {
+ return path.size() == 1 && path[0] == '/';
+}
+
+bool IsAbsolute(const std::string &path) {
+ return !path.empty() && path[0] == '/';
+}
+
+} // namespace blaze_util
diff --git a/src/main/cpp/util/path_windows.cc b/src/main/cpp/util/path_windows.cc
new file mode 100644
index 0000000000..d3756c71bf
--- /dev/null
+++ b/src/main/cpp/util/path_windows.cc
@@ -0,0 +1,420 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/main/cpp/util/path_platform.h"
+
+#include <wchar.h> // wcslen
+#include <windows.h>
+
+#include <algorithm>
+#include <memory> // unique_ptr
+#include <sstream>
+#include <vector>
+
+#include "src/main/cpp/util/errors.h"
+#include "src/main/cpp/util/exit_code.h"
+#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/strings.h"
+#include "src/main/native/windows/file.h"
+
+namespace blaze_util {
+
+using bazel::windows::HasUncPrefix;
+
+static char GetCurrentDrive();
+
+template <typename char_type>
+struct CharTraits {
+ static bool IsAlpha(char_type ch);
+};
+
+template <>
+struct CharTraits<char> {
+ static bool IsAlpha(char ch) { return isalpha(ch); }
+};
+
+template <>
+struct CharTraits<wchar_t> {
+ static bool IsAlpha(wchar_t ch) { return iswalpha(ch); }
+};
+
+template <typename char_type>
+static bool IsPathSeparator(char_type ch) {
+ return ch == '/' || ch == '\\';
+}
+
+template <typename char_type>
+static bool HasDriveSpecifierPrefix(const char_type* ch) {
+ return CharTraits<char_type>::IsAlpha(ch[0]) && ch[1] == ':';
+}
+
+std::string ConvertPath(const std::string& path) {
+ // The path may not be Windows-style and may not be normalized, so convert it.
+ std::wstring wpath;
+ std::string error;
+ if (!AsAbsoluteWindowsPath(path, &wpath, &error)) {
+ BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+ << "ConvertPath(" << path
+ << "): AsAbsoluteWindowsPath failed: " << error;
+ }
+ std::transform(wpath.begin(), wpath.end(), wpath.begin(), ::towlower);
+ return std::string(
+ WstringToCstring(RemoveUncPrefixMaybe(wpath.c_str())).get());
+}
+
+bool CompareAbsolutePaths(const std::string& a, const std::string& b) {
+ return ConvertPath(a) == ConvertPath(b);
+}
+
+std::string PathAsJvmFlag(const std::string& path) {
+ std::string spath;
+ std::string error;
+ if (!AsShortWindowsPath(path, &spath, &error)) {
+ BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+ << "PathAsJvmFlag(" << path
+ << "): AsShortWindowsPath failed: " << error;
+ }
+ // Convert backslashes to forward slashes, in order to avoid the JVM parsing
+ // Windows paths as if they contained escaped characters.
+ // See https://github.com/bazelbuild/bazel/issues/2576
+ std::replace(spath.begin(), spath.end(), '\\', '/');
+ return spath;
+}
+
+void AddUncPrefixMaybe(std::wstring* path) {
+ if (path->size() >= MAX_PATH && !HasUncPrefix(path->c_str())) {
+ *path = std::wstring(L"\\\\?\\") + *path;
+ }
+}
+
+const wchar_t* RemoveUncPrefixMaybe(const wchar_t* ptr) {
+ return ptr + (HasUncPrefix(ptr) ? 4 : 0);
+}
+
+// Checks if the path is absolute and/or is a root path.
+//
+// If `must_be_root` is true, then in addition to being absolute, the path must
+// also be just the root part, no other components, e.g. "c:\" is both absolute
+// and root, but "c:\foo" is just absolute.
+template <typename char_type>
+static bool IsRootOrAbsolute(const std::basic_string<char_type>& path,
+ bool must_be_root) {
+ // An absolute path is one that starts with "/", "\", "c:/", "c:\",
+ // "\\?\c:\", or rarely "\??\c:\" or "\\.\c:\".
+ //
+ // It is unclear whether the UNC prefix is just "\\?\" or is "\??\" also
+ // valid (in some cases it seems to be, though MSDN doesn't mention it).
+ return
+ // path is (or starts with) "/" or "\"
+ ((must_be_root ? path.size() == 1 : !path.empty()) &&
+ IsPathSeparator(path[0])) ||
+ // path is (or starts with) "c:/" or "c:\" or similar
+ ((must_be_root ? path.size() == 3 : path.size() >= 3) &&
+ HasDriveSpecifierPrefix(path.c_str()) && IsPathSeparator(path[2])) ||
+ // path is (or starts with) "\\?\c:\" or "\??\c:\" or similar
+ ((must_be_root ? path.size() == 7 : path.size() >= 7) &&
+ HasUncPrefix(path.c_str()) &&
+ HasDriveSpecifierPrefix(path.c_str() + 4) && IsPathSeparator(path[6]));
+}
+
+template <typename char_type>
+static std::pair<std::basic_string<char_type>, std::basic_string<char_type> >
+SplitPathImpl(const std::basic_string<char_type>& path) {
+ if (path.empty()) {
+ return std::make_pair(std::basic_string<char_type>(),
+ std::basic_string<char_type>());
+ }
+
+ size_t pos = path.size() - 1;
+ for (auto it = path.crbegin(); it != path.crend(); ++it, --pos) {
+ if (IsPathSeparator(*it)) {
+ if ((pos == 2 || pos == 6) &&
+ IsRootOrAbsolute(path.substr(0, pos + 1), /* must_be_root */ true)) {
+ // Windows path, top-level directory, e.g. "c:\foo",
+ // result is ("c:\", "foo").
+ // Or UNC path, top-level directory, e.g. "\\?\c:\foo"
+ // result is ("\\?\c:\", "foo").
+ return std::make_pair(
+ // Include the "/" or "\" in the drive specifier.
+ path.substr(0, pos + 1), path.substr(pos + 1));
+ } else {
+ // Windows path (neither top-level nor drive root), Unix path, or
+ // relative path.
+ return std::make_pair(
+ // If the only "/" is the leading one, then that shall be the first
+ // pair element, otherwise the substring up to the rightmost "/".
+ pos == 0 ? path.substr(0, 1) : path.substr(0, pos),
+ // If the rightmost "/" is the tail, then the second pair element
+ // should be empty.
+ pos == path.size() - 1 ? std::basic_string<char_type>()
+ : path.substr(pos + 1));
+ }
+ }
+ }
+ // Handle the case with no '/' or '\' in `path`.
+ return std::make_pair(std::basic_string<char_type>(), path);
+}
+
+std::pair<std::string, std::string> SplitPath(const std::string& path) {
+ return SplitPathImpl(path);
+}
+
+std::pair<std::wstring, std::wstring> SplitPathW(const std::wstring& path) {
+ return SplitPathImpl(path);
+}
+
+bool AsWindowsPath(const std::string& path, std::string* result,
+ std::string* error) {
+ if (path.empty()) {
+ result->clear();
+ return true;
+ }
+ if (IsDevNull(path.c_str())) {
+ result->assign("NUL");
+ return true;
+ }
+ if (HasUncPrefix(path.c_str())) {
+ // Path has "\\?\" prefix --> assume it's already Windows-style.
+ *result = path.c_str();
+ return true;
+ }
+ if (IsPathSeparator(path[0]) && path.size() > 1 && IsPathSeparator(path[1])) {
+ // Unsupported path: "\\" or "\\server\path", or some degenerate form of
+ // these, such as "//foo".
+ if (error) {
+ *error = "network paths are unsupported";
+ }
+ return false;
+ }
+ if (HasDriveSpecifierPrefix(path.c_str()) &&
+ (path.size() < 3 || !IsPathSeparator(path[2]))) {
+ // Unsupported path: "c:" or "c:foo"
+ if (error) {
+ *error = "working-directory relative paths are unsupported";
+ }
+ return false;
+ }
+
+ std::string mutable_path = path;
+ if (path[0] == '/') {
+ if (error) {
+ *error = "Unix-style paths are unsupported";
+ }
+ return false;
+ }
+
+ if (path[0] == '\\') {
+ // This is an absolute Windows path on the current drive, e.g. "\foo\bar".
+ mutable_path = std::string(1, GetCurrentDrive()) + ":" + path;
+ } // otherwise this is a relative path, or absolute Windows path.
+
+ result->assign(NormalizeWindowsPath(mutable_path));
+ return true;
+}
+
+bool AsWindowsPath(const std::string& path, std::wstring* result,
+ std::string* error) {
+ std::string normalized_win_path;
+ if (!AsWindowsPath(path, &normalized_win_path, error)) {
+ return false;
+ }
+
+ result->assign(CstringToWstring(normalized_win_path.c_str()).get());
+ return true;
+}
+
+bool AsAbsoluteWindowsPath(const std::string& path, std::wstring* result,
+ std::string* error) {
+ if (path.empty()) {
+ result->clear();
+ return true;
+ }
+ if (IsDevNull(path.c_str())) {
+ result->assign(L"NUL");
+ return true;
+ }
+ if (!AsWindowsPath(path, result, error)) {
+ return false;
+ }
+ if (!IsRootOrAbsolute(*result, /* must_be_root */ false)) {
+ *result = GetCwdW() + L"\\" + *result;
+ }
+ if (!HasUncPrefix(result->c_str())) {
+ *result = std::wstring(L"\\\\?\\") + *result;
+ }
+ return true;
+}
+
+bool AsShortWindowsPath(const std::string& path, std::string* result,
+ std::string* error) {
+ if (IsDevNull(path.c_str())) {
+ result->assign("NUL");
+ return true;
+ }
+
+ result->clear();
+ std::wstring wpath;
+ std::wstring wsuffix;
+ if (!AsAbsoluteWindowsPath(path, &wpath, error)) {
+ return false;
+ }
+ DWORD size = ::GetShortPathNameW(wpath.c_str(), nullptr, 0);
+ if (size == 0) {
+ // GetShortPathNameW can fail if `wpath` does not exist. This is expected
+ // when we are about to create a file at that path, so instead of failing,
+ // walk up in the path until we find a prefix that exists and can be
+ // shortened, or is a root directory. Save the non-existent tail in
+ // `wsuffix`, we'll add it back later.
+ std::vector<std::wstring> segments;
+ while (size == 0 && !IsRootDirectoryW(wpath)) {
+ std::pair<std::wstring, std::wstring> split = SplitPathW(wpath);
+ wpath = split.first;
+ segments.push_back(split.second);
+ size = ::GetShortPathNameW(wpath.c_str(), nullptr, 0);
+ }
+
+ // Join all segments.
+ std::wostringstream builder;
+ bool first = true;
+ for (auto it = segments.crbegin(); it != segments.crend(); ++it) {
+ if (!first || !IsRootDirectoryW(wpath)) {
+ builder << L'\\' << *it;
+ } else {
+ builder << *it;
+ }
+ first = false;
+ }
+ wsuffix = builder.str();
+ }
+
+ std::wstring wresult;
+ if (IsRootDirectoryW(wpath)) {
+ // Strip the UNC prefix from `wpath`, and the leading "\" from `wsuffix`.
+ wresult = std::wstring(RemoveUncPrefixMaybe(wpath.c_str())) + wsuffix;
+ } else {
+ std::unique_ptr<WCHAR[]> wshort(
+ new WCHAR[size]); // size includes null-terminator
+ if (size - 1 != ::GetShortPathNameW(wpath.c_str(), wshort.get(), size)) {
+ if (error) {
+ std::string last_error = GetLastErrorString();
+ std::stringstream msg;
+ msg << "AsShortWindowsPath(" << path << "): GetShortPathNameW("
+ << WstringToString(wpath) << ") failed: " << last_error;
+ *error = msg.str();
+ }
+ return false;
+ }
+ // GetShortPathNameW may preserve the UNC prefix in the result, so strip it.
+ wresult = std::wstring(RemoveUncPrefixMaybe(wshort.get())) + wsuffix;
+ }
+
+ result->assign(WstringToCstring(wresult.c_str()).get());
+ ToLower(result);
+ return true;
+}
+
+bool IsDevNull(const char* path) {
+ return path != NULL && *path != 0 &&
+ (strncmp("/dev/null\0", path, 10) == 0 ||
+ ((path[0] == 'N' || path[0] == 'n') &&
+ (path[1] == 'U' || path[1] == 'u') &&
+ (path[2] == 'L' || path[2] == 'l') && path[3] == 0));
+}
+
+bool IsRootDirectory(const std::string& path) {
+ return IsRootOrAbsolute(path, true);
+}
+
+bool IsAbsolute(const std::string& path) {
+ return IsRootOrAbsolute(path, false);
+}
+
+bool IsRootDirectoryW(const std::wstring& path) {
+ return IsRootOrAbsolute(path, true);
+}
+
+static char GetCurrentDrive() {
+ std::wstring cwd = GetCwdW();
+ wchar_t wdrive = RemoveUncPrefixMaybe(cwd.c_str())[0];
+ wchar_t offset = wdrive >= L'A' && wdrive <= L'Z' ? L'A' : L'a';
+ return 'a' + wdrive - offset;
+}
+
+std::string NormalizeWindowsPath(std::string path) {
+ if (path.empty()) {
+ return "";
+ }
+ if (path[0] == '/') {
+ // This is an absolute MSYS path, error out.
+ BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+ << "NormalizeWindowsPath(" << path << "): expected a Windows path";
+ }
+ if (path.size() >= 4 && HasUncPrefix(path.c_str())) {
+ path = path.substr(4);
+ }
+
+ static const std::string dot(".");
+ static const std::string dotdot("..");
+
+ std::vector<std::string> segments;
+ int segment_start = -1;
+ // Find the path segments in `path` (separated by "/").
+ for (int i = 0;; ++i) {
+ if (!IsPathSeparator(path[i]) && path[i] != '\0') {
+ // The current character does not end a segment, so start one unless it's
+ // already started.
+ if (segment_start < 0) {
+ segment_start = i;
+ }
+ } else if (segment_start >= 0 && i > segment_start) {
+ // The current character is "/" or "\0", so this ends a segment.
+ // Add that to `segments` if there's anything to add; handle "." and "..".
+ std::string segment(path, segment_start, i - segment_start);
+ segment_start = -1;
+ if (segment == dotdot) {
+ if (!segments.empty() &&
+ !HasDriveSpecifierPrefix(segments[0].c_str())) {
+ segments.pop_back();
+ }
+ } else if (segment != dot) {
+ segments.push_back(segment);
+ }
+ }
+ if (path[i] == '\0') {
+ break;
+ }
+ }
+
+ // Handle the case when `path` is just a drive specifier (or some degenerate
+ // form of it, e.g. "c:\..").
+ if (segments.size() == 1 && segments[0].size() == 2 &&
+ HasDriveSpecifierPrefix(segments[0].c_str())) {
+ return segments[0] + '\\';
+ }
+
+ // Join all segments.
+ bool first = true;
+ std::ostringstream result;
+ for (const auto& s : segments) {
+ if (!first) {
+ result << '\\';
+ }
+ first = false;
+ result << s;
+ }
+ return result.str();
+}
+
+} // namespace blaze_util
diff --git a/src/main/cpp/workspace_layout.cc b/src/main/cpp/workspace_layout.cc
index 64f4a6c06d..b1d3ff2fe7 100644
--- a/src/main/cpp/workspace_layout.cc
+++ b/src/main/cpp/workspace_layout.cc
@@ -19,6 +19,8 @@
#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
namespace blaze {
diff --git a/src/test/cpp/blaze_util_test.cc b/src/test/cpp/blaze_util_test.cc
index 15963b6b5a..03b43c0587 100644
--- a/src/test/cpp/blaze_util_test.cc
+++ b/src/test/cpp/blaze_util_test.cc
@@ -211,20 +211,4 @@ TEST_F(BlazeUtilTest, TestSearchUnarySkipsAfterDashDashWithoutEquals) {
"--flag"));
}
-TEST_F(BlazeUtilTest, MakeAbsolute) {
-#if defined(WIN32)
- EXPECT_EQ(MakeAbsolute("C:\\foo\\bar"), "C:\\foo\\bar");
- EXPECT_EQ(MakeAbsolute("C:/foo/bar"), "C:\\foo\\bar");
- EXPECT_EQ(MakeAbsolute("C:\\foo\\bar\\"), "C:\\foo\\bar\\");
- EXPECT_EQ(MakeAbsolute("C:/foo/bar/"), "C:\\foo\\bar\\");
- EXPECT_EQ(MakeAbsolute("foo"), blaze_util::GetCwd() + "\\foo");
-#else
- EXPECT_EQ(MakeAbsolute("/foo/bar"), "/foo/bar");
- EXPECT_EQ(MakeAbsolute("/foo/bar/"), "/foo/bar/");
- EXPECT_EQ(MakeAbsolute("foo"), blaze_util::GetCwd() + "/foo");
-#endif
- EXPECT_EQ(MakeAbsolute(std::string()), blaze_util::GetCwd());
- EXPECT_EQ(MakeAbsolute("/dev/null"), "/dev/null");
-}
-
} // namespace blaze
diff --git a/src/test/cpp/blaze_util_windows_test.cc b/src/test/cpp/blaze_util_windows_test.cc
index 2ac596957d..f577b48ca5 100644
--- a/src/test/cpp/blaze_util_windows_test.cc
+++ b/src/test/cpp/blaze_util_windows_test.cc
@@ -155,27 +155,4 @@ TEST(BlazeUtilWindowsTest, TestUnsetEnv) {
ASSERT_ENVVAR_UNSET(long_key.c_str());
}
-TEST(BlazeUtilWindowsTest, ConvertPathTest) {
- EXPECT_EQ("c:\\foo", ConvertPath("C:\\FOO"));
- EXPECT_EQ("c:\\", ConvertPath("c:/"));
- EXPECT_EQ("c:\\foo\\bar", ConvertPath("c:/../foo\\BAR\\.\\"));
- EXPECT_EQ("nul", MakeAbsolute("NUL"));
- EXPECT_EQ("nul", MakeAbsolute("nul"));
- EXPECT_EQ("nul", MakeAbsolute("/dev/null"));
-}
-
-TEST(BlazeUtilWindowsTest, TestMakeAbsolute) {
- EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:\\foo\\BAR"));
- EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:/foo/bar"));
- EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:\\foo\\bar\\"));
- EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:/foo/bar/"));
- EXPECT_EQ(blaze_util::AsLower(blaze_util::GetCwd()) + "\\foo",
- MakeAbsolute("foo"));
- EXPECT_EQ("nul", MakeAbsolute("NUL"));
- EXPECT_EQ("nul", MakeAbsolute("Nul"));
- EXPECT_EQ("nul", MakeAbsolute("nul"));
- EXPECT_EQ(blaze_util::AsLower(blaze_util::GetCwd()), MakeAbsolute(""));
- EXPECT_EQ("nul", MakeAbsolute("/dev/null"));
-}
-
} // namespace blaze
diff --git a/src/test/cpp/option_processor_test.cc b/src/test/cpp/option_processor_test.cc
index 01e2805665..10e55cf055 100644
--- a/src/test/cpp/option_processor_test.cc
+++ b/src/test/cpp/option_processor_test.cc
@@ -20,6 +20,7 @@
#include "src/main/cpp/option_processor-internal.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
#include "src/main/cpp/workspace_layout.h"
#include "googletest/include/gtest/gtest.h"
diff --git a/src/test/cpp/rc_file_test.cc b/src/test/cpp/rc_file_test.cc
index 5900db1cd1..a534778f20 100644
--- a/src/test/cpp/rc_file_test.cc
+++ b/src/test/cpp/rc_file_test.cc
@@ -21,6 +21,7 @@
#include "src/main/cpp/rc_file.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
#include "src/main/cpp/workspace_layout.h"
#include "googlemock/include/gmock/gmock.h"
#include "googletest/include/gtest/gtest.h"
diff --git a/src/test/cpp/rc_options_test.cc b/src/test/cpp/rc_options_test.cc
index 3a3ebf3c0d..58251afe9a 100644
--- a/src/test/cpp/rc_options_test.cc
+++ b/src/test/cpp/rc_options_test.cc
@@ -19,6 +19,7 @@
#include "src/main/cpp/option_processor.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/cpp/workspace_layout.h"
#include "googletest/include/gtest/gtest.h"
diff --git a/src/test/cpp/util/BUILD b/src/test/cpp/util/BUILD
index 5dcb04adfb..455994f3d9 100644
--- a/src/test/cpp/util/BUILD
+++ b/src/test/cpp/util/BUILD
@@ -31,7 +31,31 @@ cc_test(
}),
deps = [
":test_util",
- "//src/main/cpp/util:file",
+ "//src/main/cpp/util:filesystem",
+ "@com_google_googletest//:gtest_main",
+ ] + select({
+ "//src/conditions:windows": [
+ ":windows_test_util",
+ "//src/main/native/windows:lib-file",
+ ],
+ "//conditions:default": [],
+ }),
+)
+
+cc_test(
+ name = "path_test",
+ size = "small",
+ srcs = select({
+ "//src/conditions:windows": [
+ "path_windows_test.cc",
+ ],
+ "//conditions:default": [
+ "path_posix_test.cc",
+ ],
+ }),
+ deps = [
+ ":test_util",
+ "//src/main/cpp/util:filesystem",
"@com_google_googletest//:gtest_main",
] + select({
"//src/conditions:windows": [
@@ -48,7 +72,7 @@ cc_test(
deps = [
"//src/main/cpp:blaze_util",
"//src/main/cpp/util:bazel_log_handler",
- "//src/main/cpp/util:file",
+ "//src/main/cpp/util:filesystem",
"//src/main/cpp/util:logging",
"@com_google_googletest//:gtest_main",
],
diff --git a/src/test/cpp/util/file_posix_test.cc b/src/test/cpp/util/file_posix_test.cc
index 688042c8ed..75026af6b3 100644
--- a/src/test/cpp/util/file_posix_test.cc
+++ b/src/test/cpp/util/file_posix_test.cc
@@ -19,6 +19,8 @@
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/test/cpp/util/test_util.h"
#include "googletest/include/gtest/gtest.h"
@@ -46,73 +48,6 @@ static bool CreateEmptyFile(const string& path) {
return close(fd) == 0;
}
-TEST(FilePosixTest, TestDirname) {
- // The Posix version of SplitPath (thus Dirname too, which is implemented on
- // top of it) is not aware of Windows paths.
- ASSERT_EQ("", Dirname(""));
- ASSERT_EQ("/", Dirname("/"));
- ASSERT_EQ("", Dirname("foo"));
- ASSERT_EQ("/", Dirname("/foo"));
- ASSERT_EQ("/foo", Dirname("/foo/"));
- ASSERT_EQ("foo", Dirname("foo/bar"));
- ASSERT_EQ("foo/bar", Dirname("foo/bar/baz"));
- ASSERT_EQ("", Dirname("\\foo"));
- ASSERT_EQ("", Dirname("\\foo\\"));
- ASSERT_EQ("", Dirname("foo\\bar"));
- ASSERT_EQ("", Dirname("foo\\bar\\baz"));
- ASSERT_EQ("foo\\bar", Dirname("foo\\bar/baz\\qux"));
- ASSERT_EQ("c:", Dirname("c:/"));
- ASSERT_EQ("", Dirname("c:\\"));
- ASSERT_EQ("c:", Dirname("c:/foo"));
- ASSERT_EQ("", Dirname("c:\\foo"));
- ASSERT_EQ("", Dirname("\\\\?\\c:\\"));
- ASSERT_EQ("", Dirname("\\\\?\\c:\\foo"));
-}
-
-TEST(FilePosixTest, TestBasename) {
- // The Posix version of SplitPath (thus Basename too, which is implemented on
- // top of it) is not aware of Windows paths.
- ASSERT_EQ("", Basename(""));
- ASSERT_EQ("", Basename("/"));
- ASSERT_EQ("foo", Basename("foo"));
- ASSERT_EQ("foo", Basename("/foo"));
- ASSERT_EQ("", Basename("/foo/"));
- ASSERT_EQ("bar", Basename("foo/bar"));
- ASSERT_EQ("baz", Basename("foo/bar/baz"));
- ASSERT_EQ("\\foo", Basename("\\foo"));
- ASSERT_EQ("\\foo\\", Basename("\\foo\\"));
- ASSERT_EQ("foo\\bar", Basename("foo\\bar"));
- ASSERT_EQ("foo\\bar\\baz", Basename("foo\\bar\\baz"));
- ASSERT_EQ("baz\\qux", Basename("foo\\bar/baz\\qux"));
- ASSERT_EQ("qux", Basename("qux"));
- ASSERT_EQ("", Basename("c:/"));
- ASSERT_EQ("c:\\", Basename("c:\\"));
- ASSERT_EQ("foo", Basename("c:/foo"));
- ASSERT_EQ("c:\\foo", Basename("c:\\foo"));
- ASSERT_EQ("\\\\?\\c:\\", Basename("\\\\?\\c:\\"));
- ASSERT_EQ("\\\\?\\c:\\foo", Basename("\\\\?\\c:\\foo"));
-}
-
-TEST(FilePosixTest, JoinPath) {
- std::string path = JoinPath("", "");
- ASSERT_EQ("", path);
-
- path = JoinPath("a", "b");
- ASSERT_EQ("a/b", path);
-
- path = JoinPath("a/", "b");
- ASSERT_EQ("a/b", path);
-
- path = JoinPath("a", "/b");
- ASSERT_EQ("a/b", path);
-
- path = JoinPath("a/", "/b");
- ASSERT_EQ("a/b", path);
-
- path = JoinPath("/", "/");
- ASSERT_EQ("/", path);
-}
-
void MockDirectoryListingFunction(const string& path,
DirectoryEntryConsumer* consume) {
if (path == "root") {
@@ -272,24 +207,6 @@ TEST(FilePosixTest, CanAccess) {
ASSERT_EQ(0, rmdir(dir.c_str()));
}
-TEST(FilePosixTest, GetCwd) {
- char cwdbuf[PATH_MAX];
- ASSERT_EQ(cwdbuf, getcwd(cwdbuf, PATH_MAX));
-
- // Assert that GetCwd() and getcwd() return the same value.
- string cwd(cwdbuf);
- ASSERT_EQ(cwd, blaze_util::GetCwd());
-
- // Change to a different directory.
- ASSERT_EQ(0, chdir("/usr"));
-
- // Assert that GetCwd() returns the new CWD.
- ASSERT_EQ(string("/usr"), blaze_util::GetCwd());
-
- ASSERT_EQ(0, chdir(cwd.c_str()));
- ASSERT_EQ(cwd, blaze_util::GetCwd());
-}
-
TEST(FilePosixTest, ChangeDirectory) {
// Retrieve the current working directory.
char old_wd[PATH_MAX];
@@ -405,32 +322,4 @@ TEST(FilePosixTest, ForEachDirectoryEntry) {
rmdir(root.c_str());
}
-TEST(FilePosixTest, IsAbsolute) {
- ASSERT_FALSE(IsAbsolute(""));
- ASSERT_TRUE(IsAbsolute("/"));
- ASSERT_TRUE(IsAbsolute("/foo"));
- ASSERT_FALSE(IsAbsolute("\\"));
- ASSERT_FALSE(IsAbsolute("\\foo"));
- ASSERT_FALSE(IsAbsolute("c:"));
- ASSERT_FALSE(IsAbsolute("c:/"));
- ASSERT_FALSE(IsAbsolute("c:\\"));
- ASSERT_FALSE(IsAbsolute("c:\\foo"));
- ASSERT_FALSE(IsAbsolute("\\\\?\\c:\\"));
- ASSERT_FALSE(IsAbsolute("\\\\?\\c:\\foo"));
-}
-
-TEST(FilePosixTest, IsRootDirectory) {
- ASSERT_FALSE(IsRootDirectory(""));
- ASSERT_TRUE(IsRootDirectory("/"));
- ASSERT_FALSE(IsRootDirectory("/foo"));
- ASSERT_FALSE(IsRootDirectory("\\"));
- ASSERT_FALSE(IsRootDirectory("\\foo"));
- ASSERT_FALSE(IsRootDirectory("c:"));
- ASSERT_FALSE(IsRootDirectory("c:/"));
- ASSERT_FALSE(IsRootDirectory("c:\\"));
- ASSERT_FALSE(IsRootDirectory("c:\\foo"));
- ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\"));
- ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\foo"));
-}
-
} // namespace blaze_util
diff --git a/src/test/cpp/util/file_test.cc b/src/test/cpp/util/file_test.cc
index c05b3b9c24..5f6f6eaf11 100644
--- a/src/test/cpp/util/file_test.cc
+++ b/src/test/cpp/util/file_test.cc
@@ -21,6 +21,8 @@
#include <vector>
#include "src/main/cpp/util/file.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/test/cpp/util/test_util.h"
#include "googletest/include/gtest/gtest.h"
diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc
index 87987b4d89..1844736d1e 100644
--- a/src/test/cpp/util/file_windows_test.cc
+++ b/src/test/cpp/util/file_windows_test.cc
@@ -22,6 +22,8 @@
#include "gtest/gtest.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/native/windows/file.h"
#include "src/main/native/windows/util.h"
@@ -40,7 +42,6 @@ using std::unique_ptr;
using std::wstring;
// Methods defined in file_windows.cc that are only visible for testing.
-bool AsWindowsPath(const string& path, wstring* result, string* error);
string NormalizeWindowsPath(string path);
class FileWindowsTest : public ::testing::Test {
@@ -90,223 +91,6 @@ TEST_F(FileWindowsTest, TestTearDownB) {
AssertTearDown(L"test.teardown.b", L"test.teardown.a");
}
-TEST_F(FileWindowsTest, TestNormalizeWindowsPath) {
- ASSERT_EQ(string(""), NormalizeWindowsPath(""));
- ASSERT_EQ(string(""), NormalizeWindowsPath("."));
- ASSERT_EQ(string("foo"), NormalizeWindowsPath("foo"));
- ASSERT_EQ(string("foo"), NormalizeWindowsPath("foo/"));
- ASSERT_EQ(string("foo\\bar"), NormalizeWindowsPath("foo//bar"));
- ASSERT_EQ(string("foo\\bar"), NormalizeWindowsPath("../..//foo/./bar"));
- ASSERT_EQ(string("foo\\bar"), NormalizeWindowsPath("../foo/baz/../bar"));
- ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:"));
- ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:/"));
- ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:\\"));
- ASSERT_EQ(string("c:\\foo\\bar"), NormalizeWindowsPath("c:\\..//foo/./bar/"));
-}
-
-TEST_F(FileWindowsTest, TestDirname) {
- ASSERT_EQ("", Dirname(""));
- ASSERT_EQ("/", Dirname("/"));
- ASSERT_EQ("", Dirname("foo"));
- ASSERT_EQ("/", Dirname("/foo"));
- ASSERT_EQ("/foo", Dirname("/foo/"));
- ASSERT_EQ("foo", Dirname("foo/bar"));
- ASSERT_EQ("foo/bar", Dirname("foo/bar/baz"));
- ASSERT_EQ("\\", Dirname("\\foo"));
- ASSERT_EQ("\\foo", Dirname("\\foo\\"));
- ASSERT_EQ("foo", Dirname("foo\\bar"));
- ASSERT_EQ("foo\\bar", Dirname("foo\\bar\\baz"));
- ASSERT_EQ("foo\\bar/baz", Dirname("foo\\bar/baz\\qux"));
- ASSERT_EQ("c:/", Dirname("c:/"));
- ASSERT_EQ("c:\\", Dirname("c:\\"));
- ASSERT_EQ("c:/", Dirname("c:/foo"));
- ASSERT_EQ("c:\\", Dirname("c:\\foo"));
- ASSERT_EQ("\\\\?\\c:\\", Dirname("\\\\?\\c:\\"));
- ASSERT_EQ("\\\\?\\c:\\", Dirname("\\\\?\\c:\\foo"));
-}
-
-TEST_F(FileWindowsTest, TestBasename) {
- ASSERT_EQ("", Basename(""));
- ASSERT_EQ("", Basename("/"));
- ASSERT_EQ("foo", Basename("foo"));
- ASSERT_EQ("foo", Basename("/foo"));
- ASSERT_EQ("", Basename("/foo/"));
- ASSERT_EQ("bar", Basename("foo/bar"));
- ASSERT_EQ("baz", Basename("foo/bar/baz"));
- ASSERT_EQ("foo", Basename("\\foo"));
- ASSERT_EQ("", Basename("\\foo\\"));
- ASSERT_EQ("bar", Basename("foo\\bar"));
- ASSERT_EQ("baz", Basename("foo\\bar\\baz"));
- ASSERT_EQ("qux", Basename("foo\\bar/baz\\qux"));
- ASSERT_EQ("", Basename("c:/"));
- ASSERT_EQ("", Basename("c:\\"));
- ASSERT_EQ("foo", Basename("c:/foo"));
- ASSERT_EQ("foo", Basename("c:\\foo"));
- ASSERT_EQ("", Basename("\\\\?\\c:\\"));
- ASSERT_EQ("foo", Basename("\\\\?\\c:\\foo"));
-}
-
-TEST_F(FileWindowsTest, TestIsAbsolute) {
- ASSERT_FALSE(IsAbsolute(""));
- ASSERT_TRUE(IsAbsolute("/"));
- ASSERT_TRUE(IsAbsolute("/foo"));
- ASSERT_TRUE(IsAbsolute("\\"));
- ASSERT_TRUE(IsAbsolute("\\foo"));
- ASSERT_FALSE(IsAbsolute("c:"));
- ASSERT_TRUE(IsAbsolute("c:/"));
- ASSERT_TRUE(IsAbsolute("c:\\"));
- ASSERT_TRUE(IsAbsolute("c:\\foo"));
- ASSERT_TRUE(IsAbsolute("\\\\?\\c:\\"));
- ASSERT_TRUE(IsAbsolute("\\\\?\\c:\\foo"));
-}
-
-TEST_F(FileWindowsTest, TestIsRootDirectory) {
- ASSERT_FALSE(IsRootDirectory(""));
- ASSERT_TRUE(IsRootDirectory("/"));
- ASSERT_FALSE(IsRootDirectory("/foo"));
- ASSERT_TRUE(IsRootDirectory("\\"));
- ASSERT_FALSE(IsRootDirectory("\\foo"));
- ASSERT_FALSE(IsRootDirectory("c:"));
- ASSERT_TRUE(IsRootDirectory("c:/"));
- ASSERT_TRUE(IsRootDirectory("c:\\"));
- ASSERT_FALSE(IsRootDirectory("c:\\foo"));
- ASSERT_TRUE(IsRootDirectory("\\\\?\\c:\\"));
- ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\foo"));
-}
-
-TEST_F(FileWindowsTest, TestAsWindowsPath) {
- SetEnvironmentVariableA("BAZEL_SH", "c:\\some\\long/path\\bin\\bash.exe");
- wstring actual;
-
- // Null and empty input produces empty result.
- ASSERT_TRUE(AsWindowsPath("", &actual, nullptr));
- ASSERT_EQ(wstring(L""), actual);
-
- // If the path has a "\\?\" prefix, AsWindowsPath assumes it's a correct
- // Windows path. If it's not, the Windows API function that we pass the path
- // to will fail anyway.
- ASSERT_TRUE(AsWindowsPath("\\\\?\\anything/..", &actual, nullptr));
- ASSERT_EQ(wstring(L"\\\\?\\anything/.."), actual);
-
- // Trailing slash or backslash is removed.
- ASSERT_TRUE(AsWindowsPath("foo/", &actual, nullptr));
- ASSERT_EQ(wstring(L"foo"), actual);
- ASSERT_TRUE(AsWindowsPath("foo\\", &actual, nullptr));
- ASSERT_EQ(wstring(L"foo"), actual);
-
- // Slashes are converted to backslash.
- ASSERT_TRUE(AsWindowsPath("foo/bar", &actual, nullptr));
- ASSERT_EQ(wstring(L"foo\\bar"), actual);
- ASSERT_TRUE(AsWindowsPath("c:/", &actual, nullptr));
- ASSERT_EQ(wstring(L"c:\\"), actual);
- ASSERT_TRUE(AsWindowsPath("c:\\", &actual, nullptr));
- ASSERT_EQ(wstring(L"c:\\"), actual);
-
- // Invalid paths
- string error;
- ASSERT_FALSE(AsWindowsPath("c:", &actual, &error));
- EXPECT_TRUE(error.find("working-directory relative paths") != string::npos);
- ASSERT_FALSE(AsWindowsPath("c:foo", &actual, &error));
- EXPECT_TRUE(error.find("working-directory relative paths") != string::npos);
- ASSERT_FALSE(AsWindowsPath("\\\\foo", &actual, &error));
- EXPECT_TRUE(error.find("network paths") != string::npos);
-
- // /dev/null and NUL produce NUL.
- ASSERT_TRUE(AsWindowsPath("/dev/null", &actual, nullptr));
- ASSERT_EQ(wstring(L"NUL"), actual);
- ASSERT_TRUE(AsWindowsPath("Nul", &actual, nullptr));
- ASSERT_EQ(wstring(L"NUL"), actual);
-
- // MSYS path with drive letter.
- ASSERT_FALSE(AsWindowsPath("/c", &actual, &error));
- EXPECT_TRUE(error.find("Unix-style") != string::npos);
- ASSERT_FALSE(AsWindowsPath("/c/", &actual, &error));
- EXPECT_TRUE(error.find("Unix-style") != string::npos);
-
- // Absolute-on-current-drive path gets a drive letter.
- ASSERT_TRUE(AsWindowsPath("\\foo", &actual, nullptr));
- ASSERT_EQ(wstring(1, GetCwd()[0]) + L":\\foo", actual);
-
- // Even for long paths, AsWindowsPath doesn't add a "\\?\" prefix (it's the
- // caller's duty to do so).
- wstring wlongpath(L"dummy_long_path\\");
- string longpath("dummy_long_path/");
- while (longpath.size() <= MAX_PATH) {
- wlongpath += wlongpath;
- longpath += longpath;
- }
- wlongpath.pop_back(); // remove trailing "\"
- ASSERT_TRUE(AsWindowsPath(longpath, &actual, nullptr));
- ASSERT_EQ(wlongpath, actual);
-}
-
-TEST_F(FileWindowsTest, TestAsAbsoluteWindowsPath) {
- SetEnvironmentVariableA("BAZEL_SH", "c:\\some\\long/path\\bin\\bash.exe");
- wstring actual;
-
- ASSERT_TRUE(AsAbsoluteWindowsPath("c:/", &actual, nullptr));
- ASSERT_EQ(L"\\\\?\\c:\\", actual);
-
- ASSERT_TRUE(AsAbsoluteWindowsPath("c:/..\\non-existent//", &actual, nullptr));
- ASSERT_EQ(L"\\\\?\\c:\\non-existent", actual);
-
- WCHAR cwd[MAX_PATH];
- wstring cwdw(CstringToWstring(GetCwd().c_str()).get());
- wstring expected =
- wstring(L"\\\\?\\") + cwdw +
- ((cwdw.back() == L'\\') ? L"non-existent" : L"\\non-existent");
- ASSERT_TRUE(AsAbsoluteWindowsPath("non-existent", &actual, nullptr));
- ASSERT_EQ(actual, expected);
-}
-
-TEST_F(FileWindowsTest, TestAsShortWindowsPath) {
- string actual;
- ASSERT_TRUE(AsShortWindowsPath("/dev/null", &actual, nullptr));
- ASSERT_EQ(string("NUL"), actual);
-
- ASSERT_TRUE(AsShortWindowsPath("nul", &actual, nullptr));
- ASSERT_EQ(string("NUL"), actual);
-
- ASSERT_TRUE(AsShortWindowsPath("C://", &actual, nullptr));
- ASSERT_EQ(string("c:\\"), actual);
-
- string error;
- ASSERT_FALSE(AsShortWindowsPath("/C//", &actual, &error));
- EXPECT_TRUE(error.find("Unix-style") != string::npos);
-
- // The A drive usually doesn't exist but AsShortWindowsPath should still work.
- // Here we even have multiple trailing slashes, that should be handled too.
- ASSERT_TRUE(AsShortWindowsPath("A://", &actual, nullptr));
- ASSERT_EQ(string("a:\\"), actual);
-
- // Assert that we can shorten the TEST_TMPDIR.
- string tmpdir;
- GET_TEST_TMPDIR(tmpdir);
- string short_tmpdir;
- ASSERT_TRUE(AsShortWindowsPath(tmpdir, &short_tmpdir, nullptr));
- ASSERT_LT(0, short_tmpdir.size());
- ASSERT_TRUE(PathExists(short_tmpdir));
-
- // Assert that a trailing "/" doesn't change the shortening logic and it will
- // be stripped from the result.
- ASSERT_TRUE(AsShortWindowsPath(tmpdir + "/", &actual, nullptr));
- ASSERT_EQ(actual, short_tmpdir);
- ASSERT_NE(actual.back(), '/');
- ASSERT_NE(actual.back(), '\\');
-
- // Assert shortening another long path, and that the result is lowercased.
- string dirname(JoinPath(short_tmpdir, "LONGpathNAME"));
- ASSERT_EQ(0, mkdir(dirname.c_str()));
- ASSERT_TRUE(PathExists(dirname));
- ASSERT_TRUE(AsShortWindowsPath(dirname, &actual, nullptr));
- ASSERT_EQ(short_tmpdir + "\\longpa~1", actual);
-
- // Assert shortening non-existent paths.
- ASSERT_TRUE(AsShortWindowsPath(JoinPath(tmpdir, "NonExistent/FOO"), &actual,
- nullptr));
- ASSERT_EQ(short_tmpdir + "\\nonexistent\\foo", actual);
-}
-
TEST_F(FileWindowsTest, TestMsysRootRetrieval) {
wstring actual;
@@ -513,18 +297,4 @@ TEST_F(FileWindowsTest, TestMakeCanonical) {
ASSERT_EQ(dircanon, symcanon);
}
-TEST(FileTest, IsWindowsDevNullTest) {
- ASSERT_TRUE(IsDevNull("nul"));
- ASSERT_TRUE(IsDevNull("NUL"));
- ASSERT_TRUE(IsDevNull("nuL"));
- ASSERT_TRUE(IsDevNull("/dev/null"));
- ASSERT_FALSE(IsDevNull("/Dev/Null"));
- ASSERT_FALSE(IsDevNull("dev/null"));
- ASSERT_FALSE(IsDevNull("/dev/nul"));
- ASSERT_FALSE(IsDevNull("/dev/nulll"));
- ASSERT_FALSE(IsDevNull("nu"));
- ASSERT_FALSE(IsDevNull(NULL));
- ASSERT_FALSE(IsDevNull(""));
-}
-
} // namespace blaze_util
diff --git a/src/test/cpp/util/logging_test.cc b/src/test/cpp/util/logging_test.cc
index f3ced048a2..d48c177e75 100644
--- a/src/test/cpp/util/logging_test.cc
+++ b/src/test/cpp/util/logging_test.cc
@@ -20,6 +20,7 @@
#include "src/main/cpp/util/bazel_log_handler.h"
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/path.h"
#include "googlemock/include/gmock/gmock.h"
#include "googletest/include/gtest/gtest.h"
diff --git a/src/test/cpp/util/path_posix_test.cc b/src/test/cpp/util/path_posix_test.cc
new file mode 100644
index 0000000000..7d0e4979bf
--- /dev/null
+++ b/src/test/cpp/util/path_posix_test.cc
@@ -0,0 +1,162 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
+#include "src/test/cpp/util/test_util.h"
+#include "googletest/include/gtest/gtest.h"
+
+namespace blaze_util {
+
+using std::pair;
+using std::string;
+using std::vector;
+
+TEST(PathPosixTest, TestDirname) {
+ // The Posix version of SplitPath (thus Dirname too, which is implemented on
+ // top of it) is not aware of Windows paths.
+ ASSERT_EQ("", Dirname(""));
+ ASSERT_EQ("/", Dirname("/"));
+ ASSERT_EQ("", Dirname("foo"));
+ ASSERT_EQ("/", Dirname("/foo"));
+ ASSERT_EQ("/foo", Dirname("/foo/"));
+ ASSERT_EQ("foo", Dirname("foo/bar"));
+ ASSERT_EQ("foo/bar", Dirname("foo/bar/baz"));
+ ASSERT_EQ("", Dirname("\\foo"));
+ ASSERT_EQ("", Dirname("\\foo\\"));
+ ASSERT_EQ("", Dirname("foo\\bar"));
+ ASSERT_EQ("", Dirname("foo\\bar\\baz"));
+ ASSERT_EQ("foo\\bar", Dirname("foo\\bar/baz\\qux"));
+ ASSERT_EQ("c:", Dirname("c:/"));
+ ASSERT_EQ("", Dirname("c:\\"));
+ ASSERT_EQ("c:", Dirname("c:/foo"));
+ ASSERT_EQ("", Dirname("c:\\foo"));
+ ASSERT_EQ("", Dirname("\\\\?\\c:\\"));
+ ASSERT_EQ("", Dirname("\\\\?\\c:\\foo"));
+}
+
+TEST(PathPosixTest, TestBasename) {
+ // The Posix version of SplitPath (thus Basename too, which is implemented on
+ // top of it) is not aware of Windows paths.
+ ASSERT_EQ("", Basename(""));
+ ASSERT_EQ("", Basename("/"));
+ ASSERT_EQ("foo", Basename("foo"));
+ ASSERT_EQ("foo", Basename("/foo"));
+ ASSERT_EQ("", Basename("/foo/"));
+ ASSERT_EQ("bar", Basename("foo/bar"));
+ ASSERT_EQ("baz", Basename("foo/bar/baz"));
+ ASSERT_EQ("\\foo", Basename("\\foo"));
+ ASSERT_EQ("\\foo\\", Basename("\\foo\\"));
+ ASSERT_EQ("foo\\bar", Basename("foo\\bar"));
+ ASSERT_EQ("foo\\bar\\baz", Basename("foo\\bar\\baz"));
+ ASSERT_EQ("baz\\qux", Basename("foo\\bar/baz\\qux"));
+ ASSERT_EQ("qux", Basename("qux"));
+ ASSERT_EQ("", Basename("c:/"));
+ ASSERT_EQ("c:\\", Basename("c:\\"));
+ ASSERT_EQ("foo", Basename("c:/foo"));
+ ASSERT_EQ("c:\\foo", Basename("c:\\foo"));
+ ASSERT_EQ("\\\\?\\c:\\", Basename("\\\\?\\c:\\"));
+ ASSERT_EQ("\\\\?\\c:\\foo", Basename("\\\\?\\c:\\foo"));
+}
+
+TEST(PathPosixTest, JoinPath) {
+ std::string path = JoinPath("", "");
+ ASSERT_EQ("", path);
+
+ path = JoinPath("a", "b");
+ ASSERT_EQ("a/b", path);
+
+ path = JoinPath("a/", "b");
+ ASSERT_EQ("a/b", path);
+
+ path = JoinPath("a", "/b");
+ ASSERT_EQ("a/b", path);
+
+ path = JoinPath("a/", "/b");
+ ASSERT_EQ("a/b", path);
+
+ path = JoinPath("/", "/");
+ ASSERT_EQ("/", path);
+}
+
+TEST(PathPosixTest, GetCwd) {
+ char cwdbuf[PATH_MAX];
+ ASSERT_EQ(cwdbuf, getcwd(cwdbuf, PATH_MAX));
+
+ // Assert that GetCwd() and getcwd() return the same value.
+ string cwd(cwdbuf);
+ ASSERT_EQ(cwd, blaze_util::GetCwd());
+
+ // Change to a different directory.
+ ASSERT_EQ(0, chdir("/usr"));
+
+ // Assert that GetCwd() returns the new CWD.
+ ASSERT_EQ(string("/usr"), blaze_util::GetCwd());
+
+ ASSERT_EQ(0, chdir(cwd.c_str()));
+ ASSERT_EQ(cwd, blaze_util::GetCwd());
+}
+
+TEST(PathPosixTest, IsAbsolute) {
+ ASSERT_FALSE(IsAbsolute(""));
+ ASSERT_TRUE(IsAbsolute("/"));
+ ASSERT_TRUE(IsAbsolute("/foo"));
+ ASSERT_FALSE(IsAbsolute("\\"));
+ ASSERT_FALSE(IsAbsolute("\\foo"));
+ ASSERT_FALSE(IsAbsolute("c:"));
+ ASSERT_FALSE(IsAbsolute("c:/"));
+ ASSERT_FALSE(IsAbsolute("c:\\"));
+ ASSERT_FALSE(IsAbsolute("c:\\foo"));
+ ASSERT_FALSE(IsAbsolute("\\\\?\\c:\\"));
+ ASSERT_FALSE(IsAbsolute("\\\\?\\c:\\foo"));
+}
+
+TEST(PathPosixTest, IsRootDirectory) {
+ ASSERT_FALSE(IsRootDirectory(""));
+ ASSERT_TRUE(IsRootDirectory("/"));
+ ASSERT_FALSE(IsRootDirectory("/foo"));
+ ASSERT_FALSE(IsRootDirectory("\\"));
+ ASSERT_FALSE(IsRootDirectory("\\foo"));
+ ASSERT_FALSE(IsRootDirectory("c:"));
+ ASSERT_FALSE(IsRootDirectory("c:/"));
+ ASSERT_FALSE(IsRootDirectory("c:\\"));
+ ASSERT_FALSE(IsRootDirectory("c:\\foo"));
+ ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\"));
+ ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\foo"));
+}
+
+TEST(PathPosixTest, IsDevNullTest) {
+ ASSERT_TRUE(IsDevNull("/dev/null"));
+ ASSERT_FALSE(IsDevNull("dev/null"));
+ ASSERT_FALSE(IsDevNull("/dev/nul"));
+ ASSERT_FALSE(IsDevNull("/dev/nulll"));
+ ASSERT_FALSE(IsDevNull(NULL));
+ ASSERT_FALSE(IsDevNull(""));
+}
+
+TEST(PathPosixTest, MakeAbsolute) {
+ EXPECT_EQ(MakeAbsolute("/foo/bar"), "/foo/bar");
+ EXPECT_EQ(MakeAbsolute("/foo/bar/"), "/foo/bar/");
+ EXPECT_EQ(MakeAbsolute("foo"), blaze_util::GetCwd() + "/foo");
+ EXPECT_EQ(MakeAbsolute(std::string()), blaze_util::GetCwd());
+ EXPECT_EQ(MakeAbsolute("/dev/null"), "/dev/null");
+}
+
+} // namespace blaze_util
diff --git a/src/test/cpp/util/path_windows_test.cc b/src/test/cpp/util/path_windows_test.cc
new file mode 100644
index 0000000000..789f67cdaa
--- /dev/null
+++ b/src/test/cpp/util/path_windows_test.cc
@@ -0,0 +1,318 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include <stdio.h>
+#include <string.h>
+#include <windows.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
+#include "src/main/cpp/util/strings.h"
+#include "src/main/native/windows/file.h"
+#include "src/main/native/windows/util.h"
+#include "src/test/cpp/util/test_util.h"
+#include "src/test/cpp/util/windows_test_util.h"
+
+#if !defined(COMPILER_MSVC) && !defined(__CYGWIN__)
+#error("This test should only be run on Windows")
+#endif // !defined(COMPILER_MSVC) && !defined(__CYGWIN__)
+
+namespace blaze_util {
+
+using bazel::windows::CreateJunction;
+using std::string;
+using std::unique_ptr;
+using std::wstring;
+
+// Methods defined in path_windows.cc that are only visible for testing.
+string NormalizeWindowsPath(string path);
+
+TEST(PathWindowsTest, TestNormalizeWindowsPath) {
+ ASSERT_EQ(string(""), NormalizeWindowsPath(""));
+ ASSERT_EQ(string(""), NormalizeWindowsPath("."));
+ ASSERT_EQ(string("foo"), NormalizeWindowsPath("foo"));
+ ASSERT_EQ(string("foo"), NormalizeWindowsPath("foo/"));
+ ASSERT_EQ(string("foo\\bar"), NormalizeWindowsPath("foo//bar"));
+ ASSERT_EQ(string("foo\\bar"), NormalizeWindowsPath("../..//foo/./bar"));
+ ASSERT_EQ(string("foo\\bar"), NormalizeWindowsPath("../foo/baz/../bar"));
+ ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:"));
+ ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:/"));
+ ASSERT_EQ(string("c:\\"), NormalizeWindowsPath("c:\\"));
+ ASSERT_EQ(string("c:\\foo\\bar"), NormalizeWindowsPath("c:\\..//foo/./bar/"));
+}
+
+TEST(PathWindowsTest, TestDirname) {
+ ASSERT_EQ("", Dirname(""));
+ ASSERT_EQ("/", Dirname("/"));
+ ASSERT_EQ("", Dirname("foo"));
+ ASSERT_EQ("/", Dirname("/foo"));
+ ASSERT_EQ("/foo", Dirname("/foo/"));
+ ASSERT_EQ("foo", Dirname("foo/bar"));
+ ASSERT_EQ("foo/bar", Dirname("foo/bar/baz"));
+ ASSERT_EQ("\\", Dirname("\\foo"));
+ ASSERT_EQ("\\foo", Dirname("\\foo\\"));
+ ASSERT_EQ("foo", Dirname("foo\\bar"));
+ ASSERT_EQ("foo\\bar", Dirname("foo\\bar\\baz"));
+ ASSERT_EQ("foo\\bar/baz", Dirname("foo\\bar/baz\\qux"));
+ ASSERT_EQ("c:/", Dirname("c:/"));
+ ASSERT_EQ("c:\\", Dirname("c:\\"));
+ ASSERT_EQ("c:/", Dirname("c:/foo"));
+ ASSERT_EQ("c:\\", Dirname("c:\\foo"));
+ ASSERT_EQ("\\\\?\\c:\\", Dirname("\\\\?\\c:\\"));
+ ASSERT_EQ("\\\\?\\c:\\", Dirname("\\\\?\\c:\\foo"));
+}
+
+TEST(PathWindowsTest, TestBasename) {
+ ASSERT_EQ("", Basename(""));
+ ASSERT_EQ("", Basename("/"));
+ ASSERT_EQ("foo", Basename("foo"));
+ ASSERT_EQ("foo", Basename("/foo"));
+ ASSERT_EQ("", Basename("/foo/"));
+ ASSERT_EQ("bar", Basename("foo/bar"));
+ ASSERT_EQ("baz", Basename("foo/bar/baz"));
+ ASSERT_EQ("foo", Basename("\\foo"));
+ ASSERT_EQ("", Basename("\\foo\\"));
+ ASSERT_EQ("bar", Basename("foo\\bar"));
+ ASSERT_EQ("baz", Basename("foo\\bar\\baz"));
+ ASSERT_EQ("qux", Basename("foo\\bar/baz\\qux"));
+ ASSERT_EQ("", Basename("c:/"));
+ ASSERT_EQ("", Basename("c:\\"));
+ ASSERT_EQ("foo", Basename("c:/foo"));
+ ASSERT_EQ("foo", Basename("c:\\foo"));
+ ASSERT_EQ("", Basename("\\\\?\\c:\\"));
+ ASSERT_EQ("foo", Basename("\\\\?\\c:\\foo"));
+}
+
+TEST(PathWindowsTest, TestIsAbsolute) {
+ ASSERT_FALSE(IsAbsolute(""));
+ ASSERT_TRUE(IsAbsolute("/"));
+ ASSERT_TRUE(IsAbsolute("/foo"));
+ ASSERT_TRUE(IsAbsolute("\\"));
+ ASSERT_TRUE(IsAbsolute("\\foo"));
+ ASSERT_FALSE(IsAbsolute("c:"));
+ ASSERT_TRUE(IsAbsolute("c:/"));
+ ASSERT_TRUE(IsAbsolute("c:\\"));
+ ASSERT_TRUE(IsAbsolute("c:\\foo"));
+ ASSERT_TRUE(IsAbsolute("\\\\?\\c:\\"));
+ ASSERT_TRUE(IsAbsolute("\\\\?\\c:\\foo"));
+}
+
+TEST(PathWindowsTest, TestIsRootDirectory) {
+ ASSERT_FALSE(IsRootDirectory(""));
+ ASSERT_TRUE(IsRootDirectory("/"));
+ ASSERT_FALSE(IsRootDirectory("/foo"));
+ ASSERT_TRUE(IsRootDirectory("\\"));
+ ASSERT_FALSE(IsRootDirectory("\\foo"));
+ ASSERT_FALSE(IsRootDirectory("c:"));
+ ASSERT_TRUE(IsRootDirectory("c:/"));
+ ASSERT_TRUE(IsRootDirectory("c:\\"));
+ ASSERT_FALSE(IsRootDirectory("c:\\foo"));
+ ASSERT_TRUE(IsRootDirectory("\\\\?\\c:\\"));
+ ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\foo"));
+}
+
+TEST(PathWindowsTest, TestAsWindowsPath) {
+ SetEnvironmentVariableA("BAZEL_SH", "c:\\some\\long/path\\bin\\bash.exe");
+ wstring actual;
+
+ // Null and empty input produces empty result.
+ ASSERT_TRUE(AsWindowsPath("", &actual, nullptr));
+ ASSERT_EQ(wstring(L""), actual);
+
+ // If the path has a "\\?\" prefix, AsWindowsPath assumes it's a correct
+ // Windows path. If it's not, the Windows API function that we pass the path
+ // to will fail anyway.
+ ASSERT_TRUE(AsWindowsPath("\\\\?\\anything/..", &actual, nullptr));
+ ASSERT_EQ(wstring(L"\\\\?\\anything/.."), actual);
+
+ // Trailing slash or backslash is removed.
+ ASSERT_TRUE(AsWindowsPath("foo/", &actual, nullptr));
+ ASSERT_EQ(wstring(L"foo"), actual);
+ ASSERT_TRUE(AsWindowsPath("foo\\", &actual, nullptr));
+ ASSERT_EQ(wstring(L"foo"), actual);
+
+ // Slashes are converted to backslash.
+ ASSERT_TRUE(AsWindowsPath("foo/bar", &actual, nullptr));
+ ASSERT_EQ(wstring(L"foo\\bar"), actual);
+ ASSERT_TRUE(AsWindowsPath("c:/", &actual, nullptr));
+ ASSERT_EQ(wstring(L"c:\\"), actual);
+ ASSERT_TRUE(AsWindowsPath("c:\\", &actual, nullptr));
+ ASSERT_EQ(wstring(L"c:\\"), actual);
+
+ // Invalid paths
+ string error;
+ ASSERT_FALSE(AsWindowsPath("c:", &actual, &error));
+ EXPECT_TRUE(error.find("working-directory relative paths") != string::npos);
+ ASSERT_FALSE(AsWindowsPath("c:foo", &actual, &error));
+ EXPECT_TRUE(error.find("working-directory relative paths") != string::npos);
+ ASSERT_FALSE(AsWindowsPath("\\\\foo", &actual, &error));
+ EXPECT_TRUE(error.find("network paths") != string::npos);
+
+ // /dev/null and NUL produce NUL.
+ ASSERT_TRUE(AsWindowsPath("/dev/null", &actual, nullptr));
+ ASSERT_EQ(wstring(L"NUL"), actual);
+ ASSERT_TRUE(AsWindowsPath("Nul", &actual, nullptr));
+ ASSERT_EQ(wstring(L"NUL"), actual);
+
+ // MSYS path with drive letter.
+ ASSERT_FALSE(AsWindowsPath("/c", &actual, &error));
+ EXPECT_TRUE(error.find("Unix-style") != string::npos);
+ ASSERT_FALSE(AsWindowsPath("/c/", &actual, &error));
+ EXPECT_TRUE(error.find("Unix-style") != string::npos);
+
+ // Absolute-on-current-drive path gets a drive letter.
+ ASSERT_TRUE(AsWindowsPath("\\foo", &actual, nullptr));
+ ASSERT_EQ(wstring(1, GetCwd()[0]) + L":\\foo", actual);
+
+ // Even for long paths, AsWindowsPath doesn't add a "\\?\" prefix (it's the
+ // caller's duty to do so).
+ wstring wlongpath(L"dummy_long_path\\");
+ string longpath("dummy_long_path/");
+ while (longpath.size() <= MAX_PATH) {
+ wlongpath += wlongpath;
+ longpath += longpath;
+ }
+ wlongpath.pop_back(); // remove trailing "\"
+ ASSERT_TRUE(AsWindowsPath(longpath, &actual, nullptr));
+ ASSERT_EQ(wlongpath, actual);
+}
+
+TEST(PathWindowsTest, TestAsAbsoluteWindowsPath) {
+ SetEnvironmentVariableA("BAZEL_SH", "c:\\some\\long/path\\bin\\bash.exe");
+ wstring actual;
+
+ ASSERT_TRUE(AsAbsoluteWindowsPath("c:/", &actual, nullptr));
+ ASSERT_EQ(L"\\\\?\\c:\\", actual);
+
+ ASSERT_TRUE(AsAbsoluteWindowsPath("c:/..\\non-existent//", &actual, nullptr));
+ ASSERT_EQ(L"\\\\?\\c:\\non-existent", actual);
+
+ WCHAR cwd[MAX_PATH];
+ wstring cwdw(CstringToWstring(GetCwd().c_str()).get());
+ wstring expected =
+ wstring(L"\\\\?\\") + cwdw +
+ ((cwdw.back() == L'\\') ? L"non-existent" : L"\\non-existent");
+ ASSERT_TRUE(AsAbsoluteWindowsPath("non-existent", &actual, nullptr));
+ ASSERT_EQ(actual, expected);
+}
+
+TEST(PathWindowsTest, TestAsShortWindowsPath) {
+ string actual;
+ ASSERT_TRUE(AsShortWindowsPath("/dev/null", &actual, nullptr));
+ ASSERT_EQ(string("NUL"), actual);
+
+ ASSERT_TRUE(AsShortWindowsPath("nul", &actual, nullptr));
+ ASSERT_EQ(string("NUL"), actual);
+
+ ASSERT_TRUE(AsShortWindowsPath("C://", &actual, nullptr));
+ ASSERT_EQ(string("c:\\"), actual);
+
+ string error;
+ ASSERT_FALSE(AsShortWindowsPath("/C//", &actual, &error));
+ EXPECT_TRUE(error.find("Unix-style") != string::npos);
+
+ // The A drive usually doesn't exist but AsShortWindowsPath should still work.
+ // Here we even have multiple trailing slashes, that should be handled too.
+ ASSERT_TRUE(AsShortWindowsPath("A://", &actual, nullptr));
+ ASSERT_EQ(string("a:\\"), actual);
+
+ // Assert that we can shorten the TEST_TMPDIR.
+ char buf[MAX_PATH] = {0};
+ DWORD len = ::GetEnvironmentVariableA("TEST_TMPDIR", buf, MAX_PATH);
+ string tmpdir = buf;
+ ASSERT_GT(tmpdir.size(), 0);
+ string short_tmpdir;
+ ASSERT_TRUE(AsShortWindowsPath(tmpdir, &short_tmpdir, nullptr));
+ ASSERT_LT(0, short_tmpdir.size());
+ ASSERT_TRUE(PathExists(short_tmpdir));
+
+ // Assert that a trailing "/" doesn't change the shortening logic and it will
+ // be stripped from the result.
+ ASSERT_TRUE(AsShortWindowsPath(tmpdir + "/", &actual, nullptr));
+ ASSERT_EQ(actual, short_tmpdir);
+ ASSERT_NE(actual.back(), '/');
+ ASSERT_NE(actual.back(), '\\');
+
+ // Assert shortening another long path, and that the result is lowercased.
+ string dirname(JoinPath(short_tmpdir, "LONGpathNAME"));
+ ASSERT_EQ(0, mkdir(dirname.c_str()));
+ ASSERT_TRUE(PathExists(dirname));
+ ASSERT_TRUE(AsShortWindowsPath(dirname, &actual, nullptr));
+ ASSERT_EQ(short_tmpdir + "\\longpa~1", actual);
+
+ // Assert shortening non-existent paths.
+ ASSERT_TRUE(AsShortWindowsPath(JoinPath(tmpdir, "NonExistent/FOO"), &actual,
+ nullptr));
+ ASSERT_EQ(short_tmpdir + "\\nonexistent\\foo", actual);
+}
+
+TEST(PathWindowsTest, TestMsysRootRetrieval) {
+ wstring actual;
+
+ // We just need "bin/<something>" or "usr/bin/<something>".
+ // Forward slashes are converted to backslashes.
+ SetEnvironmentVariableA("BAZEL_SH", "c:/foo\\bin/some_bash.exe");
+
+ string error;
+ ASSERT_FALSE(AsWindowsPath("/blah", &actual, &error));
+ EXPECT_TRUE(error.find("Unix-style") != string::npos);
+
+ SetEnvironmentVariableA("BAZEL_SH", "c:/tools/msys64/usr/bin/bash.exe");
+ ASSERT_FALSE(AsWindowsPath("/blah", &actual, &error));
+ EXPECT_TRUE(error.find("Unix-style") != string::npos);
+}
+
+TEST(PathWindowsTest, IsWindowsDevNullTest) {
+ ASSERT_TRUE(IsDevNull("nul"));
+ ASSERT_TRUE(IsDevNull("NUL"));
+ ASSERT_TRUE(IsDevNull("nuL"));
+ ASSERT_TRUE(IsDevNull("/dev/null"));
+ ASSERT_FALSE(IsDevNull("/Dev/Null"));
+ ASSERT_FALSE(IsDevNull("dev/null"));
+ ASSERT_FALSE(IsDevNull("/dev/nul"));
+ ASSERT_FALSE(IsDevNull("/dev/nulll"));
+ ASSERT_FALSE(IsDevNull("nu"));
+ ASSERT_FALSE(IsDevNull(NULL));
+ ASSERT_FALSE(IsDevNull(""));
+}
+
+TEST(PathWindowsTest, ConvertPathTest) {
+ EXPECT_EQ("c:\\foo", ConvertPath("C:\\FOO"));
+ EXPECT_EQ("c:\\", ConvertPath("c:/"));
+ EXPECT_EQ("c:\\foo\\bar", ConvertPath("c:/../foo\\BAR\\.\\"));
+ EXPECT_EQ("nul", MakeAbsolute("NUL"));
+ EXPECT_EQ("nul", MakeAbsolute("nul"));
+ EXPECT_EQ("nul", MakeAbsolute("/dev/null"));
+}
+
+TEST(PathWindowsTest, TestMakeAbsolute) {
+ EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:\\foo\\BAR"));
+ EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:/foo/bar"));
+ EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:\\foo\\bar\\"));
+ EXPECT_EQ("c:\\foo\\bar", MakeAbsolute("C:/foo/bar/"));
+ EXPECT_EQ(blaze_util::AsLower(blaze_util::GetCwd()) + "\\foo",
+ MakeAbsolute("foo"));
+ EXPECT_EQ("nul", MakeAbsolute("NUL"));
+ EXPECT_EQ("nul", MakeAbsolute("Nul"));
+ EXPECT_EQ("nul", MakeAbsolute("nul"));
+ EXPECT_EQ(blaze_util::AsLower(blaze_util::GetCwd()), MakeAbsolute(""));
+ EXPECT_EQ("nul", MakeAbsolute("/dev/null"));
+}
+
+} // namespace blaze_util
diff --git a/src/test/cpp/workspace_layout_test.cc b/src/test/cpp/workspace_layout_test.cc
index 8ac4fcb94a..4e17d6464b 100644
--- a/src/test/cpp/workspace_layout_test.cc
+++ b/src/test/cpp/workspace_layout_test.cc
@@ -20,6 +20,7 @@
#include "src/main/cpp/blaze_util_platform.h"
#include "src/main/cpp/util/file.h"
+#include "src/main/cpp/util/path.h"
#include "googletest/include/gtest/gtest.h"
namespace blaze {
diff --git a/src/test/shell/bazel/testdata/embedded_tools_srcs_deps b/src/test/shell/bazel/testdata/embedded_tools_srcs_deps
index 47f15a1d2f..10a614141b 100644
--- a/src/test/shell/bazel/testdata/embedded_tools_srcs_deps
+++ b/src/test/shell/bazel/testdata/embedded_tools_srcs_deps
@@ -30,7 +30,7 @@
//src/main/cpp/util:util
//src/main/cpp/util:numbers
//src/main/cpp/util:md5
-//src/main/cpp/util:file
+//src/main/cpp/util:filesystem
//src/main/native/windows:lib-file
//src/main/native/windows:lib-util
//src/main/cpp/util:strings
diff --git a/src/tools/launcher/BUILD b/src/tools/launcher/BUILD
index 37b0e4dddc..075ee57d1f 100644
--- a/src/tools/launcher/BUILD
+++ b/src/tools/launcher/BUILD
@@ -28,7 +28,7 @@ cc_library(
srcs = ["launcher.cc"],
hdrs = ["launcher.h"],
deps = [
- "//src/main/cpp/util:file",
+ "//src/main/cpp/util:filesystem",
"//src/tools/launcher/util",
"//src/tools/launcher/util:data_parser",
],
diff --git a/src/tools/launcher/java_launcher.cc b/src/tools/launcher/java_launcher.cc
index 5b5a498b71..8daf209d17 100644
--- a/src/tools/launcher/java_launcher.cc
+++ b/src/tools/launcher/java_launcher.cc
@@ -20,6 +20,7 @@
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
#include "src/main/native/windows/file.h"
#include "src/tools/launcher/java_launcher.h"
diff --git a/src/tools/launcher/launcher.cc b/src/tools/launcher/launcher.cc
index ea58e2cb75..9bcc3642c2 100644
--- a/src/tools/launcher/launcher.cc
+++ b/src/tools/launcher/launcher.cc
@@ -20,7 +20,7 @@
#include <string>
#include <vector>
-#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/tools/launcher/launcher.h"
#include "src/tools/launcher/util/data_parser.h"
#include "src/tools/launcher/util/launcher_util.h"
diff --git a/src/tools/launcher/util/BUILD b/src/tools/launcher/util/BUILD
index 8d128c3ee7..d8927c6f35 100644
--- a/src/tools/launcher/util/BUILD
+++ b/src/tools/launcher/util/BUILD
@@ -19,7 +19,7 @@ cc_library(
name = "util",
srcs = ["launcher_util.cc"],
hdrs = ["launcher_util.h"],
- deps = ["//src/main/cpp/util:file"],
+ deps = ["//src/main/cpp/util:filesystem"],
)
cc_test(
diff --git a/src/tools/launcher/util/launcher_util.cc b/src/tools/launcher/util/launcher_util.cc
index 2ef25a352f..3509ce3287 100644
--- a/src/tools/launcher/util/launcher_util.cc
+++ b/src/tools/launcher/util/launcher_util.cc
@@ -23,7 +23,7 @@
#include <sstream>
#include <string>
-#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/tools/launcher/util/launcher_util.h"
namespace bazel {
diff --git a/src/tools/singlejar/test_util.cc b/src/tools/singlejar/test_util.cc
index f8ea0de8a3..722df5dc2a 100644
--- a/src/tools/singlejar/test_util.cc
+++ b/src/tools/singlejar/test_util.cc
@@ -22,6 +22,7 @@
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/path.h"
#include "src/main/cpp/util/strings.h"
#include "googletest/include/gtest/gtest.h"
diff --git a/third_party/ijar/BUILD b/third_party/ijar/BUILD
index 96f4066c01..4460243653 100644
--- a/third_party/ijar/BUILD
+++ b/third_party/ijar/BUILD
@@ -30,7 +30,7 @@ cc_library(
] + select({
"//src:windows": [
"//src/main/cpp/util:errors",
- "//src/main/cpp/util:file",
+ "//src/main/cpp/util:filesystem",
"//src/main/cpp/util:logging",
"//src/main/cpp/util:strings",
],
@@ -59,7 +59,7 @@ cc_library(
visibility = ["//visibility:private"],
deps = [
"//src/main/cpp/util:errors",
- "//src/main/cpp/util:file",
+ "//src/main/cpp/util:filesystem",
"//src/main/cpp/util:logging",
],
)
diff --git a/third_party/ijar/mapped_file_windows.cc b/third_party/ijar/mapped_file_windows.cc
index 91bff719dd..1bcef8d7e0 100644
--- a/third_party/ijar/mapped_file_windows.cc
+++ b/third_party/ijar/mapped_file_windows.cc
@@ -18,8 +18,8 @@
#include <string>
#include "src/main/cpp/util/errors.h"
-#include "src/main/cpp/util/file_platform.h"
#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/path_platform.h"
#include "src/main/cpp/util/strings.h"
#include "third_party/ijar/mapped_file.h"
diff --git a/third_party/ijar/platform_utils.cc b/third_party/ijar/platform_utils.cc
index eeb4c40225..c73827d4b5 100644
--- a/third_party/ijar/platform_utils.cc
+++ b/third_party/ijar/platform_utils.cc
@@ -31,6 +31,8 @@
#include "src/main/cpp/util/file.h"
#include "src/main/cpp/util/file_platform.h"
#include "src/main/cpp/util/logging.h"
+#include "src/main/cpp/util/path.h"
+#include "src/main/cpp/util/path_platform.h"
namespace devtools_ijar {
diff --git a/tools/cpp/runfiles/BUILD b/tools/cpp/runfiles/BUILD
index 7373b03e37..4a79245c04 100644
--- a/tools/cpp/runfiles/BUILD
+++ b/tools/cpp/runfiles/BUILD
@@ -35,7 +35,7 @@ cc_test(
visibility = ["//visibility:public"],
deps = [
":runfiles",
- "//src/main/cpp/util:file",
+ "//src/main/cpp/util:filesystem",
"@com_google_googletest//:gtest_main",
],
)