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/files/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 const char* GetExtensionForOutputType(Target::OutputType type, 168 Settings::TargetOS os) { 169 switch (os) { 170 case Settings::MAC: 171 switch (type) { 172 case Target::EXECUTABLE: 173 return ""; 174 case Target::SHARED_LIBRARY: 175 return "dylib"; 176 case Target::STATIC_LIBRARY: 177 return "a"; 178 default: 179 NOTREACHED(); 180 } 181 break; 182 183 case Settings::WIN: 184 switch (type) { 185 case Target::EXECUTABLE: 186 return "exe"; 187 case Target::SHARED_LIBRARY: 188 return "dll.lib"; // Extension of import library. 189 case Target::STATIC_LIBRARY: 190 return "lib"; 191 default: 192 NOTREACHED(); 193 } 194 break; 195 196 case Settings::LINUX: 197 switch (type) { 198 case Target::EXECUTABLE: 199 return ""; 200 case Target::SHARED_LIBRARY: 201 return "so"; 202 case Target::STATIC_LIBRARY: 203 return "a"; 204 default: 205 NOTREACHED(); 206 } 207 break; 208 209 default: 210 NOTREACHED(); 211 } 212 return ""; 213 } 214 215 std::string FilePathToUTF8(const base::FilePath::StringType& str) { 216 #if defined(OS_WIN) 217 return base::WideToUTF8(str); 218 #else 219 return str; 220 #endif 221 } 222 223 base::FilePath UTF8ToFilePath(const base::StringPiece& sp) { 224 #if defined(OS_WIN) 225 return base::FilePath(base::UTF8ToWide(sp)); 226 #else 227 return base::FilePath(sp.as_string()); 228 #endif 229 } 230 231 size_t FindExtensionOffset(const std::string& path) { 232 for (int i = static_cast<int>(path.size()); i >= 0; i--) { 233 if (IsSlash(path[i])) 234 break; 235 if (path[i] == '.') 236 return i + 1; 237 } 238 return std::string::npos; 239 } 240 241 base::StringPiece FindExtension(const std::string* path) { 242 size_t extension_offset = FindExtensionOffset(*path); 243 if (extension_offset == std::string::npos) 244 return base::StringPiece(); 245 return base::StringPiece(&path->data()[extension_offset], 246 path->size() - extension_offset); 247 } 248 249 size_t FindFilenameOffset(const std::string& path) { 250 for (int i = static_cast<int>(path.size()) - 1; i >= 0; i--) { 251 if (IsSlash(path[i])) 252 return i + 1; 253 } 254 return 0; // No filename found means everything was the filename. 255 } 256 257 base::StringPiece FindFilename(const std::string* path) { 258 size_t filename_offset = FindFilenameOffset(*path); 259 if (filename_offset == 0) 260 return base::StringPiece(*path); // Everything is the file name. 261 return base::StringPiece(&(*path).data()[filename_offset], 262 path->size() - filename_offset); 263 } 264 265 base::StringPiece FindFilenameNoExtension(const std::string* path) { 266 if (path->empty()) 267 return base::StringPiece(); 268 size_t filename_offset = FindFilenameOffset(*path); 269 size_t extension_offset = FindExtensionOffset(*path); 270 271 size_t name_len; 272 if (extension_offset == std::string::npos) 273 name_len = path->size() - filename_offset; 274 else 275 name_len = extension_offset - filename_offset - 1; 276 277 return base::StringPiece(&(*path).data()[filename_offset], name_len); 278 } 279 280 void RemoveFilename(std::string* path) { 281 path->resize(FindFilenameOffset(*path)); 282 } 283 284 bool EndsWithSlash(const std::string& s) { 285 return !s.empty() && IsSlash(s[s.size() - 1]); 286 } 287 288 base::StringPiece FindDir(const std::string* path) { 289 size_t filename_offset = FindFilenameOffset(*path); 290 if (filename_offset == 0u) 291 return base::StringPiece(); 292 return base::StringPiece(path->data(), filename_offset); 293 } 294 295 base::StringPiece FindLastDirComponent(const SourceDir& dir) { 296 const std::string& dir_string = dir.value(); 297 298 if (dir_string.empty()) 299 return base::StringPiece(); 300 int cur = static_cast<int>(dir_string.size()) - 1; 301 DCHECK(dir_string[cur] == '/'); 302 int end = cur; 303 cur--; // Skip before the last slash. 304 305 for (; cur >= 0; cur--) { 306 if (dir_string[cur] == '/') 307 return base::StringPiece(&dir_string[cur + 1], end - cur - 1); 308 } 309 return base::StringPiece(&dir_string[0], end); 310 } 311 312 bool EnsureStringIsInOutputDir(const SourceDir& dir, 313 const std::string& str, 314 const ParseNode* origin, 315 Err* err) { 316 // This check will be wrong for all proper prefixes "e.g. "/output" will 317 // match "/out" but we don't really care since this is just a sanity check. 318 const std::string& dir_str = dir.value(); 319 if (str.compare(0, dir_str.length(), dir_str) == 0) 320 return true; // Output directory is hardcoded. 321 322 *err = Err(origin, "File is not inside output directory.", 323 "The given file should be in the output directory. Normally you would " 324 "specify\n\"$target_out_dir/foo\" or " 325 "\"$target_gen_dir/foo\". I interpreted this as\n\"" 326 + str + "\"."); 327 return false; 328 } 329 330 bool IsPathAbsolute(const base::StringPiece& path) { 331 if (path.empty()) 332 return false; 333 334 if (!IsSlash(path[0])) { 335 #if defined(OS_WIN) 336 // Check for Windows system paths like "C:\foo". 337 if (path.size() > 2 && path[1] == ':' && IsSlash(path[2])) 338 return true; 339 #endif 340 return false; // Doesn't begin with a slash, is relative. 341 } 342 343 // Double forward slash at the beginning means source-relative (we don't 344 // allow backslashes for denoting this). 345 if (path.size() > 1 && path[1] == '/') 346 return false; 347 348 return true; 349 } 350 351 bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root, 352 const base::StringPiece& path, 353 std::string* dest) { 354 DCHECK(IsPathAbsolute(source_root)); 355 DCHECK(IsPathAbsolute(path)); 356 357 dest->clear(); 358 359 if (source_root.size() > path.size()) 360 return false; // The source root is longer: the path can never be inside. 361 362 #if defined(OS_WIN) 363 // Source root should be canonical on Windows. Note that the initial slash 364 // must be forward slash, but that the other ones can be either forward or 365 // backward. 366 DCHECK(source_root.size() > 2 && source_root[0] != '/' && 367 source_root[1] == ':' && IsSlash(source_root[2])); 368 369 size_t after_common_index = std::string::npos; 370 if (DoesBeginWindowsDriveLetter(path)) { 371 // Handle "C:\foo" 372 if (AreAbsoluteWindowsPathsEqual(source_root, 373 path.substr(0, source_root.size()))) 374 after_common_index = source_root.size(); 375 else 376 return false; 377 } else if (path[0] == '/' && source_root.size() <= path.size() - 1 && 378 DoesBeginWindowsDriveLetter(path.substr(1))) { 379 // Handle "/C:/foo" 380 if (AreAbsoluteWindowsPathsEqual(source_root, 381 path.substr(1, source_root.size()))) 382 after_common_index = source_root.size() + 1; 383 else 384 return false; 385 } else { 386 return false; 387 } 388 389 // If we get here, there's a match and after_common_index identifies the 390 // part after it. 391 392 // The base may or may not have a trailing slash, so skip all slashes from 393 // the path after our prefix match. 394 size_t first_after_slash = after_common_index; 395 while (first_after_slash < path.size() && IsSlash(path[first_after_slash])) 396 first_after_slash++; 397 398 dest->assign("//"); // Result is source root relative. 399 dest->append(&path.data()[first_after_slash], 400 path.size() - first_after_slash); 401 return true; 402 403 #else 404 405 // On non-Windows this is easy. Since we know both are absolute, just do a 406 // prefix check. 407 if (path.substr(0, source_root.size()) == source_root) { 408 // The base may or may not have a trailing slash, so skip all slashes from 409 // the path after our prefix match. 410 size_t first_after_slash = source_root.size(); 411 while (first_after_slash < path.size() && IsSlash(path[first_after_slash])) 412 first_after_slash++; 413 414 dest->assign("//"); // Result is source root relative. 415 dest->append(&path.data()[first_after_slash], 416 path.size() - first_after_slash); 417 return true; 418 } 419 return false; 420 #endif 421 } 422 423 std::string InvertDir(const SourceDir& path) { 424 const std::string value = path.value(); 425 if (value.empty()) 426 return std::string(); 427 428 DCHECK(value[0] == '/'); 429 size_t begin_index = 1; 430 431 // If the input begins with two slashes, skip over both (this is a 432 // source-relative dir). These must be forward slashes only. 433 if (value.size() > 1 && value[1] == '/') 434 begin_index = 2; 435 436 std::string ret; 437 for (size_t i = begin_index; i < value.size(); i++) { 438 if (IsSlash(value[i])) 439 ret.append("../"); 440 } 441 return ret; 442 } 443 444 void NormalizePath(std::string* path) { 445 char* pathbuf = path->empty() ? NULL : &(*path)[0]; 446 447 // top_index is the first character we can modify in the path. Anything 448 // before this indicates where the path is relative to. 449 size_t top_index = 0; 450 bool is_relative = true; 451 if (!path->empty() && pathbuf[0] == '/') { 452 is_relative = false; 453 454 if (path->size() > 1 && pathbuf[1] == '/') { 455 // Two leading slashes, this is a path into the source dir. 456 top_index = 2; 457 } else { 458 // One leading slash, this is a system-absolute path. 459 top_index = 1; 460 } 461 } 462 463 size_t dest_i = top_index; 464 for (size_t src_i = top_index; src_i < path->size(); /* nothing */) { 465 if (pathbuf[src_i] == '.') { 466 if (src_i == 0 || IsSlash(pathbuf[src_i - 1])) { 467 // Slash followed by a dot, see if it's something special. 468 size_t consumed_len; 469 switch (ClassifyAfterDot(*path, src_i + 1, &consumed_len)) { 470 case NOT_A_DIRECTORY: 471 // Copy the dot to the output, it means nothing special. 472 pathbuf[dest_i++] = pathbuf[src_i++]; 473 break; 474 case DIRECTORY_CUR: 475 // Current directory, just skip the input. 476 src_i += consumed_len; 477 break; 478 case DIRECTORY_UP: 479 // Back up over previous directory component. If we're already 480 // at the top, preserve the "..". 481 if (dest_i > top_index) { 482 // The previous char was a slash, remove it. 483 dest_i--; 484 } 485 486 if (dest_i == top_index) { 487 if (is_relative) { 488 // We're already at the beginning of a relative input, copy the 489 // ".." and continue. We need the trailing slash if there was 490 // one before (otherwise we're at the end of the input). 491 pathbuf[dest_i++] = '.'; 492 pathbuf[dest_i++] = '.'; 493 if (consumed_len == 3) 494 pathbuf[dest_i++] = '/'; 495 496 // This also makes a new "root" that we can't delete by going 497 // up more levels. Otherwise "../.." would collapse to 498 // nothing. 499 top_index = dest_i; 500 } 501 // Otherwise we're at the beginning of an absolute path. Don't 502 // allow ".." to go up another level and just eat it. 503 } else { 504 // Just find the previous slash or the beginning of input. 505 while (dest_i > 0 && !IsSlash(pathbuf[dest_i - 1])) 506 dest_i--; 507 } 508 src_i += consumed_len; 509 } 510 } else { 511 // Dot not preceeded by a slash, copy it literally. 512 pathbuf[dest_i++] = pathbuf[src_i++]; 513 } 514 } else if (IsSlash(pathbuf[src_i])) { 515 if (src_i > 0 && IsSlash(pathbuf[src_i - 1])) { 516 // Two slashes in a row, skip over it. 517 src_i++; 518 } else { 519 // Just one slash, copy it, normalizing to foward slash. 520 pathbuf[dest_i] = '/'; 521 dest_i++; 522 src_i++; 523 } 524 } else { 525 // Input nothing special, just copy it. 526 pathbuf[dest_i++] = pathbuf[src_i++]; 527 } 528 } 529 path->resize(dest_i); 530 } 531 532 void ConvertPathToSystem(std::string* path) { 533 #if defined(OS_WIN) 534 for (size_t i = 0; i < path->size(); i++) { 535 if ((*path)[i] == '/') 536 (*path)[i] = '\\'; 537 } 538 #endif 539 } 540 541 std::string RebaseSourceAbsolutePath(const std::string& input, 542 const SourceDir& dest_dir) { 543 CHECK(input.size() >= 2 && input[0] == '/' && input[1] == '/') 544 << "Input to rebase isn't source-absolute: " << input; 545 CHECK(dest_dir.is_source_absolute()) 546 << "Dir to rebase to isn't source-absolute: " << dest_dir.value(); 547 548 const std::string& dest = dest_dir.value(); 549 550 // Skip the common prefixes of the source and dest as long as they end in 551 // a [back]slash. 552 size_t common_prefix_len = 2; // The beginning two "//" are always the same. 553 size_t max_common_length = std::min(input.size(), dest.size()); 554 for (size_t i = common_prefix_len; i < max_common_length; i++) { 555 if (IsSlash(input[i]) && IsSlash(dest[i])) 556 common_prefix_len = i + 1; 557 else if (input[i] != dest[i]) 558 break; 559 } 560 561 // Invert the dest dir starting from the end of the common prefix. 562 std::string ret; 563 for (size_t i = common_prefix_len; i < dest.size(); i++) { 564 if (IsSlash(dest[i])) 565 ret.append("../"); 566 } 567 568 // Append any remaining unique input. 569 ret.append(&input[common_prefix_len], input.size() - common_prefix_len); 570 571 // If the result is still empty, the paths are the same. 572 if (ret.empty()) 573 ret.push_back('.'); 574 575 return ret; 576 } 577 578 std::string DirectoryWithNoLastSlash(const SourceDir& dir) { 579 std::string ret; 580 581 if (dir.value().empty()) { 582 // Just keep input the same. 583 } else if (dir.value() == "/") { 584 ret.assign("/."); 585 } else if (dir.value() == "//") { 586 ret.assign("//."); 587 } else { 588 ret.assign(dir.value()); 589 ret.resize(ret.size() - 1); 590 } 591 return ret; 592 } 593 594 SourceDir SourceDirForPath(const base::FilePath& source_root, 595 const base::FilePath& path) { 596 std::vector<base::FilePath::StringType> source_comp = 597 GetPathComponents(source_root); 598 std::vector<base::FilePath::StringType> path_comp = 599 GetPathComponents(path); 600 601 // See if path is inside the source root by looking for each of source root's 602 // components at the beginning of path. 603 bool is_inside_source; 604 if (path_comp.size() < source_comp.size()) { 605 // Too small to fit. 606 is_inside_source = false; 607 } else { 608 is_inside_source = true; 609 for (size_t i = 0; i < source_comp.size(); i++) { 610 if (!FilesystemStringsEqual(source_comp[i], path_comp[i])) { 611 is_inside_source = false; 612 break; 613 } 614 } 615 } 616 617 std::string result_str; 618 size_t initial_path_comp_to_use; 619 if (is_inside_source) { 620 // Construct a source-relative path beginning in // and skip all of the 621 // shared directories. 622 result_str = "//"; 623 initial_path_comp_to_use = source_comp.size(); 624 } else { 625 // Not inside source code, construct a system-absolute path. 626 result_str = "/"; 627 initial_path_comp_to_use = 0; 628 } 629 630 for (size_t i = initial_path_comp_to_use; i < path_comp.size(); i++) { 631 result_str.append(FilePathToUTF8(path_comp[i])); 632 result_str.push_back('/'); 633 } 634 return SourceDir(result_str); 635 } 636 637 SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) { 638 base::FilePath cd; 639 base::GetCurrentDirectory(&cd); 640 return SourceDirForPath(source_root, cd); 641 } 642 643 std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) { 644 // The default toolchain has no subdir. 645 if (is_default) 646 return std::string(); 647 648 // For now just assume the toolchain name is always a valid dir name. We may 649 // want to clean up the in the future. 650 return toolchain_label.name() + "/"; 651 } 652 653 SourceDir GetToolchainOutputDir(const Settings* settings) { 654 return settings->toolchain_output_subdir().AsSourceDir( 655 settings->build_settings()); 656 } 657 658 SourceDir GetToolchainOutputDir(const BuildSettings* build_settings, 659 const Label& toolchain_label, bool is_default) { 660 std::string result = build_settings->build_dir().value(); 661 result.append(GetOutputSubdirName(toolchain_label, is_default)); 662 return SourceDir(SourceDir::SWAP_IN, &result); 663 } 664 665 SourceDir GetToolchainGenDir(const Settings* settings) { 666 return GetToolchainGenDirAsOutputFile(settings).AsSourceDir( 667 settings->build_settings()); 668 } 669 670 OutputFile GetToolchainGenDirAsOutputFile(const Settings* settings) { 671 OutputFile result(settings->toolchain_output_subdir()); 672 result.value().append("gen/"); 673 return result; 674 } 675 676 SourceDir GetToolchainGenDir(const BuildSettings* build_settings, 677 const Label& toolchain_label, bool is_default) { 678 std::string result = GetToolchainOutputDir( 679 build_settings, toolchain_label, is_default).value(); 680 result.append("gen/"); 681 return SourceDir(SourceDir::SWAP_IN, &result); 682 } 683 684 SourceDir GetOutputDirForSourceDir(const Settings* settings, 685 const SourceDir& source_dir) { 686 return GetOutputDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir( 687 settings->build_settings()); 688 } 689 690 OutputFile GetOutputDirForSourceDirAsOutputFile(const Settings* settings, 691 const SourceDir& source_dir) { 692 OutputFile result = settings->toolchain_output_subdir(); 693 result.value().append("obj/"); 694 695 if (source_dir.is_source_absolute()) { 696 // The source dir is source-absolute, so we trim off the two leading 697 // slashes to append to the toolchain object directory. 698 result.value().append(&source_dir.value()[2], 699 source_dir.value().size() - 2); 700 } 701 return result; 702 } 703 704 SourceDir GetGenDirForSourceDir(const Settings* settings, 705 const SourceDir& source_dir) { 706 return GetGenDirForSourceDirAsOutputFile(settings, source_dir).AsSourceDir( 707 settings->build_settings()); 708 } 709 710 OutputFile GetGenDirForSourceDirAsOutputFile(const Settings* settings, 711 const SourceDir& source_dir) { 712 OutputFile result = GetToolchainGenDirAsOutputFile(settings); 713 714 if (source_dir.is_source_absolute()) { 715 // The source dir should be source-absolute, so we trim off the two leading 716 // slashes to append to the toolchain object directory. 717 DCHECK(source_dir.is_source_absolute()); 718 result.value().append(&source_dir.value()[2], 719 source_dir.value().size() - 2); 720 } 721 return result; 722 } 723 724 SourceDir GetTargetOutputDir(const Target* target) { 725 return GetOutputDirForSourceDirAsOutputFile( 726 target->settings(), target->label().dir()).AsSourceDir( 727 target->settings()->build_settings()); 728 } 729 730 OutputFile GetTargetOutputDirAsOutputFile(const Target* target) { 731 return GetOutputDirForSourceDirAsOutputFile( 732 target->settings(), target->label().dir()); 733 } 734 735 SourceDir GetTargetGenDir(const Target* target) { 736 return GetTargetGenDirAsOutputFile(target).AsSourceDir( 737 target->settings()->build_settings()); 738 } 739 740 OutputFile GetTargetGenDirAsOutputFile(const Target* target) { 741 return GetGenDirForSourceDirAsOutputFile( 742 target->settings(), target->label().dir()); 743 } 744 745 SourceDir GetCurrentOutputDir(const Scope* scope) { 746 return GetOutputDirForSourceDirAsOutputFile( 747 scope->settings(), scope->GetSourceDir()).AsSourceDir( 748 scope->settings()->build_settings()); 749 } 750 751 SourceDir GetCurrentGenDir(const Scope* scope) { 752 return GetGenDirForSourceDir(scope->settings(), scope->GetSourceDir()); 753 } 754