Home | History | Annotate | Download | only in gn
      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 (!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.
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    302 void RemoveFilename(std::string* path) {
    303   path->resize(FindFilenameOffset(*path));
    304 }
    305 
    306 bool EndsWithSlash(const std::string& s) {
    307   return !s.empty() && IsSlash(s[s.size() - 1]);
    308 }
    309 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    663 SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
    664   base::FilePath cd;
    665   base::GetCurrentDirectory(&cd);
    666   return SourceDirForPath(source_root, cd);
    667 }
    668 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    751 SourceDir GetTargetOutputDir(const Target* target) {
    752   return GetOutputDirForSourceDir(target->settings(), target->label().dir());
    753 }
    754 
    755 SourceDir GetTargetGenDir(const Target* target) {
    756   return GetGenDirForSourceDir(target->settings(), target->label().dir());
    757 }
    758 
    759 SourceDir GetCurrentOutputDir(const Scope* scope) {
    760   return GetOutputDirForSourceDir(scope->settings(), scope->GetSourceDir());
    761 }
    762 
    763 SourceDir GetCurrentGenDir(const Scope* scope) {
    764   return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir());
    765 }
    766