[filesystem_utils] Fix a bug in MakeAbsolutePathRelativeIfPossible
Before this change, this function will return true when it is called
with "/some/dir-sufix/a" and "/some/dir" and rebase it into
"//-sufix/a". This change fixed this bug.
Change-Id: Ibb40213497e1c4ee5b874ad2eca88a7704caa2d9
Bug: crbug.com/gn/58
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/5680
Commit-Queue: Brett Wilson <brettw@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index cc121b3..c378233 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -198,6 +198,18 @@
}
}
+size_t AbsPathLenWithNoTrailingSlash(const base::StringPiece& path) {
+ size_t len = path.size();
+#if defined(OS_WIN)
+ size_t min_len = 3;
+#else
+ // On posix system. The minimal abs path is "/".
+ size_t min_len = 1;
+#endif
+ for (; len > min_len && IsSlash(path[len - 1]); len--)
+ ;
+ return len;
+}
} // namespace
std::string FilePathToUTF8(const base::FilePath::StringType& str) {
@@ -353,9 +365,13 @@
dest->clear();
- if (source_root.size() > path.size())
- return false; // The source root is longer: the path can never be inside.
+ // There is no specification of how many slashes may be at the end of
+ // source_root or path. Trim them off for easier string manipulation.
+ size_t path_len = AbsPathLenWithNoTrailingSlash(path);
+ size_t source_root_len = AbsPathLenWithNoTrailingSlash(source_root);
+ if (source_root_len > path_len)
+ return false; // The source root is longer: the path can never be inside.
#if defined(OS_WIN)
// Source root should be canonical on Windows. Note that the initial slash
// must be forward slash, but that the other ones can be either forward or
@@ -366,19 +382,29 @@
size_t after_common_index = std::string::npos;
if (DoesBeginWindowsDriveLetter(path)) {
// Handle "C:\foo"
- if (AreAbsoluteWindowsPathsEqual(source_root,
- path.substr(0, source_root.size())))
- after_common_index = source_root.size();
- else
+ if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
+ path.substr(0, source_root_len))) {
+ after_common_index = source_root_len;
+ if (path_len == source_root_len) {
+ *dest = "//";
+ return true;
+ }
+ } else {
return false;
- } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
+ }
+ } else if (path[0] == '/' && source_root_len <= path_len - 1 &&
DoesBeginWindowsDriveLetter(path.substr(1))) {
// Handle "/C:/foo"
- if (AreAbsoluteWindowsPathsEqual(source_root,
- path.substr(1, source_root.size())))
- after_common_index = source_root.size() + 1;
- else
+ if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
+ path.substr(1, source_root_len))) {
+ after_common_index = source_root_len + 1;
+ if (path_len + 1 == source_root_len) {
+ *dest = "//";
+ return true;
+ }
+ } else {
return false;
+ }
} else {
return false;
}
@@ -386,12 +412,15 @@
// If we get here, there's a match and after_common_index identifies the
// part after it.
- // The base may or may not have a trailing slash, so skip all slashes from
- // the path after our prefix match.
- size_t first_after_slash = after_common_index;
- while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
+ if (!IsSlash(path[after_common_index])) {
+ // path is ${source-root}SUFIX/...
+ return false;
+ }
+ // A source-root relative path, The input may have an unknown number of
+ // slashes after the previous match. Skip over them.
+ size_t first_after_slash = after_common_index + 1;
+ while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
first_after_slash++;
-
dest->assign("//"); // Result is source root relative.
dest->append(&path.data()[first_after_slash],
path.size() - first_after_slash);
@@ -401,11 +430,21 @@
// On non-Windows this is easy. Since we know both are absolute, just do a
// prefix check.
- if (path.substr(0, source_root.size()) == source_root) {
- // The base may or may not have a trailing slash, so skip all slashes from
- // the path after our prefix match.
- size_t first_after_slash = source_root.size();
- while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
+
+ if (path.substr(0, source_root_len) ==
+ source_root.substr(0, source_root_len)) {
+ if (path_len == source_root_len) {
+ // path is equivalent to source_root.
+ *dest = "//";
+ return true;
+ } else if (!IsSlash(path[source_root_len])) {
+ // path is ${source-root}SUFIX/...
+ return false;
+ }
+ // A source-root relative path, The input may have an unknown number of
+ // slashes after the previous match. Skip over them.
+ size_t first_after_slash = source_root_len + 1;
+ while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
first_after_slash++;
dest->assign("//"); // Result is source root relative.
diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc
index dcaa099..433ea36 100644
--- a/tools/gn/filesystem_utils_unittest.cc
+++ b/tools/gn/filesystem_utils_unittest.cc
@@ -848,3 +848,21 @@
EXPECT_EQ("obj/",
GetBuildDirAsOutputFile(context, BuildDirType::OBJ).value());
}
+
+TEST(FilesystemUtils, ResolveRelativeTest) {
+ std::string result;
+#ifndef OS_WIN
+ EXPECT_TRUE(
+ MakeAbsolutePathRelativeIfPossible("/some/dir", "/some/dir/a", &result));
+ EXPECT_EQ(result, "//a");
+
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
+ "/some/dir", "/some/dir-sufix/a", &result));
+#else
+ EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("C:/some/dir",
+ "/C:/some/dir/a", &result));
+ EXPECT_EQ(result, "//a");
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
+ "C:/some/dir", "C:/some/dir-sufix/a", &result));
+#endif
+}
diff --git a/tools/gn/function_rebase_path_unittest.cc b/tools/gn/function_rebase_path_unittest.cc
index 718ecb1..fdd09dc 100644
--- a/tools/gn/function_rebase_path_unittest.cc
+++ b/tools/gn/function_rebase_path_unittest.cc
@@ -121,6 +121,9 @@
RebaseOne(scope, "foo/", "C:/ssd/out/Debug", "//"));
#else
setup.build_settings()->SetBuildDir(SourceDir("/ssd/out/Debug"));
+ setup.build_settings()->SetRootPath(base::FilePath("/ssd/out/Debug"));
+ EXPECT_EQ("../Debug-suffix/a", RebaseOne(scope, "/ssd/out/Debug-suffix/a",
+ "/ssd/out/Debug", "/ssd/out/Debug"));
setup.build_settings()->SetRootPath(base::FilePath("/hdd/src"));
// Test system absolute to-dir.