Improved GN code related to resolving paths.

Moved most of the logic related to resolving paths
to ResolvePath and ResolveRelative in filesystem_utils.cc.
Improved SourceDir class to return std::string with resolved path
for cases when returning SourceDir or SourceFile wasn't really needed.

R=brettw@chromium.org, dpranke@chromium.org

Bug: 819720
Change-Id: Ia59da00e83de45431762ebb19a9153471f7e91e3
Reviewed-on: https://chromium-review.googlesource.com/953422
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Dirk Pranke <dpranke@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#542692}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 36773f30d94495bc81193a52b49504d2e137b9e3
diff --git a/tools/gn/build_settings.cc b/tools/gn/build_settings.cc
index 38b830f..79b8365 100644
--- a/tools/gn/build_settings.cc
+++ b/tools/gn/build_settings.cc
@@ -49,6 +49,11 @@
   return dir.Resolve(root_path_).NormalizePathSeparatorsTo('/');
 }
 
+base::FilePath BuildSettings::GetFullPath(const std::string& path,
+                                          bool as_file) const {
+  return ResolvePath(path, as_file, root_path_).NormalizePathSeparatorsTo('/');
+}
+
 base::FilePath BuildSettings::GetFullPathSecondary(
     const SourceFile& file) const {
   return file.Resolve(secondary_source_path_).NormalizePathSeparatorsTo('/');
@@ -59,6 +64,12 @@
   return dir.Resolve(secondary_source_path_).NormalizePathSeparatorsTo('/');
 }
 
+base::FilePath BuildSettings::GetFullPathSecondary(const std::string& path,
+                                                   bool as_file) const {
+  return ResolvePath(path, as_file, secondary_source_path_)
+      .NormalizePathSeparatorsTo('/');
+}
+
 void BuildSettings::ItemDefined(std::unique_ptr<Item> item) const {
   DCHECK(item);
   if (!item_defined_callback_.is_null())
diff --git a/tools/gn/build_settings.h b/tools/gn/build_settings.h
index 670f996..52f3625 100644
--- a/tools/gn/build_settings.h
+++ b/tools/gn/build_settings.h
@@ -79,12 +79,21 @@
   // root source tree.
   base::FilePath GetFullPath(const SourceFile& file) const;
   base::FilePath GetFullPath(const SourceDir& dir) const;
+  // Works the same way as other GetFullPath.
+  // Parameter as_file defines whether path should be treated as a
+  // SourceFile or SourceDir value.
+  base::FilePath GetFullPath(const std::string& path, bool as_file) const;
 
   // Returns the absolute OS path inside the secondary source path. Will return
   // an empty FilePath if the secondary source path is empty. When loading a
   // buildfile, the GetFullPath should always be consulted first.
   base::FilePath GetFullPathSecondary(const SourceFile& file) const;
   base::FilePath GetFullPathSecondary(const SourceDir& dir) const;
+  // Works the same way as other GetFullPathSecondary.
+  // Parameter as_file defines whether path should be treated as a
+  // SourceFile or SourceDir value.
+  base::FilePath GetFullPathSecondary(const std::string& path,
+                                      bool as_file) const;
 
   // Called when an item is defined from a background thread.
   void ItemDefined(std::unique_ptr<Item> item) const;
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index 6cce895..f9f6794 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -705,6 +705,109 @@
   return ret;
 }
 
