Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / tools / gn / filesystem_utils.cc
index ee7c5d5..1e4194c 100644 (file)
@@ -6,11 +6,13 @@
 
 #include <algorithm>
 
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "tools/gn/location.h"
+#include "tools/gn/settings.h"
 #include "tools/gn/source_dir.h"
 
 namespace {
@@ -41,7 +43,7 @@ DotDisposition ClassifyAfterDot(const std::string& path,
     *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;
@@ -54,7 +56,7 @@ DotDisposition ClassifyAfterDot(const std::string& path,
       *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;
@@ -97,55 +99,72 @@ bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
   if (path[1] != ':')
     return false;
 
-  // Check drive letter
-  if (!((path[0] >= 'A' && path[0] <= 'Z') ||
-         path[0] >= 'a' && path[0] <= 'z'))
+  // Check drive letter.
+  if (!IsAsciiAlpha(path[0]))
     return false;
 
-  if (path[2] != '/' && path[2] != '\\')
+  if (!IsSlash(path[2]))
     return false;
   return true;
 }
 #endif
 
-}  // namespace
-
-SourceFileType GetSourceFileType(const SourceFile& file,
-                                 Settings::TargetOS os) {
-  base::StringPiece extension = FindExtension(&file.value());
-  if (extension == "cc" || extension == "cpp" || extension == "cxx")
-    return SOURCE_CC;
-  if (extension == "h")
-    return SOURCE_H;
-  if (extension == "c")
-    return SOURCE_C;
+// A wrapper around FilePath.GetComponents that works the way we need. This is
+// not super efficient since it does some O(n) transformations on the path. If
+// this is called a lot, we might want to optimize.
+std::vector<base::FilePath::StringType> GetPathComponents(
+    const base::FilePath& path) {
+  std::vector<base::FilePath::StringType> result;
+  path.GetComponents(&result);
 
-  switch (os) {
-    case Settings::MAC:
-      if (extension == "m")
-        return SOURCE_M;
-      if (extension == "mm")
-        return SOURCE_MM;
-      break;
+  if (result.empty())
+    return result;
 
-    case Settings::WIN:
-      if (extension == "rc")
-        return SOURCE_RC;
-      // TODO(brettw) asm files.
-      break;
+  // GetComponents will preserve the "/" at the beginning, which confuses us.
+  // We don't expect to have relative paths in this function.
+  // Don't use IsSeparator since we always want to allow backslashes.
+  if (result[0] == FILE_PATH_LITERAL("/") ||
+      result[0] == FILE_PATH_LITERAL("\\"))
+    result.erase(result.begin());
 
-    default:
-      break;
-  }
+#if defined(OS_WIN)
+  // On Windows, GetComponents will give us [ "C:", "/", "foo" ], and we
+  // 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].size() == 1 &&
+      IsSlash(static_cast<char>(result[1][0])))
+    result.erase(result.begin() + 1);
+#endif
 
-  if (os != Settings::WIN) {
-    if (extension == "S")
-      return SOURCE_S;
-  }
+  return result;
+}
 
-  return SOURCE_UNKNOWN;
+// Provides the equivalent of == for filesystem strings, trying to do
+// approximately the right thing with case.
+bool FilesystemStringsEqual(const base::FilePath::StringType& a,
+                            const base::FilePath::StringType& b) {
+#if defined(OS_WIN)
+  // Assume case-insensitive filesystems on Windows. We use the CompareString
+  // function to do a case-insensitive comparison based on the current locale
+  // (we don't want GN to depend on ICU which is large and requires data
+  // files). This isn't perfect, but getting this perfectly right is very
+  // difficult and requires I/O, and this comparison should cover 99.9999% of
+  // all cases.
+  //
+  // Note: The documentation for CompareString says it runs fastest on
+  // null-terminated strings with -1 passed for the length, so we do that here.
+  // There should not be embedded nulls in filesystem strings.
+  return ::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
+                         a.c_str(), -1, b.c_str(), -1) == CSTR_EQUAL;
+#else
+  // Assume case-sensitive filesystems on non-Windows.
+  return a == b;
+#endif
 }
 
