blob: ca4562574df561a23ba4f6ecefdecfba422e8404 [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
[email protected]a623db1872014-02-19 19:11:179#include "base/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.
[email protected]858ceda2014-02-27 23:26:40135 if (result.size() >= 2 && result[1].size() == 1 && IsSlash(result[1][0]))
[email protected]a623db1872014-02-19 19:11:17136 result.erase(result.begin() + 1);
137#endif
138
139 return result;
140}
141
142// Provides the equivalent of == for filesystem strings, trying to do
143// approximately the right thing with case.
144bool FilesystemStringsEqual(const base::FilePath::StringType& a,
145 const base::FilePath::StringType& b) {
146#if defined(OS_WIN)
147 // Assume case-insensitive filesystems on Windows. We use the CompareString
148 // function to do a case-insensitive comparison based on the current locale
149 // (we don't want GN to depend on ICU which is large and requires data
150 // files). This isn't perfect, but getting this perfectly right is very
151 // difficult and requires I/O, and this comparison should cover 99.9999% of
152 // all cases.
153 //
154 // Note: The documentation for CompareString says it runs fastest on
155 // null-terminated strings with -1 passed for the length, so we do that here.
156 // There should not be embedded nulls in filesystem strings.
157 return ::CompareString(LOCALE_USER_DEFAULT, LINGUISTIC_IGNORECASE,
158 a.c_str(), -1, b.c_str(), -1) == CSTR_EQUAL;
159#else
160 // Assume case-sensitive filesystems on non-Windows.
161 return a == b;
162#endif
163}
164
[email protected]f6b314d12013-08-23 21:07:47165} // namespace
[email protected]96ea63d2013-07-30 10:17:07166
[email protected]126d8b52014-04-07 22:17:35167SourceFileType GetSourceFileType(const SourceFile& file) {
[email protected]96ea63d2013-07-30 10:17:07168 base::StringPiece extension = FindExtension(&file.value());
169 if (extension == "cc" || extension == "cpp" || extension == "cxx")
170 return SOURCE_CC;
171 if (extension == "h")
172 return SOURCE_H;
173 if (extension == "c")
174 return SOURCE_C;
[email protected]126d8b52014-04-07 22:17:35175 if (extension == "m")
176 return SOURCE_M;
177 if (extension == "mm")
178 return SOURCE_MM;
179 if (extension == "rc")
180 return SOURCE_RC;
[email protected]e8ab6912014-04-21 20:54:51181 if (extension == "S" || extension == "s")
[email protected]126d8b52014-04-07 22:17:35182 return SOURCE_S;
[email protected]600ee6d2014-05-02 16:42:56183 if (extension == "o" || extension == "obj")
184 return SOURCE_O;
[email protected]c6f27f22013-08-21 21:44:59185
[email protected]96ea63d2013-07-30 10:17:07186 return SOURCE_UNKNOWN;
187}
188
189const char* GetExtensionForOutputType(Target::OutputType type,
190 Settings::TargetOS os) {
191 switch (os) {
[email protected]8338eea2013-08-05 23:08:12192 case Settings::MAC:
193 switch (type) {
[email protected]8338eea2013-08-05 23:08:12194 case Target::EXECUTABLE:
195 return "";
196 case Target::SHARED_LIBRARY:
197 return "dylib";
198 case Target::STATIC_LIBRARY:
199 return "a";
[email protected]8338eea2013-08-05 23:08:12200 default:
201 NOTREACHED();
202 }
203 break;
204
[email protected]96ea63d2013-07-30 10:17:07205 case Settings::WIN:
206 switch (type) {
[email protected]96ea63d2013-07-30 10:17:07207 case Target::EXECUTABLE:
208 return "exe";
209 case Target::SHARED_LIBRARY:
210 return "dll.lib"; // Extension of import library.
211 case Target::STATIC_LIBRARY:
212 return "lib";
[email protected]c0822d7f2013-08-13 17:10:56213 default:
214 NOTREACHED();
215 }
216 break;
217
218 case Settings::LINUX:
219 switch (type) {
220 case Target::EXECUTABLE:
221 return "";
222 case Target::SHARED_LIBRARY:
223 return "so";
224 case Target::STATIC_LIBRARY:
225 return "a";
[email protected]96ea63d2013-07-30 10:17:07226 default:
227 NOTREACHED();
228 }
229 break;
230
231 default:
232 NOTREACHED();
233 }
234 return "";
235}
236
[email protected]1bcf0012013-09-19 21:13:32237std::string FilePathToUTF8(const base::FilePath::StringType& str) {
[email protected]96ea63d2013-07-30 10:17:07238#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03239 return base::WideToUTF8(str);
[email protected]96ea63d2013-07-30 10:17:07240#else
[email protected]1bcf0012013-09-19 21:13:32241 return str;
[email protected]96ea63d2013-07-30 10:17:07242#endif
243}
244
245base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
246#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03247 return base::FilePath(base::UTF8ToWide(sp));
[email protected]96ea63d2013-07-30 10:17:07248#else
249 return base::FilePath(sp.as_string());
250#endif
251}
252
253size_t FindExtensionOffset(const std::string& path) {
254 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40255 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07256 break;
257 if (path[i] == '.')
258 return i + 1;
259 }
260 return std::string::npos;
261}
262
263base::StringPiece FindExtension(const std::string* path) {
264 size_t extension_offset = FindExtensionOffset(*path);
265 if (extension_offset == std::string::npos)
266 return base::StringPiece();
267 return base::StringPiece(&path->data()[extension_offset],
268 path->size() - extension_offset);
269}
270
271size_t FindFilenameOffset(const std::string& path) {
272 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40273 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07274 return i + 1;
275 }
276 return 0; // No filename found means everything was the filename.
277}
278
279base::StringPiece FindFilename(const std::string* path) {
280 size_t filename_offset = FindFilenameOffset(*path);
281 if (filename_offset == 0)
282 return base::StringPiece(*path); // Everything is the file name.
283 return base::StringPiece(&(*path).data()[filename_offset],
284 path->size() - filename_offset);
285}
286
287base::StringPiece FindFilenameNoExtension(const std::string* path) {
288 if (path->empty())
289 return base::StringPiece();
290 size_t filename_offset = FindFilenameOffset(*path);
291 size_t extension_offset = FindExtensionOffset(*path);
292
293 size_t name_len;
294 if (extension_offset == std::string::npos)
295 name_len = path->size() - filename_offset;
296 else
297 name_len = extension_offset - filename_offset - 1;
298
299 return base::StringPiece(&(*path).data()[filename_offset], name_len);
300}
301
302void RemoveFilename(std::string* path) {
303 path->resize(FindFilenameOffset(*path));
304}
305
306bool EndsWithSlash(const std::string& s) {
[email protected]858ceda2014-02-27 23:26:40307 return !s.empty() && IsSlash(s[s.size() - 1]);
[email protected]96ea63d2013-07-30 10:17:07308}
309
310base::StringPiece FindDir(const std::string* path) {
311 size_t filename_offset = FindFilenameOffset(*path);
312 if (filename_offset == 0u)
313 return base::StringPiece();
314 return base::StringPiece(path->data(), filename_offset);
315}
316
[email protected]a3372c72014-04-28 22:39:26317base::StringPiece FindLastDirComponent(const SourceDir& dir) {
318 const std::string& dir_string = dir.value();
319
320 if (dir_string.empty())
321 return base::StringPiece();
322 int cur = static_cast<int>(dir_string.size()) - 1;
323 DCHECK(dir_string[cur] == '/');
324 int end = cur;
325 cur--; // Skip before the last slash.
326
327 for (; cur >= 0; cur--) {
328 if (dir_string[cur] == '/')
329 return base::StringPiece(&dir_string[cur + 1], end - cur - 1);
330 }
331 return base::StringPiece(&dir_string[0], end);
332}
333
[email protected]96ea63d2013-07-30 10:17:07334bool EnsureStringIsInOutputDir(const SourceDir& dir,
335 const std::string& str,
336 const Value& originating,
337 Err* err) {
338 // The last char of the dir will be a slash. We don't care if the input ends
339 // in a slash or not, so just compare up until there.
340 //
341 // This check will be wrong for all proper prefixes "e.g. "/output" will
342 // match "/out" but we don't really care since this is just a sanity check.
343 const std::string& dir_str = dir.value();
344 if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
345 != 0) {
[email protected]b8aa81242014-01-05 11:59:51346 *err = Err(originating, "File is not inside output directory.",
[email protected]96ea63d2013-07-30 10:17:07347 "The given file should be in the output directory. Normally you would "
[email protected]b8aa81242014-01-05 11:59:51348 "specify\n\"$target_out_dir/foo\" or "
[email protected]96ea63d2013-07-30 10:17:07349 "\"$target_gen_dir/foo\". I interpreted this as\n\""
350 + str + "\".");
351 return false;
352 }
353 return true;
354}
355
[email protected]ac1128e2013-08-23 00:26:56356bool IsPathAbsolute(const base::StringPiece& path) {
357 if (path.empty())
358 return false;
359
[email protected]858ceda2014-02-27 23:26:40360 if (!IsSlash(path[0])) {
[email protected]ac1128e2013-08-23 00:26:56361#if defined(OS_WIN)
362 // Check for Windows system paths like "C:\foo".
[email protected]858ceda2014-02-27 23:26:40363 if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
[email protected]ac1128e2013-08-23 00:26:56364 return true;
365#endif
366 return false; // Doesn't begin with a slash, is relative.
367 }
368
[email protected]858ceda2014-02-27 23:26:40369 // Double forward slash at the beginning means source-relative (we don't
370 // allow backslashes for denoting this).
[email protected]ac1128e2013-08-23 00:26:56371 if (path.size() > 1 && path[1] == '/')
[email protected]858ceda2014-02-27 23:26:40372 return false;
[email protected]ac1128e2013-08-23 00:26:56373
374 return true;
375}
376
377bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
378 const base::StringPiece& path,
379 std::string* dest) {
380 DCHECK(IsPathAbsolute(source_root));
381 DCHECK(IsPathAbsolute(path));
382
383 dest->clear();
384
385 if (source_root.size() > path.size())
386 return false; // The source root is longer: the path can never be inside.
387
388#if defined(OS_WIN)
[email protected]858ceda2014-02-27 23:26:40389 // Source root should be canonical on Windows. Note that the initial slash
390 // must be forward slash, but that the other ones can be either forward or
391 // backward.
[email protected]ac1128e2013-08-23 00:26:56392 DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
[email protected]858ceda2014-02-27 23:26:40393 source_root[1] == ':' && IsSlash(source_root[2]));
[email protected]f6b314d12013-08-23 21:07:47394
395 size_t after_common_index = std::string::npos;
396 if (DoesBeginWindowsDriveLetter(path)) {
397 // Handle "C:\foo"
398 if (AreAbsoluteWindowsPathsEqual(source_root,
399 path.substr(0, source_root.size())))
400 after_common_index = source_root.size();
401 else
402 return false;
403 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
404 DoesBeginWindowsDriveLetter(path.substr(1))) {
405 // Handle "/C:/foo"
406 if (AreAbsoluteWindowsPathsEqual(source_root,
407 path.substr(1, source_root.size())))
408 after_common_index = source_root.size() + 1;
409 else
410 return false;
411 } else {
412 return false;
413 }
414
415 // If we get here, there's a match and after_common_index identifies the
416 // part after it.
417
418 // The base may or may not have a trailing slash, so skip all slashes from
419 // the path after our prefix match.
420 size_t first_after_slash = after_common_index;
[email protected]858ceda2014-02-27 23:26:40421 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]f6b314d12013-08-23 21:07:47422 first_after_slash++;
423
424 dest->assign("//"); // Result is source root relative.
425 dest->append(&path.data()[first_after_slash],
426 path.size() - first_after_slash);
427 return true;
428
[email protected]ac1128e2013-08-23 00:26:56429#else
[email protected]f6b314d12013-08-23 21:07:47430
[email protected]ac1128e2013-08-23 00:26:56431 // On non-Windows this is easy. Since we know both are absolute, just do a
432 // prefix check.
433 if (path.substr(0, source_root.size()) == source_root) {
[email protected]ac1128e2013-08-23 00:26:56434 // The base may or may not have a trailing slash, so skip all slashes from
435 // the path after our prefix match.
436 size_t first_after_slash = source_root.size();
[email protected]858ceda2014-02-27 23:26:40437 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]ac1128e2013-08-23 00:26:56438 first_after_slash++;
439
[email protected]f6b314d12013-08-23 21:07:47440 dest->assign("//"); // Result is source root relative.
[email protected]ac1128e2013-08-23 00:26:56441 dest->append(&path.data()[first_after_slash],
442 path.size() - first_after_slash);
443 return true;
444 }
[email protected]ac1128e2013-08-23 00:26:56445 return false;
[email protected]f6b314d12013-08-23 21:07:47446#endif
[email protected]ac1128e2013-08-23 00:26:56447}
448
[email protected]96ea63d2013-07-30 10:17:07449std::string InvertDir(const SourceDir& path) {
450 const std::string value = path.value();
451 if (value.empty())
452 return std::string();
453
454 DCHECK(value[0] == '/');
455 size_t begin_index = 1;
456
457 // If the input begins with two slashes, skip over both (this is a
[email protected]858ceda2014-02-27 23:26:40458 // source-relative dir). These must be forward slashes only.
[email protected]96ea63d2013-07-30 10:17:07459 if (value.size() > 1 && value[1] == '/')
460 begin_index = 2;
461
462 std::string ret;
463 for (size_t i = begin_index; i < value.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40464 if (IsSlash(value[i]))
[email protected]96ea63d2013-07-30 10:17:07465 ret.append("../");
466 }
467 return ret;
468}
469
470void NormalizePath(std::string* path) {
471 char* pathbuf = path->empty() ? NULL : &(*path)[0];
472
473 // top_index is the first character we can modify in the path. Anything
474 // before this indicates where the path is relative to.
475 size_t top_index = 0;
476 bool is_relative = true;
477 if (!path->empty() && pathbuf[0] == '/') {
478 is_relative = false;
479
480 if (path->size() > 1 && pathbuf[1] == '/') {
481 // Two leading slashes, this is a path into the source dir.
482 top_index = 2;
483 } else {
484 // One leading slash, this is a system-absolute path.
485 top_index = 1;
486 }
487 }
488
489 size_t dest_i = top_index;
490 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
491 if (pathbuf[src_i] == '.') {
[email protected]858ceda2014-02-27 23:26:40492 if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07493 // Slash followed by a dot, see if it's something special.
494 size_t consumed_len;
495 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
496 case NOT_A_DIRECTORY:
497 // Copy the dot to the output, it means nothing special.
498 pathbuf[dest_i++] = pathbuf[src_i++];
499 break;
500 case DIRECTORY_CUR:
501 // Current directory, just skip the input.
502 src_i += consumed_len;
503 break;
504 case DIRECTORY_UP:
505 // Back up over previous directory component. If we're already
506 // at the top, preserve the "..".
507 if (dest_i > top_index) {
508 // The previous char was a slash, remove it.
509 dest_i--;
510 }
511
512 if (dest_i == top_index) {
513 if (is_relative) {
514 // We're already at the beginning of a relative input, copy the
515 // ".." and continue. We need the trailing slash if there was
516 // one before (otherwise we're at the end of the input).
517 pathbuf[dest_i++] = '.';
518 pathbuf[dest_i++] = '.';
519 if (consumed_len == 3)
520 pathbuf[dest_i++] = '/';
521
522 // This also makes a new "root" that we can't delete by going
523 // up more levels. Otherwise "../.." would collapse to
524 // nothing.
525 top_index = dest_i;
526 }
527 // Otherwise we're at the beginning of an absolute path. Don't
528 // allow ".." to go up another level and just eat it.
529 } else {
530 // Just find the previous slash or the beginning of input.
[email protected]858ceda2014-02-27 23:26:40531 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
[email protected]96ea63d2013-07-30 10:17:07532 dest_i--;
533 }
534 src_i += consumed_len;
535 }
536 } else {
537 // Dot not preceeded by a slash, copy it literally.
538 pathbuf[dest_i++] = pathbuf[src_i++];
539 }
[email protected]858ceda2014-02-27 23:26:40540 } else if (IsSlash(pathbuf[src_i])) {
541 if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07542 // Two slashes in a row, skip over it.
543 src_i++;
544 } else {
[email protected]858ceda2014-02-27 23:26:40545 // Just one slash, copy it, normalizing to foward slash.
546 pathbuf[dest_i] = '/';
547 dest_i++;
548 src_i++;
[email protected]96ea63d2013-07-30 10:17:07549 }
550 } else {
551 // Input nothing special, just copy it.
552 pathbuf[dest_i++] = pathbuf[src_i++];
553 }
554 }
555 path->resize(dest_i);
556}
557
558void ConvertPathToSystem(std::string* path) {
559#if defined(OS_WIN)
560 for (size_t i = 0; i < path->size(); i++) {
561 if ((*path)[i] == '/')
562 (*path)[i] = '\\';
563 }
564#endif
565}
566
[email protected]ea3690c2013-09-23 17:59:22567std::string RebaseSourceAbsolutePath(const std::string& input,
568 const SourceDir& dest_dir) {
569 CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/')
570 << "Input to rebase isn't source-absolute: " << input;
571 CHECK(dest_dir.is_source_absolute())
572 << "Dir to rebase to isn't source-absolute: " << dest_dir.value();
573
574 const std::string& dest = dest_dir.value();
575
576 // Skip the common prefixes of the source and dest as long as they end in
577 // a [back]slash.
578 size_t common_prefix_len = 2; // The beginning two "//" are always the same.
579 size_t max_common_length = std::min(input.size(), dest.size());
580 for (size_t i = common_prefix_len; i < max_common_length; i++) {
[email protected]858ceda2014-02-27 23:26:40581 if (IsSlash(input[i]) && IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22582 common_prefix_len = i + 1;
583 else if (input[i] != dest[i])
584 break;
585 }
586
587 // Invert the dest dir starting from the end of the common prefix.
588 std::string ret;
589 for (size_t i = common_prefix_len; i < dest.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40590 if (IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22591 ret.append("../");
592 }
593
594 // Append any remaining unique input.
595 ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
596
597 // If the result is still empty, the paths are the same.
598 if (ret.empty())
599 ret.push_back('.');
600
601 return ret;
602}
[email protected]05b94b432013-11-22 22:09:55603
604std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
605 std::string ret;
606
607 if (dir.value().empty()) {
608 // Just keep input the same.
609 } else if (dir.value() == "/") {
610 ret.assign("/.");
611 } else if (dir.value() == "//") {
612 ret.assign("//.");
613 } else {
614 ret.assign(dir.value());
615 ret.resize(ret.size() - 1);
616 }
617 return ret;
618}
619
[email protected]a623db1872014-02-19 19:11:17620SourceDir SourceDirForPath(const base::FilePath& source_root,
621 const base::FilePath& path) {
622 std::vector<base::FilePath::StringType> source_comp =
623 GetPathComponents(source_root);
624 std::vector<base::FilePath::StringType> path_comp =
625 GetPathComponents(path);
626
627 // See if path is inside the source root by looking for each of source root's
628 // components at the beginning of path.
629 bool is_inside_source;
630 if (path_comp.size() < source_comp.size()) {
631 // Too small to fit.
632 is_inside_source = false;
633 } else {
634 is_inside_source = true;
635 for (size_t i = 0; i < source_comp.size(); i++) {
636 if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
637 is_inside_source = false;
638 break;
639 }
640 }
641 }
642
643 std::string result_str;
644 size_t initial_path_comp_to_use;
645 if (is_inside_source) {
646 // Construct a source-relative path beginning in // and skip all of the
647 // shared directories.
648 result_str = "//";
649 initial_path_comp_to_use = source_comp.size();
650 } else {
651 // Not inside source code, construct a system-absolute path.
652 result_str = "/";
653 initial_path_comp_to_use = 0;
654 }
655
656 for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
657 result_str.append(FilePathToUTF8(path_comp[i]));
658 result_str.push_back('/');
659 }
660 return SourceDir(result_str);
661}
662
663SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
664 base::FilePath cd;
[email protected]37b3c1992014-03-11 20:59:02665 base::GetCurrentDirectory(&cd);
[email protected]a623db1872014-02-19 19:11:17666 return SourceDirForPath(source_root, cd);
667}
668
[email protected]7380ca72014-05-13 16:56:20669std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
670 // The default toolchain has no subdir.
671 if (is_default)
672 return std::string();
673
674 // For now just assume the toolchain name is always a valid dir name. We may
675 // want to clean up the in the future.
676 return toolchain_label.name() + "/";
677}
678
[email protected]05b94b432013-11-22 22:09:55679SourceDir GetToolchainOutputDir(const Settings* settings) {
680 const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
681
682 std::string result = settings->build_settings()->build_dir().value();
683 if (!toolchain_subdir.value().empty())
684 result.append(toolchain_subdir.value());
685
686 return SourceDir(SourceDir::SWAP_IN, &result);
687}
688
[email protected]7380ca72014-05-13 16:56:20689SourceDir GetToolchainOutputDir(const BuildSettings* build_settings,
690 const Label& toolchain_label, bool is_default) {
691 std::string result = build_settings->build_dir().value();
692 result.append(GetOutputSubdirName(toolchain_label, is_default));
693 return SourceDir(SourceDir::SWAP_IN, &result);
694}
695
[email protected]05b94b432013-11-22 22:09:55696SourceDir GetToolchainGenDir(const Settings* settings) {
697 const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
698
699 std::string result = settings->build_settings()->build_dir().value();
700 if (!toolchain_subdir.value().empty())
701 result.append(toolchain_subdir.value());
702
703 result.append("gen/");
704 return SourceDir(SourceDir::SWAP_IN, &result);
705}
706
[email protected]7380ca72014-05-13 16:56:20707SourceDir GetToolchainGenDir(const BuildSettings* build_settings,
708 const Label& toolchain_label, bool is_default) {
709 std::string result = GetToolchainOutputDir(
710 build_settings, toolchain_label, is_default).value();
711 result.append("gen/");
712 return SourceDir(SourceDir::SWAP_IN, &result);
713}
714
[email protected]05b94b432013-11-22 22:09:55715SourceDir GetOutputDirForSourceDir(const Settings* settings,
716 const SourceDir& source_dir) {
717 SourceDir toolchain = GetToolchainOutputDir(settings);
718
719 std::string ret;
720 toolchain.SwapValue(&ret);
721 ret.append("obj/");
722
[email protected]e9bf4fc2014-06-19 16:50:52723 if (source_dir.is_source_absolute()) {
724 // The source dir is source-absolute, so we trim off the two leading
725 // slashes to append to the toolchain object directory.
726 ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
727 }
728 // (Put system-absolute stuff in the root obj directory.)
[email protected]05b94b432013-11-22 22:09:55729
730 return SourceDir(SourceDir::SWAP_IN, &ret);
731}
732
733SourceDir GetGenDirForSourceDir(const Settings* settings,
734 const SourceDir& source_dir) {
735 SourceDir toolchain = GetToolchainGenDir(settings);
736
737 std::string ret;
738 toolchain.SwapValue(&ret);
739
[email protected]e9bf4fc2014-06-19 16:50:52740 if (source_dir.is_source_absolute()) {
741 // The source dir should be source-absolute, so we trim off the two leading
742 // slashes to append to the toolchain object directory.
743 DCHECK(source_dir.is_source_absolute());
744 ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
745 }
746 // (Put system-absolute stuff in the root gen directory.)
[email protected]05b94b432013-11-22 22:09:55747
748 return SourceDir(SourceDir::SWAP_IN, &ret);
749}
750
751SourceDir GetTargetOutputDir(const Target* target) {
752 return GetOutputDirForSourceDir(target->settings(), target->label().dir());
753}
754
755SourceDir GetTargetGenDir(const Target* target) {
756 return GetGenDirForSourceDir(target->settings(), target->label().dir());
757}
758
759SourceDir GetCurrentOutputDir(const Scope* scope) {
760 return GetOutputDirForSourceDir(scope->settings(), scope->GetSourceDir());
761}
762
763SourceDir GetCurrentGenDir(const Scope* scope) {
764 return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
765}