Do more slash normalization in GN.
This makes GN more aggressive about converting backslashes into forward-slashes, and makes the path handling functions mostly indifferent with regard to the two types of slashes.
The previous behavior was a bit confusing in that you would end up with strings like "//out\Debug/" on Windows, and certain operations on this would fail. With this change, the backslash will be more aggressively converted. The downside is that Posix filenames with literal backslashes in them are now unrepresentable in GN. But I think for a cross-platform system on constrained input (build files) this is a reasonable tradeoff for more consistent path handling.
[email protected]
Review URL: https://codereview.chromium.org/177003008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@253969 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index af3027f..fbc8506 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -43,7 +43,7 @@
*consumed_len = 1;
return DIRECTORY_CUR;
}
- if (path[after_dot] == '/') {
+ if (IsSlash(path[after_dot])) {
// Single dot followed by a slash.
*consumed_len = 2; // Consume the slash
return DIRECTORY_CUR;
@@ -56,7 +56,7 @@
*consumed_len = 2;
return DIRECTORY_UP;
}
- if (path[after_dot + 1] == '/') {
+ if (IsSlash(path[after_dot + 1])) {
// Double dot folowed by a slash.
*consumed_len = 3;
return DIRECTORY_UP;
@@ -104,7 +104,7 @@
path[0] >= 'a' && path[0] <= 'z'))
return false;
- if (path[2] != '/' && path[2] != '\\')
+ if (!IsSlash(path[2]))
return false;
return true;
}
@@ -133,7 +133,7 @@
// don't want the slash in there. This doesn't support input like "C:foo"
// which means foo relative to the current directory of the C drive but
// that's basically legacy DOS behavior we don't need to support.
- if (result.size() >= 2 && result[1] == L"/" || result[1] == L"\\")
+ if (result.size() >= 2 && result[1].size() == 1 && IsSlash(result[1][0]))
result.erase(result.begin() + 1);
#endif
@@ -267,7 +267,7 @@
size_t FindExtensionOffset(const std::string& path) {
for (int i = static_cast<int>(path.size()); i >= 0; i--) {
- if (path[i] == '/')
+ if (IsSlash(path[i]))
break;
if (path[i] == '.')
return i + 1;
@@ -285,7 +285,7 @@
size_t FindFilenameOffset(const std::string& path) {
for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
- if (path[i] == '/')
+ if (IsSlash(path[i]))
return i + 1;
}
return 0; // No filename found means everything was the filename.
@@ -319,7 +319,7 @@
}
bool EndsWithSlash(const std::string& s) {
- return !s.empty() && s[s.size() - 1] == '/';
+ return !s.empty() && IsSlash(s[s.size() - 1]);
}
base::StringPiece FindDir(const std::string* path) {
@@ -355,18 +355,19 @@
if (path.empty())
return false;
- if (path[0] != '/') {
+ if (!IsSlash(path[0])) {
#if defined(OS_WIN)
// Check for Windows system paths like "C:\foo".
- if (path.size() > 2 &&
- path[1] == ':' && (path[2] == '/' || path[2] == '\\'))
+ if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
return true;
#endif
return false; // Doesn't begin with a slash, is relative.
}
+ // Double forward slash at the beginning means source-relative (we don't
+ // allow backslashes for denoting this).
if (path.size() > 1 && path[1] == '/')
- return false; // Double slash at the beginning means source-relative.
+ return false;
return true;
}
@@ -383,9 +384,11 @@
return false; // The source root is longer: the path can never be inside.
#if defined(OS_WIN)
- // Source root should be canonical on Windows.
+ // 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
+ // backward.
DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
- source_root[1] == ':' && source_root[2] =='\\');
+ source_root[1] == ':' && IsSlash(source_root[2]));
size_t after_common_index = std::string::npos;
if (DoesBeginWindowsDriveLetter(path)) {
@@ -413,8 +416,7 @@
// 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() &&
- (path[first_after_slash] == '/' || path[first_after_slash] == '\\'))
+ while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
first_after_slash++;
dest->assign("//"); // Result is source root relative.
@@ -430,7 +432,7 @@
// 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() && path[first_after_slash] == '/')
+ while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
first_after_slash++;
dest->assign("//"); // Result is source root relative.
@@ -451,13 +453,13 @@
size_t begin_index = 1;
// If the input begins with two slashes, skip over both (this is a
- // source-relative dir).
+ // source-relative dir). These must be forward slashes only.
if (value.size() > 1 && value[1] == '/')
begin_index = 2;
std::string ret;
for (size_t i = begin_index; i < value.size(); i++) {
- if (value[i] == '/')
+ if (IsSlash(value[i]))
ret.append("../");
}
return ret;
@@ -485,7 +487,7 @@
size_t dest_i = top_index;
for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
if (pathbuf[src_i] == '.') {
- if (src_i == 0 || pathbuf[src_i - 1] == '/') {
+ if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
// Slash followed by a dot, see if it's something special.
size_t consumed_len;
switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
@@ -524,7 +526,7 @@
// 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 && pathbuf[dest_i - 1] != '/')
+ while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
dest_i--;
}
src_i += consumed_len;
@@ -533,13 +535,15 @@
// Dot not preceeded by a slash, copy it literally.
pathbuf[dest_i++] = pathbuf[src_i++];
}
- } else if (pathbuf[src_i] == '/') {
- if (src_i > 0 && pathbuf[src_i - 1] == '/') {
+ } else if (IsSlash(pathbuf[src_i])) {
+ if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
// Two slashes in a row, skip over it.
src_i++;
} else {
- // Just one slash, copy it.
- pathbuf[dest_i++] = pathbuf[src_i++];
+ // Just one slash, copy it, normalizing to foward slash.
+ pathbuf[dest_i] = '/';
+ dest_i++;
+ src_i++;
}
} else {
// Input nothing special, just copy it.
@@ -578,8 +582,7 @@
size_t common_prefix_len = 2; // The beginning two "//" are always the same.
size_t max_common_length = std::min(input.size(), dest.size());
for (size_t i = common_prefix_len; i < max_common_length; i++) {
- if ((input[i] == '/' || input[i] == '\\') &&
- (dest[i] == '/' || dest[i] == '\\'))
+ if (IsSlash(input[i]) && IsSlash(dest[i]))
common_prefix_len = i + 1;
else if (input[i] != dest[i])
break;
@@ -588,7 +591,7 @@
// Invert the dest dir starting from the end of the common prefix.
std::string ret;
for (size_t i = common_prefix_len; i < dest.size(); i++) {
- if (dest[i] == '/' || dest[i] == '\\')
+ if (IsSlash(dest[i]))
ret.append("../");
}