+}  // namespace
+
 const char* GetExtensionForOutputType(Target::OutputType type,
                                       Settings::TargetOS os) {
   switch (os) {
@@ -196,7 +215,7 @@ const char* GetExtensionForOutputType(Target::OutputType type,
 
 std::string FilePathToUTF8(const base::FilePath::StringType& str) {
 #if defined(OS_WIN)
-  return WideToUTF8(str);
+  return base::WideToUTF8(str);
 #else
   return str;
 #endif
@@ -204,7 +223,7 @@ std::string FilePathToUTF8(const base::FilePath::StringType& str) {
 
 base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
 #if defined(OS_WIN)
-  return base::FilePath(UTF8ToWide(sp));
+  return base::FilePath(base::UTF8ToWide(sp));
 #else
   return base::FilePath(sp.as_string());
 #endif
@@ -212,7 +231,7 @@ base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
 
 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;
@@ -230,7 +249,7 @@ base::StringPiece FindExtension(const std::string* path) {
 
 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.
@@ -264,7 +283,7 @@ void RemoveFilename(std::string* path) {
 }
 
 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) {
@@ -274,44 +293,58 @@ base::StringPiece FindDir(const std::string* path) {
   return base::StringPiece(path->data(), filename_offset);
 }
 
+base::StringPiece FindLastDirComponent(const SourceDir& dir) {
+  const std::string& dir_string = dir.value();
+
+  if (dir_string.empty())
+    return base::StringPiece();
+  int cur = static_cast<int>(dir_string.size()) - 1;
+  DCHECK(dir_string[cur] == '/');
+  int end = cur;
+  cur--;  // Skip before the last slash.
+
+  for (; cur >= 0; cur--) {
+    if (dir_string[cur] == '/')
+      return base::StringPiece(&dir_string[cur + 1], end - cur - 1);
+  }
+  return base::StringPiece(&dir_string[0], end);
+}
+
 bool EnsureStringIsInOutputDir(const SourceDir& dir,
                                const std::string& str,
-                               const Value& originating,
+                               const ParseNode* origin,
                                Err* err) {
-  // The last char of the dir will be a slash. We don't care if the input ends
-  // in a slash or not, so just compare up until there.
-  //
   // This check will be wrong for all proper prefixes "e.g. "/output" will
   // match "/out" but we don't really care since this is just a sanity check.
   const std::string& dir_str = dir.value();
-  if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
-      != 0) {
-    *err = Err(originating, "File not inside output directory.",
-        "The given file should be in the output directory. Normally you would "
-        "specify\n\"$target_output_dir/foo\" or "
-        "\"$target_gen_dir/foo\". I interpreted this as\n\""
-        + str + "\".");
-    return false;
-  }
-  return true;
+  if (str.compare(0, dir_str.length(), dir_str) == 0)
+    return true;  // Output directory is hardcoded.
+
+  *err = Err(origin, "File is not inside output directory.",
+      "The given file should be in the output directory. Normally you would "
+      "specify\n\"$target_out_dir/foo\" or "
+      "\"$target_gen_dir/foo\". I interpreted this as\n\""
+      + str + "\".");
+  return false;
 }
 
 bool IsPathAbsolute(const base::StringPiece& path) {
   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;
 }
@@ -328,9 +361,11 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
     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)) {
@@ -358,8 +393,7 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& 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 = 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.
@@ -375,7 +409,7 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& 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() && 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.
@@ -396,13 +430,13 @@ std::string InvertDir(const SourceDir& path) {
   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;
@@ -430,7 +464,7 @@ void NormalizePath(std::string* path) {
   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)) {
@@ -469,7 +503,7 @@ void NormalizePath(std::string* path) {
               // 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;
@@ -478,13 +512,15 @@ void NormalizePath(std::string* path) {
         // 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.
@@ -503,12 +539,6 @@ void ConvertPathToSystem(std::string* path) {
 #endif
 }
 
-std::string PathToSystem(const std::string& path) {
-  std::string ret(path);
-  ConvertPathToSystem(&ret);
-  return ret;
-}
-
 std::string RebaseSourceAbsolutePath(const std::string& input,
                                      const SourceDir& dest_dir) {
   CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/')
@@ -523,8 +553,7 @@ std::string RebaseSourceAbsolutePath(const std::string& input,
   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;
@@ -533,7 +562,7 @@ std::string RebaseSourceAbsolutePath(const std::string& input,
   // 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("../");
   }
 
@@ -546,3 +575,180 @@ std::string RebaseSourceAbsolutePath(const std::string& input,
 
   return ret;
 }
+
+std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
+  std::string ret;
+
+  if (dir.value().empty()) {
+    // Just keep input the same.
+  } else if (dir.value() == "/") {
+    ret.assign("/.");
+  } else if (dir.value() == "//") {
+    ret.assign("//.");
+  } else {
+    ret.assign(dir.value());
+    ret.resize(ret.size() - 1);
+  }
+  return ret;
+}
+
+SourceDir SourceDirForPath(const base::FilePath& source_root,
+                           const base::FilePath& path) {
+  std::vector<base::FilePath::StringType> source_comp =
+      GetPathComponents(source_root);
+  std::vector<base::FilePath::StringType> path_comp =
+      GetPathComponents(path);
+
+  // See if path is inside the source root by looking for each of source root's
+  // components at the beginning of path.
+  bool is_inside_source;
+  if (path_comp.size() < source_comp.size()) {
+    // Too small to fit.
+    is_inside_source = false;
+  } else {
+    is_inside_source = true;
+    for (size_t i = 0; i < source_comp.size(); i++) {
+      if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
+        is_inside_source = false;
+        break;
+      }
+    }
+  }
+
+  std::string result_str;
+  size_t initial_path_comp_to_use;
+  if (is_inside_source) {
+    // Construct a source-relative path beginning in // and skip all of the
+    // shared directories.
+    result_str = "//";
+    initial_path_comp_to_use = source_comp.size();
+  } else {
+    // Not inside source code, construct a system-absolute path.
+    result_str = "/";
+    initial_path_comp_to_use = 0;
+  }
+
+  for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
+    result_str.append(FilePathToUTF8(path_comp[i]));
+    result_str.push_back('/');
+  }
+  return SourceDir(result_str);
+}
+
+SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
+  base::FilePath cd;
+  base::GetCurrentDirectory(&cd);
+  return SourceDirForPath(source_root, cd);
+}
+
+std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
+  // The default toolchain has no subdir.
+  if (is_default)
+    return std::string();
+
+  // For now just assume the toolchain name is always a valid dir name. We may
+  // want to clean up the in the future.
+  return toolchain_label.name() + "/";
+}
+
+SourceDir GetToolchainOutputDir(const Settings* settings) {
+  return settings->toolchain_output_subdir().AsSourceDir(
+      settings->build_settings());
+}
+
+SourceDir GetToolchainOutputDir(const BuildSettings* build_settings,
+                                const Label& toolchain_label, bool is_default) {
+  std::string result = build_settings->build_dir().value();
+  result.append(GetOutputSubdirName(toolchain_label, is_default));
+  return SourceDir(SourceDir::SWAP_IN, &result);
+}
+
+SourceDir GetToolchainGenDir(const Settings* settings) {
+  return GetToolchainGenDirAsOutputFile(settings).AsSourceDir(
+      settings->build_settings());
+}
+
+OutputFile GetToolchainGenDirAsOutputFile(const Settings* settings) {
+  OutputFile result(settings->toolchain_output_subdir());
+  result.value().append("gen/");
+  return result;
+}
+
+SourceDir GetToolchainGenDir(const BuildSettings* build_settings,
+                             const Label& toolchain_label, bool is_default) {
+  std::string result = GetToolchainOutputDir(
+      build_settings, toolchain_label, is_default).value();
+  result.append("gen/");
+  return SourceDir(SourceDir::SWAP_IN, &result);
+}
+
+SourceDir GetOutputDirForSourceDir(const Settings* settings,
+                                   const SourceDir& source_dir) {
+  return GetOutputDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
+      settings->build_settings());
+}
+
+OutputFile GetOutputDirForSourceDirAsOutputFile(const Settings* settings,
+                                                const SourceDir& source_dir) {
+  OutputFile result = settings->toolchain_output_subdir();
+  result.value().append("obj/");
+
+  if (source_dir.is_source_absolute()) {
+    // The source dir is source-absolute, so we trim off the two leading
+    // slashes to append to the toolchain object directory.
+    result.value().append(&source_dir.value()[2],
+                          source_dir.value().size() - 2);
+  }
+  return result;
+}
+
+SourceDir GetGenDirForSourceDir(const Settings* settings,
+                                const SourceDir& source_dir) {
+  return GetGenDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
+      settings->build_settings());
+}
+
+OutputFile GetGenDirForSourceDirAsOutputFile(const Settings* settings,
+                                             const SourceDir& source_dir) {
+  OutputFile result = GetToolchainGenDirAsOutputFile(settings);
+
+  if (source_dir.is_source_absolute()) {
+    // The source dir should be source-absolute, so we trim off the two leading
+    // slashes to append to the toolchain object directory.
+    DCHECK(source_dir.is_source_absolute());
+    result.value().append(&source_dir.value()[2],
+                          source_dir.value().size() - 2);
+  }
+  return result;
+}
+
+SourceDir GetTargetOutputDir(const Target* target) {
+  return GetOutputDirForSourceDirAsOutputFile(
+      target->settings(), target->label().dir()).AsSourceDir(
+          target->settings()->build_settings());
+}
+
+OutputFile GetTargetOutputDirAsOutputFile(const Target* target) {
+  return GetOutputDirForSourceDirAsOutputFile(
+      target->settings(), target->label().dir());
+}
+
+SourceDir GetTargetGenDir(const Target* target) {
+  return GetTargetGenDirAsOutputFile(target).AsSourceDir(
+      target->settings()->build_settings());
+}
+
+OutputFile GetTargetGenDirAsOutputFile(const Target* target) {
+  return GetGenDirForSourceDirAsOutputFile(
+      target->settings(), target->label().dir());
+}
+
+SourceDir GetCurrentOutputDir(const Scope* scope) {
+  return GetOutputDirForSourceDirAsOutputFile(
+      scope->settings(), scope->GetSourceDir()).AsSourceDir(
+          scope->settings()->build_settings());
+}
+
+SourceDir GetCurrentGenDir(const Scope* scope) {
+  return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
+}