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