+base::FilePath ResolvePath(const std::string& value,
+                           bool as_file,
+                           const base::FilePath& source_root) {
+  if (value.empty())
+    return base::FilePath();
+
+  std::string converted;
+  if (!IsPathSourceAbsolute(value)) {
+    if (value.size() > 2 && value[2] == ':') {
+      // Windows path, strip the leading slash.
+      converted.assign(&value[1], value.size() - 1);
+    } else {
+      converted.assign(value);
+    }
+    return base::FilePath(UTF8ToFilePath(converted));
+  }
+
+  // String the double-leading slash for source-relative paths.
+  converted.assign(&value[2], value.size() - 2);
+
+  if (as_file && source_root.empty())
+    return UTF8ToFilePath(converted).NormalizePathSeparatorsTo('/');
+
+  return source_root.Append(UTF8ToFilePath(converted))
+      .NormalizePathSeparatorsTo('/');
+}
+
+template <typename StringType>
+std::string ResolveRelative(const StringType& input,
+                            const std::string& value,
+                            bool as_file,
+                            const base::StringPiece& source_root) {
+  std::string result;
+
+  if (input.size() >= 2 && input[0] == '/' && input[1] == '/') {
+    // Source-relative.
+    result.assign(input.data(), input.size());
+    if (!as_file) {
+      if (!EndsWithSlash(result))
+        result.push_back('/');
+    }
+    NormalizePath(&result, source_root);
+    return result;
+  } else if (IsPathAbsolute(input)) {
+    if (source_root.empty() ||
+        !MakeAbsolutePathRelativeIfPossible(source_root, input, &result)) {
+#if defined(OS_WIN)
+      if (input[0] != '/')  // See the file case for why we do this check.
+        result = "/";
+#endif
+      result.append(input.data(), input.size());
+    }
+    NormalizePath(&result);
+    if (!as_file) {
+      if (!EndsWithSlash(result))
+        result.push_back('/');
+    }
+    return result;
+  }
+
+  if (!source_root.empty()) {
+    std::string absolute =
+        FilePathToUTF8(ResolvePath(value, as_file, UTF8ToFilePath(source_root))
+                           .AppendASCII(input)
+                           .value());
+    NormalizePath(&absolute);
+    if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute, &result)) {
+#if defined(OS_WIN)
+      if (absolute[0] != '/')  // See the file case for why we do this check.
+        result = "/";
+#endif
+      result.append(absolute.data(), absolute.size());
+    }
+    if (!as_file && !EndsWithSlash(result))
+      result.push_back('/');
+    return result;
+  }
+
+  // With no source_root, there's nothing we can do about
+  // e.g. input=../../../path/to/file and value=//source and we'll
+  // errornously return //file.
+  result.reserve(value.size() + input.size());
+  result.assign(value);
+  result.append(input.data(), input.size());
+
+  NormalizePath(&result);
+  if (!as_file && !EndsWithSlash(result))
+    result.push_back('/');
+
+  return result;
+}
+
+// Explicit template instantiation
+template std::string ResolveRelative(const base::StringPiece& input,
+                                     const std::string& value,
+                                     bool as_file,
+                                     const base::StringPiece& source_root);
+
+template std::string ResolveRelative(const std::string& input,
+                                     const std::string& value,
+                                     bool as_file,
+                                     const base::StringPiece& source_root);
+
 std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
   std::string ret;
 
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
index 0396283..1313279 100644
--- a/tools/gn/filesystem_utils.h
+++ b/tools/gn/filesystem_utils.h
@@ -152,6 +152,31 @@
     const SourceDir& dest_dir,
     const base::StringPiece& source_root = base::StringPiece());
 
+// Resolves a file or dir name (parameter input) relative to
+// value directory. Will return an empty SourceDir/File on error
+// and set the give *err pointer (required). Empty input is always an error.
+// Returned value can be used to set value in either SourceFile or SourceDir
+// (based on as_file parameter).
+//
+// Parameter as_file defines whether result path will look like a file path
+// or it should be treated as a directory (contains "/" and the end
+// of the string).
+//
+// If source_root is supplied, these functions will additionally handle the
+// case where the input is a system-absolute but still inside the source
+// tree. This is the case for some external tools.
+template <typename StringType>
+std::string ResolveRelative(const StringType& input,
+                            const std::string& value,
+                            bool as_file,
+                            const base::StringPiece& source_root);
+
+// Resolves source file or directory relative to some given source root. Returns
+// an empty file path on error.
+base::FilePath ResolvePath(const std::string& value,
+                           bool as_file,
+                           const base::FilePath& source_root);
+
 // Returns the given directory with no terminating slash at the end, such that
 // appending a slash and more stuff will produce a valid path.
 //
