OLD | NEW |
(Empty) | |
| 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::WIN: |
| 104 switch (type) { |
| 105 case Target::NONE: |
| 106 NOTREACHED(); |
| 107 return ""; |
| 108 case Target::EXECUTABLE: |
| 109 return "exe"; |
| 110 case Target::SHARED_LIBRARY: |
| 111 return "dll.lib"; // Extension of import library. |
| 112 case Target::STATIC_LIBRARY: |
| 113 return "lib"; |
| 114 case Target::LOADABLE_MODULE: |
| 115 return "dll"; // TODO(brettw) what's this? |
| 116 default: |
| 117 NOTREACHED(); |
| 118 } |
| 119 break; |
| 120 |
| 121 default: |
| 122 NOTREACHED(); |
| 123 } |
| 124 return ""; |
| 125 } |
| 126 |
| 127 std::string FilePathToUTF8(const base::FilePath& path) { |
| 128 #if defined(OS_WIN) |
| 129 return WideToUTF8(path.value()); |
| 130 #else |
| 131 return path.value(); |
| 132 #endif |
| 133 } |
| 134 |
| 135 base::FilePath UTF8ToFilePath(const base::StringPiece& sp) { |
| 136 #if defined(OS_WIN) |
| 137 return base::FilePath(UTF8ToWide(sp)); |
| 138 #else |
| 139 return base::FilePath(sp.as_string()); |
| 140 #endif |
| 141 } |
| 142 |
| 143 size_t FindExtensionOffset(const std::string& path) { |
| 144 for (int i = static_cast<int>(path.size()); i >= 0; i--) { |
| 145 if (path[i] == '/') |
| 146 break; |
| 147 if (path[i] == '.') |
| 148 return i + 1; |
| 149 } |
| 150 return std::string::npos; |
| 151 } |
| 152 |
| 153 base::StringPiece FindExtension(const std::string* path) { |
| 154 size_t extension_offset = FindExtensionOffset(*path); |
| 155 if (extension_offset == std::string::npos) |
| 156 return base::StringPiece(); |
| 157 return base::StringPiece(&path->data()[extension_offset], |
| 158 path->size() - extension_offset); |
| 159 } |
| 160 |
| 161 size_t FindFilenameOffset(const std::string& path) { |
| 162 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) { |
| 163 if (path[i] == '/') |
| 164 return i + 1; |
| 165 } |
| 166 return 0; // No filename found means everything was the filename. |
| 167 } |
| 168 |
| 169 base::StringPiece FindFilename(const std::string* path) { |
| 170 size_t filename_offset = FindFilenameOffset(*path); |
| 171 if (filename_offset == 0) |
| 172 return base::StringPiece(*path); // Everything is the file name. |
| 173 return base::StringPiece(&(*path).data()[filename_offset], |
| 174 path->size() - filename_offset); |
| 175 } |
| 176 |
| 177 base::StringPiece FindFilenameNoExtension(const std::string* path) { |
| 178 if (path->empty()) |
| 179 return base::StringPiece(); |
| 180 size_t filename_offset = FindFilenameOffset(*path); |
| 181 size_t extension_offset = FindExtensionOffset(*path); |
| 182 |
| 183 size_t name_len; |
| 184 if (extension_offset == std::string::npos) |
| 185 name_len = path->size() - filename_offset; |
| 186 else |
| 187 name_len = extension_offset - filename_offset - 1; |
| 188 |
| 189 return base::StringPiece(&(*path).data()[filename_offset], name_len); |
| 190 } |
| 191 |
| 192 void RemoveFilename(std::string* path) { |
| 193 path->resize(FindFilenameOffset(*path)); |
| 194 } |
| 195 |
| 196 bool EndsWithSlash(const std::string& s) { |
| 197 return !s.empty() && s[s.size() - 1] == '/'; |
| 198 } |
| 199 |
| 200 base::StringPiece FindDir(const std::string* path) { |
| 201 size_t filename_offset = FindFilenameOffset(*path); |
| 202 if (filename_offset == 0u) |
| 203 return base::StringPiece(); |
| 204 return base::StringPiece(path->data(), filename_offset); |
| 205 } |
| 206 |
| 207 bool EnsureStringIsInOutputDir(const SourceDir& dir, |
| 208 const std::string& str, |
| 209 const Value& originating, |
| 210 Err* err) { |
| 211 // The last char of the dir will be a slash. We don't care if the input ends |
| 212 // in a slash or not, so just compare up until there. |
| 213 // |
| 214 // This check will be wrong for all proper prefixes "e.g. "/output" will |
| 215 // match "/out" but we don't really care since this is just a sanity check. |
| 216 const std::string& dir_str = dir.value(); |
| 217 if (str.compare(0, dir_str.length() - 1, dir_str, 0, dir_str.length() - 1) |
| 218 != 0) { |
| 219 *err = Err(originating, "File not inside output directory.", |
| 220 "The given file should be in the output directory. Normally you would " |
| 221 "specify\n\"$target_output_dir/foo\" or " |
| 222 "\"$target_gen_dir/foo\". I interpreted this as\n\"" |
| 223 + str + "\"."); |
| 224 return false; |
| 225 } |
| 226 return true; |
| 227 } |
| 228 |
| 229 std::string InvertDir(const SourceDir& path) { |
| 230 const std::string value = path.value(); |
| 231 if (value.empty()) |
| 232 return std::string(); |
| 233 |
| 234 DCHECK(value[0] == '/'); |
| 235 size_t begin_index = 1; |
| 236 |
| 237 // If the input begins with two slashes, skip over both (this is a |
| 238 // source-relative dir). |
| 239 if (value.size() > 1 && value[1] == '/') |
| 240 begin_index = 2; |
| 241 |
| 242 std::string ret; |
| 243 for (size_t i = begin_index; i < value.size(); i++) { |
| 244 if (value[i] == '/') |
| 245 ret.append("../"); |
| 246 } |
| 247 return ret; |
| 248 } |
| 249 |
| 250 void NormalizePath(std::string* path) { |
| 251 char* pathbuf = path->empty() ? NULL : &(*path)[0]; |
| 252 |
| 253 // top_index is the first character we can modify in the path. Anything |
| 254 // before this indicates where the path is relative to. |
| 255 size_t top_index = 0; |
| 256 bool is_relative = true; |
| 257 if (!path->empty() && pathbuf[0] == '/') { |
| 258 is_relative = false; |
| 259 |
| 260 if (path->size() > 1 && pathbuf[1] == '/') { |
| 261 // Two leading slashes, this is a path into the source dir. |
| 262 top_index = 2; |
| 263 } else { |
| 264 // One leading slash, this is a system-absolute path. |
| 265 top_index = 1; |
| 266 } |
| 267 } |
| 268 |
| 269 size_t dest_i = top_index; |
| 270 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) { |
| 271 if (pathbuf[src_i] == '.') { |
| 272 if (src_i == 0 || pathbuf[src_i - 1] == '/') { |
| 273 // Slash followed by a dot, see if it's something special. |
| 274 size_t consumed_len; |
| 275 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) { |
| 276 case NOT_A_DIRECTORY: |
| 277 // Copy the dot to the output, it means nothing special. |
| 278 pathbuf[dest_i++] = pathbuf[src_i++]; |
| 279 break; |
| 280 case DIRECTORY_CUR: |
| 281 // Current directory, just skip the input. |
| 282 src_i += consumed_len; |
| 283 break; |
| 284 case DIRECTORY_UP: |
| 285 // Back up over previous directory component. If we're already |
| 286 // at the top, preserve the "..". |
| 287 if (dest_i > top_index) { |
| 288 // The previous char was a slash, remove it. |
| 289 dest_i--; |
| 290 } |
| 291 |
| 292 if (dest_i == top_index) { |
| 293 if (is_relative) { |
| 294 // We're already at the beginning of a relative input, copy the |
| 295 // ".." and continue. We need the trailing slash if there was |
| 296 // one before (otherwise we're at the end of the input). |
| 297 pathbuf[dest_i++] = '.'; |
| 298 pathbuf[dest_i++] = '.'; |
| 299 if (consumed_len == 3) |
| 300 pathbuf[dest_i++] = '/'; |
| 301 |
| 302 // This also makes a new "root" that we can't delete by going |
| 303 // up more levels. Otherwise "../.." would collapse to |
| 304 // nothing. |
| 305 top_index = dest_i; |
| 306 } |
| 307 // Otherwise we're at the beginning of an absolute path. Don't |
| 308 // allow ".." to go up another level and just eat it. |
| 309 } else { |
| 310 // Just find the previous slash or the beginning of input. |
| 311 while (dest_i > 0 && pathbuf[dest_i - 1] != '/') |
| 312 dest_i--; |
| 313 } |
| 314 src_i += consumed_len; |
| 315 } |
| 316 } else { |
| 317 // Dot not preceeded by a slash, copy it literally. |
| 318 pathbuf[dest_i++] = pathbuf[src_i++]; |
| 319 } |
| 320 } else if (pathbuf[src_i] == '/') { |
| 321 if (src_i > 0 && pathbuf[src_i - 1] == '/') { |
| 322 // Two slashes in a row, skip over it. |
| 323 src_i++; |
| 324 } else { |
| 325 // Just one slash, copy it. |
| 326 pathbuf[dest_i++] = pathbuf[src_i++]; |
| 327 } |
| 328 } else { |
| 329 // Input nothing special, just copy it. |
| 330 pathbuf[dest_i++] = pathbuf[src_i++]; |
| 331 } |
| 332 } |
| 333 path->resize(dest_i); |
| 334 } |
| 335 |
| 336 void ConvertPathToSystem(std::string* path) { |
| 337 #if defined(OS_WIN) |
| 338 for (size_t i = 0; i < path->size(); i++) { |
| 339 if ((*path)[i] == '/') |
| 340 (*path)[i] = '\\'; |
| 341 } |
| 342 #endif |
| 343 } |
| 344 |
| 345 std::string PathToSystem(const std::string& path) { |
| 346 std::string ret(path); |
| 347 ConvertPathToSystem(&ret); |
| 348 return ret; |
| 349 } |
| 350 |
OLD | NEW |