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
18namespace {
19
20enum DotDisposition {
21  // The given dot is just part of a filename and is not special.
22  NOT_A_DIRECTORY,
23
24  // The given dot is the current directory.
25  DIRECTORY_CUR,
26
27  // The given dot is the first of a double dot that should take us up one.
28  DIRECTORY_UP
29};
30
31// When we find a dot, this function is called with the character following
32// that dot to see what it is. The return value indicates what type this dot is
33// (see above). This code handles the case where the dot is at the end of the
34// input.
35//
36// |*consumed_len| will contain the number of characters in the input that
37// express what we found.
38DotDisposition ClassifyAfterDot(const std::string& path,
39                                size_t after_dot,
40                                size_t* consumed_len) {
41  if (after_dot == path.size()) {
42    // Single dot at the end.
43    *consumed_len = 1;
44    return DIRECTORY_CUR;
45  }
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)
72inline char NormalizeWindowsPathChar(char c) {
73  if (c == '/')
74    return '\\';
75  return base::ToLowerASCII(c);
76}
77
78// Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
79// paths.
80bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
81                                  const base::StringPiece& b) {
82  if (a.size() != b.size())
83    return false;
84
85  // For now, just do a case-insensitive ASCII comparison. We could convert to
86  // UTF-16 and use ICU if necessary. Or maybe base::strcasecmp is good enough?
87  for (size_t i = 0; i < a.size(); i++) {
88    if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
89      return false;
90  }
91  return true;
92}
93
94bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
95  if (path.size() < 3)
96    return false;
97
98  // Check colon first, this will generally fail fastest.
99  if (path[1] != ':')
100    return false;
101
102  // Check drive letter.
103  if (!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.
115std::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.
144bool 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
167const 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
215std::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
223base::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
231size_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
241base::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
249size_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
257base::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
265base::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
280void RemoveFilename(std::string* path) {
281  path->resize(FindFilenameOffset(*path));
282}
283
284bool EndsWithSlash(const std::string& s) {
285  return !s.empty() && IsSlash(s[s.size() - 1]);
286}
287
288base::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
295base::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
312bool 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
330bool 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
351bool 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
423std::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
444void 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
532void 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
541std::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
578std::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
594SourceDir 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
637SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
638  base::FilePath cd;
639  base::GetCurrentDirectory(&cd);
640  return SourceDirForPath(source_root, cd);
641}
642
643std::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
653SourceDir GetToolchainOutputDir(const Settings* settings) {
654  return settings->toolchain_output_subdir().AsSourceDir(
655      settings->build_settings());
656}
657
658SourceDir 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
665SourceDir GetToolchainGenDir(const Settings* settings) {
666  return GetToolchainGenDirAsOutputFile(settings).AsSourceDir(
667      settings->build_settings());
668}
669
670OutputFile GetToolchainGenDirAsOutputFile(const Settings* settings) {
671  OutputFile result(settings->toolchain_output_subdir());
672  result.value().append("gen/");
673  return result;
674}
675
676SourceDir 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
684SourceDir GetOutputDirForSourceDir(const Settings* settings,
685                                   const SourceDir& source_dir) {
686  return GetOutputDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
687      settings->build_settings());
688}
689
690OutputFile 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
704SourceDir GetGenDirForSourceDir(const Settings* settings,
705                                const SourceDir& source_dir) {
706  return GetGenDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir(
707      settings->build_settings());
708}
709
710OutputFile 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
724SourceDir GetTargetOutputDir(const Target* target) {
725  return GetOutputDirForSourceDirAsOutputFile(
726      target->settings(), target->label().dir()).AsSourceDir(
727          target->settings()->build_settings());
728}
729
730OutputFile GetTargetOutputDirAsOutputFile(const Target* target) {
731  return GetOutputDirForSourceDirAsOutputFile(
732      target->settings(), target->label().dir());
733}
734
735SourceDir GetTargetGenDir(const Target* target) {
736  return GetTargetGenDirAsOutputFile(target).AsSourceDir(
737      target->settings()->build_settings());
738}
739
740OutputFile GetTargetGenDirAsOutputFile(const Target* target) {
741  return GetGenDirForSourceDirAsOutputFile(
742      target->settings(), target->label().dir());
743}
744
745SourceDir GetCurrentOutputDir(const Scope* scope) {
746  return GetOutputDirForSourceDirAsOutputFile(
747      scope->settings(), scope->GetSourceDir()).AsSourceDir(
748          scope->settings()->build_settings());
749}
750
751SourceDir GetCurrentGenDir(const Scope* scope) {
752  return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
753}
754