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