Retry fdbff0e3b60139805609f643eb40:
[chromium-blink-merge.git] / tools / gn / source_dir.cc
blob0966cc604eea55b63db42716aa1fb898bce3b28e
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "tools/gn/source_dir.h"
7 #include "base/logging.h"
8 #include "tools/gn/filesystem_utils.h"
9 #include "tools/gn/source_file.h"
11 namespace {
13 void AssertValueSourceDirString(const std::string& s) {
14 if (!s.empty()) {
15 #if defined(OS_WIN)
16 DCHECK(s[0] == '/' ||
17 (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2])));
18 #else
19 DCHECK(s[0] == '/');
20 #endif
21 DCHECK(EndsWithSlash(s)) << s;
25 } // namespace
27 SourceDir::SourceDir() {
30 SourceDir::SourceDir(const base::StringPiece& p)
31 : value_(p.data(), p.size()) {
32 if (!EndsWithSlash(value_))
33 value_.push_back('/');
34 AssertValueSourceDirString(value_);
37 SourceDir::SourceDir(SwapIn, std::string* s) {
38 value_.swap(*s);
39 if (!EndsWithSlash(value_))
40 value_.push_back('/');
41 AssertValueSourceDirString(value_);
44 SourceDir::~SourceDir() {
47 SourceFile SourceDir::ResolveRelativeFile(
48 const Value& p,
49 Err* err,
50 const base::StringPiece& source_root) const {
51 SourceFile ret;
52 if (!p.VerifyTypeIs(Value::STRING, err))
53 return ret;
55 // It's an error to resolve an empty string or one that is a directory
56 // (indicated by a trailing slash) because this is the function that expects
57 // to return a file.
58 const std::string& str = p.string_value();
59 if (str.empty()) {
60 *err = Err(p, "Empty file path.",
61 "You can't use empty strings as file paths. That's just wrong.");
62 return ret;
63 } else if (str[str.size() - 1] == '/') {
64 *err = Err(p, "File path ends in a slash.",
65 "You specified the path\n " + str + "\n"
66 "and it ends in a slash, indicating you think it's a directory."
67 "\nBut here you're supposed to be listing a file.");
68 return ret;
71 if (str.size() >= 2 && str[0] == '/' && str[1] == '/') {
72 // Source-relative.
73 ret.value_.assign(str.data(), str.size());
74 NormalizePath(&ret.value_);
75 return ret;
76 } else if (IsPathAbsolute(str)) {
77 if (source_root.empty() ||
78 !MakeAbsolutePathRelativeIfPossible(source_root, str, &ret.value_)) {
79 #if defined(OS_WIN)
80 // On Windows we'll accept "C:\foo" as an absolute path, which we want
81 // to convert to "/C:..." here.
82 if (str[0] != '/')
83 ret.value_ = "/";
84 #endif
85 ret.value_.append(str.data(), str.size());
87 NormalizePath(&ret.value_);
88 return ret;
91 if (!source_root.empty()) {
92 std::string absolute =
93 FilePathToUTF8(Resolve(UTF8ToFilePath(source_root)).AppendASCII(
94 str).value());
95 NormalizePath(&absolute);
96 if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute,
97 &ret.value_)) {
98 #if defined(OS_WIN)
99 // On Windows we'll accept "C:\foo" as an absolute path, which we want
100 // to convert to "/C:..." here.
101 if (absolute[0] != '/')
102 ret.value_ = "/";
103 #endif
104 ret.value_.append(absolute.data(), absolute.size());
106 return ret;
109 // With no source_root_, there's nothing we can do about
110 // e.g. p=../../../path/to/file and value_=//source and we'll
111 // errornously return //file.
112 ret.value_.reserve(value_.size() + str.size());
113 ret.value_.assign(value_);
114 ret.value_.append(str.data(), str.size());
116 NormalizePath(&ret.value_);
117 return ret;
120 SourceDir SourceDir::ResolveRelativeDir(
121 const Value& p,
122 Err* err,
123 const base::StringPiece& source_root) const {
124 if (!p.VerifyTypeIs(Value::STRING, err))
125 return SourceDir();
126 return ResolveRelativeDir(p, p.string_value(), err, source_root);
129 SourceDir SourceDir::ResolveRelativeDir(
130 const Value& blame_but_dont_use,
131 const base::StringPiece& str,
132 Err* err,
133 const base::StringPiece& source_root) const {
134 SourceDir ret;
136 if (str.empty()) {
137 *err = Err(blame_but_dont_use, "Empty directory path.",
138 "You can't use empty strings as directories. "
139 "That's just wrong.");
140 return ret;
143 if (str.size() >= 2 && str[0] == '/' && str[1] == '/') {
144 // Source-relative.
145 ret.value_.assign(str.data(), str.size());
146 if (!EndsWithSlash(ret.value_))
147 ret.value_.push_back('/');
148 NormalizePath(&ret.value_);
149 return ret;
150 } else if (IsPathAbsolute(str)) {
151 if (source_root.empty() ||
152 !MakeAbsolutePathRelativeIfPossible(source_root, str, &ret.value_)) {
153 #if defined(OS_WIN)
154 if (str[0] != '/') // See the file case for why we do this check.
155 ret.value_ = "/";
156 #endif
157 ret.value_.append(str.data(), str.size());
159 NormalizePath(&ret.value_);
160 if (!EndsWithSlash(ret.value_))
161 ret.value_.push_back('/');
162 return ret;
165 if (!source_root.empty()) {
166 std::string absolute =
167 FilePathToUTF8(Resolve(UTF8ToFilePath(source_root)).AppendASCII(
168 str.as_string()).value());
169 NormalizePath(&absolute);
170 if (!MakeAbsolutePathRelativeIfPossible(source_root, absolute,
171 &ret.value_)) {
172 #if defined(OS_WIN)
173 if (absolute[0] != '/') // See the file case for why we do this check.
174 ret.value_ = "/";
175 #endif
176 ret.value_.append(absolute.data(), absolute.size());
178 if (!EndsWithSlash(ret.value_))
179 ret.value_.push_back('/');
180 return ret;
183 ret.value_.reserve(value_.size() + str.size());
184 ret.value_.assign(value_);
185 ret.value_.append(str.data(), str.size());
187 NormalizePath(&ret.value_);
188 if (!EndsWithSlash(ret.value_))
189 ret.value_.push_back('/');
190 AssertValueSourceDirString(ret.value_);
192 return ret;
195 base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
196 if (is_null())
197 return base::FilePath();
199 std::string converted;
200 if (is_system_absolute()) {
201 if (value_.size() > 2 && value_[2] == ':') {
202 // Windows path, strip the leading slash.
203 converted.assign(&value_[1], value_.size() - 1);
204 } else {
205 converted.assign(value_);
207 return base::FilePath(UTF8ToFilePath(converted));
210 // String the double-leading slash for source-relative paths.
211 converted.assign(&value_[2], value_.size() - 2);
212 return source_root.Append(UTF8ToFilePath(converted))
213 .NormalizePathSeparatorsTo('/');
216 void SourceDir::SwapValue(std::string* v) {
217 value_.swap(*v);
218 AssertValueSourceDirString(value_);