1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "tools/gn/filesystem_utils.h"
6
7 #include <algorithm>
8
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "build/build_config.h"
14 #include "tools/gn/location.h"
15 #include "tools/gn/settings.h"
16 #include "tools/gn/source_dir.h"
17
18 namespace {
19
20 enum 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.
ClassifyAfterDot(const std::string & path,size_t after_dot,size_t * consumed_len)38 DotDisposition 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 }
46 if (IsSlash(path[after_dot])) {
47 // 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 }
59 if (IsSlash(path[after_dot + 1])) {
60 // 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
71 #if defined(OS_WIN)
NormalizeWindowsPathChar(char c)72 inline 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.
AreAbsoluteWindowsPathsEqual(const base::StringPiece & a,const base::StringPiece & b)80 bool 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
DoesBeginWindowsDriveLetter(const base::StringPiece & path)94 bool 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 (!IsAsciiAlpha(path[0]))
104 return false;
105
106 if (!IsSlash(path[2]))
107 return false;
108 return true;
109 }
110 #endif
111
112 // 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.
GetPathComponents(const base::FilePath & path)115 std::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.
135 if (result.size() >= 2 && result[1].size() == 1 && IsSlash(result[1][0]))
136 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.
FilesystemStringsEqual(const base::FilePath::StringType & a,const base::FilePath::StringType & b)144 bool 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
165 } // namespace
166
GetExtensionForOutputType(Target::OutputType type,Settings::TargetOS os)167 const char* GetExtensionForOutputType(Target::OutputType type,
168 Settings::TargetOS os) {
169 switch (os) {
170 case Settings::MAC:
171 switch (type) {
172 case Target::EXECUTABLE:
173 return "";
174 case Target::SHARED_LIBRARY:
175 return "dylib";
176 case Target::STATIC_LIBRARY:
177 return "a";
178 default:
179 NOTREACHED();
180 }
181 break;
182
183 case Settings::WIN:
184 switch (type) {
185 case Target::EXECUTABLE:
186 return "exe";
187 case Target::SHARED_LIBRARY:
188 return "dll.lib"; // Extension of import library.
189 case Target::STATIC_LIBRARY:
190 return "lib";
191 default:
192 NOTREACHED();
193 }
194 break;
195
196 case Settings::LINUX:
197 switch (type) {
198 case Target::EXECUTABLE:
199 return "";
200 case Target::SHARED_LIBRARY:
201 return "so";
202 case Target::STATIC_LIBRARY:
203 return "a";
204 default:
205 NOTREACHED();
206 }
207 break;
208
209 default:
210 NOTREACHED();
211 }
212 return "";
213 }
214
FilePathToUTF8(const base::FilePath::StringType & str)215 std::string FilePathToUTF8(const base::FilePath::StringType& str) {
216 #if defined(OS_WIN)
217 return base::WideToUTF8(str);
218 #else
219 return str;
220 #endif
221 }
222
UTF8ToFilePath(const base::StringPiece & sp)223 base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
224 #if defined(OS_WIN)
225 return base::FilePath(base::UTF8ToWide(sp));
226 #else
227 return base::FilePath(sp.as_string());
228 #endif
229 }
230
FindExtensionOffset(const std::string & path)231 size_t FindExtensionOffset(const std::string& path) {
232 for (int i = static_cast<int>(path.size()); i >= 0; i--) {
233 if (IsSlash(path[i]))
234 break;
235 if (path[i] == '.')
236 return i + 1;
237 }
238 return std::string::npos;
239 }
240
FindExtension(const std::string * path)241 base::StringPiece FindExtension(const std::string* path) {
242 size_t extension_offset = FindExtensionOffset(*path);
243 if (extension_offset == std::string::npos)
244 return base::StringPiece();
245 return base::StringPiece(&path->data()[extension_offset],
246 path->size() - extension_offset);
247 }
248
FindFilenameOffset(const std::string & path)249 size_t FindFilenameOffset(const std::string& path) {
250 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
251 if (IsSlash(path[i]))
252 return i + 1;
253 }
254 return 0; // No filename found means everything was the filename.
255 }
256
FindFilename(const std::string * path)257 base::StringPiece FindFilename(const std::string* path) {
258 size_t filename_offset = FindFilenameOffset(*path);
259 if (filename_offset == 0)
260 return base::StringPiece(*path); // Everything is the file name.
261 return base::StringPiece(&(*path).data()[filename_offset],
262 path->size() - filename_offset);
263 }
264
FindFilenameNoExtension(const std::string * path)265 base::StringPiece FindFilenameNoExtension(const std::string* path) {
266 if (path->empty())
267 return base::StringPiece();
268 size_t filename_offset = FindFilenameOffset(*path);
269 size_t extension_offset = FindExtensionOffset(*path);
270
271 size_t name_len;
272 if (extension_offset == std::string::npos)
273 name_len = path->size() - filename_offset;
274 else
275 name_len = extension_offset - filename_offset - 1;
276
277 return base::StringPiece(&(*path).data()[filename_offset], name_len);
278 }
279
RemoveFilename(std::string * path)280 void RemoveFilename(std::string* path) {
281 path->resize(FindFilenameOffset(*path));
282 }
283
EndsWithSlash(const std::string & s)284 bool EndsWithSlash(const std::string& s) {
285 return !s.empty() && IsSlash(s[s.size() - 1]);
286 }
287
FindDir(const std::string * path)288 base::StringPiece FindDir(const std::string* path) {
289 size_t filename_offset = FindFilenameOffset(*path);
290 if (filename_offset == 0u)
291 return base::StringPiece();
292 return base::StringPiece(path->data(), filename_offset);
293 }
294
FindLastDirComponent(const SourceDir & dir)295 base::StringPiece FindLastDirComponent(const SourceDir& dir) {
296 const std::string& dir_string = dir.value();
297
298 if (dir_string.empty())
299 return base::StringPiece();
300 int cur = static_cast<int>(dir_string.size()) - 1;
301 DCHECK(dir_string[cur] == '/');
302 int end = cur;
303 cur--; // Skip before the last slash.
304
305 for (; cur >= 0; cur--) {
306 if (dir_string[cur] == '/')
307 return base::StringPiece(&dir_string[cur + 1], end - cur - 1);
308 }
309 return base::StringPiece(&dir_string[0], end);
310 }
311
EnsureStringIsInOutputDir(const SourceDir & dir,const std::string & str,const ParseNode * origin,Err * err)312 bool EnsureStringIsInOutputDir(const SourceDir& dir,
313 const std::string& str,
314 const ParseNode* origin,
315 Err* err) {
316 // This check will be wrong for all proper prefixes "e.g. "/output" will
317 // match "/out" but we don't really care since this is just a sanity check.
318 const std::string& dir_str = dir.value();
319 if (str.compare(0, dir_str.length(), dir_str) == 0)
320 return true; // Output directory is hardcoded.
321
322 *err = Err(origin, "File is not inside output directory.",
323 "The given file should be in the output directory. Normally you would "
324 "specify\n\"$target_out_dir/foo\" or "
325 "\"$target_gen_dir/foo\". I interpreted this as\n\""
326 + str + "\".");
327 return false;
328 }
329
IsPathAbsolute(const base::StringPiece & path)330 bool IsPathAbsolute(const base::StringPiece& path) {
331 if (path.empty())
332 return false;
333
334 if (!IsSlash(path[0])) {
335 #if defined(OS_WIN)
336 // Check for Windows system paths like "C:\foo".
337 if (path.size() > 2 && path[1] == ':' && IsSlash(path[2]))
338 return true;
339 #endif
340 return false; // Doesn't begin with a slash, is relative.
341 }
342
343 // Double forward slash at the beginning means source-relative (we don't
344 // allow backslashes for denoting this).
345 if (path.size() > 1 && path[1] == '/')
346 return false;
347
348 return true;
349 }
350
MakeAbsolutePathRelativeIfPossible(const base::StringPiece & source_root,const base::StringPiece & path,std::string * dest)351 bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
352 const base::StringPiece& path,
353 std::string* dest) {
354 DCHECK(IsPathAbsolute(source_root));
355 DCHECK(IsPathAbsolute(path));
356
357 dest->clear();
358
359 if (source_root.size() > path.size())
360 return false; // The source root is longer: the path can never be inside.
361
362 #if defined(OS_WIN)
363 // Source root should be canonical on Windows. Note that the initial slash
364 // must be forward slash, but that the other ones can be either forward or
365 // backward.
366 DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
367 source_root[1] == ':' && IsSlash(source_root[2]));
368
369 size_t after_common_index = std::string::npos;
370 if (DoesBeginWindowsDriveLetter(path)) {
371 // Handle "C:\foo"
372 if (AreAbsoluteWindowsPathsEqual(source_root,
373 path.substr(0, source_root.size())))
374 after_common_index = source_root.size();
375 else
376 return false;
377 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
378 DoesBeginWindowsDriveLetter(path.substr(1))) {
379 // Handle "/C:/foo"
380 if (AreAbsoluteWindowsPathsEqual(source_root,
381 path.substr(1, source_root.size())))
382 after_common_index = source_root.size() + 1;
383 else
384 return false;
385 } else {
386 return false;
387 }
388
389 // If we get here, there's a match and after_common_index identifies the
390 // part after it.
391
392 // The base may or may not have a trailing slash, so skip all slashes from
393 // the path after our prefix match.
394 size_t first_after_slash = after_common_index;
395 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
396 first_after_slash++;
397
398 dest->assign("//"); // Result is source root relative.
399 dest->append(&path.data()[first_after_slash],
400 path.size() - first_after_slash);
401 return true;
402
403 #else
404
405 // On non-Windows this is easy. Since we know both are absolute, just do a
406 // prefix check.
407 if (path.substr(0, source_root.size()) == source_root) {
408 // The base may or may not have a trailing slash, so skip all slashes from
409 // the path after our prefix match.
410 size_t first_after_slash = source_root.size();
411 while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
412 first_after_slash++;
413
414 dest->assign("//"); // Result is source root relative.
415 dest->append(&path.data()[first_after_slash],
416 path.size() - first_after_slash);
417 return true;
418 }
419 return false;
420 #endif
421 }
422
InvertDir(const SourceDir & path)423 std::string InvertDir(const SourceDir& path) {
424 const std::string value = path.value();
425 if (value.empty())
426 return std::string();
427
428 DCHECK(value[0] == '/');
429 size_t begin_index = 1;
430
431 // If the input begins with two slashes, skip over both (this is a
432 // source-relative dir). These must be forward slashes only.
433 if (value.size() > 1 && value[1] == '/')
434 begin_index = 2;
435
436 std::string ret;
437 for (size_t i = begin_index; i < value.size(); i++) {
438 if (IsSlash(value[i]))
439 ret.append("../");
440 }
441 return ret;
442 }
443
NormalizePath(std::string * path)444 void NormalizePath(std::string* path) {
445 char* pathbuf = path->empty() ? NULL : &(*path)[0];
446
447 // top_index is the first character we can modify in the path. Anything
448 // before this indicates where the path is relative to.
449 size_t top_index = 0;
450 bool is_relative = true;
451 if (!path->empty() && pathbuf[0] == '/') {
452 is_relative = false;
453
454 if (path->size() > 1 && pathbuf[1] == '/') {
455 // Two leading slashes, this is a path into the source dir.
456 top_index = 2;
457 } else {
458 // One leading slash, this is a system-absolute path.
459 top_index = 1;
460 }
461 }
462
463 size_t dest_i = top_index;
464 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
465 if (pathbuf[src_i] == '.') {
466 if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) {
467 // Slash followed by a dot, see if it's something special.
468 size_t consumed_len;
469 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
470 case NOT_A_DIRECTORY:
471 // Copy the dot to the output, it means nothing special.
472 pathbuf[dest_i++] = pathbuf[src_i++];
473 break;
474 case DIRECTORY_CUR:
475 // Current directory, just skip the input.
476 src_i += consumed_len;
477 break;
478 case DIRECTORY_UP:
479 // Back up over previous directory component. If we're already
480 // at the top, preserve the "..".
481 if (dest_i > top_index) {
482 // The previous char was a slash, remove it.
483 dest_i--;
484 }
485
486 if (dest_i == top_index) {
487 if (is_relative) {
488 // We're already at the beginning of a relative input, copy the
489 // ".." and continue. We need the trailing slash if there was
490 // one before (otherwise we're at the end of the input).
491 pathbuf[dest_i++] = '.';
492 pathbuf[dest_i++] = '.';
493 if (consumed_len == 3)
494 pathbuf[dest_i++] = '/';
495
496 // This also makes a new "root" that we can't delete by going
497 // up more levels. Otherwise "../.." would collapse to
498 // nothing.
499 top_index = dest_i;
500 }
501 // Otherwise we're at the beginning of an absolute path. Don't
502 // allow ".." to go up another level and just eat it.
503 } else {
504 // Just find the previous slash or the beginning of input.
505 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1]))
506 dest_i--;
507 }
508 src_i += consumed_len;
509 }
510 } else {
511 // Dot not preceeded by a slash, copy it literally.
512 pathbuf[dest_i++] = pathbuf[src_i++];
513 }
514 } else if (IsSlash(pathbuf[src_i])) {
515 if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) {
516 // Two slashes in a row, skip over it.
517 src_i++;
518 } else {
519 // Just one slash, copy it, normalizing to foward slash.
520 pathbuf[dest_i] = '/';
521 dest_i++;
522 src_i++;
523 }
524 } else {
525 // Input nothing special, just copy it.
526 pathbuf[dest_i++] = pathbuf[src_i++];
527 }
528 }
529 path->resize(dest_i);
530 }
531
ConvertPathToSystem(std::string * path)532 void ConvertPathToSystem(std::string* path) {
533 #if defined(OS_WIN)
534 for (size_t i = 0; i < path->size(); i++) {
535 if ((*path)[i] == '/')
536 (*path)[i] = '\\';
537 }
538 #endif
539 }
540
RebaseSourceAbsolutePath(const std::string & input,const SourceDir & dest_dir)541 std::string RebaseSourceAbsolutePath(const std::string& input,
542 const SourceDir& dest_dir) {
543 CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/')
544 << "Input to rebase isn't source-absolute: " << input;
545 CHECK(dest_dir.is_source_absolute())
546 << "Dir to rebase to isn't source-absolute: " << dest_dir.value();
547
548 const std::string& dest = dest_dir.value();
549
550 // Skip the common prefixes of the source and dest as long as they end in
551 // a [back]slash.
552 size_t common_prefix_len = 2; // The beginning two "//" are always the same.
553 size_t max_common_length = std::min(input.size(), dest.size());
554 for (size_t i = common_prefix_len; i < max_common_length; i++) {
555 if (IsSlash(input[i]) && IsSlash(dest[i]))
556 common_prefix_len = i + 1;
557 else if (input[i] != dest[i])
558 break;
559 }
560
561 // Invert the dest dir starting from the end of the common prefix.
562 std::string ret;
563 for (size_t i = common_prefix_len; i < dest.size(); i++) {
564 if (IsSlash(dest[i]))
565 ret.append("../");
566 }
567
568 // Append any remaining unique input.
569 ret.append(&input[common_prefix_len], input.size() - common_prefix_len);
570
571 // If the result is still empty, the paths are the same.
572 if (ret.empty())
573 ret.push_back('.');
574
575 return ret;
576 }
577
DirectoryWithNoLastSlash(const SourceDir & dir)578 std::string DirectoryWithNoLastSlash(const SourceDir& dir) {
579 std::string ret;
580
581 if (dir.value().empty()) {
582 // Just keep input the same.
583 } else if (dir.value() == "/") {
584 ret.assign("/.");
585 } else if (dir.value() == "//") {
586 ret.assign("//.");
587 } else {
588 ret.assign(dir.value());
589 ret.resize(ret.size() - 1);
590 }
591 return ret;
592 }
593
SourceDirForPath(const base::FilePath & source_root,const base::FilePath & path)594 SourceDir SourceDirForPath(const base::FilePath& source_root,
595 const base::FilePath& path) {
596 std::vector<base::FilePath::StringType> source_comp =
597 GetPathComponents(source_root);
598 std::vector<base::FilePath::StringType> path_comp =
599 GetPathComponents(path);
600
601 // See if path is inside the source root by looking for each of source root's
602 // components at the beginning of path.
603 bool is_inside_source;
604 if (path_comp.size() < source_comp.size()) {
605 // Too small to fit.
606 is_inside_source = false;
607 } else {
608 is_inside_source = true;
609 for (size_t i = 0; i < source_comp.size(); i++) {
610 if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) {
611 is_inside_source = false;
612 break;
613 }
614 }
615 }
616
617 std::string result_str;
618 size_t initial_path_comp_to_use;
619 if (is_inside_source) {
620 // Construct a source-relative path beginning in // and skip all of the
621 // shared directories.
622 result_str = "//";
623 initial_path_comp_to_use = source_comp.size();
624 } else {
625 // Not inside source code, construct a system-absolute path.
626 result_str = "/";
627 initial_path_comp_to_use = 0;
628 }
629
630 for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) {
631 result_str.append(FilePathToUTF8(path_comp[i]));
632 result_str.push_back('/');
633 }
634 return SourceDir(result_str);
635 }
636
SourceDirForCurrentDirectory(const base::FilePath & source_root)637 SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
638 base::FilePath cd;
639 base::GetCurrentDirectory(&cd);
640 return SourceDirForPath(source_root, cd);
641 }
642
GetOutputSubdirName(const Label & toolchain_label,bool is_default)643 std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
644 // The default toolchain has no subdir.
645 if (is_default)
646 return std::string();
647
648 // For now just assume the toolchain name is always a valid dir name. We may
649 // want to clean up the in the future.
650 return toolchain_label.name() + "/";
651 }
652
GetToolchainOutputDir(const Settings * settings)653 SourceDir GetToolchainOutputDir(const Settings* settings) {
654 return settings->toolchain_output_subdir().AsSourceDir(
655 settings->build_settings());
656 }
657
GetToolchainOutputDir(const BuildSettings * build_settings,const Label & toolchain_label,bool is_default)658 SourceDir GetToolchainOutputDir(const BuildSettings* build_settings,
659 const Label& toolchain_label, bool is_default) {
660 std::string result = build_settings->build_dir().value();
661 result.append(GetOutputSubdirName(toolchain_label, is_default));
662 return SourceDir(SourceDir::SWAP_IN, &result);
663 }
664
GetToolchainGenDir(const Settings * settings)665 SourceDir GetToolchainGenDir(const Settings* settings) {
666 return GetToolchainGenDirAsOutputFile(settings).AsSourceDir(
667 settings->build_settings());
668 }
669
GetToolchainGenDirAsOutputFile(const Settings * settings)670 OutputFile GetToolchainGenDirAsOutputFile(const Settings* settings) {
671 OutputFile result(settings->toolchain_output_subdir());
672 result.value().append("gen/");
673 return result;
674 }
675
GetToolchainGenDir(const BuildSettings * build_settings,const Label & toolchain_label,bool is_default)676 SourceDir GetToolchainGenDir(const BuildSettings* build_settings,
677 const Label& toolchain_label, bool is_default) {
678 std::string result = GetToolchainOutputDir(
679 build_settings, toolchain_label, is_default).value();
680 result.append("gen/");
681 return SourceDir(SourceDir::SWAP_IN, &result);
682 }
683
GetOutputDirForSourceDir(const Settings * settings,const SourceDir & source_dir)684 SourceDir GetOutputDirForSourceDir(const Settings* settings,
685 const SourceDir& source_dir) {
686 return GetOutputDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
687 settings->build_settings());
688 }
689
GetOutputDirForSourceDirAsOutputFile(const Settings * settings,const SourceDir & source_dir)690 OutputFile GetOutputDirForSourceDirAsOutputFile(const Settings* settings,
691 const SourceDir& source_dir) {
692 OutputFile result = settings->toolchain_output_subdir();
693 result.value().append("obj/");
694
695 if (source_dir.is_source_absolute()) {
696 // The source dir is source-absolute, so we trim off the two leading
697 // slashes to append to the toolchain object directory.
698 result.value().append(&source_dir.value()[2],
699 source_dir.value().size() - 2);
700 }
701 return result;
702 }
703
GetGenDirForSourceDir(const Settings * settings,const SourceDir & source_dir)704 SourceDir GetGenDirForSourceDir(const Settings* settings,
705 const SourceDir& source_dir) {
706 return GetGenDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
707 settings->build_settings());
708 }
709
GetGenDirForSourceDirAsOutputFile(const Settings * settings,const SourceDir & source_dir)710 OutputFile GetGenDirForSourceDirAsOutputFile(const Settings* settings,
711 const SourceDir& source_dir) {
712 OutputFile result = GetToolchainGenDirAsOutputFile(settings);
713
714 if (source_dir.is_source_absolute()) {
715 // The source dir should be source-absolute, so we trim off the two leading
716 // slashes to append to the toolchain object directory.
717 DCHECK(source_dir.is_source_absolute());
718 result.value().append(&source_dir.value()[2],
719 source_dir.value().size() - 2);
720 }
721 return result;
722 }
723
GetTargetOutputDir(const Target * target)724 SourceDir GetTargetOutputDir(const Target* target) {
725 return GetOutputDirForSourceDirAsOutputFile(
726 target->settings(), target->label().dir()).AsSourceDir(
727 target->settings()->build_settings());
728 }
729
GetTargetOutputDirAsOutputFile(const Target * target)730 OutputFile GetTargetOutputDirAsOutputFile(const Target* target) {
731 return GetOutputDirForSourceDirAsOutputFile(
732 target->settings(), target->label().dir());
733 }
734
GetTargetGenDir(const Target * target)735 SourceDir GetTargetGenDir(const Target* target) {
736 return GetTargetGenDirAsOutputFile(target).AsSourceDir(
737 target->settings()->build_settings());
738 }
739
GetTargetGenDirAsOutputFile(const Target * target)740 OutputFile GetTargetGenDirAsOutputFile(const Target* target) {
741 return GetGenDirForSourceDirAsOutputFile(
742 target->settings(), target->label().dir());
743 }
744
GetCurrentOutputDir(const Scope * scope)745 SourceDir GetCurrentOutputDir(const Scope* scope) {
746 return GetOutputDirForSourceDirAsOutputFile(
747 scope->settings(), scope->GetSourceDir()).AsSourceDir(
748 scope->settings()->build_settings());
749 }
750
GetCurrentGenDir(const Scope * scope)751 SourceDir GetCurrentGenDir(const Scope* scope) {
752 return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
753 }
754