| 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 |