diff --git a/tools/gn/function_exec_script.cc b/tools/gn/function_exec_script.cc
index 139b811..0f6397c 100644
--- a/tools/gn/function_exec_script.cc
+++ b/tools/gn/function_exec_script.cc
@@ -137,19 +137,21 @@
     return Value();
 
   // Find the python script to run.
-  SourceFile script_source =
-      cur_dir.ResolveRelativeFile(args[0], err,
-          scope->settings()->build_settings()->root_path_utf8());
+  std::string script_source_path = cur_dir.ResolveRelativeAs(
+      true, args[0], err,
+      scope->settings()->build_settings()->root_path_utf8());
   if (err->has_error())
     return Value();
-  base::FilePath script_path = build_settings->GetFullPath(script_source);
+  base::FilePath script_path =
+      build_settings->GetFullPath(script_source_path, true);
   if (!build_settings->secondary_source_path().empty() &&
       !base::PathExists(script_path)) {
     // Fall back to secondary source root when the file doesn't exist.
-    script_path = build_settings->GetFullPathSecondary(script_source);
+    script_path =
+        build_settings->GetFullPathSecondary(script_source_path, true);
   }
 
-  ScopedTrace trace(TraceItem::TRACE_SCRIPT_EXECUTE, script_source.value());
+  ScopedTrace trace(TraceItem::TRACE_SCRIPT_EXECUTE, script_source_path);
   trace.SetToolchain(settings->toolchain_label());
 
   // Add all dependencies of this script, including the script itself, to the
@@ -163,10 +165,11 @@
     for (const auto& dep : deps_value.list_value()) {
       if (!dep.VerifyTypeIs(Value::STRING, err))
         return Value();
-      g_scheduler->AddGenDependency(
-          build_settings->GetFullPath(cur_dir.ResolveRelativeFile(
-              dep, err,
-              scope->settings()->build_settings()->root_path_utf8())));
+      g_scheduler->AddGenDependency(build_settings->GetFullPath(
+          cur_dir.ResolveRelativeAs(
+              true, dep, err,
+              scope->settings()->build_settings()->root_path_utf8()),
+          true));
       if (err->has_error())
         return Value();
     }
@@ -236,10 +239,12 @@
     }
   }
   if (g_scheduler->verbose_logging()) {
-    g_scheduler->Log("Pythoning", script_source.value() + " took " +
-        base::Int64ToString(
-            (base::TimeTicks::Now() - begin_exec).InMilliseconds()) +
-        "ms");
+    g_scheduler->Log(
+        "Pythoning",
+        script_source_path + " took " +
+            base::Int64ToString(
+                (base::TimeTicks::Now() - begin_exec).InMilliseconds()) +
+            "ms");
   }
 
   if (exit_code != 0) {
diff --git a/tools/gn/function_get_path_info.cc b/tools/gn/function_get_path_info.cc
index 1c542aa..e08eca1 100644
--- a/tools/gn/function_get_path_info.cc
+++ b/tools/gn/function_get_path_info.cc
@@ -41,7 +41,7 @@
         settings->build_settings()->root_path_utf8());
   }
 
-  // Input is a directory.
+  // Input is a file.
   return current_dir.ResolveRelativeFile(input, err,
       settings->build_settings()->root_path_utf8()).GetDir();
 }
@@ -100,14 +100,12 @@
           BuildDirType::OBJ));
     }
     case WHAT_ABSPATH: {
-      if (!input_string.empty() &&
-          input_string[input_string.size() - 1] == '/') {
-        return current_dir.ResolveRelativeDir(input, err,
-            settings->build_settings()->root_path_utf8()).value();
-      } else {
-        return current_dir.ResolveRelativeFile(input, err,
-            settings->build_settings()->root_path_utf8()).value();
-      }
+      bool as_dir =
+          !input_string.empty() && input_string[input_string.size() - 1] == '/';
+
+      return current_dir.ResolveRelativeAs(
+          !as_dir, input, err, settings->build_settings()->root_path_utf8(),
+          &input_string);
     }
     default:
       NOTREACHED();
diff --git a/tools/gn/source_dir.cc b/tools/gn/source_dir.cc
index e8fdff0..17eeb61 100644
--- a/tools/gn/source_dir.cc
+++ b/tools/gn/source_dir.cc
@@ -23,6 +23,38 @@
   }
 }
 
+// Validates input value (input_value) and sets proper error message.
+// Note: Parameter blame_input is used only for generating error message.
+template <typename StringType>
+bool ValidateResolveInput(bool as_file,
+                          const Value& blame_input_value,
+                          const StringType& input_value,
+                          Err* err) {
+  if (as_file) {
+    // It's an error to resolve an empty string or one that is a directory
+    // (indicated by a trailing slash) because this is the function that expects
+    // to return a file.
+    if (input_value.empty()) {
+      *err = Err(blame_input_value, "Empty file path.",
+                 "You can't use empty strings as file paths.");
+      return false;
+    } else if (input_value[input_value.size() - 1] == '/') {
+      std::string help = "You specified the path\n  ";
+      help.append(std::string(input_value));
+      help.append(
+          "\nand it ends in a slash, indicating you think it's a directory."
+          "\nBut here you're supposed to be listing a file.");
+      *err = Err(blame_input_value, "File path ends in a slash.", help);
+      return false;
+    }
+  } else if (input_value.empty()) {
+    *err = Err(blame_input_value, "Empty directory path.",
+               "You can't use empty strings as directories.");
+    return false;
+  }
+  return true;
+}
+
 }  // namespace
 
 SourceDir::SourceDir() = default;
@@ -43,176 +75,85 @@
 
 SourceDir::~SourceDir() = default;
 
+template <typename StringType>
+std::string SourceDir::ResolveRelativeAs(
+    bool as_file,
+    const Value& blame_input_value,
+    const StringType& input_value,
+    Err* err,
+    const base::StringPiece& source_root) const {
+  if (!ValidateResolveInput<StringType>(as_file, blame_input_value, input_value,
+                                        err)) {
+    return std::string();
+  }
+  return ResolveRelative(input_value, value_, as_file, source_root);
+}
+
 SourceFile SourceDir::ResolveRelativeFile(
     const Value& p,
     Err* err,
     const base::StringPiece& source_root) const {
   SourceFile ret;
+
   if (!p.VerifyTypeIs(Value::STRING, err))
     return ret;
 
-  // It's an error to resolve an empty string or one that is a directory
-  // (indicated by a trailing slash) because this is the function that expects
-  // to return a file.
-  const std::string& str = p.string_value();
-  if (str.empty()) {
-    *err = Err(p, "Empty file path.",
-               "You can't use empty strings as file paths. That's just wrong.");
-    return ret;
-  } else if (str[str.size() - 1] == '/') {
-    *err = Err(p, "File path ends in a slash.",
-               "You specified the path\n  " + str + "\n"
-               "and it ends in a slash, indicating you think it's a directory."
-               "\nBut here you're supposed to be listing a file.");
+  const std::string& input_string = p.string_value();
+  if (!ValidateResolveInput<std::string>(true, p, input_string, err)) {
     return ret;
   }
-
-  if (str.size() >= 2 && str[0] == '/' && str[1] == '/') {
-    // Source-relative.
-    ret.value_.assign(str.data(), str.size());
-    NormalizePath(&ret.value_, source_root);
-    return ret;
-  } else if (IsPathAbsolute(str)) {
-    if (source_root.empty() ||
-        !MakeAbsolutePathRelativeIfPossible(source_root, str, &ret.value_)) {
-#if defined(OS_WIN)
-      // On Windows we'll accept "C:\foo" as an absolute path, which we want
-      // to convert to "/C:..." here.
-      if (str[0] != '/')
-        ret.value_ = "/";
-#endif
-      ret.value_.append(str.data(), str.size());
-    }
-    NormalizePath(&ret.value_);
-    return ret;
-  }
-
-  if (!source_root.empty()) {
-    std::string absolute =
-        FilePathToUTF8(Resolve(UTF8ToFilePath(source_root)).AppendASCII(
-            str).value());
-    NormalizePath(&absolute);
-    if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute,
-                                            &ret.value_)) {
-#if defined(OS_WIN)
-      // On Windows we'll accept "C:\foo" as an absolute path, which we want
-      // to convert to "/C:..." here.
-      if (absolute[0] != '/')
-        ret.value_ = "/";
-#endif
-      ret.value_.append(absolute.data(), absolute.size());
-    }
-    return ret;
-  }
-
-  // With no source_root_, there's nothing we can do about
-  // e.g. p=../../../path/to/file and value_=//source and we'll
-  // errornously return //file.
-  ret.value_.reserve(value_.size() + str.size());
-  ret.value_.assign(value_);
-  ret.value_.append(str.data(), str.size());
-
-  NormalizePath(&ret.value_);
+  ret.value_ = ResolveRelative(input_string, value_, true, source_root);
   return ret;
 }
 
+std::string SourceDir::ResolveRelativeAs(bool as_file,
+                                         const Value& v,
+                                         Err* err,
+                                         const base::StringPiece& source_root,
+                                         const std::string* v_value) const {
+  if (!v.VerifyTypeIs(Value::STRING, err))
+    return std::string();
+
+  if (!v_value) {
+    v_value = &v.string_value();
+  }
+  std::string result =
+      ResolveRelativeAs(as_file, v, *v_value, err, source_root);
+  if (!as_file)
+    AssertValueSourceDirString(result);
+  return result;
+}
+
 SourceDir SourceDir::ResolveRelativeDir(
-    const Value& p,
+    const Value& v,
     Err* err,
     const base::StringPiece& source_root) const {
-  if (!p.VerifyTypeIs(Value::STRING, err))
+  if (!v.VerifyTypeIs(Value::STRING, err))
     return SourceDir();
-  return ResolveRelativeDir(p, p.string_value(), err, source_root);
-}
 
-SourceDir SourceDir::ResolveRelativeDir(
-    const Value& blame_but_dont_use,
-    const base::StringPiece& str,
-    Err* err,
-    const base::StringPiece& source_root) const {
-  SourceDir ret;
-
-  if (str.empty()) {
-    *err = Err(blame_but_dont_use, "Empty directory path.",
-               "You can't use empty strings as directories. "
-               "That's just wrong.");
-    return ret;
-  }
-
-  if (str.size() >= 2 && str[0] == '/' && str[1] == '/') {
-    // Source-relative.
-    ret.value_.assign(str.data(), str.size());
-    if (!EndsWithSlash(ret.value_))
-      ret.value_.push_back('/');
-    NormalizePath(&ret.value_, source_root);
-    return ret;
-  } else if (IsPathAbsolute(str)) {
-    if (source_root.empty() ||
-        !MakeAbsolutePathRelativeIfPossible(source_root, str, &ret.value_)) {
-#if defined(OS_WIN)
-      if (str[0] != '/')  // See the file case for why we do this check.
-        ret.value_ = "/";
-#endif
-      ret.value_.append(str.data(), str.size());
-    }
-    NormalizePath(&ret.value_);
-    if (!EndsWithSlash(ret.value_))
-      ret.value_.push_back('/');
-    return ret;
-  }
-
-  if (!source_root.empty()) {
-    std::string absolute =
-        FilePathToUTF8(Resolve(UTF8ToFilePath(source_root)).AppendASCII(
-            str.as_string()).value());
-    NormalizePath(&absolute);
-    if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute,
-                                            &ret.value_)) {
-#if defined(OS_WIN)
-      if (absolute[0] != '/')  // See the file case for why we do this check.
-        ret.value_ = "/";
-#endif
-      ret.value_.append(absolute.data(), absolute.size());
-    }
-    if (!EndsWithSlash(ret.value_))
-      ret.value_.push_back('/');
-    return ret;
-  }
-
-  ret.value_.reserve(value_.size() + str.size());
-  ret.value_.assign(value_);
-  ret.value_.append(str.data(), str.size());
-
-  NormalizePath(&ret.value_);
-  if (!EndsWithSlash(ret.value_))
-    ret.value_.push_back('/');
-  AssertValueSourceDirString(ret.value_);
-
-  return ret;
+  return ResolveRelativeDir<std::string>(v, v.string_value(), err, source_root);
 }
 
 base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
-  if (is_null())
-    return base::FilePath();
-
-  std::string converted;
-  if (is_system_absolute()) {
-    if (value_.size() > 2 && value_[2] == ':') {
-      // Windows path, strip the leading slash.
-      converted.assign(&value_[1], value_.size() - 1);
-    } else {
-      converted.assign(value_);
-    }
-    return base::FilePath(UTF8ToFilePath(converted));
-  }
-
-  // String the double-leading slash for source-relative paths.
-  converted.assign(&value_[2], value_.size() - 2);
-  return source_root.Append(UTF8ToFilePath(converted))
-      .NormalizePathSeparatorsTo('/');
+  return ResolvePath(value_, false, source_root);
 }
 
 void SourceDir::SwapValue(std::string* v) {
   value_.swap(*v);
   AssertValueSourceDirString(value_);
 }
+
+// Explicit template instantiation
+template std::string SourceDir::ResolveRelativeAs(
+    bool as_file,
+    const Value& blame_input_value,
+    const std::string& input_value,
+    Err* err,
+    const base::StringPiece& source_root) const;
+
+template std::string SourceDir::ResolveRelativeAs(
+    bool as_file,
+    const Value& blame_input_value,
+    const base::StringPiece& input_value,
+    Err* err,
+    const base::StringPiece& source_root) const;
diff --git a/tools/gn/source_dir.h b/tools/gn/source_dir.h
index 422fa89..142285c 100644
--- a/tools/gn/source_dir.h
+++ b/tools/gn/source_dir.h
@@ -37,28 +37,57 @@
   SourceDir(SwapIn, std::string* s);
   ~SourceDir();
 
-  // Resolves a file or dir name relative to this source directory. Will return
-  // an empty SourceDir/File on error and set the give *err pointer (required).
-  // Empty input is always an error.
+  // Resolves a file or dir name (based on as_file parameter) relative
+  // to this source directory. Will return an empty string on error
+  // and set the give *err pointer (required). Empty input is always an error.
+  //
+  // Passed non null v_value will be used to resolve path (in cases where
+  // a substring has been extracted from the value, as with label resolution).
+  // In this use case parameter v is used to generate proper error.
   //
   // If source_root is supplied, these functions will additionally handle the
   // case where the input is a system-absolute but still inside the source
   // tree. This is the case for some external tools.
+  std::string ResolveRelativeAs(
+      bool as_file,
+      const Value& v,
+      Err* err,
+      const base::StringPiece& source_root = base::StringPiece(),
+      const std::string* v_value = nullptr) const;
+
+  // Like ResolveRelativeAs above, but allows to produce result
+  // without overhead for string conversion (on input value).
+  template <typename StringType>
+  std::string ResolveRelativeAs(
+      bool as_file,
+      const Value& blame_input_value,
+      const StringType& input_value,
+      Err* err,
+      const base::StringPiece& source_root = base::StringPiece()) const;
+
+  // Wrapper for ResolveRelativeAs.
   SourceFile ResolveRelativeFile(
       const Value& p,
       Err* err,
       const base::StringPiece& source_root = base::StringPiece()) const;
-  SourceDir ResolveRelativeDir(
-      const Value& p,
-      Err* err,
-      const base::StringPiece& source_root = base::StringPiece()) const;
 
-  // Like ResolveRelativeDir but takes a separate value (which gets blamed)
-  // and string to use (in cases where a substring has been extracted from the
-  // value, as with label resolution).
+  // Wrapper for ResolveRelativeAs.
+  template <typename StringType>
   SourceDir ResolveRelativeDir(
-      const Value& blame_but_dont_use,
-      const base::StringPiece& p,
+      const Value& blame_input_value,
+      const StringType& input_value,
+      Err* err,
+      const base::StringPiece& source_root = base::StringPiece()) const {
+    SourceDir ret;
+    ret.value_ = ResolveRelativeAs<StringType>(false, blame_input_value,
+                                               input_value, err, source_root);
+    return ret;
+  }
+
+  // Wrapper for ResolveRelativeDir where input_value equals to
+  // v.string_value().
+  SourceDir ResolveRelativeDir(
+      const Value& v,
       Err* err,
       const base::StringPiece& source_root = base::StringPiece()) const;
 
diff --git a/tools/gn/source_file.cc b/tools/gn/source_file.cc
index b28c3ac..befc5a5 100644
--- a/tools/gn/source_file.cc
+++ b/tools/gn/source_file.cc
@@ -61,23 +61,5 @@
 }
 
 base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const {
-  if (is_null())
-    return base::FilePath();
-
-  std::string converted;
-  if (is_system_absolute()) {
-    if (value_.size() > 2 && value_[2] == ':') {
-      // Windows path, strip the leading slash.
-      converted.assign(&value_[1], value_.size() - 1);
-    } else {
-      converted.assign(value_);
-    }
-    return base::FilePath(UTF8ToFilePath(converted));
-  }
-
-  converted.assign(&value_[2], value_.size() - 2);
-  if (source_root.empty())
-    return UTF8ToFilePath(converted).NormalizePathSeparatorsTo('/');
-  return source_root.Append(UTF8ToFilePath(converted))
-      .NormalizePathSeparatorsTo('/');
+  return ResolvePath(value_, true, source_root);
 }
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
index bbc9793..ab85729 100644
--- a/tools/gn/target_generator.cc
+++ b/tools/gn/target_generator.cc
@@ -222,24 +222,18 @@
     const Value& input = input_list[i];
     if (!input.VerifyTypeIs(Value::STRING, err_))
       return false;
-    const std::string& input_str = input.string_value();
+    const std::string input_str = input.string_value();
 
     // Treat each input as either a file or a directory, depending on the
     // last character.
-    if (!input_str.empty() && input_str[input_str.size() - 1] == '/') {
-      // Resolve as directory.
-      SourceDir resolved =
-          dir.ResolveRelativeDir(input, input_str, err_, root_path);
-      if (err_->has_error())
-        return false;
-      output_list.push_back(resolved.value());
-    } else {
-      // Resolve as file.
-      SourceFile resolved = dir.ResolveRelativeFile(input, err_, root_path);
-      if (err_->has_error())
-        return false;
-      output_list.push_back(resolved.value());
-    }
+    bool as_dir = !input_str.empty() && input_str[input_str.size() - 1] == '/';
+
+    std::string resolved =
+        dir.ResolveRelativeAs(!as_dir, input, err_, root_path, &input_str);
+    if (err_->has_error())
+      return false;
+
+    output_list.push_back(resolved);
   }
   return true;
 }