blob: 18d31d0a7ab016d6afcdb7f794ac586581855079 [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
102 // Check drive letter
103 if (!((path[0] >= 'A' && path[0] <= 'Z') ||
104 path[0] >= 'a' && path[0] <= 'z'))
105 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
168SourceFileType GetSourceFileType(const SourceFile& file,
169 Settings::TargetOS os) {
170 base::StringPiece extension = FindExtension(&file.value());
171 if (extension == "cc" || extension == "cpp" || extension == "cxx")
172 return SOURCE_CC;
173 if (extension == "h")
174 return SOURCE_H;
175 if (extension == "c")
176 return SOURCE_C;
177
178 switch (os) {
179 case Settings::MAC:
180 if (extension == "m")
181 return SOURCE_M;
182 if (extension == "mm")
183 return SOURCE_MM;
184 break;
185
186 case Settings::WIN:
187 if (extension == "rc")
188 return SOURCE_RC;
[email protected]c6f27f22013-08-21 21:44:59189 // TODO(brettw) asm files.
[email protected]96ea63d2013-07-30 10:17:07190 break;
191
192 default:
193 break;
194 }
195
[email protected]c6f27f22013-08-21 21:44:59196 if (os != Settings::WIN) {
197 if (extension == "S")
198 return SOURCE_S;
199 }
200
[email protected]96ea63d2013-07-30 10:17:07201 return SOURCE_UNKNOWN;
202}
203
204const char* GetExtensionForOutputType(Target::OutputType type,
205 Settings::TargetOS os) {
206 switch (os) {
[email protected]8338eea2013-08-05 23:08:12207 case Settings::MAC:
208 switch (type) {
[email protected]8338eea2013-08-05 23:08:12209 case Target::EXECUTABLE:
210 return "";
211 case Target::SHARED_LIBRARY:
212 return "dylib";
213 case Target::STATIC_LIBRARY:
214 return "a";
[email protected]8338eea2013-08-05 23:08:12215 default:
216 NOTREACHED();
217 }
218 break;
219
[email protected]96ea63d2013-07-30 10:17:07220 case Settings::WIN:
221 switch (type) {
[email protected]96ea63d2013-07-30 10:17:07222 case Target::EXECUTABLE:
223 return "exe";
224 case Target::SHARED_LIBRARY:
225 return "dll.lib"; // Extension of import library.
226 case Target::STATIC_LIBRARY:
227 return "lib";
[email protected]c0822d7f2013-08-13 17:10:56228 default:
229 NOTREACHED();
230 }
231 break;
232
233 case Settings::LINUX:
234 switch (type) {
235 case Target::EXECUTABLE:
236 return "";
237 case Target::SHARED_LIBRARY:
238 return "so";
239 case Target::STATIC_LIBRARY:
240 return "a";
[email protected]96ea63d2013-07-30 10:17:07241 default:
242 NOTREACHED();
243 }
244 break;
245
246 default:
247 NOTREACHED();
248 }
249 return "";
250}
251
[email protected]1bcf0012013-09-19 21:13:32252std::string FilePathToUTF8(const base::FilePath::StringType& str) {
[email protected]96ea63d2013-07-30 10:17:07253#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03254 return base::WideToUTF8(str);
[email protected]96ea63d2013-07-30 10:17:07255#else
[email protected]1bcf0012013-09-19 21:13:32256 return str;
[email protected]96ea63d2013-07-30 10:17:07257#endif
258}
259
260base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
261#if defined(OS_WIN)
[email protected]6c3bf032013-12-25 19:37:03262 return base::FilePath(base::UTF8ToWide(sp));
[email protected]96ea63d2013-07-30 10:17:07263#else
264 return base::FilePath(sp.as_string());
265#endif
266}
267
268size_t FindExtensionOffset(const std::string& path) {
269 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40270 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07271 break;
272 if (path[i] == '.')
273 return i + 1;
274 }
275 return std::string::npos;
276}
277
278base::StringPiece FindExtension(const std::string* path) {
279 size_t extension_offset = FindExtensionOffset(*path);
280 if (extension_offset == std::string::npos)
281 return base::StringPiece();
282 return base::StringPiece(&path->data()[extension_offset],
283 path->size() - extension_offset);
284}
285
286size_t FindFilenameOffset(const std::string& path) {
287 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
[email protected]858ceda2014-02-27 23:26:40288 if (IsSlash(path[i]))
[email protected]96ea63d2013-07-30 10:17:07289 return i + 1;
290 }
291 return 0; // No filename found means everything was the filename.
292}
293
294base::StringPiece FindFilename(const std::string* path) {
295 size_t filename_offset = FindFilenameOffset(*path);
296 if (filename_offset == 0)
297 return base::StringPiece(*path); // Everything is the file name.
298 return base::StringPiece(&(*path).data()[filename_offset],
299 path->size() - filename_offset);
300}
301
302base::StringPiece FindFilenameNoExtension(const std::string* path) {
303 if (path->empty())
304 return base::StringPiece();
305 size_t filename_offset = FindFilenameOffset(*path);
306 size_t extension_offset = FindExtensionOffset(*path);
307
308 size_t name_len;
309 if (extension_offset == std::string::npos)
310 name_len = path->size() - filename_offset;
311 else
312 name_len = extension_offset - filename_offset - 1;
313
314 return base::StringPiece(&(*path).data()[filename_offset], name_len);
315}
316
317void RemoveFilename(std::string* path) {
318 path->resize(FindFilenameOffset(*path));
319}
320
321bool EndsWithSlash(const std::string& s) {
[email protected]858ceda2014-02-27 23:26:40322 return !s.empty() && IsSlash(s[s.size() - 1]);
[email protected]96ea63d2013-07-30 10:17:07323}
324
325base::StringPiece FindDir(const std::string* path) {
326 size_t filename_offset = FindFilenameOffset(*path);
327 if (filename_offset == 0u)
328 return base::StringPiece();
329 return base::StringPiece(path->data(), filename_offset);
330}
331
332bool EnsureStringIsInOutputDir(const SourceDir& dir,
333 const std::string& str,
334 const Value& originating,
335 Err* err) {
336 // The last char of the dir will be a slash. We don't care if the input ends
337 // in a slash or not, so just compare up until there.
338 //
339 // This check will be wrong for all proper prefixes "e.g. "/output" will
340 // match "/out" but we don't really care since this is just a sanity check.
341 const std::string& dir_str = dir.value();
342 if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
343 != 0) {
[email protected]b8aa81242014-01-05 11:59:51344 *err = Err(originating, "File is not inside output directory.",
[email protected]96ea63d2013-07-30 10:17:07345 "The given file should be in the output directory. Normally you would "
[email protected]b8aa81242014-01-05 11:59:51346 "specify\n\"$target_out_dir/foo\" or "
[email protected]96ea63d2013-07-30 10:17:07347 "\"$target_gen_dir/foo\". I interpreted this as\n\""
348 + str + "\".");
349 return false;
350 }
351 return true;
352}
353
[email protected]ac1128e2013-08-23 00:26:56354bool IsPathAbsolute(const base::StringPiece& path) {
355 if (path.empty())
356 return false;
357
[email protected]858ceda2014-02-27 23:26:40358 if (!IsSlash(path[0])) {
[email protected]ac1128e2013-08-23 00:26:56359#if defined(OS_WIN)
360 // Check for Windows system paths like "C:\foo".
[email protected]858ceda2014-02-27 23:26:40361 if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
[email protected]ac1128e2013-08-23 00:26:56362 return true;
363#endif
364 return false; // Doesn't begin with a slash, is relative.
365 }
366
[email protected]858ceda2014-02-27 23:26:40367 // Double forward slash at the beginning means source-relative (we don't
368 // allow backslashes for denoting this).
[email protected]ac1128e2013-08-23 00:26:56369 if (path.size() > 1 && path[1] == '/')
[email protected]858ceda2014-02-27 23:26:40370 return false;
[email protected]ac1128e2013-08-23 00:26:56371
372 return true;
373}
374
375bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
376 const base::StringPiece& path,
377 std::string* dest) {
378 DCHECK(IsPathAbsolute(source_root));
379 DCHECK(IsPathAbsolute(path));
380
381 dest->clear();
382
383 if (source_root.size() > path.size())
384 return false; // The source root is longer: the path can never be inside.
385
386#if defined(OS_WIN)
[email protected]858ceda2014-02-27 23:26:40387 // Source root should be canonical on Windows. Note that the initial slash
388 // must be forward slash, but that the other ones can be either forward or
389 // backward.
[email protected]ac1128e2013-08-23 00:26:56390 DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
[email protected]858ceda2014-02-27 23:26:40391 source_root[1] == ':' && IsSlash(source_root[2]));
[email protected]f6b314d12013-08-23 21:07:47392
393 size_t after_common_index = std::string::npos;
394 if (DoesBeginWindowsDriveLetter(path)) {
395 // Handle "C:\foo"
396 if (AreAbsoluteWindowsPathsEqual(source_root,
397 path.substr(0, source_root.size())))
398 after_common_index = source_root.size();
399 else
400 return false;
401 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
402 DoesBeginWindowsDriveLetter(path.substr(1))) {
403 // Handle "/C:/foo"
404 if (AreAbsoluteWindowsPathsEqual(source_root,
405 path.substr(1, source_root.size())))
406 after_common_index = source_root.size() + 1;
407 else
408 return false;
409 } else {
410 return false;
411 }
412
413 // If we get here, there's a match and after_common_index identifies the
414 // part after it.
415
416 // The base may or may not have a trailing slash, so skip all slashes from
417 // the path after our prefix match.
418 size_t first_after_slash = after_common_index;
[email protected]858ceda2014-02-27 23:26:40419 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]f6b314d12013-08-23 21:07:47420 first_after_slash++;
421
422 dest->assign("//"); // Result is source root relative.
423 dest->append(&path.data()[first_after_slash],
424 path.size() - first_after_slash);
425 return true;
426
[email protected]ac1128e2013-08-23 00:26:56427#else
[email protected]f6b314d12013-08-23 21:07:47428
[email protected]ac1128e2013-08-23 00:26:56429 // On non-Windows this is easy. Since we know both are absolute, just do a
430 // prefix check.
431 if (path.substr(0, source_root.size()) == source_root) {
[email protected]ac1128e2013-08-23 00:26:56432 // The base may or may not have a trailing slash, so skip all slashes from
433 // the path after our prefix match.
434 size_t first_after_slash = source_root.size();
[email protected]858ceda2014-02-27 23:26:40435 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
[email protected]ac1128e2013-08-23 00:26:56436 first_after_slash++;
437
[email protected]f6b314d12013-08-23 21:07:47438 dest->assign("//"); // Result is source root relative.
[email protected]ac1128e2013-08-23 00:26:56439 dest->append(&path.data()[first_after_slash],
440 path.size() - first_after_slash);
441 return true;
442 }
[email protected]ac1128e2013-08-23 00:26:56443 return false;
[email protected]f6b314d12013-08-23 21:07:47444#endif
[email protected]ac1128e2013-08-23 00:26:56445}
446
[email protected]96ea63d2013-07-30 10:17:07447std::string InvertDir(const SourceDir& path) {
448 const std::string value = path.value();
449 if (value.empty())
450 return std::string();
451
452 DCHECK(value[0] == '/');
453 size_t begin_index = 1;
454
455 // If the input begins with two slashes, skip over both (this is a
[email protected]858ceda2014-02-27 23:26:40456 // source-relative dir). These must be forward slashes only.
[email protected]96ea63d2013-07-30 10:17:07457 if (value.size() > 1 && value[1] == '/')
458 begin_index = 2;
459
460 std::string ret;
461 for (size_t i = begin_index; i < value.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40462 if (IsSlash(value[i]))
[email protected]96ea63d2013-07-30 10:17:07463 ret.append("../");
464 }
465 return ret;
466}
467
468void NormalizePath(std::string* path) {
469 char* pathbuf = path->empty() ? NULL : &(*path)[0];
470
471 // top_index is the first character we can modify in the path. Anything
472 // before this indicates where the path is relative to.
473 size_t top_index = 0;
474 bool is_relative = true;
475 if (!path->empty() && pathbuf[0] == '/') {
476 is_relative = false;
477
478 if (path->size() > 1 && pathbuf[1] == '/') {
479 // Two leading slashes, this is a path into the source dir.
480 top_index = 2;
481 } else {
482 // One leading slash, this is a system-absolute path.
483 top_index = 1;
484 }
485 }
486
487 size_t dest_i = top_index;
488 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
489 if (pathbuf[src_i] == '.') {
[email protected]858ceda2014-02-27 23:26:40490 if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07491 // Slash followed by a dot, see if it's something special.
492 size_t consumed_len;
493 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
494 case NOT_A_DIRECTORY:
495 // Copy the dot to the output, it means nothing special.
496 pathbuf[dest_i++] = pathbuf[src_i++];
497 break;
498 case DIRECTORY_CUR:
499 // Current directory, just skip the input.
500 src_i += consumed_len;
501 break;
502 case DIRECTORY_UP:
503 // Back up over previous directory component. If we're already
504 // at the top, preserve the "..".
505 if (dest_i > top_index) {
506 // The previous char was a slash, remove it.
507 dest_i--;
508 }
509
510 if (dest_i == top_index) {
511 if (is_relative) {
512 // We're already at the beginning of a relative input, copy the
513 // ".." and continue. We need the trailing slash if there was
514 // one before (otherwise we're at the end of the input).
515 pathbuf[dest_i++] = '.';
516 pathbuf[dest_i++] = '.';
517 if (consumed_len == 3)
518 pathbuf[dest_i++] = '/';
519
520 // This also makes a new "root" that we can't delete by going
521 // up more levels. Otherwise "../.." would collapse to
522 // nothing.
523 top_index = dest_i;
524 }
525 // Otherwise we're at the beginning of an absolute path. Don't
526 // allow ".." to go up another level and just eat it.
527 } else {
528 // Just find the previous slash or the beginning of input.
[email protected]858ceda2014-02-27 23:26:40529 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
[email protected]96ea63d2013-07-30 10:17:07530 dest_i--;
531 }
532 src_i += consumed_len;
533 }
534 } else {
535 // Dot not preceeded by a slash, copy it literally.
536 pathbuf[dest_i++] = pathbuf[src_i++];
537 }
[email protected]858ceda2014-02-27 23:26:40538 } else if (IsSlash(pathbuf[src_i])) {
539 if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
[email protected]96ea63d2013-07-30 10:17:07540 // Two slashes in a row, skip over it.
541 src_i++;
542 } else {
[email protected]858ceda2014-02-27 23:26:40543 // Just one slash, copy it, normalizing to foward slash.
544 pathbuf[dest_i] = '/';
545 dest_i++;
546 src_i++;
[email protected]96ea63d2013-07-30 10:17:07547 }
548 } else {
549 // Input nothing special, just copy it.
550 pathbuf[dest_i++] = pathbuf[src_i++];
551 }
552 }
553 path->resize(dest_i);
554}
555
556void ConvertPathToSystem(std::string* path) {
557#if defined(OS_WIN)
558 for (size_t i = 0; i < path->size(); i++) {
559 if ((*path)[i] == '/')
560 (*path)[i] = '\\';
561 }
562#endif
563}
564
565std::string PathToSystem(const std::string& path) {
566 std::string ret(path);
567 ConvertPathToSystem(&ret);
568 return ret;
569}
570
[email protected]ea3690c2013-09-23 17:59:22571std::string RebaseSourceAbsolutePath(const std::string& input,
572 const SourceDir& dest_dir) {
573 CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/')
574 << "Input to rebase isn't source-absolute: " << input;
575 CHECK(dest_dir.is_source_absolute())
576 << "Dir to rebase to isn't source-absolute: " << dest_dir.value();
577
578 const std::string& dest = dest_dir.value();
579
580 // Skip the common prefixes of the source and dest as long as they end in
581 // a [back]slash.
582 size_t common_prefix_len = 2; // The beginning two "//" are always the same.
583 size_t max_common_length = std::min(input.size(), dest.size());
584 for (size_t i = common_prefix_len; i < max_common_length; i++) {
[email protected]858ceda2014-02-27 23:26:40585 if (IsSlash(input[i]) && IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22586 common_prefix_len = i + 1;
587 else if (input[i] != dest[i])
588 break;
589 }
590
591 // Invert the dest dir starting from the end of the common prefix.
592 std::string ret;
593 for (size_t i = common_prefix_len; i < dest.size(); i++) {
[email protected]858ceda2014-02-27 23:26:40594 if (IsSlash(dest[i]))
[email protected]ea3690c2013-09-23 17:59:22595 ret.append("../");
596 }
597
598 // Append any remaining unique input.
599 ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
600
601 // If the result is still empty, the paths are the same.
602 if (ret.empty())
603 ret.push_back('.');
604
605 return ret;
606}
[email protected]05b94b432013-11-22 22:09:55607
608std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
609 std::string ret;
610
611 if (dir.value().empty()) {
612 // Just keep input the same.
613 } else if (dir.value() == "/") {
614 ret.assign("/.");
615 } else if (dir.value() == "//") {
616 ret.assign("//.");
617 } else {
618 ret.assign(dir.value());
619 ret.resize(ret.size() - 1);
620 }
621 return ret;
622}
623
[email protected]a623db1872014-02-19 19:11:17624SourceDir SourceDirForPath(const base::FilePath& source_root,
625 const base::FilePath& path) {
626 std::vector<base::FilePath::StringType> source_comp =
627 GetPathComponents(source_root);
628 std::vector<base::FilePath::StringType> path_comp =
629 GetPathComponents(path);
630
631 // See if path is inside the source root by looking for each of source root's
632 // components at the beginning of path.
633 bool is_inside_source;
634 if (path_comp.size() < source_comp.size()) {
635 // Too small to fit.
636 is_inside_source = false;
637 } else {
638 is_inside_source = true;
639 for (size_t i = 0; i < source_comp.size(); i++) {
640 if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
641 is_inside_source = false;
642 break;
643 }
644 }
645 }
646
647 std::string result_str;
648 size_t initial_path_comp_to_use;
649 if (is_inside_source) {
650 // Construct a source-relative path beginning in // and skip all of the
651 // shared directories.
652 result_str = "//";
653 initial_path_comp_to_use = source_comp.size();
654 } else {
655 // Not inside source code, construct a system-absolute path.
656 result_str = "/";
657 initial_path_comp_to_use = 0;
658 }
659
660 for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
661 result_str.append(FilePathToUTF8(path_comp[i]));
662 result_str.push_back('/');
663 }
664 return SourceDir(result_str);
665}
666
667SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
668 base::FilePath cd;
[email protected]37b3c1992014-03-11 20:59:02669 base::GetCurrentDirectory(&cd);
[email protected]a623db1872014-02-19 19:11:17670 return SourceDirForPath(source_root, cd);
671}
672
[email protected]05b94b432013-11-22 22:09:55673SourceDir GetToolchainOutputDir(const Settings* settings) {
674 const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
675
676 std::string result = settings->build_settings()->build_dir().value();
677 if (!toolchain_subdir.value().empty())
678 result.append(toolchain_subdir.value());
679
680 return SourceDir(SourceDir::SWAP_IN, &result);
681}
682
683SourceDir GetToolchainGenDir(const Settings* settings) {
684 const OutputFile& toolchain_subdir = settings->toolchain_output_subdir();
685
686 std::string result = settings->build_settings()->build_dir().value();
687 if (!toolchain_subdir.value().empty())
688 result.append(toolchain_subdir.value());
689
690 result.append("gen/");
691 return SourceDir(SourceDir::SWAP_IN, &result);
692}
693
694SourceDir GetOutputDirForSourceDir(const Settings* settings,
695 const SourceDir& source_dir) {
696 SourceDir toolchain = GetToolchainOutputDir(settings);
697
698 std::string ret;
699 toolchain.SwapValue(&ret);
700 ret.append("obj/");
701
702 // The source dir should be source-absolute, so we trim off the two leading
703 // slashes to append to the toolchain object directory.
704 DCHECK(source_dir.is_source_absolute());
705 ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
706
707 return SourceDir(SourceDir::SWAP_IN, &ret);
708}
709
710SourceDir GetGenDirForSourceDir(const Settings* settings,
711 const SourceDir& source_dir) {
712 SourceDir toolchain = GetToolchainGenDir(settings);
713
714 std::string ret;
715 toolchain.SwapValue(&ret);
716
717 // The source dir should be source-absolute, so we trim off the two leading
718 // slashes to append to the toolchain object directory.
719 DCHECK(source_dir.is_source_absolute());
720 ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
721
722 return SourceDir(SourceDir::SWAP_IN, &ret);
723}
724
725SourceDir GetTargetOutputDir(const Target* target) {
726 return GetOutputDirForSourceDir(target->settings(), target->label().dir());
727}
728
729SourceDir GetTargetGenDir(const Target* target) {
730 return GetGenDirForSourceDir(target->settings(), target->label().dir());
731}
732
733SourceDir GetCurrentOutputDir(const Scope* scope) {
734 return GetOutputDirForSourceDir(scope->settings(), scope->GetSourceDir());
735}
736
737SourceDir GetCurrentGenDir(const Scope* scope) {
738 return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
739}