blob: 17fec9f4fb6187d30d73750883896a80ba5b13e3 [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"
[email protected]f0bbcdc22014-07-11 17:13:3514#include "tools/gn/file_template.h"
[email protected]96ea63d2013-07-30 10:17:0715#include "tools/gn/location.h"
[email protected]05b94b432013-11-22 22:09:5516#include "tools/gn/settings.h"
[email protected]96ea63d2013-07-30 10:17:0717#include "tools/gn/source_dir.h"
18
19namespace {
20
21enum DotDisposition {
22 // The given dot is just part of a filename and is not special.
23 NOT_A_DIRECTORY,
24
25 // The given dot is the current directory.
26 DIRECTORY_CUR,
27
28 // The given dot is the first of a double dot that should take us up one.
29 DIRECTORY_UP
30};
31
32// When we find a dot, this function is called with the character following
33// that dot to see what it is. The return value indicates what type this dot is
34// (see above). This code handles the case where the dot is at the end of the
35// input.
36//
37// |*consumed_len| will contain the number of characters in the input that
38// express what we found.
39DotDisposition ClassifyAfterDot(const std::string& path,
40 size_t after_dot,
41 size_t* consumed_len) {
42 if (after_dot == path.size()) {
43 // Single dot at the end.
44 *consumed_len = 1;
45 return DIRECTORY_CUR;
46 }
[email protected]858ceda2014-02-27 23:26:4047 if (IsSlash(path[after_dot])) {
[email protected]96ea63d2013-07-30 10:17:0748 // Single dot followed by a slash.
49 *consumed_len = 2; // Consume the slash
50 return DIRECTORY_CUR;
51 }
52
53 if (path[after_dot] == '.') {
54 // Two dots.
55 if (after_dot + 1 == path.size()) {
56 // Double dot at the end.
57 *consumed_len = 2;
58 return DIRECTORY_UP;
59 }
[email protected]858ceda2014-02-27 23:26:4060 if (IsSlash(path[after_dot + 1])) {
[email protected]96ea63d2013-07-30 10:17:0761 // Double dot folowed by a slash.
62 *consumed_len = 3;
63 return DIRECTORY_UP;
64 }
65 }
66
67 // The dots are followed by something else, not a directory.
68 *consumed_len = 1;
69 return NOT_A_DIRECTORY;
70}
71
[email protected]f6b314d12013-08-23 21:07:4772#if defined(OS_WIN)
73inline char NormalizeWindowsPathChar(char c) {
74 if (c == '/')
75 return '\\';
76 return base::ToLowerASCII(c);
77}
78
79// Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
80// paths.
81bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
82 const base::StringPiece& b) {
83 if (a.size() != b.size())
84 return false;
85
86 // For now, just do a case-insensitive ASCII comparison. We could convert to
87 // UTF-16 and use ICU if necessary. Or maybe base::strcasecmp is good enough?
88 for (size_t i = 0; i < a.size(); i++) {
89 if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
90 return false;
91 }
92 return true;
93}
94
95bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
96 if (path.size() < 3)
97 return false;
98
99 // Check colon first, this will generally fail fastest.
100 if (path[1] != ':')
101 return false;
102
[email protected]92df9852014-05-15 21:52:35103 // Check drive letter.
104 if (!IsAsciiAlpha(path[0]))
[email protected]f6b314d12013-08-23 21:07:47105 return false;
106
[email protected]858ceda2014-02-27 23:26:40107 if (!IsSlash(path[2]))
[email protected]f6b314d12013-08-23 21:07:47108 return false;
109 return true;
110}
111#endif
112
[email protected]a623db1872014-02-19 19:11:17113// A wrapper around FilePath.GetComponents that works the way we need. This is
114// not super efficient since it does some O(n) transformations on the path. If
115// this is called a lot, we might want to optimize.
116std::vector<base::FilePath::StringType> GetPathComponents(
117 const base::FilePath& path) {
118 std::vector<base::FilePath::StringType> result;
119 path.GetComponents(&result);
120
121 if (result.empty())
122 return result;
123
124 // GetComponents will preserve the "/" at the beginning, which confuses us.
125 // We don't expect to have relative paths in this function.
126 // Don't use IsSeparator since we always want to allow backslashes.
127 if (result[0] == FILE_PATH_LITERAL("/") ||
128 result[0] == FILE_PATH_LITERAL("\\"))
129 result.erase(result.begin());
130
131#if defined(OS_WIN)
132 // On Windows, GetComponents will give us [ "C:", "/", "foo" ], and we
133 // don't want the slash in there. This doesn't support input like "C:foo"
134 // which means foo relative to the current directory of the C drive but
135 // that's basically legacy DOS behavior we don't need to support.
[email protected]858ceda2014-02-27 23:26:40136 if (result.size() >= 2 && result[1].size() == 1 && IsSlash(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]126d8b52014-04-07 22:17:35168SourceFileType GetSourceFileType(const SourceFile& file) {
[email protected]96ea63d2013-07-30 10:17:07169 base::StringPiece extension = FindExtension(&file.value());
170 if (extension == "cc" || extension == "cpp" || extension == "cxx")
171 return SOURCE_CC;
172 if (extension == "h")
173 return SOURCE_H;
174 if (extension == "c")
175 return SOURCE_C;
[email protected]126d8b52014-04-07 22:17:35176 if (extension == "m")
177 return SOURCE_M;
178 if (extension == "mm")
179 return SOURCE_MM;
180 if (extension == "rc")
181 return SOURCE_RC;
[email protected]e8ab6912014-04-21 20:54:51182 if (extension == "S" || extension == "s")
[email protected]126d8b52014-04-07 22:17:35183 return SOURCE_S;
[email protected]600ee6d2014-05-02 16:42:56184 if (extension == "o" || extension == "obj")
185 return SOURCE_O;
[email protected]c6f27f22013-08-21 21:44:59186
[email protected]96ea63d2013-07-30 10:17:07187 return SOURCE_UNKNOWN;
188}
189
190const char* GetExtensionForOutputType(Target::OutputType type,
191 Settings::TargetOS os) {
192 switch (os) {
[email protected]8338eea2013-08-05 23:08:12193 case Settings::MAC:
194 switch (type) {
[email protected]8338eea2013-08-05 23:08:12195 case Target::EXECUTABLE:
196 return "";
197 case Target::SHARED_LIBRARY:
198 return "dylib";
199 case Target::STATIC_LIBRARY:
200 return "a";
[email protected]8338eea2013-08-05 23:08:12201 default:
202 NOTREACHED();
203 }
204 break;
205
[email protected]96ea63d2013-07-30 10:17:07206 case Settings::WIN:
207 switch (type) {
[email protected]96ea63d2013-07-30 10:17:07208 case Target::EXECUTABLE:
209 return "exe";
210 case Target::SHARED_LIBRARY:
211 return "dll.lib"; // Extension of import library.
212 case Target::STATIC_LIBRARY:
213 return "lib";
[email protected]c0822d7f2013-08-13 17:10:56214 default:
215 NOTREACHED();
216 }
217 break;
218
219 case Settings::LINUX:
220 switch (type) {
221 case Target::EXECUTABLE:
222 return "";
223 case Target::SHARED_LIBRARY:
224 return "so";
225 case Target::STATIC_LIBRARY:
226 return "a";
[email protected]96ea63d2013-07-30 10:17:07227 default:
228 NOTREACHED();
229 }
230 break;
231
232 default:
233 NOTREACHED();
234 }
235 return "";
236}
237
[email protected]1bcf0012013-09-19 21:13:32238std::string FilePathToUTF8(const base::FilePath::StringType& str) {
[email protected]96ea63d2013-07-30 10:17:07239#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03240 return base::WideToUTF8(str);
[email protected]96ea63d2013-07-30 10:17:07241#else
[email protected]1bcf0012013-09-19 21:13:32242 return str;
[email protected]96ea63d2013-07-30 10:17:07243#endif
244}
245
246base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
247#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03248 return base::FilePath(base::UTF8ToWide(sp));
[email protected]96ea63d2013-07-30 10:17:07249#else
250 return base::FilePath(sp.as_string());
251#endif
252}
253
254size_t FindExtensionOffset(const std::string& path) {
255 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40256 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07257 break;
258 if (path[i] == '.')
259 return i + 1;
260 }
261 return std::string::npos;
262}
263
264base::StringPiece FindExtension(const std::string* path) {
265 size_t extension_offset = FindExtensionOffset(*path);
266 if (extension_offset == std::string::npos)
267 return base::StringPiece();
268 return base::StringPiece(&path->data()[extension_offset],
269 path->size() - extension_offset);
270}
271
272size_t FindFilenameOffset(const std::string& path) {
273 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40274 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07275 return i + 1;
276 }
277 return 0; // No filename found means everything was the filename.
278}
279
280base::StringPiece FindFilename(const std::string* path) {
281 size_t filename_offset = FindFilenameOffset(*path);
282 if (filename_offset == 0)
283 return base::StringPiece(*path); // Everything is the file name.
284 return base::StringPiece(&(*path).data()[filename_offset],
285 path->size() - filename_offset);
286}
287
288base::StringPiece FindFilenameNoExtension(const std::string* path) {
289 if (path->empty())
290 return base::StringPiece();
291 size_t filename_offset = FindFilenameOffset(*path);
292 size_t extension_offset = FindExtensionOffset(*path);
293
294 size_t name_len;
295 if (extension_offset == std::string::npos)
296 name_len = path->size() - filename_offset;
297 else
298 name_len = extension_offset - filename_offset - 1;
299
300 return base::StringPiece(&(*path).data()[filename_offset], name_len);
301}
302
303void RemoveFilename(std::string* path) {
304 path->resize(FindFilenameOffset(*path));
305}
306
307bool EndsWithSlash(const std::string& s) {
[email protected]858ceda2014-02-27 23:26:40308 return !s.empty() && IsSlash(s[s.size() - 1]);
[email protected]96ea63d2013-07-30 10:17:07309}
310
311base::StringPiece FindDir(const std::string* path) {
312 size_t filename_offset = FindFilenameOffset(*path);
313 if (filename_offset == 0u)
314 return base::StringPiece();
315 return base::StringPiece(path->data(), filename_offset);
316}
317
[email protected]a3372c72014-04-28 22:39:26318base::StringPiece FindLastDirComponent(const SourceDir& dir) {
319 const std::string& dir_string = dir.value();
320
321 if (dir_string.empty())
322 return base::StringPiece();
323 int cur = static_cast<int>(dir_string.size()) - 1;
324 DCHECK(dir_string[cur] == '/');
325 int end = cur;
326 cur--; // Skip before the last slash.
327
328 for (; cur >= 0; cur--) {
329 if (dir_string[cur] == '/')
330 return base::StringPiece(&dir_string[cur + 1], end - cur - 1);
331 }
332 return base::StringPiece(&dir_string[0], end);
333}
334
[email protected]96ea63d2013-07-30 10:17:07335bool EnsureStringIsInOutputDir(const SourceDir& dir,
336 const std::string& str,
337 const Value& originating,
[email protected]f0bbcdc22014-07-11 17:13:35338 bool allow_templates,
[email protected]96ea63d2013-07-30 10:17:07339 Err* err) {
[email protected]96ea63d2013-07-30 10:17:07340 // This check will be wrong for all proper prefixes "e.g. "/output" will
341 // match "/out" but we don't really care since this is just a sanity check.
342 const std::string& dir_str = dir.value();
[email protected]f0bbcdc22014-07-11 17:13:35343 if (str.compare(0, dir_str.length(), dir_str, 0, dir_str.length()) == 0)
344 return true; // Output directory is hardcoded.
345
346 if (allow_templates) {
347 // Allow the string to begin with any source expansion inside the output
348 // directory.
349 if (StartsWithASCII(str, FileTemplate::kSourceGenDir, true) ||
350 StartsWithASCII(str, FileTemplate::kSourceOutDir, true))
351 return true;
[email protected]96ea63d2013-07-30 10:17:07352 }
[email protected]f0bbcdc22014-07-11 17:13:35353
354 *err = Err(originating, "File is not inside output directory.",
355 "The given file should be in the output directory. Normally you would "
356 "specify\n\"$target_out_dir/foo\" or "
357 "\"$target_gen_dir/foo\". I interpreted this as\n\""
358 + str + "\".");
359 return false;
[email protected]96ea63d2013-07-30 10:17:07360}
361
[email protected]ac1128e2013-08-23 00:26:56362bool IsPathAbsolute(const base::StringPiece& path) {
363 if (path.empty())
364 return false;
365
[email protected]858ceda2014-02-27 23:26:40366 if (!IsSlash(path[0])) {
[email protected]ac1128e2013-08-23 00:26:56367#if defined(OS_WIN)
368 // Check for Windows system paths like "C:\foo".
[email protected]858ceda2014-02-27 23:26:40369 if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
[email protected]ac1128e2013-08-23 00:26:56370 return true;
371#endif
372 return false; // Doesn't begin with a slash, is relative.
373 }
374
[email protected]858ceda2014-02-27 23:26:40375 // Double forward slash at the beginning means source-relative (we don't
376 // allow backslashes for denoting this).
[email protected]ac1128e2013-08-23 00:26:56377 if (path.size() > 1 && path[1] == '/')
[email protected]858ceda2014-02-27 23:26:40378 return false;
[email protected]ac1128e2013-08-23 00:26:56379
380 return true;
381}
382
383bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
384 const base::StringPiece& path,
385 std::string* dest) {
386 DCHECK(IsPathAbsolute(source_root));
387 DCHECK(IsPathAbsolute(path));
388
389 dest->clear();
390
391 if (source_root.size() > path.size())
392 return false; // The source root is longer: the path can never be inside.
393
394#if defined(OS_WIN)
[email protected]858ceda2014-02-27 23:26:40395 // Source root should be canonical on Windows. Note that the initial slash
396 // must be forward slash, but that the other ones can be either forward or
397 // backward.
[email protected]ac1128e2013-08-23 00:26:56398 DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
[email protected]858ceda2014-02-27 23:26:40399 source_root[1] == ':' && IsSlash(source_root[2]));
[email protected]f6b314d12013-08-23 21:07:47400
401 size_t after_common_index = std::string::npos;
402 if (DoesBeginWindowsDriveLetter(path)) {
403 // Handle "C:\foo"
404 if (AreAbsoluteWindowsPathsEqual(source_root,
405 path.substr(0, source_root.size())))
406 after_common_index = source_root.size();
407 else
408 return false;
409 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
410 DoesBeginWindowsDriveLetter(path.substr(1))) {
411 // Handle "/C:/foo"
412 if (AreAbsoluteWindowsPathsEqual(source_root,
413 path.substr(1, source_root.size())))
414 after_common_index = source_root.size() + 1;
415 else
416 return false;
417 } else {
418 return false;
419 }
420
421 // If we get here, there's a match and after_common_index identifies the
422 // part after it.
423
424 // The base may or may not have a trailing slash, so skip all slashes from
425 // the path after our prefix match.
426 size_t first_after_slash = after_common_index;
[email protected]858ceda2014-02-27 23:26:40427 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]f6b314d12013-08-23 21:07:47428 first_after_slash++;
429
430 dest->assign("//"); // Result is source root relative.
431 dest->append(&path.data()[first_after_slash],
432 path.size() - first_after_slash);
433 return true;
434
[email protected]ac1128e2013-08-23 00:26:56435#else
[email protected]f6b314d12013-08-23 21:07:47436
[email protected]ac1128e2013-08-23 00:26:56437 // On non-Windows this is easy. Since we know both are absolute, just do a
438 // prefix check.
439 if (path.substr(0, source_root.size()) == source_root) {
[email protected]ac1128e2013-08-23 00:26:56440 // The base may or may not have a trailing slash, so skip all slashes from
441 // the path after our prefix match.
442 size_t first_after_slash = source_root.size();
[email protected]858ceda2014-02-27 23:26:40443 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]ac1128e2013-08-23 00:26:56444 first_after_slash++;
445
[email protected]f6b314d12013-08-23 21:07:47446 dest->assign("//"); // Result is source root relative.
[email protected]ac1128e2013-08-23 00:26:56447 dest->append(&path.data()[first_after_slash],
448 path.size() - first_after_slash);
449 return true;
450 }
[email protected]ac1128e2013-08-23 00:26:56451 return false;
[email protected]f6b314d12013-08-23 21:07:47452#endif
[email protected]ac1128e2013-08-23 00:26:56453}
454
[email protected]96ea63d2013-07-30 10:17:07455std::string InvertDir(const SourceDir& path) {
456 const std::string value = path.value();
457 if (value.empty())
458 return std::string();
459
460 DCHECK(value[0] == '/');
461 size_t begin_index = 1;
462
463 // If the input begins with two slashes, skip over both (this is a
[email protected]858ceda2014-02-27 23:26:40464 // source-relative dir). These must be forward slashes only.
[email protected]96ea63d2013-07-30 10:17:07465 if (value.size() > 1 && value[1] == '/')
466 begin_index = 2;
467
468 std::string ret;
469 for (size_t i = begin_index; i < value.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40470 if (IsSlash(value[i]))
[email protected]96ea63d2013-07-30 10:17:07471 ret.append("../");
472 }
473 return ret;
474}
475
476void NormalizePath(std::string* path) {
477 char* pathbuf = path->empty() ? NULL : &(*path)[0];
478
479 // top_index is the first character we can modify in the path. Anything
480 // before this indicates where the path is relative to.
481 size_t top_index = 0;
482 bool is_relative = true;
483 if (!path->empty() && pathbuf[0] == '/') {
484 is_relative = false;
485
486 if (path->size() > 1 && pathbuf[1] == '/') {
487 // Two leading slashes, this is a path into the source dir.
488 top_index = 2;
489 } else {
490 // One leading slash, this is a system-absolute path.
491 top_index = 1;
492 }
493 }
494
495 size_t dest_i = top_index;
496 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
497 if (pathbuf[src_i] == '.') {
[email protected]858ceda2014-02-27 23:26:40498 if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07499 // Slash followed by a dot, see if it's something special.
500 size_t consumed_len;
501 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
502 case NOT_A_DIRECTORY:
503 // Copy the dot to the output, it means nothing special.
504 pathbuf[dest_i++] = pathbuf[src_i++];
505 break;
506 case DIRECTORY_CUR:
507 // Current directory, just skip the input.
508 src_i += consumed_len;
509 break;
510 case DIRECTORY_UP:
511 // Back up over previous directory component. If we're already
512 // at the top, preserve the "..".
513 if (dest_i > top_index) {
514 // The previous char was a slash, remove it.
515 dest_i--;
516 }
517
518 if (dest_i == top_index) {
519 if (is_relative) {
520 // We're already at the beginning of a relative input, copy the
521 // ".." and continue. We need the trailing slash if there was
522 // one before (otherwise we're at the end of the input).
523 pathbuf[dest_i++] = '.';
524 pathbuf[dest_i++] = '.';
525 if (consumed_len == 3)
526 pathbuf[dest_i++] = '/';
527
528 // This also makes a new "root" that we can't delete by going
529 // up more levels. Otherwise "../.." would collapse to
530 // nothing.
531 top_index = dest_i;
532 }
533 // Otherwise we're at the beginning of an absolute path. Don't
534 // allow ".." to go up another level and just eat it.
535 } else {
536 // Just find the previous slash or the beginning of input.
[email protected]858ceda2014-02-27 23:26:40537 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
[email protected]96ea63d2013-07-30 10:17:07538 dest_i--;
539 }
540 src_i += consumed_len;
541 }
542 } else {
543 // Dot not preceeded by a slash, copy it literally.
544 pathbuf[dest_i++] = pathbuf[src_i++];
545 }
[email protected]858ceda2014-02-27 23:26:40546 } else if (IsSlash(pathbuf[src_i])) {
547 if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07548 // Two slashes in a row, skip over it.
549 src_i++;
550 } else {
[email protected]858ceda2014-02-27 23:26:40551 // Just one slash, copy it, normalizing to foward slash.
552 pathbuf[dest_i] = '/';
553 dest_i++;
554 src_i++;
[email protected]96ea63d2013-07-30 10:17:07555 }
556 } else {
557 // Input nothing special, just copy it.
558 pathbuf[dest_i++] = pathbuf[src_i++];
559 }
560 }
561 path->resize(dest_i);
562}
563
564void ConvertPathToSystem(std::string* path) {
565#if defined(OS_WIN)
566 for (size_t i = 0; i < path->size(); i++) {
567 if ((*path)[i] == '/')
568 (*path)[i] = '\\';
569 }
570#endif
571}
572
[email protected]ea3690c2013-09-23 17:59:22573std::string RebaseSourceAbsolutePath(const std::string& input,
574 const SourceDir& dest_dir) {
575 CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/')
576 << "Input to rebase isn't source-absolute: " << input;
577 CHECK(dest_dir.is_source_absolute())
578 << "Dir to rebase to isn't source-absolute: " << dest_dir.value();
579
580 const std::string& dest = dest_dir.value();
581
582 // Skip the common prefixes of the source and dest as long as they end in
583 // a [back]slash.
584 size_t common_prefix_len = 2; // The beginning two "//" are always the same.
585 size_t max_common_length = std::min(input.size(), dest.size());
586 for (size_t i = common_prefix_len; i < max_common_length; i++) {
[email protected]858ceda2014-02-27 23:26:40587 if (IsSlash(input[i]) && IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22588 common_prefix_len = i + 1;
589 else if (input[i] != dest[i])
590 break;
591 }
592
593 // Invert the dest dir starting from the end of the common prefix.
594 std::string ret;
595 for (size_t i = common_prefix_len; i < dest.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40596 if (IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22597 ret.append("../");
598 }
599
600 // Append any remaining unique input.
601 ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
602
603 // If the result is still empty, the paths are the same.
604 if (ret.empty())
605 ret.push_back('.');
606
607 return ret;
608}
[email protected]05b94b432013-11-22 22:09:55609
610std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
611 std::string ret;
612
613 if (dir.value().empty()) {
614 // Just keep input the same.
615 } else if (dir.value() == "/") {
616 ret.assign("/.");
617 } else if (dir.value() == "//") {
618 ret.assign("//.");
619 } else {
620 ret.assign(dir.value());
621 ret.resize(ret.size() - 1);
622 }
623 return ret;
624}
625
[email protected]a623db1872014-02-19 19:11:17626SourceDir SourceDirForPath(const base::FilePath& source_root,
627 const base::FilePath& path) {
628 std::vector<base::FilePath::StringType> source_comp =
629 GetPathComponents(source_root);
630 std::vector<base::FilePath::StringType> path_comp =
631 GetPathComponents(path);
632
633 // See if path is inside the source root by looking for each of source root's
634 // components at the beginning of path.
635 bool is_inside_source;
636 if (path_comp.size() < source_comp.size()) {
637 // Too small to fit.
638 is_inside_source = false;
639 } else {
640 is_inside_source = true;
641 for (size_t i = 0; i < source_comp.size(); i++) {
642 if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
643 is_inside_source = false;
644 break;
645 }
646 }
647 }
648
649 std::string result_str;
650 size_t initial_path_comp_to_use;
651 if (is_inside_source) {
652 // Construct a source-relative path beginning in // and skip all of the
653 // shared directories.
654 result_str = "//";
655 initial_path_comp_to_use = source_comp.size();
656 } else {
657 // Not inside source code, construct a system-absolute path.
658 result_str = "/";
659 initial_path_comp_to_use = 0;
660 }
661
662 for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
663 result_str.append(FilePathToUTF8(path_comp[i]));
664 result_str.push_back('/');
665 }
666 return SourceDir(result_str);
667}
668
669SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
670 base::FilePath cd;
[email protected]37b3c1992014-03-11 20:59:02671 base::GetCurrentDirectory(&cd);
[email protected]a623db1872014-02-19 19:11:17672 return SourceDirForPath(source_root, cd);
673}
674
[email protected]7380ca72014-05-13 16:56:20675std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
676 // The default toolchain has no subdir.
677 if (is_default)
678 return std::string();
679
680 // For now just assume the toolchain name is always a valid dir name. We may
681 // want to clean up the in the future.
682 return toolchain_label.name() + "/";
683}
684
[email protected]05b94b432013-11-22 22:09:55685SourceDir GetToolchainOutputDir(const Settings* settings) {
686 const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
687
688 std::string result = settings->build_settings()->build_dir().value();
689 if (!toolchain_subdir.value().empty())
690 result.append(toolchain_subdir.value());
691
692 return SourceDir(SourceDir::SWAP_IN, &result);
693}
694
[email protected]7380ca72014-05-13 16:56:20695SourceDir GetToolchainOutputDir(const BuildSettings* build_settings,
696 const Label& toolchain_label, bool is_default) {
697 std::string result = build_settings->build_dir().value();
698 result.append(GetOutputSubdirName(toolchain_label, is_default));
699 return SourceDir(SourceDir::SWAP_IN, &result);
700}
701
[email protected]05b94b432013-11-22 22:09:55702SourceDir GetToolchainGenDir(const Settings* settings) {
703 const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
704
705 std::string result = settings->build_settings()->build_dir().value();
706 if (!toolchain_subdir.value().empty())
707 result.append(toolchain_subdir.value());
708
709 result.append("gen/");
710 return SourceDir(SourceDir::SWAP_IN, &result);
711}
712
[email protected]7380ca72014-05-13 16:56:20713SourceDir GetToolchainGenDir(const BuildSettings* build_settings,
714 const Label& toolchain_label, bool is_default) {
715 std::string result = GetToolchainOutputDir(
716 build_settings, toolchain_label, is_default).value();
717 result.append("gen/");
718 return SourceDir(SourceDir::SWAP_IN, &result);
719}
720
[email protected]05b94b432013-11-22 22:09:55721SourceDir GetOutputDirForSourceDir(const Settings* settings,
722 const SourceDir& source_dir) {
723 SourceDir toolchain = GetToolchainOutputDir(settings);
724
725 std::string ret;
726 toolchain.SwapValue(&ret);
727 ret.append("obj/");
728
[email protected]e9bf4fc2014-06-19 16:50:52729 if (source_dir.is_source_absolute()) {
730 // The source dir is source-absolute, so we trim off the two leading
731 // slashes to append to the toolchain object directory.
732 ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
733 }
734 // (Put system-absolute stuff in the root obj directory.)
[email protected]05b94b432013-11-22 22:09:55735
736 return SourceDir(SourceDir::SWAP_IN, &ret);
737}
738
739SourceDir GetGenDirForSourceDir(const Settings* settings,
740 const SourceDir& source_dir) {
741 SourceDir toolchain = GetToolchainGenDir(settings);
742
743 std::string ret;
744 toolchain.SwapValue(&ret);
745
[email protected]e9bf4fc2014-06-19 16:50:52746 if (source_dir.is_source_absolute()) {
747 // The source dir should be source-absolute, so we trim off the two leading
748 // slashes to append to the toolchain object directory.
749 DCHECK(source_dir.is_source_absolute());
750 ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
751 }
752 // (Put system-absolute stuff in the root gen directory.)
[email protected]05b94b432013-11-22 22:09:55753
754 return SourceDir(SourceDir::SWAP_IN, &ret);
755}
756
757SourceDir GetTargetOutputDir(const Target* target) {
758 return GetOutputDirForSourceDir(target->settings(), target->label().dir());
759}
760
761SourceDir GetTargetGenDir(const Target* target) {
762 return GetGenDirForSourceDir(target->settings(), target->label().dir());
763}
764
765SourceDir GetCurrentOutputDir(const Scope* scope) {
766 return GetOutputDirForSourceDir(scope->settings(), scope->GetSourceDir());
767}
768
769SourceDir GetCurrentGenDir(const Scope* scope) {
770 return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
771}