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.
5 #include "tools/gn/filesystem_utils.h"
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"
21 // The given dot is just part of a filename and is not special.
24 // The given dot is the current directory.
27 // The given dot is the first of a double dot that should take us up one.
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
36 // |*consumed_len| will contain the number of characters in the input that
37 // express what we found.
38 DotDisposition
ClassifyAfterDot(const std::string
& path
,
40 size_t* consumed_len
) {
41 if (after_dot
== path
.size()) {
42 // Single dot at the end.
46 if (IsSlash(path
[after_dot
])) {
47 // Single dot followed by a slash.
48 *consumed_len
= 2; // Consume the slash
52 if (path
[after_dot
] == '.') {
54 if (after_dot
+ 1 == path
.size()) {
55 // Double dot at the end.
59 if (IsSlash(path
[after_dot
+ 1])) {
60 // Double dot folowed by a slash.
66 // The dots are followed by something else, not a directory.
68 return NOT_A_DIRECTORY
;
72 inline char NormalizeWindowsPathChar(char c
) {
75 return base::ToLowerASCII(c
);
78 // Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
80 bool AreAbsoluteWindowsPathsEqual(const base::StringPiece
& a
,
81 const base::StringPiece
& b
) {
82 if (a
.size() != b
.size())
85 // For now, just do a case-insensitive ASCII comparison. We could convert to
86 // UTF-16 and use ICU if necessary.
87 for (size_t i
= 0; i
< a
.size(); i
++) {
88 if (NormalizeWindowsPathChar(a
[i
]) != NormalizeWindowsPathChar(b
[i
]))
94 bool DoesBeginWindowsDriveLetter(const base::StringPiece
& path
) {
98 // Check colon first, this will generally fail fastest.
102 // Check drive letter.
103 if (!base::IsAsciiAlpha(path
[0]))
106 if (!IsSlash(path
[2]))
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.
115 std::vector
<base::FilePath::StringType
> GetPathComponents(
116 const base::FilePath
& path
) {
117 std::vector
<base::FilePath::StringType
> result
;
118 path
.GetComponents(&result
);
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());
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 &&
136 IsSlash(static_cast<char>(result
[1][0])))
137 result
.erase(result
.begin() + 1);
143 // Provides the equivalent of == for filesystem strings, trying to do
144 // approximately the right thing with case.
145 bool FilesystemStringsEqual(const base::FilePath::StringType
& a
,
146 const base::FilePath::StringType
& b
) {
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
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
;
161 // Assume case-sensitive filesystems on non-Windows.
168 const char* GetExtensionForOutputType(Target::OutputType type
,
169 Settings::TargetOS os
) {
173 case Target::EXECUTABLE
:
175 case Target::SHARED_LIBRARY
:
177 case Target::STATIC_LIBRARY
:
186 case Target::EXECUTABLE
:
188 case Target::SHARED_LIBRARY
:
189 return "dll.lib"; // Extension of import library.
190 case Target::STATIC_LIBRARY
:
197 case Settings::LINUX
:
199 case Target::EXECUTABLE
:
201 case Target::SHARED_LIBRARY
:
203 case Target::STATIC_LIBRARY
:
216 std::string
FilePathToUTF8(const base::FilePath::StringType
& str
) {
218 return base::WideToUTF8(str
);
224 base::FilePath
UTF8ToFilePath(const base::StringPiece
& sp
) {
226 return base::FilePath(base::UTF8ToWide(sp
));
228 return base::FilePath(sp
.as_string());
232 size_t FindExtensionOffset(const std::string
& path
) {
233 for (int i
= static_cast<int>(path
.size()); i
>= 0; i
--) {
234 if (IsSlash(path
[i
]))
239 return std::string::npos
;
242 base::StringPiece
FindExtension(const std::string
* path
) {
243 size_t extension_offset
= FindExtensionOffset(*path
);
244 if (extension_offset
== std::string::npos
)
245 return base::StringPiece();
246 return base::StringPiece(&path
->data()[extension_offset
],
247 path
->size() - extension_offset
);
250 size_t FindFilenameOffset(const std::string
& path
) {
251 for (int i
= static_cast<int>(path
.size()) - 1; i
>= 0; i
--) {
252 if (IsSlash(path
[i
]))
255 return 0; // No filename found means everything was the filename.
258 base::StringPiece
FindFilename(const std::string
* path
) {
259 size_t filename_offset
= FindFilenameOffset(*path
);
260 if (filename_offset
== 0)
261 return base::StringPiece(*path
); // Everything is the file name.
262 return base::StringPiece(&(*path
).data()[filename_offset
],
263 path
->size() - filename_offset
);
266 base::StringPiece
FindFilenameNoExtension(const std::string
* path
) {
268 return base::StringPiece();
269 size_t filename_offset
= FindFilenameOffset(*path
);
270 size_t extension_offset
= FindExtensionOffset(*path
);
273 if (extension_offset
== std::string::npos
)
274 name_len
= path
->size() - filename_offset
;
276 name_len
= extension_offset
- filename_offset
- 1;
278 return base::StringPiece(&(*path
).data()[filename_offset
], name_len
);
281 void RemoveFilename(std::string
* path
) {
282 path
->resize(FindFilenameOffset(*path
));
285 bool EndsWithSlash(const std::string
& s
) {
286 return !s
.empty() && IsSlash(s
[s
.size() - 1]);
289 base::StringPiece
FindDir(const std::string
* path
) {
290 size_t filename_offset
= FindFilenameOffset(*path
);
291 if (filename_offset
== 0u)
292 return base::StringPiece();
293 return base::StringPiece(path
->data(), filename_offset
);
296 base::StringPiece
FindLastDirComponent(const SourceDir
& dir
) {
297 const std::string
& dir_string
= dir
.value();
299 if (dir_string
.empty())
300 return base::StringPiece();
301 int cur
= static_cast<int>(dir_string
.size()) - 1;
302 DCHECK(dir_string
[cur
] == '/');
304 cur
--; // Skip before the last slash.
306 for (; cur
>= 0; cur
--) {
307 if (dir_string
[cur
] == '/')
308 return base::StringPiece(&dir_string
[cur
+ 1], end
- cur
- 1);
310 return base::StringPiece(&dir_string
[0], end
);
313 bool IsStringInOutputDir(const SourceDir
& output_dir
, const std::string
& str
) {
314 // This check will be wrong for all proper prefixes "e.g. "/output" will
315 // match "/out" but we don't really care since this is just a sanity check.
316 const std::string
& dir_str
= output_dir
.value();
317 return str
.compare(0, dir_str
.length(), dir_str
) == 0;
320 bool EnsureStringIsInOutputDir(const SourceDir
& output_dir
,
321 const std::string
& str
,
322 const ParseNode
* origin
,
324 if (IsStringInOutputDir(output_dir
, str
))
325 return true; // Output directory is hardcoded.
327 *err
= Err(origin
, "File is not inside output directory.",
328 "The given file should be in the output directory. Normally you would "
329 "specify\n\"$target_out_dir/foo\" or "
330 "\"$target_gen_dir/foo\". I interpreted this as\n\""
335 bool IsPathAbsolute(const base::StringPiece
& path
) {
339 if (!IsSlash(path
[0])) {
341 // Check for Windows system paths like "C:\foo".
342 if (path
.size() > 2 && path
[1] == ':' && IsSlash(path
[2]))
345 return false; // Doesn't begin with a slash, is relative.
348 // Double forward slash at the beginning means source-relative (we don't
349 // allow backslashes for denoting this).
350 if (path
.size() > 1 && path
[1] == '/')
356 bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece
& source_root
,
357 const base::StringPiece
& path
,
359 DCHECK(IsPathAbsolute(source_root
));
360 DCHECK(IsPathAbsolute(path
));
364 if (source_root
.size() > path
.size())
365 return false; // The source root is longer: the path can never be inside.
368 // Source root should be canonical on Windows. Note that the initial slash
369 // must be forward slash, but that the other ones can be either forward or
371 DCHECK(source_root
.size() > 2 && source_root
[0] != '/' &&
372 source_root
[1] == ':' && IsSlash(source_root
[2]));
374 size_t after_common_index
= std::string::npos
;
375 if (DoesBeginWindowsDriveLetter(path
)) {
377 if (AreAbsoluteWindowsPathsEqual(source_root
,
378 path
.substr(0, source_root
.size())))
379 after_common_index
= source_root
.size();
382 } else if (path
[0] == '/' && source_root
.size() <= path
.size() - 1 &&
383 DoesBeginWindowsDriveLetter(path
.substr(1))) {
385 if (AreAbsoluteWindowsPathsEqual(source_root
,
386 path
.substr(1, source_root
.size())))
387 after_common_index
= source_root
.size() + 1;
394 // If we get here, there's a match and after_common_index identifies the
397 // The base may or may not have a trailing slash, so skip all slashes from
398 // the path after our prefix match.
399 size_t first_after_slash
= after_common_index
;
400 while (first_after_slash
< path
.size() && IsSlash(path
[first_after_slash
]))
403 dest
->assign("//"); // Result is source root relative.
404 dest
->append(&path
.data()[first_after_slash
],
405 path
.size() - first_after_slash
);
410 // On non-Windows this is easy. Since we know both are absolute, just do a
412 if (path
.substr(0, source_root
.size()) == source_root
) {
413 // The base may or may not have a trailing slash, so skip all slashes from
414 // the path after our prefix match.
415 size_t first_after_slash
= source_root
.size();
416 while (first_after_slash
< path
.size() && IsSlash(path
[first_after_slash
]))
419 dest
->assign("//"); // Result is source root relative.
420 dest
->append(&path
.data()[first_after_slash
],
421 path
.size() - first_after_slash
);
428 void NormalizePath(std::string
* path
) {
429 char* pathbuf
= path
->empty() ? nullptr : &(*path
)[0];
431 // top_index is the first character we can modify in the path. Anything
432 // before this indicates where the path is relative to.
433 size_t top_index
= 0;
434 bool is_relative
= true;
435 if (!path
->empty() && pathbuf
[0] == '/') {
438 if (path
->size() > 1 && pathbuf
[1] == '/') {
439 // Two leading slashes, this is a path into the source dir.
442 // One leading slash, this is a system-absolute path.
447 size_t dest_i
= top_index
;
448 for (size_t src_i
= top_index
; src_i
< path
->size(); /* nothing */) {
449 if (pathbuf
[src_i
] == '.') {
450 if (src_i
== 0 || IsSlash(pathbuf
[src_i
- 1])) {
451 // Slash followed by a dot, see if it's something special.
453 switch (ClassifyAfterDot(*path
, src_i
+ 1, &consumed_len
)) {
454 case NOT_A_DIRECTORY
:
455 // Copy the dot to the output, it means nothing special.
456 pathbuf
[dest_i
++] = pathbuf
[src_i
++];
459 // Current directory, just skip the input.
460 src_i
+= consumed_len
;
463 // Back up over previous directory component. If we're already
464 // at the top, preserve the "..".
465 if (dest_i
> top_index
) {
466 // The previous char was a slash, remove it.
470 if (dest_i
== top_index
) {
472 // We're already at the beginning of a relative input, copy the
473 // ".." and continue. We need the trailing slash if there was
474 // one before (otherwise we're at the end of the input).
475 pathbuf
[dest_i
++] = '.';
476 pathbuf
[dest_i
++] = '.';
477 if (consumed_len
== 3)
478 pathbuf
[dest_i
++] = '/';
480 // This also makes a new "root" that we can't delete by going
481 // up more levels. Otherwise "../.." would collapse to
485 // Otherwise we're at the beginning of an absolute path. Don't
486 // allow ".." to go up another level and just eat it.
488 // Just find the previous slash or the beginning of input.
489 while (dest_i
> 0 && !IsSlash(pathbuf
[dest_i
- 1]))
492 src_i
+= consumed_len
;
495 // Dot not preceeded by a slash, copy it literally.
496 pathbuf
[dest_i
++] = pathbuf
[src_i
++];
498 } else if (IsSlash(pathbuf
[src_i
])) {
499 if (src_i
> 0 && IsSlash(pathbuf
[src_i
- 1])) {
500 // Two slashes in a row, skip over it.
503 // Just one slash, copy it, normalizing to foward slash.
504 pathbuf
[dest_i
] = '/';
509 // Input nothing special, just copy it.
510 pathbuf
[dest_i
++] = pathbuf
[src_i
++];
513 path
->resize(dest_i
);
516 void ConvertPathToSystem(std::string
* path
) {
518 for (size_t i
= 0; i
< path
->size(); i
++) {
519 if ((*path
)[i
] == '/')
525 std::string
MakeRelativePath(const std::string
& input
,
526 const std::string
& dest
) {
528 // Make sure that absolute |input| path starts with a slash if |dest| path
529 // does. Otherwise skipping common prefixes won't work properly. Ensure the
530 // same for |dest| path too.
531 if (IsPathAbsolute(input
) && !IsSlash(input
[0]) && IsSlash(dest
[0])) {
532 std::string
corrected_input(1, dest
[0]);
533 corrected_input
.append(input
);
534 return MakeRelativePath(corrected_input
, dest
);
536 if (IsPathAbsolute(dest
) && !IsSlash(dest
[0]) && IsSlash(input
[0])) {
537 std::string
corrected_dest(1, input
[0]);
538 corrected_dest
.append(dest
);
539 return MakeRelativePath(input
, corrected_dest
);
545 // Skip the common prefixes of the source and dest as long as they end in
547 size_t common_prefix_len
= 0;
548 size_t max_common_length
= std::min(input
.size(), dest
.size());
549 for (size_t i
= common_prefix_len
; i
< max_common_length
; i
++) {
550 if (IsSlash(input
[i
]) && IsSlash(dest
[i
]))
551 common_prefix_len
= i
+ 1;
552 else if (input
[i
] != dest
[i
])
556 // Invert the dest dir starting from the end of the common prefix.
557 for (size_t i
= common_prefix_len
; i
< dest
.size(); i
++) {
558 if (IsSlash(dest
[i
]))
562 // Append any remaining unique input.
563 ret
.append(&input
[common_prefix_len
], input
.size() - common_prefix_len
);
565 // If the result is still empty, the paths are the same.
572 std::string
RebasePath(const std::string
& input
,
573 const SourceDir
& dest_dir
,
574 const base::StringPiece
& source_root
) {
576 DCHECK(source_root
.empty() || !source_root
.ends_with("/"));
578 bool input_is_source_path
= (input
.size() >= 2 &&
579 input
[0] == '/' && input
[1] == '/');
581 if (!source_root
.empty() &&
582 (!input_is_source_path
|| !dest_dir
.is_source_absolute())) {
583 std::string input_full
;
584 std::string dest_full
;
585 if (input_is_source_path
) {
586 source_root
.AppendToString(&input_full
);
587 input_full
.push_back('/');
588 input_full
.append(input
, 2, std::string::npos
);
590 input_full
.append(input
);
592 if (dest_dir
.is_source_absolute()) {
593 source_root
.AppendToString(&dest_full
);
594 dest_full
.push_back('/');
595 dest_full
.append(dest_dir
.value(), 2, std::string::npos
);
598 // On Windows, SourceDir system-absolute paths start
599 // with /, e.g. "/C:/foo/bar".
600 const std::string
& value
= dest_dir
.value();
601 if (value
.size() > 2 && value
[2] == ':')
602 dest_full
.append(dest_dir
.value().substr(1));
604 dest_full
.append(dest_dir
.value());
606 dest_full
.append(dest_dir
.value());
609 bool remove_slash
= false;
610 if (!EndsWithSlash(input_full
)) {
611 input_full
.push_back('/');
614 ret
= MakeRelativePath(input_full
, dest_full
);
615 if (remove_slash
&& ret
.size() > 1)
616 ret
.resize(ret
.size() - 1);
620 ret
= MakeRelativePath(input
, dest_dir
.value());
624 std::string
DirectoryWithNoLastSlash(const SourceDir
& dir
) {
627 if (dir
.value().empty()) {
628 // Just keep input the same.
629 } else if (dir
.value() == "/") {
631 } else if (dir
.value() == "//") {
634 ret
.assign(dir
.value());
635 ret
.resize(ret
.size() - 1);
640 SourceDir
SourceDirForPath(const base::FilePath
& source_root
,
641 const base::FilePath
& path
) {
642 std::vector
<base::FilePath::StringType
> source_comp
=
643 GetPathComponents(source_root
);
644 std::vector
<base::FilePath::StringType
> path_comp
=
645 GetPathComponents(path
);
647 // See if path is inside the source root by looking for each of source root's
648 // components at the beginning of path.
649 bool is_inside_source
;
650 if (path_comp
.size() < source_comp
.size() || source_root
.empty()) {
652 is_inside_source
= false;
654 is_inside_source
= true;
655 for (size_t i
= 0; i
< source_comp
.size(); i
++) {
656 if (!FilesystemStringsEqual(source_comp
[i
], path_comp
[i
])) {
657 is_inside_source
= false;
663 std::string result_str
;
664 size_t initial_path_comp_to_use
;
665 if (is_inside_source
) {
666 // Construct a source-relative path beginning in // and skip all of the
667 // shared directories.
669 initial_path_comp_to_use
= source_comp
.size();
671 // Not inside source code, construct a system-absolute path.
673 initial_path_comp_to_use
= 0;
676 for (size_t i
= initial_path_comp_to_use
; i
< path_comp
.size(); i
++) {
677 result_str
.append(FilePathToUTF8(path_comp
[i
]));
678 result_str
.push_back('/');
680 return SourceDir(result_str
);
683 SourceDir
SourceDirForCurrentDirectory(const base::FilePath
& source_root
) {
685 base::GetCurrentDirectory(&cd
);
686 return SourceDirForPath(source_root
, cd
);
689 std::string
GetOutputSubdirName(const Label
& toolchain_label
, bool is_default
) {
690 // The default toolchain has no subdir.
692 return std::string();
694 // For now just assume the toolchain name is always a valid dir name. We may
695 // want to clean up the in the future.
696 return toolchain_label
.name() + "/";
699 SourceDir
GetToolchainOutputDir(const Settings
* settings
) {
700 return settings
->toolchain_output_subdir().AsSourceDir(
701 settings
->build_settings());
704 SourceDir
GetToolchainOutputDir(const BuildSettings
* build_settings
,
705 const Label
& toolchain_label
, bool is_default
) {
706 std::string result
= build_settings
->build_dir().value();
707 result
.append(GetOutputSubdirName(toolchain_label
, is_default
));
708 return SourceDir(SourceDir::SWAP_IN
, &result
);
711 SourceDir
GetToolchainGenDir(const Settings
* settings
) {
712 return GetToolchainGenDirAsOutputFile(settings
).AsSourceDir(
713 settings
->build_settings());
716 OutputFile
GetToolchainGenDirAsOutputFile(const Settings
* settings
) {
717 OutputFile
result(settings
->toolchain_output_subdir());
718 result
.value().append("gen/");
722 SourceDir
GetToolchainGenDir(const BuildSettings
* build_settings
,
723 const Label
& toolchain_label
, bool is_default
) {
724 std::string result
= GetToolchainOutputDir(
725 build_settings
, toolchain_label
, is_default
).value();
726 result
.append("gen/");
727 return SourceDir(SourceDir::SWAP_IN
, &result
);
730 SourceDir
GetOutputDirForSourceDir(const Settings
* settings
,
731 const SourceDir
& source_dir
) {
732 return GetOutputDirForSourceDirAsOutputFile(settings
, source_dir
).AsSourceDir(
733 settings
->build_settings());
736 OutputFile
GetOutputDirForSourceDirAsOutputFile(const Settings
* settings
,
737 const SourceDir
& source_dir
) {
738 OutputFile result
= settings
->toolchain_output_subdir();
739 result
.value().append("obj/");
741 if (source_dir
.is_source_absolute()) {
742 // The source dir is source-absolute, so we trim off the two leading
743 // slashes to append to the toolchain object directory.
744 result
.value().append(&source_dir
.value()[2],
745 source_dir
.value().size() - 2);
748 const std::string
& build_dir
=
749 settings
->build_settings()->build_dir().value();
751 if (base::StartsWith(source_dir
.value(), build_dir
,
752 base::CompareCase::SENSITIVE
)) {
753 size_t build_dir_size
= build_dir
.size();
754 result
.value().append(&source_dir
.value()[build_dir_size
],
755 source_dir
.value().size() - build_dir_size
);
757 result
.value().append("ABS_PATH");
759 // Windows absolute path contains ':' after drive letter. Remove it to
760 // avoid inserting ':' in the middle of path (eg. "ABS_PATH/C:/").
761 std::string src_dir_value
= source_dir
.value();
762 const auto colon_pos
= src_dir_value
.find(':');
763 if (colon_pos
!= std::string::npos
)
764 src_dir_value
.erase(src_dir_value
.begin() + colon_pos
);
766 const std::string
& src_dir_value
= source_dir
.value();
768 result
.value().append(src_dir_value
);
774 SourceDir
GetGenDirForSourceDir(const Settings
* settings
,
775 const SourceDir
& source_dir
) {
776 return GetGenDirForSourceDirAsOutputFile(settings
, source_dir
).AsSourceDir(
777 settings
->build_settings());
780 OutputFile
GetGenDirForSourceDirAsOutputFile(const Settings
* settings
,
781 const SourceDir
& source_dir
) {
782 OutputFile result
= GetToolchainGenDirAsOutputFile(settings
);
784 if (source_dir
.is_source_absolute()) {
785 // The source dir should be source-absolute, so we trim off the two leading
786 // slashes to append to the toolchain object directory.
787 DCHECK(source_dir
.is_source_absolute());
788 result
.value().append(&source_dir
.value()[2],
789 source_dir
.value().size() - 2);
794 SourceDir
GetTargetOutputDir(const Target
* target
) {
795 return GetOutputDirForSourceDirAsOutputFile(
796 target
->settings(), target
->label().dir()).AsSourceDir(
797 target
->settings()->build_settings());
800 OutputFile
GetTargetOutputDirAsOutputFile(const Target
* target
) {
801 return GetOutputDirForSourceDirAsOutputFile(
802 target
->settings(), target
->label().dir());
805 SourceDir
GetTargetGenDir(const Target
* target
) {
806 return GetTargetGenDirAsOutputFile(target
).AsSourceDir(
807 target
->settings()->build_settings());
810 OutputFile
GetTargetGenDirAsOutputFile(const Target
* target
) {
811 return GetGenDirForSourceDirAsOutputFile(
812 target
->settings(), target
->label().dir());
815 SourceDir
GetCurrentOutputDir(const Scope
* scope
) {
816 return GetOutputDirForSourceDirAsOutputFile(
817 scope
->settings(), scope
->GetSourceDir()).AsSourceDir(
818 scope
->settings()->build_settings());
821 SourceDir
GetCurrentGenDir(const Scope
* scope
) {
822 return GetGenDirForSourceDir(scope
->settings(), scope
->GetSourceDir());