Commit 910ac443 authored by slan's avatar slan Committed by Commit bot

[GN] Add support to rebase_path to resolve paths above the source root.

Currently, rebase_path clips any path which references the filesystem
above or outside of the source root. Extend this functionality to allow
rebase_path to properly resolve paths outside the source root when the
absolute path to the source root is known. When the source root is
unknown, the behavior will continue to clip.

BUG=554241
TEST=gn_unittests

Review URL: https://codereview.chromium.org/1455203002

Cr-Commit-Position: refs/heads/master@{#363080}
parent 45dbe839
......@@ -377,7 +377,7 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
#endif
}
void NormalizePath(std::string* path) {
void NormalizePath(std::string* path, const base::StringPiece& source_root) {
char* pathbuf = path->empty() ? nullptr : &(*path)[0];
// top_index is the first character we can modify in the path. Anything
......@@ -433,9 +433,48 @@ void NormalizePath(std::string* path) {
// up more levels. Otherwise "../.." would collapse to
// nothing.
top_index = dest_i;
} else if (top_index == 2 && !source_root.empty()) {
// |path| was passed in as a source-absolute path. Prepend
// |source_root| to make |path| absolute. |source_root| must not
// end with a slash unless we are at root.
DCHECK(source_root.size() == 1u ||
!IsSlash(source_root[source_root.size() - 1u]));
size_t source_root_len = source_root.size();
#if defined(OS_WIN)
// On Windows, if the source_root does not start with a slash,
// append one here for consistency.
if (!IsSlash(source_root[0])) {
path->insert(0, "/" + source_root.as_string());
source_root_len++;
} else {
path->insert(0, source_root.data(), source_root_len);
}
// Normalize slashes in source root portion.
for (size_t i = 0; i < source_root_len; ++i) {
if ((*path)[i] == '\\')
(*path)[i] = '/';
}
#else
path->insert(0, source_root.data(), source_root_len);
#endif
// |path| is now absolute, so |top_index| is 1. |dest_i| and
// |src_i| should be incremented to keep the same relative
// position. Comsume the leading "//" by decrementing |dest_i|.
top_index = 1;
pathbuf = &(*path)[0];
dest_i += source_root_len - 2;
src_i += source_root_len;
// Just find the previous slash or the beginning of input.
while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
dest_i--;
}
// Otherwise we're at the beginning of an absolute path. Don't
// allow ".." to go up another level and just eat it.
// Otherwise we're at the beginning of a system-absolute path, or
// a source-absolute path for which we don't know the absolute
// path. Don't allow ".." to go up another level, and just eat it.
} else {
// Just find the previous slash or the beginning of input.
while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
......
......@@ -111,8 +111,15 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
const base::StringPiece& path,
std::string* dest);
// Collapses "." and sequential "/"s and evaluates "..".
void NormalizePath(std::string* path);
// Collapses "." and sequential "/"s and evaluates "..". |path| may be
// system-absolute, source-absolute, or relative. If |path| is source-absolute
// and |source_root| is non-empty, |path| may be system absolute after this
// function returns, if |path| references the filesystem outside of
// |source_root| (ex. path = "//.."). In this case on Windows, |path| will have
// a leading slash. Otherwise, |path| will retain its relativity. |source_root|
// must not end with a slash.
void NormalizePath(std::string* path,
const base::StringPiece& source_root = base::StringPiece());
// Converts slashes to backslashes for Windows. Keeps the string unchanged
// for other systems.
......
......@@ -204,7 +204,7 @@ TEST(FilesystemUtils, NormalizePath) {
NormalizePath(&input);
EXPECT_EQ("../bar", input);
input = "/../foo"; // Don't go aboe the root dir.
input = "/../foo"; // Don't go above the root dir.
NormalizePath(&input);
EXPECT_EQ("/foo", input);
......@@ -241,6 +241,134 @@ TEST(FilesystemUtils, NormalizePath) {
input = "//foo/bar/";
NormalizePath(&input);
EXPECT_EQ("//foo/bar/", input);
#if defined(OS_WIN)
// Go above and outside of the source root.
input = "//../foo";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("/C:/source/foo", input);
input = "//../foo";
NormalizePath(&input, "C:\\source\\root");
EXPECT_EQ("/C:/source/foo", input);
input = "//../";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("/C:/source/", input);
input = "//../foo.txt";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("/C:/source/foo.txt", input);
input = "//../foo/bar/";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("/C:/source/foo/bar/", input);
// Go above and back into the source root. This should return a system-
// absolute path. We could arguably return this as a source-absolute path,
// but that would require additional handling to account for a rare edge
// case.
input = "//../root/foo";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("/C:/source/root/foo", input);
input = "//../root/foo/bar/";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("/C:/source/root/foo/bar/", input);
// Stay inside the source root
input = "//foo/bar";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("//foo/bar", input);
input = "//foo/bar/";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("//foo/bar/", input);
// The path should not go above the system root. Note that on Windows, this
// will consume the drive (C:).
input = "//../../../../../foo/bar";
NormalizePath(&input, "/C:/source/root");
EXPECT_EQ("/foo/bar", input);
// Test when the source root is the letter drive.
input = "//../foo";
NormalizePath(&input, "/C:");
EXPECT_EQ("/foo", input);
input = "//../foo";
NormalizePath(&input, "C:");
EXPECT_EQ("/foo", input);
input = "//../foo";
NormalizePath(&input, "/");
EXPECT_EQ("/foo", input);
input = "//../";
NormalizePath(&input, "\\C:");
EXPECT_EQ("/", input);
input = "//../foo.txt";
NormalizePath(&input, "/C:");
EXPECT_EQ("/foo.txt", input);
#else
// Go above and outside of the source root.
input = "//../foo";
NormalizePath(&input, "/source/root");
EXPECT_EQ("/source/foo", input);
input = "//../";
NormalizePath(&input, "/source/root");
EXPECT_EQ("/source/", input);
input = "//../foo.txt";
NormalizePath(&input, "/source/root");
EXPECT_EQ("/source/foo.txt", input);
input = "//../foo/bar/";
NormalizePath(&input, "/source/root");
EXPECT_EQ("/source/foo/bar/", input);
// Go above and back into the source root. This should return a system-
// absolute path. We could arguably return this as a source-absolute path,
// but that would require additional handling to account for a rare edge
// case.
input = "//../root/foo";
NormalizePath(&input, "/source/root");
EXPECT_EQ("/source/root/foo", input);
input = "//../root/foo/bar/";
NormalizePath(&input, "/source/root");
EXPECT_EQ("/source/root/foo/bar/", input);
// Stay inside the source root
input = "//foo/bar";
NormalizePath(&input, "/source/root");
EXPECT_EQ("//foo/bar", input);
input = "//foo/bar/";
NormalizePath(&input, "/source/root");
EXPECT_EQ("//foo/bar/", input);
// The path should not go above the system root.
input = "//../../../../../foo/bar";
NormalizePath(&input, "/source/root");
EXPECT_EQ("/foo/bar", input);
// Test when the source root is the system root.
input = "//../foo/bar/";
NormalizePath(&input, "/");
EXPECT_EQ("/foo/bar/", input);
input = "//../";
NormalizePath(&input, "/");
EXPECT_EQ("/", input);
input = "//../foo.txt";
NormalizePath(&input, "/");
EXPECT_EQ("/foo.txt", input);
#endif
}
TEST(FilesystemUtils, RebasePath) {
......
......@@ -43,7 +43,7 @@ TEST(RebasePath, Strings) {
EXPECT_EQ("../..", RebaseOne(scope, "../..", "//out/Debug", "."));
EXPECT_EQ("../../", RebaseOne(scope, "../../", "//out/Debug", "."));
// We don't allow going above the root source dir.
// Without a source root defined, we cannot move out of the source tree.
EXPECT_EQ("../..", RebaseOne(scope, "../../..", "//out/Debug", "."));
// Source-absolute input paths.
......@@ -62,19 +62,36 @@ TEST(RebasePath, Strings) {
// Test system path output.
#if defined(OS_WIN)
setup.build_settings()->SetRootPath(base::FilePath(L"C:/source"));
EXPECT_EQ("C:/source", RebaseOne(scope, ".", "", "//"));
EXPECT_EQ("C:/source/", RebaseOne(scope, "//", "", "//"));
EXPECT_EQ("C:/source/foo", RebaseOne(scope, "foo", "", "//"));
EXPECT_EQ("C:/source/foo/", RebaseOne(scope, "foo/", "", "//"));
EXPECT_EQ("C:/source/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
setup.build_settings()->SetRootPath(base::FilePath(L"C:/path/to/src"));
EXPECT_EQ("C:/path/to/src", RebaseOne(scope, ".", "", "//"));
EXPECT_EQ("C:/path/to/src/", RebaseOne(scope, "//", "", "//"));
EXPECT_EQ("C:/path/to/src/foo", RebaseOne(scope, "foo", "", "//"));
EXPECT_EQ("C:/path/to/src/foo/", RebaseOne(scope, "foo/", "", "//"));
EXPECT_EQ("C:/path/to/src/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
EXPECT_EQ("C:/path/to/other/tools",
RebaseOne(scope, "//../other/tools", "", "//"));
EXPECT_EQ("C:/path/to/src/foo/bar",
RebaseOne(scope, "//../src/foo/bar", "", "//"));
EXPECT_EQ("C:/path/to", RebaseOne(scope, "//..", "", "//"));
EXPECT_EQ("C:/path", RebaseOne(scope, "../../../..", "", "."));
EXPECT_EQ("C:/path/to/external/dir/",
RebaseOne(scope, "//../external/dir/", "", "//"));
#else
setup.build_settings()->SetRootPath(base::FilePath("/source"));
EXPECT_EQ("/source", RebaseOne(scope, ".", "", "//"));
EXPECT_EQ("/source/", RebaseOne(scope, "//", "", "//"));
EXPECT_EQ("/source/foo", RebaseOne(scope, "foo", "", "//"));
EXPECT_EQ("/source/foo/", RebaseOne(scope, "foo/", "", "//"));
EXPECT_EQ("/source/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
setup.build_settings()->SetRootPath(base::FilePath("/path/to/src"));
EXPECT_EQ("/path/to/src", RebaseOne(scope, ".", "", "//"));
EXPECT_EQ("/path/to/src/", RebaseOne(scope, "//", "", "//"));
EXPECT_EQ("/path/to/src/foo", RebaseOne(scope, "foo", "", "//"));
EXPECT_EQ("/path/to/src/foo/", RebaseOne(scope, "foo/", "", "//"));
EXPECT_EQ("/path/to/src/tools/gn/foo", RebaseOne(scope, "foo", "", "."));
EXPECT_EQ("/path/to/other/tools",
RebaseOne(scope, "//../other/tools", "", "//"));
EXPECT_EQ("/path/to/src/foo/bar",
RebaseOne(scope, "//../src/foo/bar", "", "//"));
EXPECT_EQ("/path/to", RebaseOne(scope, "//..", "", "//"));
EXPECT_EQ("/path", RebaseOne(scope, "../../../..", "", "."));
EXPECT_EQ("/path/to/external/dir/",
RebaseOne(scope, "//../external/dir/", "", "//"));
#endif
}
......
......@@ -71,7 +71,7 @@ SourceFile SourceDir::ResolveRelativeFile(
if (str.size() >= 2 && str[0] == '/' && str[1] == '/') {
// Source-relative.
ret.value_.assign(str.data(), str.size());
NormalizePath(&ret.value_);
NormalizePath(&ret.value_, source_root);
return ret;
} else if (IsPathAbsolute(str)) {
if (source_root.empty() ||
......@@ -145,7 +145,7 @@ SourceDir SourceDir::ResolveRelativeDir(
ret.value_.assign(str.data(), str.size());
if (!EndsWithSlash(ret.value_))
ret.value_.push_back('/');
NormalizePath(&ret.value_);
NormalizePath(&ret.value_, source_root);
return ret;
} else if (IsPathAbsolute(str)) {
if (source_root.empty() ||
......
......@@ -61,11 +61,41 @@ TEST(SourceDir, ResolveRelativeFile) {
Value(nullptr, "../../foo"), &err, source_root) ==
SourceFile("/C:/source/foo"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeFile(
Value(nullptr, "//../foo"), &err, source_root) ==
SourceFile("/C:/source/foo"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeFile(
Value(nullptr, "//../root/foo"), &err, source_root) ==
SourceFile("/C:/source/root/foo"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeFile(
Value(nullptr, "//../../../foo/bar"), &err, source_root) ==
SourceFile("/foo/bar"));
EXPECT_FALSE(err.has_error());
#else
EXPECT_TRUE(base.ResolveRelativeFile(
Value(nullptr, "../../foo"), &err, source_root) ==
SourceFile("/source/foo"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeFile(
Value(nullptr, "//../foo"), &err, source_root) ==
SourceFile("/source/foo"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeFile(
Value(nullptr, "//../root/foo"), &err, source_root) ==
SourceFile("/source/root/foo"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeFile(
Value(nullptr, "//../../../foo/bar"), &err, source_root) ==
SourceFile("/foo/bar"));
EXPECT_FALSE(err.has_error());
#endif
#if defined(OS_WIN)
......@@ -120,11 +150,27 @@ TEST(SourceDir, ResolveRelativeDir) {
Value(nullptr, "../../foo"), &err, source_root) ==
SourceDir("/C:/source/foo/"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeDir(
Value(nullptr, "//../foo"), &err, source_root) ==
SourceDir("/C:/source/foo/"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeDir(
Value(nullptr, "//.."), &err, source_root) ==
SourceDir("/C:/source/"));
EXPECT_FALSE(err.has_error());
#else
EXPECT_TRUE(base.ResolveRelativeDir(
Value(nullptr, "../../foo"), &err, source_root) ==
SourceDir("/source/foo/"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeDir(
Value(nullptr, "//../foo"), &err, source_root) ==
SourceDir("/source/foo/"));
EXPECT_FALSE(err.has_error());
EXPECT_TRUE(base.ResolveRelativeDir(
Value(nullptr, "//.."), &err, source_root) ==
SourceDir("/source/"));
EXPECT_FALSE(err.has_error());
#endif
#if defined(OS_WIN)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment