gn: Support build directories outside the source tree.
The main use-case is to support using a fast SSD or ramdisk for the
build directory, but a slower persistent disk for the source code.
The general idea behind this change is modifying
- RebaseSourceAbsolutePath()
- SourceDir::ResolveRelativeFile()
- SourceDir::ResolveRelativeDir()
- GetOutputDirForSourceDirAsOutputFile()
- PathOutput
to work with paths reaching out of the source directory.
Thanks to jam@ for the Windows-fixes.
BUG=343728
TEST=New unit tests + Unit tests pass.
TEST=`gn gen /ssd/out/Debug && ninja -C /ssd/out/Debug` work as expected.
Review URL: https://codereview.chromium.org/630223002
Cr-Commit-Position: refs/heads/master@{#303719}
diff --git a/tools/gn/action_target_generator.cc b/tools/gn/action_target_generator.cc
index 7936359..09c89e4 100644
--- a/tools/gn/action_target_generator.cc
+++ b/tools/gn/action_target_generator.cc
@@ -73,7 +73,8 @@
return false;
SourceFile script_file =
- scope_->GetSourceDir().ResolveRelativeFile(value->string_value());
+ scope_->GetSourceDir().ResolveRelativeFile(value->string_value(),
+ scope_->settings()->build_settings()->root_path_utf8());
if (script_file.value().empty()) {
*err_ = Err(*value, "script name is empty");
return false;
diff --git a/tools/gn/build_settings.cc b/tools/gn/build_settings.cc
index 37d50f5..3ffb8283 100644
--- a/tools/gn/build_settings.cc
+++ b/tools/gn/build_settings.cc
@@ -17,7 +17,6 @@
python_path_(other.python_path_),
build_config_file_(other.build_config_file_),
build_dir_(other.build_dir_),
- build_to_source_dir_string_(other.build_to_source_dir_string_),
build_args_(other.build_args_) {
}
@@ -36,7 +35,6 @@
void BuildSettings::SetBuildDir(const SourceDir& d) {
build_dir_ = d;
- build_to_source_dir_string_ = InvertDir(d);
}
base::FilePath BuildSettings::GetFullPath(const SourceFile& file) const {
diff --git a/tools/gn/build_settings.h b/tools/gn/build_settings.h
index daea82dd..33e454b 100644
--- a/tools/gn/build_settings.h
+++ b/tools/gn/build_settings.h
@@ -57,12 +57,6 @@
const SourceDir& build_dir() const { return build_dir_; }
void SetBuildDir(const SourceDir& dir);
- // The inverse of relative_build_dir, ending with a separator.
- // Example: relative_build_dir_ = "out/Debug/" this will be "../../"
- const std::string& build_to_source_dir_string() const {
- return build_to_source_dir_string_;
- }
-
// The build args are normally specified on the command-line.
Args& build_args() { return build_args_; }
const Args& build_args() const { return build_args_; }
@@ -99,7 +93,6 @@
SourceFile build_config_file_;
SourceDir build_dir_;
- std::string build_to_source_dir_string_;
Args build_args_;
ItemDefinedCallback item_defined_callback_;
diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc
index 890cf2c0..8162698 100644
--- a/tools/gn/command_format.cc
+++ b/tools/gn/command_format.cc
@@ -746,7 +746,8 @@
Setup setup;
SourceDir source_dir =
SourceDirForCurrentDirectory(setup.build_settings().root_path());
- SourceFile file = source_dir.ResolveRelativeFile(args[0]);
+ SourceFile file = source_dir.ResolveRelativeFile(args[0],
+ setup.build_settings().root_path_utf8());
std::string output_string;
if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index 1e4194c..ebfb942 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -421,27 +421,6 @@
#endif
}
-std::string InvertDir(const SourceDir& path) {
- const std::string value = path.value();
- if (value.empty())
- return std::string();
-
- DCHECK(value[0] == '/');
- size_t begin_index = 1;
-
- // If the input begins with two slashes, skip over both (this is a
- // 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 (IsSlash(value[i]))
- ret.append("../");
- }
- return ret;
-}
-
void NormalizePath(std::string* path) {
char* pathbuf = path->empty() ? NULL : &(*path)[0];
@@ -539,18 +518,13 @@
#endif
}
-std::string RebaseSourceAbsolutePath(const std::string& input,
- const SourceDir& dest_dir) {
- CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/')
- << "Input to rebase isn't source-absolute: " << input;
- CHECK(dest_dir.is_source_absolute())
- << "Dir to rebase to isn't source-absolute: " << dest_dir.value();
-
- const std::string& dest = dest_dir.value();
+std::string MakeRelativePath(const std::string& input,
+ const std::string& dest) {
+ std::string ret;
// Skip the common prefixes of the source and dest as long as they end in
// a [back]slash.
- size_t common_prefix_len = 2; // The beginning two "//" are always the same.
+ size_t common_prefix_len = 0;
size_t max_common_length = std::min(input.size(), dest.size());
for (size_t i = common_prefix_len; i < max_common_length; i++) {
if (IsSlash(input[i]) && IsSlash(dest[i]))
@@ -560,7 +534,6 @@
}
// 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 (IsSlash(dest[i]))
ret.append("../");
@@ -576,6 +549,58 @@
return ret;
}
+std::string RebasePath(const std::string& input,
+ const SourceDir& dest_dir,
+ const base::StringPiece& source_root) {
+ std::string ret;
+ DCHECK(source_root.empty() || !source_root.ends_with("/"));
+
+ bool input_is_source_path = (input.size() >= 2 &&
+ input[0] == '/' && input[1] == '/');
+
+ if (!source_root.empty() &&
+ (!input_is_source_path || !dest_dir.is_source_absolute())) {
+ std::string input_full;
+ std::string dest_full;
+ if (input_is_source_path) {
+ source_root.AppendToString(&input_full);
+ input_full.push_back('/');
+ input_full.append(input, 2, std::string::npos);
+ } else {
+ input_full.append(input);
+ }
+ if (dest_dir.is_source_absolute()) {
+ source_root.AppendToString(&dest_full);
+ dest_full.push_back('/');
+ dest_full.append(dest_dir.value(), 2, std::string::npos);
+ } else {
+#if defined(OS_WIN)
+ // On Windows, SourceDir system-absolute paths start
+ // with /, e.g. "/C:/foo/bar".
+ const std::string& value = dest_dir.value();
+ if (value.size() > 2 && value[2] == ':')
+ dest_full.append(dest_dir.value().substr(1));
+ else
+ dest_full.append(dest_dir.value());
+#else
+ dest_full.append(dest_dir.value());
+#endif
+ }
+ bool remove_slash = false;
+ if (!EndsWithSlash(input_full)) {
+ input_full.push_back('/');
+ remove_slash = true;
+ }
+ ret = MakeRelativePath(input_full, dest_full);
+ if (remove_slash && ret.size() > 1)
+ ret.resize(ret.size() - 1);
+ return ret;
+ }
+
+ ret = MakeRelativePath(input, dest_dir.value());
+ return ret;
+}
+
std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
std::string ret;
@@ -698,6 +723,16 @@
// slashes to append to the toolchain object directory.
result.value().append(&source_dir.value()[2],
source_dir.value().size() - 2);
+ } else {
+ // system-absolute
+ const std::string& build_dir =
+ settings->build_settings()->build_dir().value();
+
+ if (StartsWithASCII(source_dir.value(), build_dir, true)) {
+ size_t build_dir_size = build_dir.size();
+ result.value().append(&source_dir.value()[build_dir_size],
+ source_dir.value().size() - build_dir_size);
+ }
}
return result;
}
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
index 20773cc7..f5cf494 100644
--- a/tools/gn/filesystem_utils.h
+++ b/tools/gn/filesystem_utils.h
@@ -116,11 +116,6 @@
const base::StringPiece& path,
std::string* dest);
-// Converts a directory to its inverse (e.g. "/foo/bar/" -> "../../").
-// This will be the empty string for the root directories ("/" and "//"), and
-// in all other cases, this is guaranteed to end in a slash.
-std::string InvertDir(const SourceDir& dir);
-
// Collapses "." and sequential "/"s and evaluates "..".
void NormalizePath(std::string* path);
@@ -128,10 +123,17 @@
// for other systems.
void ConvertPathToSystem(std::string* path);
-// Takes a source-absolute path (must begin with "//") and makes it relative
-// to the given directory, which also must be source-absolute.
-std::string RebaseSourceAbsolutePath(const std::string& input,
- const SourceDir& dest_dir);
+// Takes a path, |input|, and makes it relative to the given directory
+// |dest_dir|. Both inputs may be source-relative (e.g. begins with
+// with "//") or may be absolute.
+//
+// If supplied, the |source_root| parameter is the absolute path to
+// the source root and not end in a slash. Unless you know that the
+// inputs are always source relative, this should be supplied.
+std::string RebasePath(
+ const std::string& input,
+ const SourceDir& dest_dir,
+ const base::StringPiece& source_root = base::StringPiece());
// 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/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc
index ba6b9bd..7bcfd1a 100644
--- a/tools/gn/filesystem_utils_unittest.cc
+++ b/tools/gn/filesystem_utils_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/files/file_path.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -169,16 +170,6 @@
#endif
}
-TEST(FilesystemUtils, InvertDir) {
- EXPECT_TRUE(InvertDir(SourceDir()) == "");
- EXPECT_TRUE(InvertDir(SourceDir("/")) == "");
- EXPECT_TRUE(InvertDir(SourceDir("//")) == "");
-
- EXPECT_TRUE(InvertDir(SourceDir("//foo/bar")) == "../../");
- EXPECT_TRUE(InvertDir(SourceDir("//foo\\bar")) == "../../");
- EXPECT_TRUE(InvertDir(SourceDir("/foo/bar/")) == "../../");
-}
-
TEST(FilesystemUtils, NormalizePath) {
std::string input;
@@ -247,54 +238,120 @@
EXPECT_EQ("../bar", input);
}
-TEST(FilesystemUtils, RebaseSourceAbsolutePath) {
+TEST(FilesystemUtils, RebasePath) {
+ base::StringPiece source_root("/source/root");
+
// Degenerate case.
- EXPECT_EQ(".", RebaseSourceAbsolutePath("//", SourceDir("//")));
- EXPECT_EQ(".",
- RebaseSourceAbsolutePath("//foo/bar/", SourceDir("//foo/bar/")));
+ EXPECT_EQ(".", RebasePath("//", SourceDir("//"), source_root));
+ EXPECT_EQ(".", RebasePath("//foo/bar/", SourceDir("//foo/bar/"),
+ source_root));
// Going up the tree.
- EXPECT_EQ("../foo",
- RebaseSourceAbsolutePath("//foo", SourceDir("//bar/")));
- EXPECT_EQ("../foo/",
- RebaseSourceAbsolutePath("//foo/", SourceDir("//bar/")));
- EXPECT_EQ("../../foo",
- RebaseSourceAbsolutePath("//foo", SourceDir("//bar/moo")));
- EXPECT_EQ("../../foo/",
- RebaseSourceAbsolutePath("//foo/", SourceDir("//bar/moo")));
+ EXPECT_EQ("../foo", RebasePath("//foo", SourceDir("//bar/"), source_root));
+ EXPECT_EQ("../foo/", RebasePath("//foo/", SourceDir("//bar/"), source_root));
+ EXPECT_EQ("../../foo", RebasePath("//foo", SourceDir("//bar/moo"),
+ source_root));
+ EXPECT_EQ("../../foo/", RebasePath("//foo/", SourceDir("//bar/moo"),
+ source_root));
// Going down the tree.
- EXPECT_EQ("foo/bar",
- RebaseSourceAbsolutePath("//foo/bar", SourceDir("//")));
- EXPECT_EQ("foo/bar/",
- RebaseSourceAbsolutePath("//foo/bar/", SourceDir("//")));
+ EXPECT_EQ("foo/bar", RebasePath("//foo/bar", SourceDir("//"), source_root));
+ EXPECT_EQ("foo/bar/", RebasePath("//foo/bar/", SourceDir("//"),
+ source_root));
// Going up and down the tree.
- EXPECT_EQ("../../foo/bar",
- RebaseSourceAbsolutePath("//foo/bar", SourceDir("//a/b/")));
- EXPECT_EQ("../../foo/bar/",
- RebaseSourceAbsolutePath("//foo/bar/", SourceDir("//a/b/")));
+ EXPECT_EQ("../../foo/bar", RebasePath("//foo/bar", SourceDir("//a/b/"),
+ source_root));
+ EXPECT_EQ("../../foo/bar/", RebasePath("//foo/bar/", SourceDir("//a/b/"),
+ source_root));
// Sharing prefix.
- EXPECT_EQ("foo",
- RebaseSourceAbsolutePath("//a/foo", SourceDir("//a/")));
- EXPECT_EQ("foo/",
- RebaseSourceAbsolutePath("//a/foo/", SourceDir("//a/")));
- EXPECT_EQ("foo",
- RebaseSourceAbsolutePath("//a/b/foo", SourceDir("//a/b/")));
- EXPECT_EQ("foo/",
- RebaseSourceAbsolutePath("//a/b/foo/", SourceDir("//a/b/")));
- EXPECT_EQ("foo/bar",
- RebaseSourceAbsolutePath("//a/b/foo/bar", SourceDir("//a/b/")));
- EXPECT_EQ("foo/bar/",
- RebaseSourceAbsolutePath("//a/b/foo/bar/", SourceDir("//a/b/")));
+ EXPECT_EQ("foo", RebasePath("//a/foo", SourceDir("//a/"), source_root));
+ EXPECT_EQ("foo/", RebasePath("//a/foo/", SourceDir("//a/"), source_root));
+ EXPECT_EQ("foo", RebasePath("//a/b/foo", SourceDir("//a/b/"), source_root));
+ EXPECT_EQ("foo/", RebasePath("//a/b/foo/", SourceDir("//a/b/"),
+ source_root));
+ EXPECT_EQ("foo/bar", RebasePath("//a/b/foo/bar", SourceDir("//a/b/"),
+ source_root));
+ EXPECT_EQ("foo/bar/", RebasePath("//a/b/foo/bar/", SourceDir("//a/b/"),
+ source_root));
// One could argue about this case. Since the input doesn't have a slash it
// would normally not be treated like a directory and we'd go up, which is
// simpler. However, since it matches the output directory's name, we could
// potentially infer that it's the same and return "." for this.
- EXPECT_EQ("../bar",
- RebaseSourceAbsolutePath("//foo/bar", SourceDir("//foo/bar/")));
+ EXPECT_EQ("../bar", RebasePath("//foo/bar", SourceDir("//foo/bar/"),
+ source_root));
+
+ // Check when only |input| is system-absolute
+ EXPECT_EQ("foo", RebasePath("/source/root/foo", SourceDir("//"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("foo/", RebasePath("/source/root/foo/", SourceDir("//"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug", SourceDir("//"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("../../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug", SourceDir("//"),
+ base::StringPiece("/source/root/foo")));
+ EXPECT_EQ("../../../builddir/Out/Debug/",
+ RebasePath("/builddir/Out/Debug/", SourceDir("//"),
+ base::StringPiece("/source/root/foo")));
+ EXPECT_EQ("../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("//"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("//a"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("//a/b"),
+ base::StringPiece("/source/root")));
+
+ // Check when only |dest_dir| is system-absolute.
+ EXPECT_EQ(".",
+ RebasePath("//", SourceDir("/source/root"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("foo",
+ RebasePath("//foo", SourceDir("/source/root"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("../foo",
+ RebasePath("//foo", SourceDir("/source/root/bar"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("../../../source/root/foo",
+ RebasePath("//foo", SourceDir("/other/source/root"),
+ base::StringPiece("/source/root")));
+ EXPECT_EQ("../../../../source/root/foo",
+ RebasePath("//foo", SourceDir("/other/source/root/bar"),
+ base::StringPiece("/source/root")));
+
+ // Check when |input| and |dest_dir| are both system-absolute. Also,
+ // in this case |source_root| is never used so set it to a dummy
+ // value.
+ EXPECT_EQ("foo",
+ RebasePath("/source/root/foo", SourceDir("/source/root"),
+ base::StringPiece("/x/y/z")));
+ EXPECT_EQ("foo/",
+ RebasePath("/source/root/foo/", SourceDir("/source/root"),
+ base::StringPiece("/x/y/z")));
+ EXPECT_EQ("../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug",SourceDir("/source/root"),
+ base::StringPiece("/x/y/z")));
+ EXPECT_EQ("../../../builddir/Out/Debug",
+ RebasePath("/builddir/Out/Debug", SourceDir("/source/root/foo"),
+ base::StringPiece("/source/root/foo")));
+ EXPECT_EQ("../../../builddir/Out/Debug/",
+ RebasePath("/builddir/Out/Debug/", SourceDir("/source/root/foo"),
+ base::StringPiece("/source/root/foo")));
+ EXPECT_EQ("../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("/source/root"),
+ base::StringPiece("/x/y/z")));
+ EXPECT_EQ("../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("/source/root/a"),
+ base::StringPiece("/x/y/z")));
+ EXPECT_EQ("../../../../path/to/foo",
+ RebasePath("/path/to/foo", SourceDir("/source/root/a/b"),
+ base::StringPiece("/x/y/z")));
+
}
TEST(FilesystemUtils, DirectoryWithNoLastSlash) {
diff --git a/tools/gn/function_exec_script.cc b/tools/gn/function_exec_script.cc
index 05270b2..2b21b72 100644
--- a/tools/gn/function_exec_script.cc
+++ b/tools/gn/function_exec_script.cc
@@ -100,7 +100,8 @@
if (!args[0].VerifyTypeIs(Value::STRING, err))
return Value();
SourceFile script_source =
- cur_dir.ResolveRelativeFile(args[0].string_value());
+ cur_dir.ResolveRelativeFile(args[0].string_value(),
+ scope->settings()->build_settings()->root_path_utf8());
base::FilePath script_path = build_settings->GetFullPath(script_source);
if (!build_settings->secondary_source_path().empty() &&
!base::PathExists(script_path)) {
@@ -124,7 +125,8 @@
return Value();
g_scheduler->AddGenDependency(
build_settings->GetFullPath(cur_dir.ResolveRelativeFile(
- dep.string_value())));
+ dep.string_value(),
+ scope->settings()->build_settings()->root_path_utf8())));
}
}
diff --git a/tools/gn/function_get_path_info.cc b/tools/gn/function_get_path_info.cc
index 67f9344..a3f2c10 100644
--- a/tools/gn/function_get_path_info.cc
+++ b/tools/gn/function_get_path_info.cc
@@ -26,15 +26,18 @@
// Returns the directory containing the input (resolving it against the
// |current_dir|), regardless of whether the input is a directory or a file.
-SourceDir DirForInput(const SourceDir& current_dir,
+SourceDir DirForInput(const Settings* settings,
+ const SourceDir& current_dir,
const std::string& input_string) {
if (!input_string.empty() && input_string[input_string.size() - 1] == '/') {
// Input is a directory.
- return current_dir.ResolveRelativeDir(input_string);
+ return current_dir.ResolveRelativeDir(input_string,
+ settings->build_settings()->root_path_utf8());
}
// Input is a directory.
- return current_dir.ResolveRelativeFile(input_string).GetDir();
+ return current_dir.ResolveRelativeFile(input_string,
+ settings->build_settings()->root_path_utf8()).GetDir();
}
std::string GetOnePathInfo(const Settings* settings,
@@ -81,18 +84,24 @@
case WHAT_GEN_DIR: {
return DirectoryWithNoLastSlash(
GetGenDirForSourceDir(settings,
- DirForInput(current_dir, input_string)));
+ DirForInput(settings, current_dir,
+ input_string)));
}
case WHAT_OUT_DIR: {
return DirectoryWithNoLastSlash(
GetOutputDirForSourceDir(settings,
- DirForInput(current_dir, input_string)));
+ DirForInput(settings, current_dir,
+ input_string)));
}
case WHAT_ABSPATH: {
- if (!input_string.empty() && input_string[input_string.size() - 1] == '/')
- return current_dir.ResolveRelativeDir(input_string).value();
- else
- return current_dir.ResolveRelativeFile(input_string).value();
+ if (!input_string.empty() &&
+ input_string[input_string.size() - 1] == '/') {
+ return current_dir.ResolveRelativeDir(input_string,
+ settings->build_settings()->root_path_utf8()).value();
+ } else {
+ return current_dir.ResolveRelativeFile(input_string,
+ settings->build_settings()->root_path_utf8()).value();
+ }
}
default:
NOTREACHED();
diff --git a/tools/gn/function_read_file.cc b/tools/gn/function_read_file.cc
index f35e5a6..5a7662a 100644
--- a/tools/gn/function_read_file.cc
+++ b/tools/gn/function_read_file.cc
@@ -53,7 +53,8 @@
// Compute the file name.
const SourceDir& cur_dir = scope->GetSourceDir();
- SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value());
+ SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value(),
+ scope->settings()->build_settings()->root_path_utf8());
base::FilePath file_path =
scope->settings()->build_settings()->GetFullPath(source_file);
diff --git a/tools/gn/function_rebase_path.cc b/tools/gn/function_rebase_path.cc
index 078f007..0dde36af 100644
--- a/tools/gn/function_rebase_path.cc
+++ b/tools/gn/function_rebase_path.cc
@@ -71,10 +71,12 @@
base::FilePath system_path;
if (looks_like_dir) {
system_path = scope->settings()->build_settings()->GetFullPath(
- from_dir.ResolveRelativeDir(string_value));
+ from_dir.ResolveRelativeDir(string_value,
+ scope->settings()->build_settings()->root_path_utf8()));
} else {
system_path = scope->settings()->build_settings()->GetFullPath(
- from_dir.ResolveRelativeFile(string_value));
+ from_dir.ResolveRelativeFile(string_value,
+ scope->settings()->build_settings()->root_path_utf8()));
}
result = Value(function, FilePathToUTF8(system_path));
if (looks_like_dir)
@@ -82,23 +84,20 @@
return result;
}
- if (from_dir.is_system_absolute() || to_dir.is_system_absolute()) {
- *err = Err(function, "System-absolute directories are not supported for "
- "the source or dest dir for rebase_path. It would be nice to add this "
- "if you're so inclined!");
- return result;
- }
-
result = Value(function, Value::STRING);
if (looks_like_dir) {
- result.string_value() = RebaseSourceAbsolutePath(
- from_dir.ResolveRelativeDir(string_value).value(),
- to_dir);
+ result.string_value() = RebasePath(
+ from_dir.ResolveRelativeDir(string_value,
+ scope->settings()->build_settings()->root_path_utf8()).value(),
+ to_dir,
+ scope->settings()->build_settings()->root_path_utf8());
MakeSlashEndingMatchInput(string_value, &result.string_value());
} else {
- result.string_value() = RebaseSourceAbsolutePath(
- from_dir.ResolveRelativeFile(string_value).value(),
- to_dir);
+ result.string_value() = RebasePath(
+ from_dir.ResolveRelativeFile(string_value,
+ scope->settings()->build_settings()->root_path_utf8()).value(),
+ to_dir,
+ scope->settings()->build_settings()->root_path_utf8());
}
return result;
@@ -233,8 +232,9 @@
if (!args[kArgIndexDest].VerifyTypeIs(Value::STRING, err))
return result;
if (!args[kArgIndexDest].string_value().empty()) {
- to_dir =
- current_dir.ResolveRelativeDir(args[kArgIndexDest].string_value());
+ to_dir = current_dir.ResolveRelativeDir(
+ args[kArgIndexDest].string_value(),
+ scope->settings()->build_settings()->root_path_utf8());
convert_to_system_absolute = false;
}
}
@@ -244,8 +244,9 @@
if (args.size() > kArgIndexFrom) {
if (!args[kArgIndexFrom].VerifyTypeIs(Value::STRING, err))
return result;
- from_dir =
- current_dir.ResolveRelativeDir(args[kArgIndexFrom].string_value());
+ from_dir = current_dir.ResolveRelativeDir(
+ args[kArgIndexFrom].string_value(),
+ scope->settings()->build_settings()->root_path_utf8());
} else {
// Default to current directory if unspecified.
from_dir = current_dir;
diff --git a/tools/gn/function_rebase_path_unittest.cc b/tools/gn/function_rebase_path_unittest.cc
index 0537d10..20b492e 100644
--- a/tools/gn/function_rebase_path_unittest.cc
+++ b/tools/gn/function_rebase_path_unittest.cc
@@ -78,6 +78,59 @@
#endif
}
+TEST(RebasePath, StringsSystemPaths) {
+ TestWithScope setup;
+ Scope* scope = setup.scope();
+
+#if defined(OS_WIN)
+ setup.build_settings()->SetBuildDir(SourceDir("C:/ssd/out/Debug"));
+ setup.build_settings()->SetRootPath(base::FilePath(L"C:/hdd/src"));
+
+ // Test system absolute to-dir.
+ EXPECT_EQ("../../ssd/out/Debug",
+ RebaseOne(scope, ".", "//", "C:/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/",
+ RebaseOne(scope, "./", "//", "C:/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo",
+ RebaseOne(scope, "foo", "//", "C:/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo/",
+ RebaseOne(scope, "foo/", "//", "C:/ssd/out/Debug"));
+
+ // Test system absolute from-dir.
+ EXPECT_EQ("../../../hdd/src",
+ RebaseOne(scope, ".", "C:/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/",
+ RebaseOne(scope, "./", "C:/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo",
+ RebaseOne(scope, "foo", "C:/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo/",
+ RebaseOne(scope, "foo/", "C:/ssd/out/Debug", "//"));
+#else
+ setup.build_settings()->SetBuildDir(SourceDir("/ssd/out/Debug"));
+ setup.build_settings()->SetRootPath(base::FilePath("/hdd/src"));
+
+ // Test system absolute to-dir.
+ EXPECT_EQ("../../ssd/out/Debug",
+ RebaseOne(scope, ".", "//", "/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/",
+ RebaseOne(scope, "./", "//", "/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo",
+ RebaseOne(scope, "foo", "//", "/ssd/out/Debug"));
+ EXPECT_EQ("../../ssd/out/Debug/foo/",
+ RebaseOne(scope, "foo/", "//", "/ssd/out/Debug"));
+
+ // Test system absolute from-dir.
+ EXPECT_EQ("../../../hdd/src",
+ RebaseOne(scope, ".", "/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/",
+ RebaseOne(scope, "./", "/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo",
+ RebaseOne(scope, "foo", "/ssd/out/Debug", "//"));
+ EXPECT_EQ("../../../hdd/src/foo/",
+ RebaseOne(scope, "foo/", "/ssd/out/Debug", "//"));
+#endif
+}
+
// Test list input.
TEST(RebasePath, List) {
TestWithScope setup;
diff --git a/tools/gn/function_write_file.cc b/tools/gn/function_write_file.cc
index 55f4d5d..0d273fc 100644
--- a/tools/gn/function_write_file.cc
+++ b/tools/gn/function_write_file.cc
@@ -57,7 +57,8 @@
if (!args[0].VerifyTypeIs(Value::STRING, err))
return Value();
const SourceDir& cur_dir = scope->GetSourceDir();
- SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value());
+ SourceFile source_file = cur_dir.ResolveRelativeFile(args[0].string_value(),
+ scope->settings()->build_settings()->root_path_utf8());
if (!EnsureStringIsInOutputDir(
scope->settings()->build_settings()->build_dir(),
source_file.value(), args[0].origin(), err))
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index 0d60d688a..84d5ea43 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -468,7 +468,8 @@
const SourceDir& input_dir = scope->GetSourceDir();
SourceFile import_file =
- input_dir.ResolveRelativeFile(args[0].string_value());
+ input_dir.ResolveRelativeFile(args[0].string_value(),
+ scope->settings()->build_settings()->root_path_utf8());
scope->settings()->import_manager().DoImport(import_file, function,
scope, err);
return Value();
diff --git a/tools/gn/item.cc b/tools/gn/item.cc
index 7dfd4ad..a1118e8 100644
--- a/tools/gn/item.cc
+++ b/tools/gn/item.cc
@@ -5,6 +5,7 @@
#include "tools/gn/item.h"
#include "base/logging.h"
+#include "tools/gn/settings.h"
Item::Item(const Settings* settings, const Label& label)
: settings_(settings),
diff --git a/tools/gn/ninja_action_target_writer.cc b/tools/gn/ninja_action_target_writer.cc
index 25c86337..84ec072 100644
--- a/tools/gn/ninja_action_target_writer.cc
+++ b/tools/gn/ninja_action_target_writer.cc
@@ -17,6 +17,7 @@
: NinjaTargetWriter(target, out),
path_output_no_escaping_(
target->settings()->build_settings()->build_dir(),
+ target->settings()->build_settings()->root_path_utf8(),
ESCAPE_NONE) {
}
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc
index b341639..16cc320 100644
--- a/tools/gn/ninja_binary_target_writer.cc
+++ b/tools/gn/ninja_binary_target_writer.cc
@@ -102,8 +102,10 @@
// Include directories.
if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) {
out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " =";
- PathOutput include_path_output(path_output_.current_dir(),
- ESCAPE_NINJA_COMMAND);
+ PathOutput include_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA_COMMAND);
RecursiveTargetConfigToStream<SourceDir>(
target_, &ConfigValues::include_dirs,
IncludeWriter(include_path_output), out_);
@@ -150,6 +152,7 @@
if (tool_type != Toolchain::TYPE_NONE) {
out_ << "build";
path_output_.WriteFiles(out_, tool_outputs);
+
out_ << ": " << rule_prefix << Toolchain::ToolTypeToName(tool_type);
out_ << " ";
path_output_.WriteFile(out_, source);
@@ -288,6 +291,7 @@
// Since we're passing these on the command line to the linker and not
// to Ninja, we need to do shell escaping.
PathOutput lib_path_output(path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(),
ESCAPE_NINJA_COMMAND);
for (size_t i = 0; i < all_lib_dirs.size(); i++) {
out_ << " " << tool_->lib_dir_switch();
diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc
index fa83874b..deb0d7f 100644
--- a/tools/gn/ninja_build_writer.cc
+++ b/tools/gn/ninja_build_writer.cc
@@ -86,7 +86,8 @@
default_toolchain_targets_(default_toolchain_targets),
out_(out),
dep_out_(dep_out),
- path_output_(build_settings->build_dir(), ESCAPE_NINJA) {
+ path_output_(build_settings->build_dir(),
+ build_settings->root_path_utf8(), ESCAPE_NINJA) {
}
NinjaBuildWriter::~NinjaBuildWriter() {
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index 870cbb6..8a5d0ee 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -28,7 +28,9 @@
: settings_(target->settings()),
target_(target),
out_(out),
- path_output_(settings_->build_settings()->build_dir(), ESCAPE_NINJA) {
+ path_output_(settings_->build_settings()->build_dir(),
+ settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA) {
}
NinjaTargetWriter::~NinjaTargetWriter() {
@@ -174,8 +176,9 @@
// optimal thing in all cases.
OutputFile input_stamp_file(
- RebaseSourceAbsolutePath(GetTargetOutputDir(target_).value(),
- settings_->build_settings()->build_dir()));
+ RebasePath(GetTargetOutputDir(target_).value(),
+ settings_->build_settings()->build_dir(),
+ settings_->build_settings()->root_path_utf8()));
input_stamp_file.value().append(target_->label().name());
input_stamp_file.value().append(".inputdeps.stamp");
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
index 5deead5..0122471 100644
--- a/tools/gn/ninja_toolchain_writer.cc
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -32,7 +32,9 @@
toolchain_(toolchain),
targets_(targets),
out_(out),
- path_output_(settings_->build_settings()->build_dir(), ESCAPE_NINJA) {
+ path_output_(settings_->build_settings()->build_dir(),
+ settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA) {
}
NinjaToolchainWriter::~NinjaToolchainWriter() {
diff --git a/tools/gn/output_file.cc b/tools/gn/output_file.cc
index d1fb88e8..cc6dc98 100644
--- a/tools/gn/output_file.cc
+++ b/tools/gn/output_file.cc
@@ -16,8 +16,9 @@
OutputFile::OutputFile(const BuildSettings* build_settings,
const SourceFile& source_file)
- : value_(RebaseSourceAbsolutePath(source_file.value(),
- build_settings->build_dir())) {
+ : value_(RebasePath(source_file.value(),
+ build_settings->build_dir(),
+ build_settings->root_path_utf8())) {
}
OutputFile::~OutputFile() {
diff --git a/tools/gn/path_output.cc b/tools/gn/path_output.cc
index de398ec..5b333d8 100644
--- a/tools/gn/path_output.cc
+++ b/tools/gn/path_output.cc
@@ -4,19 +4,19 @@
#include "tools/gn/path_output.h"
+#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/output_file.h"
#include "tools/gn/string_utils.h"
-PathOutput::PathOutput(const SourceDir& current_dir, EscapingMode escaping)
+PathOutput::PathOutput(const SourceDir& current_dir,
+ const base::StringPiece& source_root,
+ EscapingMode escaping)
: current_dir_(current_dir) {
- CHECK(current_dir.is_source_absolute())
- << "Currently this only supports writing to output directories inside "
- "the source root. There needs to be some tweaks to PathOutput to make "
- "doing this work correctly.";
- inverse_current_dir_ = InvertDir(current_dir_);
-
+ inverse_current_dir_ = RebasePath("//", current_dir, source_root);
+ if (!EndsWithSlash(inverse_current_dir_))
+ inverse_current_dir_.push_back('/');
options_.mode = escaping;
}
diff --git a/tools/gn/path_output.h b/tools/gn/path_output.h
index 12fc5ac5..f7beb42 100644
--- a/tools/gn/path_output.h
+++ b/tools/gn/path_output.h
@@ -33,7 +33,8 @@
DIR_NO_LAST_SLASH,
};
- PathOutput(const SourceDir& current_dir, EscapingMode escaping);
+ PathOutput(const SourceDir& current_dir, const base::StringPiece& source_root,
+ EscapingMode escaping);
~PathOutput();
// Read-only since inverse_current_dir_ is computed depending on this.
diff --git a/tools/gn/path_output_unittest.cc b/tools/gn/path_output_unittest.cc
index 217f6dcf..131f55ec5 100644
--- a/tools/gn/path_output_unittest.cc
+++ b/tools/gn/path_output_unittest.cc
@@ -4,6 +4,7 @@
#include <sstream>
+#include "base/files/file_path.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "tools/gn/output_file.h"
#include "tools/gn/path_output.h"
@@ -12,7 +13,8 @@
TEST(PathOutput, Basic) {
SourceDir build_dir("//out/Debug/");
- PathOutput writer(build_dir, ESCAPE_NONE);
+ base::StringPiece source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NONE);
{
// Normal source-root path.
std::ostringstream out;
@@ -53,7 +55,8 @@
// Same as basic but the output dir is the root.
TEST(PathOutput, BasicInRoot) {
SourceDir build_dir("//");
- PathOutput writer(build_dir, ESCAPE_NONE);
+ base::StringPiece source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NONE);
{
// Normal source-root path.
std::ostringstream out;
@@ -70,7 +73,8 @@
TEST(PathOutput, NinjaEscaping) {
SourceDir build_dir("//out/Debug/");
- PathOutput writer(build_dir, ESCAPE_NINJA);
+ base::StringPiece source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
{
// Spaces and $ in filenames.
std::ostringstream out;
@@ -87,7 +91,8 @@
TEST(PathOutput, NinjaForkEscaping) {
SourceDir build_dir("//out/Debug/");
- PathOutput writer(build_dir, ESCAPE_NINJA_COMMAND);
+ base::StringPiece source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA_COMMAND);
// Spaces in filenames should get quoted on Windows.
writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
@@ -140,7 +145,8 @@
TEST(PathOutput, InhibitQuoting) {
SourceDir build_dir("//out/Debug/");
- PathOutput writer(build_dir, ESCAPE_NINJA_COMMAND);
+ base::StringPiece source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA_COMMAND);
writer.set_inhibit_quoting(true);
writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
@@ -163,7 +169,8 @@
TEST(PathOutput, WriteDir) {
{
SourceDir build_dir("//out/Debug/");
- PathOutput writer(build_dir, ESCAPE_NINJA);
+ base::StringPiece source_root("/source/root");
+ PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
{
std::ostringstream out;
writer.WriteDir(out, SourceDir("//foo/bar/"),
@@ -259,7 +266,8 @@
}
{
// Empty build dir writer.
- PathOutput root_writer(SourceDir("//"), ESCAPE_NINJA);
+ base::StringPiece source_root("/source/root");
+ PathOutput root_writer(SourceDir("//"), source_root, ESCAPE_NINJA);
{
std::ostringstream out;
root_writer.WriteDir(out, SourceDir("//"),
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index fdbf0f247..5ba87b9 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -441,7 +441,7 @@
bool Setup::FillBuildDir(const std::string& build_dir, bool require_exists) {
SourceDir resolved =
SourceDirForCurrentDirectory(build_settings_.root_path()).
- ResolveRelativeDir(build_dir);
+ ResolveRelativeDir(build_dir, build_settings_.root_path_utf8());
if (resolved.is_null()) {
Err(Location(), "Couldn't resolve build directory.",
"The build directory supplied (\"" + build_dir + "\") was not valid.").
diff --git a/tools/gn/source_dir.cc b/tools/gn/source_dir.cc
index c59cf88..9f6c745 100644
--- a/tools/gn/source_dir.cc
+++ b/tools/gn/source_dir.cc
@@ -12,7 +12,12 @@
void AssertValueSourceDirString(const std::string& s) {
if (!s.empty()) {
+#if defined(OS_WIN)
+ DCHECK(s[0] == '/' ||
+ (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
+#else
DCHECK(s[0] == '/');
+#endif
DCHECK(EndsWithSlash(s));
}
}
@@ -44,6 +49,8 @@
const base::StringPiece& source_root) const {
SourceFile ret;
+ DCHECK(source_root.empty() || !source_root.ends_with("/"));
+
// 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.
@@ -69,6 +76,20 @@
return ret;
}
+ if (!source_root.empty()) {
+ std::string absolute =
+ FilePathToUTF8(Resolve(UTF8ToFilePath(source_root)).AppendASCII(
+ p.as_string()).value());
+ NormalizePath(&absolute);
+ if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute,
+ &ret.value_))
+ ret.value_ = absolute;
+ 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() + p.size());
ret.value_.assign(value_);
ret.value_.append(p.data(), p.size());
@@ -82,6 +103,8 @@
const base::StringPiece& source_root) const {
SourceDir ret;
+ DCHECK(source_root.empty() || !source_root.ends_with("/"));
+
if (p.empty())
return ret;
if (p.size() >= 2 && p[0] == '/' && p[1] == '/') {
@@ -106,6 +129,18 @@
return ret;
}
+ if (!source_root.empty()) {
+ std::string absolute =
+ FilePathToUTF8(Resolve(UTF8ToFilePath(source_root)).AppendASCII(
+ p.as_string()).value());
+ NormalizePath(&absolute);
+ if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute, &ret.value_))
+ ret.value_ = absolute;
+ if (!EndsWithSlash(ret.value_))
+ ret.value_.push_back('/');
+ return ret;
+ }
+
ret.value_.reserve(value_.size() + p.size());
ret.value_.assign(value_);
ret.value_.append(p.data(), p.size());
diff --git a/tools/gn/source_dir_unittest.cc b/tools/gn/source_dir_unittest.cc
index d2ea3aa..5825b97 100644
--- a/tools/gn/source_dir_unittest.cc
+++ b/tools/gn/source_dir_unittest.cc
@@ -8,48 +8,88 @@
TEST(SourceDir, ResolveRelativeFile) {
SourceDir base("//base/");
+#if defined(OS_WIN)
+ base::StringPiece source_root("C:/source/root");
+#else
+ base::StringPiece source_root("/source/root");
+#endif
// Empty input is an error.
- EXPECT_TRUE(base.ResolveRelativeFile("") == SourceFile());
+ EXPECT_TRUE(base.ResolveRelativeFile("", source_root) == SourceFile());
// These things are directories, so should be an error.
- EXPECT_TRUE(base.ResolveRelativeFile("//foo/bar/") == SourceFile());
- EXPECT_TRUE(base.ResolveRelativeFile("bar/") == SourceFile());
+ EXPECT_TRUE(base.ResolveRelativeFile("//foo/bar/", source_root) ==
+ SourceFile());
+ EXPECT_TRUE(base.ResolveRelativeFile("bar/", source_root) ==
+ SourceFile());
// Absolute paths should be passed unchanged.
- EXPECT_TRUE(base.ResolveRelativeFile("//foo") == SourceFile("//foo"));
- EXPECT_TRUE(base.ResolveRelativeFile("/foo") == SourceFile("/foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("//foo",source_root) ==
+ SourceFile("//foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("/foo", source_root) ==
+ SourceFile("/foo"));
// Basic relative stuff.
- EXPECT_TRUE(base.ResolveRelativeFile("foo") == SourceFile("//base/foo"));
- EXPECT_TRUE(base.ResolveRelativeFile("./foo") == SourceFile("//base/foo"));
- EXPECT_TRUE(base.ResolveRelativeFile("../foo") == SourceFile("//foo"));
- EXPECT_TRUE(base.ResolveRelativeFile("../../foo") == SourceFile("//foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("foo", source_root) ==
+ SourceFile("//base/foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("./foo", source_root) ==
+ SourceFile("//base/foo"));
+ EXPECT_TRUE(base.ResolveRelativeFile("../foo", source_root) ==
+ SourceFile("//foo"));
+
+ // If the given relative path points outside the source root, we
+ // expect an absolute path.
+#if defined(OS_WIN)
+ EXPECT_TRUE(base.ResolveRelativeFile("../../foo", source_root) ==
+ SourceFile("C:/source/foo"));
+#else
+ EXPECT_TRUE(base.ResolveRelativeFile("../../foo", source_root) ==
+ SourceFile("/source/foo"));
+#endif
#if defined(OS_WIN)
// Note that we don't canonicalize the backslashes to forward slashes.
// This could potentially be changed in the future which would mean we should
// just change the expected result.
- EXPECT_TRUE(base.ResolveRelativeFile("C:\\foo\\bar.txt") ==
+ EXPECT_TRUE(base.ResolveRelativeFile("C:\\foo\\bar.txt", source_root) ==
SourceFile("/C:/foo/bar.txt"));
#endif
}
TEST(SourceDir, ResolveRelativeDir) {
SourceDir base("//base/");
+#if defined(OS_WIN)
+ base::StringPiece source_root("C:/source/root");
+#else
+ base::StringPiece source_root("/source/root");
+#endif
// Empty input is an error.
- EXPECT_TRUE(base.ResolveRelativeDir("") == SourceDir());
+ EXPECT_TRUE(base.ResolveRelativeDir("", source_root) == SourceDir());
// Absolute paths should be passed unchanged.
- EXPECT_TRUE(base.ResolveRelativeDir("//foo") == SourceDir("//foo/"));
- EXPECT_TRUE(base.ResolveRelativeDir("/foo") == SourceDir("/foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("//foo", source_root) ==
+ SourceDir("//foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("/foo", source_root) ==
+ SourceDir("/foo/"));
// Basic relative stuff.
- EXPECT_TRUE(base.ResolveRelativeDir("foo") == SourceDir("//base/foo/"));
- EXPECT_TRUE(base.ResolveRelativeDir("./foo") == SourceDir("//base/foo/"));
- EXPECT_TRUE(base.ResolveRelativeDir("../foo") == SourceDir("//foo/"));
- EXPECT_TRUE(base.ResolveRelativeDir("../../foo/") == SourceDir("//foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("foo", source_root) ==
+ SourceDir("//base/foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("./foo", source_root) ==
+ SourceDir("//base/foo/"));
+ EXPECT_TRUE(base.ResolveRelativeDir("../foo", source_root) ==
+ SourceDir("//foo/"));
+
+ // If the given relative path points outside the source root, we
+ // expect an absolute path.
+#if defined(OS_WIN)
+ EXPECT_TRUE(base.ResolveRelativeDir("../../foo", source_root) ==
+ SourceDir("C:/source/foo/"));
+#else
+ EXPECT_TRUE(base.ResolveRelativeDir("../../foo", source_root) ==
+ SourceDir("/source/foo/"));
+#endif
#if defined(OS_WIN)
// Note that we don't canonicalize the existing backslashes to forward
diff --git a/tools/gn/source_file.cc b/tools/gn/source_file.cc
index b3eb560..71d8592c 100644
--- a/tools/gn/source_file.cc
+++ b/tools/gn/source_file.cc
@@ -9,21 +9,33 @@
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/source_dir.h"
+namespace {
+
+void AssertValueSourceFileString(const std::string& s) {
+#if defined(OS_WIN)
+ DCHECK(s[0] == '/' ||
+ (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
+#else
+ DCHECK(s[0] == '/');
+#endif
+ DCHECK(!EndsWithSlash(s));
+}
+
+} // namespace
+
SourceFile::SourceFile() {
}
SourceFile::SourceFile(const base::StringPiece& p)
: value_(p.data(), p.size()) {
DCHECK(!value_.empty());
- DCHECK(value_[0] == '/');
- DCHECK(!EndsWithSlash(value_));
+ AssertValueSourceFileString(value_);
}
SourceFile::SourceFile(SwapIn, std::string* value) {
value_.swap(*value);
DCHECK(!value_.empty());
- DCHECK(value_[0] == '/');
- DCHECK(!EndsWithSlash(value_));
+ AssertValueSourceFileString(value_);
}
SourceFile::~SourceFile() {
diff --git a/tools/gn/substitution_writer.cc b/tools/gn/substitution_writer.cc
index 65a1d1fe..a642e47 100644
--- a/tools/gn/substitution_writer.cc
+++ b/tools/gn/substitution_writer.cc
@@ -235,10 +235,6 @@
const SubstitutionPattern& pattern,
const SourceFile& source) {
SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source);
- CHECK(result_as_source.is_source_absolute())
- << "The result of the pattern \""
- << pattern.AsString()
- << "\" was not an absolute path beginning in \"//\".";
return OutputFile(settings->build_settings(), result_as_source);
}
@@ -357,8 +353,9 @@
case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
if (source.is_system_absolute())
return DirectoryWithNoLastSlash(source.GetDir());
- return RebaseSourceAbsolutePath(
- DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"));
+ return RebasePath(
+ DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"),
+ settings->build_settings()->root_path_utf8());
case SUBSTITUTION_SOURCE_GEN_DIR:
to_rebase = DirectoryWithNoLastSlash(
@@ -382,7 +379,8 @@
// extension extraction) will have been handled via early return above.
if (output_style == OUTPUT_ABSOLUTE)
return to_rebase;
- return RebaseSourceAbsolutePath(to_rebase, relative_to);
+ return RebasePath(to_rebase, relative_to,
+ settings->build_settings()->root_path_utf8());
}
// static
diff --git a/tools/gn/value_extractors.cc b/tools/gn/value_extractors.cc
index 9e6a37e..6dfaa2f 100644
--- a/tools/gn/value_extractors.cc
+++ b/tools/gn/value_extractors.cc
@@ -58,8 +58,6 @@
return true;
}
-// This extractor rejects files with system-absolute file paths. If we need
-// that in the future, we'll have to add some flag to control this.
struct RelativeFileConverter {
RelativeFileConverter(const BuildSettings* build_settings_in,
const SourceDir& current_dir_in)
@@ -71,13 +69,6 @@
return false;
*out = current_dir.ResolveRelativeFile(v.string_value(),
build_settings->root_path_utf8());
- if (out->is_system_absolute()) {
- *err = Err(v, "System-absolute file path.",
- "You can't list a system-absolute file path here. Please include "
- "only files in\nthe source tree. Maybe you meant to begin with two "
- "slashes to indicate an\nabsolute path in the source tree?");
- return false;
- }
return true;
}
const BuildSettings* build_settings;