blob: 325cfc68d55e3601e5d953eef99792d7f8656da7 [file] [log] [blame]
[email protected]96ea63d2013-07-30 10:17:071// 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.
4
5#include "tools/gn/filesystem_utils.h"
6
[email protected]ea3690c2013-09-23 17:59:227#include <algorithm>
8
thestig1ecdcf42014-09-12 05:09:149#include "base/files/file_util.h"
[email protected]96ea63d2013-07-30 10:17:0710#include "base/logging.h"
[email protected]f6b314d12013-08-23 21:07:4711#include "base/strings/string_util.h"
[email protected]96ea63d2013-07-30 10:17:0712#include "base/strings/utf_string_conversions.h"
13#include "build/build_config.h"
14#include "tools/gn/location.h"
[email protected]05b94b432013-11-22 22:09:5515#include "tools/gn/settings.h"
[email protected]96ea63d2013-07-30 10:17:0716#include "tools/gn/source_dir.h"
17
18namespace {
19
20enum DotDisposition {
21 // The given dot is just part of a filename and is not special.
22 NOT_A_DIRECTORY,
23
24 // The given dot is the current directory.
25 DIRECTORY_CUR,
26
27 // The given dot is the first of a double dot that should take us up one.
28 DIRECTORY_UP
29};
30
31// When we find a dot, this function is called with the character following
32// that dot to see what it is. The return value indicates what type this dot is
33// (see above). This code handles the case where the dot is at the end of the
34// input.
35//
36// |*consumed_len| will contain the number of characters in the input that
37// express what we found.
38DotDisposition ClassifyAfterDot(const std::string& path,
39 size_t after_dot,
40 size_t* consumed_len) {
41 if (after_dot == path.size()) {
42 // Single dot at the end.
43 *consumed_len = 1;
44 return DIRECTORY_CUR;
45 }
[email protected]858ceda2014-02-27 23:26:4046 if (IsSlash(path[after_dot])) {
[email protected]96ea63d2013-07-30 10:17:0747 // Single dot followed by a slash.
48 *consumed_len = 2; // Consume the slash
49 return DIRECTORY_CUR;
50 }
51
52 if (path[after_dot] == '.') {
53 // Two dots.
54 if (after_dot + 1 == path.size()) {
55 // Double dot at the end.
56 *consumed_len = 2;
57 return DIRECTORY_UP;
58 }
[email protected]858ceda2014-02-27 23:26:4059 if (IsSlash(path[after_dot + 1])) {
[email protected]96ea63d2013-07-30 10:17:0760 // Double dot folowed by a slash.
61 *consumed_len = 3;
62 return DIRECTORY_UP;
63 }
64 }
65
66 // The dots are followed by something else, not a directory.
67 *consumed_len = 1;
68 return NOT_A_DIRECTORY;
69}
70
[email protected]f6b314d12013-08-23 21:07:4771#if defined(OS_WIN)
72inline char NormalizeWindowsPathChar(char c) {
73 if (c == '/')
74 return '\\';
75 return base::ToLowerASCII(c);
76}
77
78// Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
79// paths.
80bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
81 const base::StringPiece& b) {
82 if (a.size() != b.size())
83 return false;
84
85 // For now, just do a case-insensitive ASCII comparison. We could convert to
86 // UTF-16 and use ICU if necessary. Or maybe base::strcasecmp is good enough?
87 for (size_t i = 0; i < a.size(); i++) {
88 if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
89 return false;
90 }
91 return true;
92}
93
94bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
95 if (path.size() < 3)
96 return false;
97
98 // Check colon first, this will generally fail fastest.
99 if (path[1] != ':')
100 return false;
101
[email protected]92df9852014-05-15 21:52:35102 // Check drive letter.
103 if (!IsAsciiAlpha(path[0]))
[email protected]f6b314d12013-08-23 21:07:47104 return false;
105
[email protected]858ceda2014-02-27 23:26:40106 if (!IsSlash(path[2]))
[email protected]f6b314d12013-08-23 21:07:47107 return false;
108 return true;
109}
110#endif
111
[email protected]a623db1872014-02-19 19:11:17112// A wrapper around FilePath.GetComponents that works the way we need. This is
113// not super efficient since it does some O(n) transformations on the path. If
114// this is called a lot, we might want to optimize.
115std::vector<base::FilePath::StringType> GetPathComponents(
116 const base::FilePath& path) {
117 std::vector<base::FilePath::StringType> result;
118 path.GetComponents(&result);
119
120 if (result.empty())
121 return result;
122
123 // GetComponents will preserve the "/" at the beginning, which confuses us.
124 // We don't expect to have relative paths in this function.
125 // Don't use IsSeparator since we always want to allow backslashes.
126 if (result[0] == FILE_PATH_LITERAL("/") ||
127 result[0] == FILE_PATH_LITERAL("\\"))
128 result.erase(result.begin());
129
130#if defined(OS_WIN)
131 // On Windows, GetComponents will give us [ "C:", "/", "foo" ], and we
132 // don't want the slash in there. This doesn't support input like "C:foo"
133 // which means foo relative to the current directory of the C drive but
134 // that's basically legacy DOS behavior we don't need to support.
pkasting5c4699c22014-10-17 18:17:15135 if (result.size() >= 2 && result[1].size() == 1 &&
136 IsSlash(static_cast<char>(result[1][0])))
[email protected]a623db1872014-02-19 19:11:17137 result.erase(result.begin() + 1);
138#endif
139
140 return result;
141}
142
143// Provides the equivalent of == for filesystem strings, trying to do
144// approximately the right thing with case.
145bool FilesystemStringsEqual(const base::FilePath::StringType& a,
146 const base::FilePath::StringType& b) {
147#if defined(OS_WIN)
148 // Assume case-insensitive filesystems on Windows. We use the CompareString
149 // function to do a case-insensitive comparison based on the current locale
150 // (we don't want GN to depend on ICU which is large and requires data
151 // files). This isn't perfect, but getting this perfectly right is very
152 // difficult and requires I/O, and this comparison should cover 99.9999% of
153 // all cases.
154 //
155 // Note: The documentation for CompareString says it runs fastest on
156 // null-terminated strings with -1 passed for the length, so we do that here.
157 // There should not be embedded nulls in filesystem strings.
158 return ::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
159 a.c_str(), -1, b.c_str(), -1) == CSTR_EQUAL;
160#else
161 // Assume case-sensitive filesystems on non-Windows.
162 return a == b;
163#endif
164}
165
[email protected]f6b314d12013-08-23 21:07:47166} // namespace
[email protected]96ea63d2013-07-30 10:17:07167
[email protected]96ea63d2013-07-30 10:17:07168const char* GetExtensionForOutputType(Target::OutputType type,
169 Settings::TargetOS os) {
170 switch (os) {
[email protected]8338eea2013-08-05 23:08:12171 case Settings::MAC:
172 switch (type) {
[email protected]8338eea2013-08-05 23:08:12173 case Target::EXECUTABLE:
174 return "";
175 case Target::SHARED_LIBRARY:
176 return "dylib";
177 case Target::STATIC_LIBRARY:
178 return "a";
[email protected]8338eea2013-08-05 23:08:12179 default:
180 NOTREACHED();
181 }
182 break;
183
[email protected]96ea63d2013-07-30 10:17:07184 case Settings::WIN:
185 switch (type) {
[email protected]96ea63d2013-07-30 10:17:07186 case Target::EXECUTABLE:
187 return "exe";
188 case Target::SHARED_LIBRARY:
189 return "dll.lib"; // Extension of import library.
190 case Target::STATIC_LIBRARY:
191 return "lib";
[email protected]c0822d7f2013-08-13 17:10:56192 default:
193 NOTREACHED();
194 }
195 break;
196
197 case Settings::LINUX:
198 switch (type) {
199 case Target::EXECUTABLE:
200 return "";
201 case Target::SHARED_LIBRARY:
202 return "so";
203 case Target::STATIC_LIBRARY:
204 return "a";
[email protected]96ea63d2013-07-30 10:17:07205 default:
206 NOTREACHED();
207 }
208 break;
209
210 default:
211 NOTREACHED();
212 }
213 return "";
214}
215
[email protected]1bcf0012013-09-19 21:13:32216std::string FilePathToUTF8(const base::FilePath::StringType& str) {
[email protected]96ea63d2013-07-30 10:17:07217#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03218 return base::WideToUTF8(str);
[email protected]96ea63d2013-07-30 10:17:07219#else
[email protected]1bcf0012013-09-19 21:13:32220 return str;
[email protected]96ea63d2013-07-30 10:17:07221#endif
222}
223
224base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
225#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03226 return base::FilePath(base::UTF8ToWide(sp));
[email protected]96ea63d2013-07-30 10:17:07227#else
228 return base::FilePath(sp.as_string());
229#endif
230}
231
232size_t FindExtensionOffset(const std::string& path) {
233 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40234 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07235 break;
236 if (path[i] == '.')
237 return i + 1;
238 }
239 return std::string::npos;
240}
241
242base::StringPiece FindExtension(const std::string* path) {
243 size_t extension_offset = FindExtensionOffset(*path);
244 if (extension_offset == std::string::npos)
245 return base::StringPiece();
246 return base::StringPiece(&path->data()[extension_offset],
247 path->size() - extension_offset);
248}
249
250size_t FindFilenameOffset(const std::string& path) {
251 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40252 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07253 return i + 1;
254 }
255 return 0; // No filename found means everything was the filename.
256}
257
258base::StringPiece FindFilename(const std::string* path) {
259 size_t filename_offset = FindFilenameOffset(*path);
260 if (filename_offset == 0)
261 return base::StringPiece(*path); // Everything is the file name.
262 return base::StringPiece(&(*path).data()[filename_offset],
263 path->size() - filename_offset);
264}
265
266base::StringPiece FindFilenameNoExtension(const std::string* path) {
267 if (path->empty())
268 return base::StringPiece();
269 size_t filename_offset = FindFilenameOffset(*path);
270 size_t extension_offset = FindExtensionOffset(*path);
271
272 size_t name_len;
273 if (extension_offset == std::string::npos)
274 name_len = path->size() - filename_offset;
275 else
276 name_len = extension_offset - filename_offset - 1;
277
278 return base::StringPiece(&(*path).data()[filename_offset], name_len);
279}
280
281void RemoveFilename(std::string* path) {
282 path->resize(FindFilenameOffset(*path));
283}
284
285bool EndsWithSlash(const std::string& s) {
[email protected]858ceda2014-02-27 23:26:40286 return !s.empty() && IsSlash(s[s.size() - 1]);
[email protected]96ea63d2013-07-30 10:17:07287}
288
289base::StringPiece FindDir(const std::string* path) {
290 size_t filename_offset = FindFilenameOffset(*path);
291 if (filename_offset == 0u)
292 return base::StringPiece();
293 return base::StringPiece(path->data(), filename_offset);
294}
295
[email protected]a3372c72014-04-28 22:39:26296base::StringPiece FindLastDirComponent(const SourceDir& dir) {
297 const std::string& dir_string = dir.value();
298
299 if (dir_string.empty())
300 return base::StringPiece();
301 int cur = static_cast<int>(dir_string.size()) - 1;
302 DCHECK(dir_string[cur] == '/');
303 int end = cur;
304 cur--; // Skip before the last slash.
305
306 for (; cur >= 0; cur--) {
307 if (dir_string[cur] == '/')
308 return base::StringPiece(&dir_string[cur + 1], end - cur - 1);
309 }
310 return base::StringPiece(&dir_string[0], end);
311}
312
brettw56affab2015-06-04 22:01:03313bool IsStringInOutputDir(const SourceDir& output_dir, const std::string& str) {
314 // This check will be wrong for all proper prefixes "e.g. "/output" will
315 // match "/out" but we don't really care since this is just a sanity check.
316 const std::string& dir_str = output_dir.value();
317 return str.compare(0, dir_str.length(), dir_str) == 0;
318}
319
320bool EnsureStringIsInOutputDir(const SourceDir& output_dir,
[email protected]96ea63d2013-07-30 10:17:07321 const std::string& str,
[email protected]0dfcae72014-08-19 22:52:16322 const ParseNode* origin,
[email protected]96ea63d2013-07-30 10:17:07323 Err* err) {
brettw56affab2015-06-04 22:01:03324 if (IsStringInOutputDir(output_dir, str))
[email protected]f0bbcdc22014-07-11 17:13:35325 return true; // Output directory is hardcoded.
326
[email protected]0dfcae72014-08-19 22:52:16327 *err = Err(origin, "File is not inside output directory.",
[email protected]f0bbcdc22014-07-11 17:13:35328 "The given file should be in the output directory. Normally you would "
329 "specify\n\"$target_out_dir/foo\" or "
330 "\"$target_gen_dir/foo\". I interpreted this as\n\""
331 + str + "\".");
332 return false;
[email protected]96ea63d2013-07-30 10:17:07333}
334
[email protected]ac1128e2013-08-23 00:26:56335bool IsPathAbsolute(const base::StringPiece& path) {
336 if (path.empty())
337 return false;
338
[email protected]858ceda2014-02-27 23:26:40339 if (!IsSlash(path[0])) {
[email protected]ac1128e2013-08-23 00:26:56340#if defined(OS_WIN)
341 // Check for Windows system paths like "C:\foo".
[email protected]858ceda2014-02-27 23:26:40342 if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
[email protected]ac1128e2013-08-23 00:26:56343 return true;
344#endif
345 return false; // Doesn't begin with a slash, is relative.
346 }
347
[email protected]858ceda2014-02-27 23:26:40348 // Double forward slash at the beginning means source-relative (we don't
349 // allow backslashes for denoting this).
[email protected]ac1128e2013-08-23 00:26:56350 if (path.size() > 1 && path[1] == '/')
[email protected]858ceda2014-02-27 23:26:40351 return false;
[email protected]ac1128e2013-08-23 00:26:56352
353 return true;
354}
355
356bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
357 const base::StringPiece& path,
358 std::string* dest) {
359 DCHECK(IsPathAbsolute(source_root));
360 DCHECK(IsPathAbsolute(path));
361
362 dest->clear();
363
364 if (source_root.size() > path.size())
365 return false; // The source root is longer: the path can never be inside.
366
367#if defined(OS_WIN)
[email protected]858ceda2014-02-27 23:26:40368 // Source root should be canonical on Windows. Note that the initial slash
369 // must be forward slash, but that the other ones can be either forward or
370 // backward.
[email protected]ac1128e2013-08-23 00:26:56371 DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
[email protected]858ceda2014-02-27 23:26:40372 source_root[1] == ':' && IsSlash(source_root[2]));
[email protected]f6b314d12013-08-23 21:07:47373
374 size_t after_common_index = std::string::npos;
375 if (DoesBeginWindowsDriveLetter(path)) {
376 // Handle "C:\foo"
377 if (AreAbsoluteWindowsPathsEqual(source_root,
378 path.substr(0, source_root.size())))
379 after_common_index = source_root.size();
380 else
381 return false;
382 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
383 DoesBeginWindowsDriveLetter(path.substr(1))) {
384 // Handle "/C:/foo"
385 if (AreAbsoluteWindowsPathsEqual(source_root,
386 path.substr(1, source_root.size())))
387 after_common_index = source_root.size() + 1;
388 else
389 return false;
390 } else {
391 return false;
392 }
393
394 // If we get here, there's a match and after_common_index identifies the
395 // part after it.
396
397 // The base may or may not have a trailing slash, so skip all slashes from
398 // the path after our prefix match.
399 size_t first_after_slash = after_common_index;
[email protected]858ceda2014-02-27 23:26:40400 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]f6b314d12013-08-23 21:07:47401 first_after_slash++;
402
403 dest->assign("//"); // Result is source root relative.
404 dest->append(&path.data()[first_after_slash],
405 path.size() - first_after_slash);
406 return true;
407
[email protected]ac1128e2013-08-23 00:26:56408#else
[email protected]f6b314d12013-08-23 21:07:47409
[email protected]ac1128e2013-08-23 00:26:56410 // On non-Windows this is easy. Since we know both are absolute, just do a
411 // prefix check.
412 if (path.substr(0, source_root.size()) == source_root) {
[email protected]ac1128e2013-08-23 00:26:56413 // The base may or may not have a trailing slash, so skip all slashes from
414 // the path after our prefix match.
415 size_t first_after_slash = source_root.size();
[email protected]858ceda2014-02-27 23:26:40416 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]ac1128e2013-08-23 00:26:56417 first_after_slash++;
418
[email protected]f6b314d12013-08-23 21:07:47419 dest->assign("//"); // Result is source root relative.
[email protected]ac1128e2013-08-23 00:26:56420 dest->append(&path.data()[first_after_slash],
421 path.size() - first_after_slash);
422 return true;
423 }
[email protected]ac1128e2013-08-23 00:26:56424 return false;
[email protected]f6b314d12013-08-23 21:07:47425#endif
[email protected]ac1128e2013-08-23 00:26:56426}
427
[email protected]96ea63d2013-07-30 10:17:07428void NormalizePath(std::string* path) {
tfarina9b636af2014-12-23 00:52:07429 char* pathbuf = path->empty() ? nullptr : &(*path)[0];
[email protected]96ea63d2013-07-30 10:17:07430
431 // top_index is the first character we can modify in the path. Anything
432 // before this indicates where the path is relative to.
433 size_t top_index = 0;
434 bool is_relative = true;
435 if (!path->empty() && pathbuf[0] == '/') {
436 is_relative = false;
437
438 if (path->size() > 1 && pathbuf[1] == '/') {
439 // Two leading slashes, this is a path into the source dir.
440 top_index = 2;
441 } else {
442 // One leading slash, this is a system-absolute path.
443 top_index = 1;
444 }
445 }
446
447 size_t dest_i = top_index;
448 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
449 if (pathbuf[src_i] == '.') {
[email protected]858ceda2014-02-27 23:26:40450 if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07451 // Slash followed by a dot, see if it's something special.
452 size_t consumed_len;
453 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
454 case NOT_A_DIRECTORY:
455 // Copy the dot to the output, it means nothing special.
456 pathbuf[dest_i++] = pathbuf[src_i++];
457 break;
458 case DIRECTORY_CUR:
459 // Current directory, just skip the input.
460 src_i += consumed_len;
461 break;
462 case DIRECTORY_UP:
463 // Back up over previous directory component. If we're already
464 // at the top, preserve the "..".
465 if (dest_i > top_index) {
466 // The previous char was a slash, remove it.
467 dest_i--;
468 }
469
470 if (dest_i == top_index) {
471 if (is_relative) {
472 // We're already at the beginning of a relative input, copy the
473 // ".." and continue. We need the trailing slash if there was
474 // one before (otherwise we're at the end of the input).
475 pathbuf[dest_i++] = '.';
476 pathbuf[dest_i++] = '.';
477 if (consumed_len == 3)
478 pathbuf[dest_i++] = '/';
479
480 // This also makes a new "root" that we can't delete by going
481 // up more levels. Otherwise "../.." would collapse to
482 // nothing.
483 top_index = dest_i;
484 }
485 // Otherwise we're at the beginning of an absolute path. Don't
486 // allow ".." to go up another level and just eat it.
487 } else {
488 // Just find the previous slash or the beginning of input.
[email protected]858ceda2014-02-27 23:26:40489 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
[email protected]96ea63d2013-07-30 10:17:07490 dest_i--;
491 }
492 src_i += consumed_len;
493 }
494 } else {
495 // Dot not preceeded by a slash, copy it literally.
496 pathbuf[dest_i++] = pathbuf[src_i++];
497 }
[email protected]858ceda2014-02-27 23:26:40498 } else if (IsSlash(pathbuf[src_i])) {
499 if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07500 // Two slashes in a row, skip over it.
501 src_i++;
502 } else {
[email protected]858ceda2014-02-27 23:26:40503 // Just one slash, copy it, normalizing to foward slash.
504 pathbuf[dest_i] = '/';
505 dest_i++;
506 src_i++;
[email protected]96ea63d2013-07-30 10:17:07507 }
508 } else {
509 // Input nothing special, just copy it.
510 pathbuf[dest_i++] = pathbuf[src_i++];
511 }
512 }
513 path->resize(dest_i);
514}
515
516void ConvertPathToSystem(std::string* path) {
517#if defined(OS_WIN)
518 for (size_t i = 0; i < path->size(); i++) {
519 if ((*path)[i] == '/')
520 (*path)[i] = '\\';
521 }
522#endif
523}
524
zeuthen6f9c64562014-11-11 21:01:13525std::string MakeRelativePath(const std::string& input,
526 const std::string& dest) {
ohrn8808c5542015-02-10 10:18:38527#if defined(OS_WIN)
528 // Make sure that absolute |input| path starts with a slash if |dest| path
529 // does. Otherwise skipping common prefixes won't work properly. Ensure the
530 // same for |dest| path too.
531 if (IsPathAbsolute(input) && !IsSlash(input[0]) && IsSlash(dest[0])) {
532 std::string corrected_input(1, dest[0]);
533 corrected_input.append(input);
534 return MakeRelativePath(corrected_input, dest);
535 }
536 if (IsPathAbsolute(dest) && !IsSlash(dest[0]) && IsSlash(input[0])) {
537 std::string corrected_dest(1, input[0]);
538 corrected_dest.append(dest);
539 return MakeRelativePath(input, corrected_dest);
540 }
541#endif
542
zeuthen6f9c64562014-11-11 21:01:13543 std::string ret;
[email protected]ea3690c2013-09-23 17:59:22544
545 // Skip the common prefixes of the source and dest as long as they end in
546 // a [back]slash.
zeuthen6f9c64562014-11-11 21:01:13547 size_t common_prefix_len = 0;
[email protected]ea3690c2013-09-23 17:59:22548 size_t max_common_length = std::min(input.size(), dest.size());
549 for (size_t i = common_prefix_len; i < max_common_length; i++) {
[email protected]858ceda2014-02-27 23:26:40550 if (IsSlash(input[i]) && IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22551 common_prefix_len = i + 1;
552 else if (input[i] != dest[i])
553 break;
554 }
555
556 // Invert the dest dir starting from the end of the common prefix.
[email protected]ea3690c2013-09-23 17:59:22557 for (size_t i = common_prefix_len; i < dest.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40558 if (IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22559 ret.append("../");
560 }
561
562 // Append any remaining unique input.
563 ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
564
565 // If the result is still empty, the paths are the same.
566 if (ret.empty())
567 ret.push_back('.');
568
569 return ret;
570}
[email protected]05b94b432013-11-22 22:09:55571
zeuthen6f9c64562014-11-11 21:01:13572std::string RebasePath(const std::string& input,
573 const SourceDir& dest_dir,
574 const base::StringPiece& source_root) {
575 std::string ret;
576 DCHECK(source_root.empty() || !source_root.ends_with("/"));
577
578 bool input_is_source_path = (input.size() >= 2 &&
579 input[0] == '/' && input[1] == '/');
580
581 if (!source_root.empty() &&
582 (!input_is_source_path || !dest_dir.is_source_absolute())) {
583 std::string input_full;
584 std::string dest_full;
585 if (input_is_source_path) {
586 source_root.AppendToString(&input_full);
587 input_full.push_back('/');
588 input_full.append(input, 2, std::string::npos);
589 } else {
590 input_full.append(input);
591 }
592 if (dest_dir.is_source_absolute()) {
593 source_root.AppendToString(&dest_full);
594 dest_full.push_back('/');
595 dest_full.append(dest_dir.value(), 2, std::string::npos);
596 } else {
597#if defined(OS_WIN)
598 // On Windows, SourceDir system-absolute paths start
599 // with /, e.g. "/C:/foo/bar".
600 const std::string& value = dest_dir.value();
601 if (value.size() > 2 && value[2] == ':')
602 dest_full.append(dest_dir.value().substr(1));
603 else
604 dest_full.append(dest_dir.value());
605#else
606 dest_full.append(dest_dir.value());
607#endif
608 }
609 bool remove_slash = false;
610 if (!EndsWithSlash(input_full)) {
611 input_full.push_back('/');
612 remove_slash = true;
613 }
614 ret = MakeRelativePath(input_full, dest_full);
615 if (remove_slash && ret.size() > 1)
616 ret.resize(ret.size() - 1);
617 return ret;
618 }
619
620 ret = MakeRelativePath(input, dest_dir.value());
621 return ret;
622}
623
[email protected]05b94b432013-11-22 22:09:55624std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
625 std::string ret;
626
627 if (dir.value().empty()) {
628 // Just keep input the same.
629 } else if (dir.value() == "/") {
630 ret.assign("/.");
631 } else if (dir.value() == "//") {
632 ret.assign("//.");
633 } else {
634 ret.assign(dir.value());
635 ret.resize(ret.size() - 1);
636 }
637 return ret;
638}
639
[email protected]a623db1872014-02-19 19:11:17640SourceDir SourceDirForPath(const base::FilePath& source_root,
641 const base::FilePath& path) {
642 std::vector<base::FilePath::StringType> source_comp =
643 GetPathComponents(source_root);
644 std::vector<base::FilePath::StringType> path_comp =
645 GetPathComponents(path);
646
647 // See if path is inside the source root by looking for each of source root's
648 // components at the beginning of path.
649 bool is_inside_source;
scottmg865b9bb2014-12-02 10:48:29650 if (path_comp.size() < source_comp.size() || source_root.empty()) {
[email protected]a623db1872014-02-19 19:11:17651 // Too small to fit.
652 is_inside_source = false;
653 } else {
654 is_inside_source = true;
655 for (size_t i = 0; i < source_comp.size(); i++) {
656 if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
657 is_inside_source = false;
658 break;
659 }
660 }
661 }
662
663 std::string result_str;
664 size_t initial_path_comp_to_use;
665 if (is_inside_source) {
666 // Construct a source-relative path beginning in // and skip all of the
667 // shared directories.
668 result_str = "//";
669 initial_path_comp_to_use = source_comp.size();
670 } else {
671 // Not inside source code, construct a system-absolute path.
672 result_str = "/";
673 initial_path_comp_to_use = 0;
674 }
675
676 for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
677 result_str.append(FilePathToUTF8(path_comp[i]));
678 result_str.push_back('/');
679 }
680 return SourceDir(result_str);
681}
682
683SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
684 base::FilePath cd;
[email protected]37b3c1992014-03-11 20:59:02685 base::GetCurrentDirectory(&cd);
[email protected]a623db1872014-02-19 19:11:17686 return SourceDirForPath(source_root, cd);
687}
688
[email protected]7380ca72014-05-13 16:56:20689std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
690 // The default toolchain has no subdir.
691 if (is_default)
692 return std::string();
693
694 // For now just assume the toolchain name is always a valid dir name. We may
695 // want to clean up the in the future.
696 return toolchain_label.name() + "/";
697}
698
[email protected]05b94b432013-11-22 22:09:55699SourceDir GetToolchainOutputDir(const Settings* settings) {
[email protected]0dfcae72014-08-19 22:52:16700 return settings->toolchain_output_subdir().AsSourceDir(
701 settings->build_settings());
[email protected]05b94b432013-11-22 22:09:55702}
703
[email protected]7380ca72014-05-13 16:56:20704SourceDir GetToolchainOutputDir(const BuildSettings* build_settings,
705 const Label& toolchain_label, bool is_default) {
706 std::string result = build_settings->build_dir().value();
707 result.append(GetOutputSubdirName(toolchain_label, is_default));
708 return SourceDir(SourceDir::SWAP_IN, &result);
709}
710
[email protected]05b94b432013-11-22 22:09:55711SourceDir GetToolchainGenDir(const Settings* settings) {
[email protected]0dfcae72014-08-19 22:52:16712 return GetToolchainGenDirAsOutputFile(settings).AsSourceDir(
713 settings->build_settings());
714}
[email protected]05b94b432013-11-22 22:09:55715
[email protected]0dfcae72014-08-19 22:52:16716OutputFile GetToolchainGenDirAsOutputFile(const Settings* settings) {
717 OutputFile result(settings->toolchain_output_subdir());
718 result.value().append("gen/");
719 return result;
[email protected]05b94b432013-11-22 22:09:55720}
721
[email protected]7380ca72014-05-13 16:56:20722SourceDir GetToolchainGenDir(const BuildSettings* build_settings,
723 const Label& toolchain_label, bool is_default) {
724 std::string result = GetToolchainOutputDir(
725 build_settings, toolchain_label, is_default).value();
726 result.append("gen/");
727 return SourceDir(SourceDir::SWAP_IN, &result);
728}
729
[email protected]05b94b432013-11-22 22:09:55730SourceDir GetOutputDirForSourceDir(const Settings* settings,
731 const SourceDir& source_dir) {
[email protected]0dfcae72014-08-19 22:52:16732 return GetOutputDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
733 settings->build_settings());
734}
[email protected]05b94b432013-11-22 22:09:55735
[email protected]0dfcae72014-08-19 22:52:16736OutputFile GetOutputDirForSourceDirAsOutputFile(const Settings* settings,
737 const SourceDir& source_dir) {
738 OutputFile result = settings->toolchain_output_subdir();
739 result.value().append("obj/");
[email protected]05b94b432013-11-22 22:09:55740
[email protected]e9bf4fc2014-06-19 16:50:52741 if (source_dir.is_source_absolute()) {
742 // The source dir is source-absolute, so we trim off the two leading
743 // slashes to append to the toolchain object directory.
[email protected]0dfcae72014-08-19 22:52:16744 result.value().append(&source_dir.value()[2],
745 source_dir.value().size() - 2);
zeuthen6f9c64562014-11-11 21:01:13746 } else {
747 // system-absolute
748 const std::string& build_dir =
749 settings->build_settings()->build_dir().value();
750
751 if (StartsWithASCII(source_dir.value(), build_dir, true)) {
752 size_t build_dir_size = build_dir.size();
753 result.value().append(&source_dir.value()[build_dir_size],
754 source_dir.value().size() - build_dir_size);
ohrn8808c5542015-02-10 10:18:38755 } else {
756 result.value().append("ABS_PATH");
757#if defined(OS_WIN)
758 // Windows absolute path contains ':' after drive letter. Remove it to
759 // avoid inserting ':' in the middle of path (eg. "ABS_PATH/C:/").
760 std::string src_dir_value = source_dir.value();
761 const auto colon_pos = src_dir_value.find(':');
762 if (colon_pos != std::string::npos)
763 src_dir_value.erase(src_dir_value.begin() + colon_pos);
764#else
765 const std::string& src_dir_value = source_dir.value();
766#endif
767 result.value().append(src_dir_value);
zeuthen6f9c64562014-11-11 21:01:13768 }
[email protected]e9bf4fc2014-06-19 16:50:52769 }
[email protected]0dfcae72014-08-19 22:52:16770 return result;
[email protected]05b94b432013-11-22 22:09:55771}
772
773SourceDir GetGenDirForSourceDir(const Settings* settings,
774 const SourceDir& source_dir) {
[email protected]0dfcae72014-08-19 22:52:16775 return GetGenDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
776 settings->build_settings());
777}
[email protected]05b94b432013-11-22 22:09:55778
[email protected]0dfcae72014-08-19 22:52:16779OutputFile GetGenDirForSourceDirAsOutputFile(const Settings* settings,
780 const SourceDir& source_dir) {
781 OutputFile result = GetToolchainGenDirAsOutputFile(settings);
[email protected]05b94b432013-11-22 22:09:55782
[email protected]e9bf4fc2014-06-19 16:50:52783 if (source_dir.is_source_absolute()) {
784 // The source dir should be source-absolute, so we trim off the two leading
785 // slashes to append to the toolchain object directory.
786 DCHECK(source_dir.is_source_absolute());
[email protected]0dfcae72014-08-19 22:52:16787 result.value().append(&source_dir.value()[2],
788 source_dir.value().size() - 2);
[email protected]e9bf4fc2014-06-19 16:50:52789 }
[email protected]0dfcae72014-08-19 22:52:16790 return result;
[email protected]05b94b432013-11-22 22:09:55791}
792
793SourceDir GetTargetOutputDir(const Target* target) {
[email protected]0dfcae72014-08-19 22:52:16794 return GetOutputDirForSourceDirAsOutputFile(
795 target->settings(), target->label().dir()).AsSourceDir(
796 target->settings()->build_settings());
797}
798
799OutputFile GetTargetOutputDirAsOutputFile(const Target* target) {
800 return GetOutputDirForSourceDirAsOutputFile(
801 target->settings(), target->label().dir());
[email protected]05b94b432013-11-22 22:09:55802}
803
804SourceDir GetTargetGenDir(const Target* target) {
[email protected]0dfcae72014-08-19 22:52:16805 return GetTargetGenDirAsOutputFile(target).AsSourceDir(
806 target->settings()->build_settings());
807}
808
809OutputFile GetTargetGenDirAsOutputFile(const Target* target) {
810 return GetGenDirForSourceDirAsOutputFile(
811 target->settings(), target->label().dir());
[email protected]05b94b432013-11-22 22:09:55812}
813
814SourceDir GetCurrentOutputDir(const Scope* scope) {
[email protected]0dfcae72014-08-19 22:52:16815 return GetOutputDirForSourceDirAsOutputFile(
816 scope->settings(), scope->GetSourceDir()).AsSourceDir(
817 scope->settings()->build_settings());
[email protected]05b94b432013-11-22 22:09:55818}
819
820SourceDir GetCurrentGenDir(const Scope* scope) {
821 return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
822}