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