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 "base/logging.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "build/build_config.h"
     10 #include "tools/gn/location.h"
     11 #include "tools/gn/source_dir.h"
     12 
     13 namespace {
     14 
     15 enum DotDisposition {
     16   // The given dot is just part of a filename and is not special.
     17   NOT_A_DIRECTORY,
     18 
     19   // The given dot is the current directory.
     20   DIRECTORY_CUR,
     21 
     22   // The given dot is the first of a double dot that should take us up one.
     23   DIRECTORY_UP
     24 };
     25 
     26 // When we find a dot, this function is called with the character following
     27 // that dot to see what it is. The return value indicates what type this dot is
     28 // (see above). This code handles the case where the dot is at the end of the
     29 // input.
     30 //
     31 // |*consumed_len| will contain the number of characters in the input that
     32 // express what we found.
     33 DotDisposition ClassifyAfterDot(const std::string& path,
     34                                 size_t after_dot,
     35                                 size_t* consumed_len) {
     36   if (after_dot == path.size()) {
     37     // Single dot at the end.
     38     *consumed_len = 1;
     39     return DIRECTORY_CUR;
     40   }
     41   if (path[after_dot] == '/') {
     42     // Single dot followed by a slash.
     43     *consumed_len = 2;  // Consume the slash
     44     return DIRECTORY_CUR;
     45   }
     46 
     47   if (path[after_dot] == '.') {
     48     // Two dots.
     49     if (after_dot + 1 == path.size()) {
     50       // Double dot at the end.
     51       *consumed_len = 2;
     52       return DIRECTORY_UP;
     53     }
     54     if (path[after_dot + 1] == '/') {
     55       // Double dot folowed by a slash.
     56       *consumed_len = 3;
     57       return DIRECTORY_UP;
     58     }
     59   }
     60 
     61   // The dots are followed by something else, not a directory.
     62   *consumed_len = 1;
     63   return NOT_A_DIRECTORY;
     64 }
     65 
     66 }  // namesapce
     67 
     68 SourceFileType GetSourceFileType(const SourceFile& file,
     69                                  Settings::TargetOS os) {
     70   base::StringPiece extension = FindExtension(&file.value());
     71   if (extension == "cc" || extension == "cpp" || extension == "cxx")
     72     return SOURCE_CC;
     73   if (extension == "h")
     74     return SOURCE_H;
     75   if (extension == "c")
     76     return SOURCE_C;
     77 
     78   switch (os) {
     79     case Settings::MAC:
     80       if (extension == "m")
     81         return SOURCE_M;
     82       if (extension == "mm")
     83         return SOURCE_MM;
     84       break;
     85 
     86     case Settings::WIN:
     87       if (extension == "rc")
     88         return SOURCE_RC;
     89       break;
     90 
     91     default:
     92       break;
     93   }
     94 
     95   // TODO(brettw) asm files.
     96   // TODO(brettw) weird thing with .S on non-Windows platforms.
     97   return SOURCE_UNKNOWN;
     98 }
     99 
    100 const char* GetExtensionForOutputType(Target::OutputType type,
    101                                       Settings::TargetOS os) {
    102   switch (os) {
    103     case Settings::MAC:
    104       switch (type) {
    105         case Target::NONE:
    106           NOTREACHED();
    107           return "";
    108         case Target::EXECUTABLE:
    109           return "";
    110         case Target::SHARED_LIBRARY:
    111           return "dylib";
    112         case Target::STATIC_LIBRARY:
    113           return "a";
    114         case Target::LOADABLE_MODULE:
    115           return "dylib";  // TODO(brettw) what's this?
    116         default:
    117           NOTREACHED();
    118       }
    119       break;
    120 
    121     case Settings::WIN:
    122       switch (type) {
    123         case Target::NONE:
    124           NOTREACHED();
    125           return "";
    126         case Target::EXECUTABLE:
    127           return "exe";
    128         case Target::SHARED_LIBRARY:
    129           return "dll.lib";  // Extension of import library.
    130         case Target::STATIC_LIBRARY:
    131           return "lib";
    132         case Target::LOADABLE_MODULE:
    133           return "dll";  // TODO(brettw) what's this?
    134         default:
    135           NOTREACHED();
    136       }
    137       break;
    138 
    139     default:
    140       NOTREACHED();
    141   }
    142   return "";
    143 }
    144 
    145 std::string FilePathToUTF8(const base::FilePath& path) {
    146 #if defined(OS_WIN)
    147   return WideToUTF8(path.value());
    148 #else
    149   return path.value();
    150 #endif
    151 }
    152 
    153 base::FilePath UTF8ToFilePath(const base::StringPiece& sp) {
    154 #if defined(OS_WIN)
    155   return base::FilePath(UTF8ToWide(sp));
    156 #else
    157   return base::FilePath(sp.as_string());
    158 #endif
    159 }
    160 
    161 size_t FindExtensionOffset(const std::string& path) {
    162   for (int i = static_cast<int>(path.size()); i >= 0; i--) {
    163     if (path[i] == '/')
    164       break;
    165     if (path[i] == '.')
    166       return i + 1;
    167   }
    168   return std::string::npos;
    169 }
    170 
    171 base::StringPiece FindExtension(const std::string* path) {
    172   size_t extension_offset = FindExtensionOffset(*path);
    173   if (extension_offset == std::string::npos)
    174     return base::StringPiece();
    175   return base::StringPiece(&path->data()[extension_offset],
    176                            path->size() - extension_offset);
    177 }
    178 
    179 size_t FindFilenameOffset(const std::string& path) {
    180   for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) {
    181     if (path[i] == '/')
    182       return i + 1;
    183   }
    184   return 0;  // No filename found means everything was the filename.
    185 }
    186 
    187 base::StringPiece FindFilename(const std::string* path) {
    188   size_t filename_offset = FindFilenameOffset(*path);
    189   if (filename_offset == 0)
    190     return base::StringPiece(*path);  // Everything is the file name.
    191   return base::StringPiece(&(*path).data()[filename_offset],
    192                            path->size() - filename_offset);
    193 }
    194 
    195 base::StringPiece FindFilenameNoExtension(const std::string* path) {
    196   if (path->empty())
    197     return base::StringPiece();
    198   size_t filename_offset = FindFilenameOffset(*path);
    199   size_t extension_offset = FindExtensionOffset(*path);
    200 
    201   size_t name_len;
    202   if (extension_offset == std::string::npos)
    203     name_len = path->size() - filename_offset;
    204   else
    205     name_len = extension_offset - filename_offset - 1;
    206 
    207   return base::StringPiece(&(*path).data()[filename_offset], name_len);
    208 }
    209 
    210 void RemoveFilename(std::string* path) {
    211   path->resize(FindFilenameOffset(*path));
    212 }
    213 
    214 bool EndsWithSlash(const std::string& s) {
    215   return !s.empty() && s[s.size() - 1] == '/';
    216 }
    217 
    218 base::StringPiece FindDir(const std::string* path) {
    219   size_t filename_offset = FindFilenameOffset(*path);
    220   if (filename_offset == 0u)
    221     return base::StringPiece();
    222   return base::StringPiece(path->data(), filename_offset);
    223 }
    224 
    225 bool EnsureStringIsInOutputDir(const SourceDir& dir,
    226                                const std::string& str,
    227                                const Value& originating,
    228                                Err* err) {
    229   // The last char of the dir will be a slash. We don't care if the input ends
    230   // in a slash or not, so just compare up until there.
    231   //
    232   // This check will be wrong for all proper prefixes "e.g. "/output" will
    233   // match "/out" but we don't really care since this is just a sanity check.
    234   const std::string& dir_str = dir.value();
    235   if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1)
    236       != 0) {
    237     *err = Err(originating, "File not inside output directory.",
    238         "The given file should be in the output directory. Normally you would "
    239         "specify\n\"$target_output_dir/foo\" or "
    240         "\"$target_gen_dir/foo\". I interpreted this as\n\""
    241         + str + "\".");
    242     return false;
    243   }
    244   return true;
    245 }
    246 
    247 std::string InvertDir(const SourceDir& path) {
    248   const std::string value = path.value();
    249   if (value.empty())
    250     return std::string();
    251 
    252   DCHECK(value[0] == '/');
    253   size_t begin_index = 1;
    254 
    255   // If the input begins with two slashes, skip over both (this is a
    256   // source-relative dir).
    257   if (value.size() > 1 && value[1] == '/')
    258     begin_index = 2;
    259 
    260   std::string ret;
    261   for (size_t i = begin_index; i < value.size(); i++) {
    262     if (value[i] == '/')
    263       ret.append("../");
    264   }
    265   return ret;
    266 }
    267 
    268 void NormalizePath(std::string* path) {
    269   char* pathbuf = path->empty() ? NULL : &(*path)[0];
    270 
    271   // top_index is the first character we can modify in the path. Anything
    272   // before this indicates where the path is relative to.
    273   size_t top_index = 0;
    274   bool is_relative = true;
    275   if (!path->empty() && pathbuf[0] == '/') {
    276     is_relative = false;
    277 
    278     if (path->size() > 1 && pathbuf[1] == '/') {
    279       // Two leading slashes, this is a path into the source dir.
    280       top_index = 2;
    281     } else {
    282       // One leading slash, this is a system-absolute path.
    283       top_index = 1;
    284     }
    285   }
    286 
    287   size_t dest_i = top_index;
    288   for (size_t src_i = top_index; src_i < path->size(); /* nothing */) {
    289     if (pathbuf[src_i] == '.') {
    290       if (src_i == 0 || pathbuf[src_i - 1] == '/') {
    291         // Slash followed by a dot, see if it's something special.
    292         size_t consumed_len;
    293         switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) {
    294           case NOT_A_DIRECTORY:
    295             // Copy the dot to the output, it means nothing special.
    296             pathbuf[dest_i++] = pathbuf[src_i++];
    297             break;
    298           case DIRECTORY_CUR:
    299             // Current directory, just skip the input.
    300             src_i += consumed_len;
    301             break;
    302           case DIRECTORY_UP:
    303             // Back up over previous directory component. If we're already
    304             // at the top, preserve the "..".
    305             if (dest_i > top_index) {
    306               // The previous char was a slash, remove it.
    307               dest_i--;
    308             }
    309 
    310             if (dest_i == top_index) {
    311               if (is_relative) {
    312                 // We're already at the beginning of a relative input, copy the
    313                 // ".." and continue. We need the trailing slash if there was
    314                 // one before (otherwise we're at the end of the input).
    315                 pathbuf[dest_i++] = '.';
    316                 pathbuf[dest_i++] = '.';
    317                 if (consumed_len == 3)
    318                   pathbuf[dest_i++] = '/';
    319 
    320                 // This also makes a new "root" that we can't delete by going
    321                 // up more levels.  Otherwise "../.." would collapse to
    322                 // nothing.
    323                 top_index = dest_i;
    324               }
    325               // Otherwise we're at the beginning of an absolute path. Don't
    326               // allow ".." to go up another level and just eat it.
    327             } else {
    328               // Just find the previous slash or the beginning of input.
    329               while (dest_i > 0 && pathbuf[dest_i - 1] != '/')
    330                 dest_i--;
    331             }
    332             src_i += consumed_len;
    333         }
    334       } else {
    335         // Dot not preceeded by a slash, copy it literally.
    336         pathbuf[dest_i++] = pathbuf[src_i++];
    337       }
    338     } else if (pathbuf[src_i] == '/') {
    339       if (src_i > 0 && pathbuf[src_i - 1] == '/') {
    340         // Two slashes in a row, skip over it.
    341         src_i++;
    342       } else {
    343         // Just one slash, copy it.
    344         pathbuf[dest_i++] = pathbuf[src_i++];
    345       }
    346     } else {
    347       // Input nothing special, just copy it.
    348       pathbuf[dest_i++] = pathbuf[src_i++];
    349     }
    350   }
    351   path->resize(dest_i);
    352 }
    353 
    354 void ConvertPathToSystem(std::string* path) {
    355 #if defined(OS_WIN)
    356   for (size_t i = 0; i < path->size(); i++) {
    357     if ((*path)[i] == '/')
    358       (*path)[i] = '\\';
    359   }
    360 #endif
    361 }
    362 
    363 std::string PathToSystem(const std::string& path) {
    364   std::string ret(path);
    365   ConvertPathToSystem(&ret);
    366   return ret;
    367 }
    368 
    369