| author | |
| committer | |
| log | 81d2135ca6ebd71b8c121a19957c8fbf7f87125b |
| tree | e6ed1b33a6a02479e661b11103c5f7b8342d2345 |
| parent | 32ce2f91a92c23d46c6836a6dd68ae0f08bb04c5 |
| parent | 984acae12d0dd4e24577c485b90b313c5c2d2089 |
| signature |
Cache: introduce prefixes to manifests15 files changed, 400 insertions(+), 321 deletions(-)
ci/linux/build-aarch64.sh+1-4| ... | @@ -63,7 +63,4 @@ stage3-release/bin/zig build test docs \ | ... | @@ -63,7 +63,4 @@ stage3-release/bin/zig build test docs \ |
| 63 | tidy --drop-empty-elements no -qe ../zig-cache/langref.html | 63 | tidy --drop-empty-elements no -qe ../zig-cache/langref.html |
| 64 | 64 | ||
| 65 | # Produce the experimental std lib documentation. | 65 | # Produce the experimental std lib documentation. |
| 66 | stage3-release/bin/zig test ../lib/std/std.zig \ | 66 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| 67 | -femit-docs \ | ||
| 68 | -fno-emit-bin \ | ||
| 69 | --zig-lib-dir "$(pwd)/../lib" |
ci/linux/build-x86_64-debug.sh+1-4| ... | @@ -67,7 +67,4 @@ stage3-debug/bin/zig build test \ | ... | @@ -67,7 +67,4 @@ stage3-debug/bin/zig build test \ |
| 67 | #tidy --drop-empty-elements no -qe ../zig-cache/langref.html | 67 | #tidy --drop-empty-elements no -qe ../zig-cache/langref.html |
| 68 | 68 | ||
| 69 | # Produce the experimental std lib documentation. | 69 | # Produce the experimental std lib documentation. |
| 70 | stage3-debug/bin/zig test ../lib/std/std.zig \ | 70 | stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| 71 | -femit-docs \ | ||
| 72 | -fno-emit-bin \ | ||
| 73 | --zig-lib-dir "$(pwd)/../lib" |
ci/linux/build-x86_64-release.sh+1-4| ... | @@ -63,10 +63,7 @@ stage3-release/bin/zig build test docs \ | ... | @@ -63,10 +63,7 @@ stage3-release/bin/zig build test docs \ |
| 63 | tidy --drop-empty-elements no -qe ../zig-cache/langref.html | 63 | tidy --drop-empty-elements no -qe ../zig-cache/langref.html |
| 64 | 64 | ||
| 65 | # Produce the experimental std lib documentation. | 65 | # Produce the experimental std lib documentation. |
| 66 | stage3-release/bin/zig test ../lib/std/std.zig \ | 66 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| 67 | -femit-docs \ | ||
| 68 | -fno-emit-bin \ | ||
| 69 | --zig-lib-dir "$(pwd)/../lib" | ||
| 70 | 67 | ||
| 71 | stage3-release/bin/zig build \ | 68 | stage3-release/bin/zig build \ |
| 72 | --prefix stage4-release \ | 69 | --prefix stage4-release \ |
ci/macos/build-aarch64.sh+1-5| ... | @@ -44,8 +44,4 @@ stage3-release/bin/zig build test docs \ | ... | @@ -44,8 +44,4 @@ stage3-release/bin/zig build test docs \ |
| 44 | --search-prefix "$PREFIX" | 44 | --search-prefix "$PREFIX" |
| 45 | 45 | ||
| 46 | # Produce the experimental std lib documentation. | 46 | # Produce the experimental std lib documentation. |
| 47 | mkdir -p "stage3-release/doc/std" | 47 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| 48 | stage3-release/bin/zig test "$(pwd)/../lib/std/std.zig" \ | ||
| 49 | --zig-lib-dir "$(pwd)/../lib" \ | ||
| 50 | -femit-docs="$(pwd)/stage3-release/doc/std" \ | ||
| 51 | -fno-emit-bin |
ci/macos/build-x86_64.sh+1-5| ... | @@ -51,8 +51,4 @@ stage3-release/bin/zig build test docs \ | ... | @@ -51,8 +51,4 @@ stage3-release/bin/zig build test docs \ |
| 51 | --search-prefix "$PREFIX" | 51 | --search-prefix "$PREFIX" |
| 52 | 52 | ||
| 53 | # Produce the experimental std lib documentation. | 53 | # Produce the experimental std lib documentation. |
| 54 | mkdir -p "stage3-release/doc/std" | 54 | stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib |
| 55 | stage3-release/bin/zig test "$(pwd)/../lib/std/std.zig" \ | ||
| 56 | --zig-lib-dir "$(pwd)/../lib" \ | ||
| 57 | -femit-docs="$(pwd)/stage3-release/doc/std" \ | ||
| 58 | -fno-emit-bin |
ci/windows/build.ps1+1-3| ... | @@ -53,11 +53,9 @@ Write-Output " zig build test docs..." | ... | @@ -53,11 +53,9 @@ Write-Output " zig build test docs..." |
| 53 | CheckLastExitCode | 53 | CheckLastExitCode |
| 54 | 54 | ||
| 55 | # Produce the experimental std lib documentation. | 55 | # Produce the experimental std lib documentation. |
| 56 | mkdir "$ZIGINSTALLDIR\doc\std" -force | ||
| 57 | |||
| 58 | Write-Output "zig test std/std.zig..." | 56 | Write-Output "zig test std/std.zig..." |
| 59 | 57 | ||
| 60 | & "$ZIGINSTALLDIR\bin\zig.exe" test "$ZIGLIBDIR\std\std.zig" ` | 58 | & "$ZIGINSTALLDIR\bin\zig.exe" test "$ZIGLIBDIR\std\std.zig" ` |
| 61 | --zig-lib-dir "$ZIGLIBDIR" ` | 59 | --zig-lib-dir "$ZIGLIBDIR" ` |
| 62 | -femit-docs="$ZIGINSTALLDIR\doc\std" ` | 60 | -femit-docs ` |
| 63 | -fno-emit-bin | 61 | -fno-emit-bin |
lib/std/fs/path.zig+179-211| ... | @@ -467,55 +467,49 @@ pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8 { | ... | @@ -467,55 +467,49 @@ pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8 { |
| 467 | /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. | 467 | /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. |
| 468 | /// Note: all usage of this function should be audited due to the existence of symlinks. | 468 | /// Note: all usage of this function should be audited due to the existence of symlinks. |
| 469 | /// Without performing actual syscalls, resolving `..` could be incorrect. | 469 | /// Without performing actual syscalls, resolving `..` could be incorrect. |
| 470 | /// This API may break in the future: https://github.com/ziglang/zig/issues/13613 | ||
| 470 | pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { | 471 | pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { |
| 471 | if (paths.len == 0) { | 472 | assert(paths.len > 0); |
| 472 | assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd | ||
| 473 | return process.getCwdAlloc(allocator); | ||
| 474 | } | ||
| 475 | 473 | ||
| 476 | // determine which disk designator we will result with, if any | 474 | // determine which disk designator we will result with, if any |
| 477 | var result_drive_buf = "_:".*; | 475 | var result_drive_buf = "_:".*; |
| 478 | var result_disk_designator: []const u8 = ""; | 476 | var disk_designator: []const u8 = ""; |
| 479 | var have_drive_kind = WindowsPath.Kind.None; | 477 | var drive_kind = WindowsPath.Kind.None; |
| 480 | var have_abs_path = false; | 478 | var have_abs_path = false; |
| 481 | var first_index: usize = 0; | 479 | var first_index: usize = 0; |
| 482 | var max_size: usize = 0; | ||
| 483 | for (paths) |p, i| { | 480 | for (paths) |p, i| { |
| 484 | const parsed = windowsParsePath(p); | 481 | const parsed = windowsParsePath(p); |
| 485 | if (parsed.is_abs) { | 482 | if (parsed.is_abs) { |
| 486 | have_abs_path = true; | 483 | have_abs_path = true; |
| 487 | first_index = i; | 484 | first_index = i; |
| 488 | max_size = result_disk_designator.len; | ||
| 489 | } | 485 | } |
| 490 | switch (parsed.kind) { | 486 | switch (parsed.kind) { |
| 491 | WindowsPath.Kind.Drive => { | 487 | .Drive => { |
| 492 | result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]); | 488 | result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]); |
| 493 | result_disk_designator = result_drive_buf[0..]; | 489 | disk_designator = result_drive_buf[0..]; |
| 494 | have_drive_kind = WindowsPath.Kind.Drive; | 490 | drive_kind = WindowsPath.Kind.Drive; |
| 495 | }, | 491 | }, |
| 496 | WindowsPath.Kind.NetworkShare => { | 492 | .NetworkShare => { |
| 497 | result_disk_designator = parsed.disk_designator; | 493 | disk_designator = parsed.disk_designator; |
| 498 | have_drive_kind = WindowsPath.Kind.NetworkShare; | 494 | drive_kind = WindowsPath.Kind.NetworkShare; |
| 499 | }, | 495 | }, |
| 500 | WindowsPath.Kind.None => {}, | 496 | .None => {}, |
| 501 | } | 497 | } |
| 502 | max_size += p.len + 1; | ||
| 503 | } | 498 | } |
| 504 | 499 | ||
| 505 | // if we will result with a disk designator, loop again to determine | 500 | // if we will result with a disk designator, loop again to determine |
| 506 | // which is the last time the disk designator is absolutely specified, if any | 501 | // which is the last time the disk designator is absolutely specified, if any |
| 507 | // and count up the max bytes for paths related to this disk designator | 502 | // and count up the max bytes for paths related to this disk designator |
| 508 | if (have_drive_kind != WindowsPath.Kind.None) { | 503 | if (drive_kind != WindowsPath.Kind.None) { |
| 509 | have_abs_path = false; | 504 | have_abs_path = false; |
| 510 | first_index = 0; | 505 | first_index = 0; |
| 511 | max_size = result_disk_designator.len; | ||
| 512 | var correct_disk_designator = false; | 506 | var correct_disk_designator = false; |
| 513 | 507 | ||
| 514 | for (paths) |p, i| { | 508 | for (paths) |p, i| { |
| 515 | const parsed = windowsParsePath(p); | 509 | const parsed = windowsParsePath(p); |
| 516 | if (parsed.kind != WindowsPath.Kind.None) { | 510 | if (parsed.kind != WindowsPath.Kind.None) { |
| 517 | if (parsed.kind == have_drive_kind) { | 511 | if (parsed.kind == drive_kind) { |
| 518 | correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); | 512 | correct_disk_designator = compareDiskDesignators(drive_kind, disk_designator, parsed.disk_designator); |
| 519 | } else { | 513 | } else { |
| 520 | continue; | 514 | continue; |
| 521 | } | 515 | } |
| ... | @@ -525,92 +519,51 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { | ... | @@ -525,92 +519,51 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { |
| 525 | } | 519 | } |
| 526 | if (parsed.is_abs) { | 520 | if (parsed.is_abs) { |
| 527 | first_index = i; | 521 | first_index = i; |
| 528 | max_size = result_disk_designator.len; | ||
| 529 | have_abs_path = true; | 522 | have_abs_path = true; |
| 530 | } | 523 | } |
| 531 | max_size += p.len + 1; | ||
| 532 | } | 524 | } |
| 533 | } | 525 | } |
| 534 | 526 | ||
| 535 | // Allocate result and fill in the disk designator, calling getCwd if we have to. | 527 | // Allocate result and fill in the disk designator. |
| 536 | var result: []u8 = undefined; | 528 | var result = std.ArrayList(u8).init(allocator); |
| 537 | var result_index: usize = 0; | 529 | defer result.deinit(); |
| 538 | |||
| 539 | if (have_abs_path) { | ||
| 540 | switch (have_drive_kind) { | ||
| 541 | WindowsPath.Kind.Drive => { | ||
| 542 | result = try allocator.alloc(u8, max_size); | ||
| 543 | 530 | ||
| 544 | mem.copy(u8, result, result_disk_designator); | 531 | const disk_designator_len: usize = l: { |
| 545 | result_index += result_disk_designator.len; | 532 | if (!have_abs_path) break :l 0; |
| 533 | switch (drive_kind) { | ||
| 534 | .Drive => { | ||
| 535 | try result.appendSlice(disk_designator); | ||
| 536 | break :l disk_designator.len; | ||
| 546 | }, | 537 | }, |
| 547 | WindowsPath.Kind.NetworkShare => { | 538 | .NetworkShare => { |
| 548 | result = try allocator.alloc(u8, max_size); | ||
| 549 | var it = mem.tokenize(u8, paths[first_index], "/\\"); | 539 | var it = mem.tokenize(u8, paths[first_index], "/\\"); |
| 550 | const server_name = it.next().?; | 540 | const server_name = it.next().?; |
| 551 | const other_name = it.next().?; | 541 | const other_name = it.next().?; |
| 552 | 542 | ||
| 553 | result[result_index] = '\\'; | 543 | try result.ensureUnusedCapacity(2 + 1 + server_name.len + other_name.len); |
| 554 | result_index += 1; | 544 | result.appendSliceAssumeCapacity("\\\\"); |
| 555 | result[result_index] = '\\'; | 545 | result.appendSliceAssumeCapacity(server_name); |
| 556 | result_index += 1; | 546 | result.appendAssumeCapacity('\\'); |
| 557 | mem.copy(u8, result[result_index..], server_name); | 547 | result.appendSliceAssumeCapacity(other_name); |
| 558 | result_index += server_name.len; | 548 | |
| 559 | result[result_index] = '\\'; | 549 | break :l result.items.len; |
| 560 | result_index += 1; | ||
| 561 | mem.copy(u8, result[result_index..], other_name); | ||
| 562 | result_index += other_name.len; | ||
| 563 | |||
| 564 | result_disk_designator = result[0..result_index]; | ||
| 565 | }, | 550 | }, |
| 566 | WindowsPath.Kind.None => { | 551 | .None => { |
| 567 | assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd | 552 | break :l 1; |
| 568 | const cwd = try process.getCwdAlloc(allocator); | ||
| 569 | defer allocator.free(cwd); | ||
| 570 | const parsed_cwd = windowsParsePath(cwd); | ||
| 571 | result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1); | ||
| 572 | mem.copy(u8, result, parsed_cwd.disk_designator); | ||
| 573 | result_index += parsed_cwd.disk_designator.len; | ||
| 574 | result_disk_designator = result[0..parsed_cwd.disk_designator.len]; | ||
| 575 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { | ||
| 576 | result[0] = ascii.toUpper(result[0]); | ||
| 577 | } | ||
| 578 | have_drive_kind = parsed_cwd.kind; | ||
| 579 | }, | 553 | }, |
| 580 | } | 554 | } |
| 581 | } else { | 555 | }; |
| 582 | assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd | ||
| 583 | // TODO call get cwd for the result_disk_designator instead of the global one | ||
| 584 | const cwd = try process.getCwdAlloc(allocator); | ||
| 585 | defer allocator.free(cwd); | ||
| 586 | |||
| 587 | result = try allocator.alloc(u8, max_size + cwd.len + 1); | ||
| 588 | |||
| 589 | mem.copy(u8, result, cwd); | ||
| 590 | result_index += cwd.len; | ||
| 591 | const parsed_cwd = windowsParsePath(result[0..result_index]); | ||
| 592 | result_disk_designator = parsed_cwd.disk_designator; | ||
| 593 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { | ||
| 594 | result[0] = ascii.toUpper(result[0]); | ||
| 595 | // Remove the trailing slash if present, eg. if the cwd is a root | ||
| 596 | // directory. | ||
| 597 | if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) { | ||
| 598 | result_index -= 1; | ||
| 599 | } | ||
| 600 | } | ||
| 601 | have_drive_kind = parsed_cwd.kind; | ||
| 602 | } | ||
| 603 | errdefer allocator.free(result); | ||
| 604 | 556 | ||
| 605 | // Now we know the disk designator to use, if any, and what kind it is. And our result | ||
| 606 | // is big enough to append all the paths to. | ||
| 607 | var correct_disk_designator = true; | 557 | var correct_disk_designator = true; |
| 558 | var negative_count: usize = 0; | ||
| 559 | |||
| 608 | for (paths[first_index..]) |p| { | 560 | for (paths[first_index..]) |p| { |
| 609 | const parsed = windowsParsePath(p); | 561 | const parsed = windowsParsePath(p); |
| 610 | 562 | ||
| 611 | if (parsed.kind != WindowsPath.Kind.None) { | 563 | if (parsed.kind != .None) { |
| 612 | if (parsed.kind == have_drive_kind) { | 564 | if (parsed.kind == drive_kind) { |
| 613 | correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); | 565 | const dd = result.items[0..disk_designator_len]; |
| 566 | correct_disk_designator = compareDiskDesignators(drive_kind, dd, parsed.disk_designator); | ||
| 614 | } else { | 567 | } else { |
| 615 | continue; | 568 | continue; |
| 616 | } | 569 | } |
| ... | @@ -619,154 +572,167 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { | ... | @@ -619,154 +572,167 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { |
| 619 | continue; | 572 | continue; |
| 620 | } | 573 | } |
| 621 | var it = mem.tokenize(u8, p[parsed.disk_designator.len..], "/\\"); | 574 | var it = mem.tokenize(u8, p[parsed.disk_designator.len..], "/\\"); |
| 622 | while (it.next()) |component| { | 575 | component: while (it.next()) |component| { |
| 623 | if (mem.eql(u8, component, ".")) { | 576 | if (mem.eql(u8, component, ".")) { |
| 624 | continue; | 577 | continue; |
| 625 | } else if (mem.eql(u8, component, "..")) { | 578 | } else if (mem.eql(u8, component, "..")) { |
| 626 | while (true) { | 579 | while (true) { |
| 627 | if (result_index == 0 or result_index == result_disk_designator.len) | 580 | if (result.items.len == 0) { |
| 628 | break; | 581 | negative_count += 1; |
| 629 | result_index -= 1; | 582 | continue :component; |
| 630 | if (result[result_index] == '\\' or result[result_index] == '/') | 583 | } |
| 584 | if (result.items.len == disk_designator_len) { | ||
| 631 | break; | 585 | break; |
| 586 | } | ||
| 587 | const end_with_sep = switch (result.items[result.items.len - 1]) { | ||
| 588 | '\\', '/' => true, | ||
| 589 | else => false, | ||
| 590 | }; | ||
| 591 | result.items.len -= 1; | ||
| 592 | if (end_with_sep) break; | ||
| 632 | } | 593 | } |
| 594 | } else if (!have_abs_path and result.items.len == 0) { | ||
| 595 | try result.appendSlice(component); | ||
| 633 | } else { | 596 | } else { |
| 634 | result[result_index] = sep_windows; | 597 | try result.ensureUnusedCapacity(1 + component.len); |
| 635 | result_index += 1; | 598 | result.appendAssumeCapacity('\\'); |
| 636 | mem.copy(u8, result[result_index..], component); | 599 | result.appendSliceAssumeCapacity(component); |
| 637 | result_index += component.len; | ||
| 638 | } | 600 | } |
| 639 | } | 601 | } |
| 640 | } | 602 | } |
| 641 | 603 | ||
| 642 | if (result_index == result_disk_designator.len) { | 604 | if (disk_designator_len != 0 and result.items.len == disk_designator_len) { |
| 643 | result[result_index] = '\\'; | 605 | try result.append('\\'); |
| 644 | result_index += 1; | 606 | return result.toOwnedSlice(); |
| 607 | } | ||
| 608 | |||
| 609 | if (result.items.len == 0) { | ||
| 610 | if (negative_count == 0) { | ||
| 611 | return allocator.dupe(u8, "."); | ||
| 612 | } else { | ||
| 613 | const real_result = try allocator.alloc(u8, 3 * negative_count - 1); | ||
| 614 | var count = negative_count - 1; | ||
| 615 | var i: usize = 0; | ||
| 616 | while (count > 0) : (count -= 1) { | ||
| 617 | real_result[i..][0..3].* = "..\\".*; | ||
| 618 | i += 3; | ||
| 619 | } | ||
| 620 | real_result[i..][0..2].* = "..".*; | ||
| 621 | return real_result; | ||
| 622 | } | ||
| 645 | } | 623 | } |
| 646 | 624 | ||
| 647 | return allocator.shrink(result, result_index); | 625 | if (negative_count == 0) { |
| 626 | return result.toOwnedSlice(); | ||
| 627 | } else { | ||
| 628 | const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len); | ||
| 629 | var count = negative_count; | ||
| 630 | var i: usize = 0; | ||
| 631 | while (count > 0) : (count -= 1) { | ||
| 632 | real_result[i..][0..3].* = "..\\".*; | ||
| 633 | i += 3; | ||
| 634 | } | ||
| 635 | mem.copy(u8, real_result[i..], result.items); | ||
| 636 | return real_result; | ||
| 637 | } | ||
| 648 | } | 638 | } |
| 649 | 639 | ||
| 650 | /// This function is like a series of `cd` statements executed one after another. | 640 | /// This function is like a series of `cd` statements executed one after another. |
| 651 | /// It resolves "." and "..". | 641 | /// It resolves "." and "..". |
| 652 | /// The result does not have a trailing path separator. | 642 | /// The result does not have a trailing path separator. |
| 653 | /// If all paths are relative it uses the current working directory as a starting point. | 643 | /// This function does not perform any syscalls. Executing this series of path |
| 654 | /// Note: all usage of this function should be audited due to the existence of symlinks. | 644 | /// lookups on the actual filesystem may produce different results due to |
| 655 | /// Without performing actual syscalls, resolving `..` could be incorrect. | 645 | /// symlinks. |
| 656 | pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 { | 646 | pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) Allocator.Error![]u8 { |
| 657 | if (paths.len == 0) { | 647 | assert(paths.len > 0); |
| 658 | assert(native_os != .windows); // resolvePosix called on windows can't use getCwd | ||
| 659 | return process.getCwdAlloc(allocator); | ||
| 660 | } | ||
| 661 | 648 | ||
| 662 | var first_index: usize = 0; | 649 | var result = std.ArrayList(u8).init(allocator); |
| 663 | var have_abs = false; | 650 | defer result.deinit(); |
| 664 | var max_size: usize = 0; | ||
| 665 | for (paths) |p, i| { | ||
| 666 | if (isAbsolutePosix(p)) { | ||
| 667 | first_index = i; | ||
| 668 | have_abs = true; | ||
| 669 | max_size = 0; | ||
| 670 | } | ||
| 671 | max_size += p.len + 1; | ||
| 672 | } | ||
| 673 | |||
| 674 | var result: []u8 = undefined; | ||
| 675 | var result_index: usize = 0; | ||
| 676 | 651 | ||
| 677 | if (have_abs) { | 652 | var negative_count: usize = 0; |
| 678 | result = try allocator.alloc(u8, max_size); | 653 | var is_abs = false; |
| 679 | } else { | ||
| 680 | assert(native_os != .windows); // resolvePosix called on windows can't use getCwd | ||
| 681 | const cwd = try process.getCwdAlloc(allocator); | ||
| 682 | defer allocator.free(cwd); | ||
| 683 | result = try allocator.alloc(u8, max_size + cwd.len + 1); | ||
| 684 | mem.copy(u8, result, cwd); | ||
| 685 | result_index += cwd.len; | ||
| 686 | } | ||
| 687 | errdefer allocator.free(result); | ||
| 688 | 654 | ||
| 689 | for (paths[first_index..]) |p| { | 655 | for (paths) |p| { |
| 656 | if (isAbsolutePosix(p)) { | ||
| 657 | is_abs = true; | ||
| 658 | negative_count = 0; | ||
| 659 | result.clearRetainingCapacity(); | ||
| 660 | } | ||
| 690 | var it = mem.tokenize(u8, p, "/"); | 661 | var it = mem.tokenize(u8, p, "/"); |
| 691 | while (it.next()) |component| { | 662 | component: while (it.next()) |component| { |
| 692 | if (mem.eql(u8, component, ".")) { | 663 | if (mem.eql(u8, component, ".")) { |
| 693 | continue; | 664 | continue; |
| 694 | } else if (mem.eql(u8, component, "..")) { | 665 | } else if (mem.eql(u8, component, "..")) { |
| 695 | while (true) { | 666 | while (true) { |
| 696 | if (result_index == 0) | 667 | if (result.items.len == 0) { |
| 697 | break; | 668 | negative_count += @boolToInt(!is_abs); |
| 698 | result_index -= 1; | 669 | continue :component; |
| 699 | if (result[result_index] == '/') | 670 | } |
| 700 | break; | 671 | const ends_with_slash = result.items[result.items.len - 1] == '/'; |
| 672 | result.items.len -= 1; | ||
| 673 | if (ends_with_slash) break; | ||
| 701 | } | 674 | } |
| 675 | } else if (result.items.len > 0 or is_abs) { | ||
| 676 | try result.ensureUnusedCapacity(1 + component.len); | ||
| 677 | result.appendAssumeCapacity('/'); | ||
| 678 | result.appendSliceAssumeCapacity(component); | ||
| 702 | } else { | 679 | } else { |
| 703 | result[result_index] = '/'; | 680 | try result.appendSlice(component); |
| 704 | result_index += 1; | ||
| 705 | mem.copy(u8, result[result_index..], component); | ||
| 706 | result_index += component.len; | ||
| 707 | } | 681 | } |
| 708 | } | 682 | } |
| 709 | } | 683 | } |
| 710 | 684 | ||
| 711 | if (result_index == 0) { | 685 | if (result.items.len == 0) { |
| 712 | result[0] = '/'; | 686 | if (is_abs) { |
| 713 | result_index += 1; | 687 | return allocator.dupe(u8, "/"); |
| 688 | } | ||
| 689 | if (negative_count == 0) { | ||
| 690 | return allocator.dupe(u8, "."); | ||
| 691 | } else { | ||
| 692 | const real_result = try allocator.alloc(u8, 3 * negative_count - 1); | ||
| 693 | var count = negative_count - 1; | ||
| 694 | var i: usize = 0; | ||
| 695 | while (count > 0) : (count -= 1) { | ||
| 696 | real_result[i..][0..3].* = "../".*; | ||
| 697 | i += 3; | ||
| 698 | } | ||
| 699 | real_result[i..][0..2].* = "..".*; | ||
| 700 | return real_result; | ||
| 701 | } | ||
| 714 | } | 702 | } |
| 715 | 703 | ||
| 716 | return allocator.shrink(result, result_index); | 704 | if (negative_count == 0) { |
| 705 | return result.toOwnedSlice(); | ||
| 706 | } else { | ||
| 707 | const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len); | ||
| 708 | var count = negative_count; | ||
| 709 | var i: usize = 0; | ||
| 710 | while (count > 0) : (count -= 1) { | ||
| 711 | real_result[i..][0..3].* = "../".*; | ||
| 712 | i += 3; | ||
| 713 | } | ||
| 714 | mem.copy(u8, real_result[i..], result.items); | ||
| 715 | return real_result; | ||
| 716 | } | ||
| 717 | } | 717 | } |
| 718 | 718 | ||
| 719 | test "resolve" { | 719 | test "resolve" { |
| 720 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 720 | try testResolveWindows(&[_][]const u8{ "a\\b\\c\\", "..\\..\\.." }, ".."); |
| 721 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); | 721 | try testResolveWindows(&[_][]const u8{"."}, "."); |
| 722 | 722 | ||
| 723 | const cwd = try process.getCwdAlloc(testing.allocator); | 723 | try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, ".."); |
| 724 | defer testing.allocator.free(cwd); | 724 | try testResolvePosix(&[_][]const u8{"."}, "."); |
| 725 | if (native_os == .windows) { | ||
| 726 | if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { | ||
| 727 | cwd[0] = ascii.toUpper(cwd[0]); | ||
| 728 | } | ||
| 729 | try testResolveWindows(&[_][]const u8{"."}, cwd); | ||
| 730 | } else { | ||
| 731 | try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, cwd); | ||
| 732 | try testResolvePosix(&[_][]const u8{"."}, cwd); | ||
| 733 | } | ||
| 734 | } | 725 | } |
| 735 | 726 | ||
| 736 | test "resolveWindows" { | 727 | test "resolveWindows" { |
| 737 | if (builtin.target.cpu.arch == .aarch64) { | 728 | try testResolveWindows( |
| 738 | // TODO https://github.com/ziglang/zig/issues/3288 | 729 | &[_][]const u8{ "Z:\\", "/usr/local", "lib\\zig\\std\\array_list.zig" }, |
| 739 | return error.SkipZigTest; | 730 | "Z:\\usr\\local\\lib\\zig\\std\\array_list.zig", |
| 740 | } | 731 | ); |
| 741 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 732 | try testResolveWindows( |
| 742 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); | 733 | &[_][]const u8{ "z:\\", "usr/local", "lib\\zig" }, |
| 743 | if (native_os == .windows) { | 734 | "Z:\\usr\\local\\lib\\zig", |
| 744 | const cwd = try process.getCwdAlloc(testing.allocator); | 735 | ); |
| 745 | defer testing.allocator.free(cwd); | ||
| 746 | const parsed_cwd = windowsParsePath(cwd); | ||
| 747 | { | ||
| 748 | const expected = try join(testing.allocator, &[_][]const u8{ | ||
| 749 | parsed_cwd.disk_designator, | ||
| 750 | "usr\\local\\lib\\zig\\std\\array_list.zig", | ||
| 751 | }); | ||
| 752 | defer testing.allocator.free(expected); | ||
| 753 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { | ||
| 754 | expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]); | ||
| 755 | } | ||
| 756 | try testResolveWindows(&[_][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }, expected); | ||
| 757 | } | ||
| 758 | { | ||
| 759 | const expected = try join(testing.allocator, &[_][]const u8{ | ||
| 760 | cwd, | ||
| 761 | "usr\\local\\lib\\zig", | ||
| 762 | }); | ||
| 763 | defer testing.allocator.free(expected); | ||
| 764 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { | ||
| 765 | expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]); | ||
| 766 | } | ||
| 767 | try testResolveWindows(&[_][]const u8{ "usr/local", "lib\\zig" }, expected); | ||
| 768 | } | ||
| 769 | } | ||
| 770 | 736 | ||
| 771 | try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok"); | 737 | try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok"); |
| 772 | try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a"); | 738 | try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a"); |
| ... | @@ -781,12 +747,12 @@ test "resolveWindows" { | ... | @@ -781,12 +747,12 @@ test "resolveWindows" { |
| 781 | try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\"); | 747 | try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\"); |
| 782 | try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir"); | 748 | try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir"); |
| 783 | try testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }, "C:\\foo\\tmp.3\\cycles\\root.js"); | 749 | try testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }, "C:\\foo\\tmp.3\\cycles\\root.js"); |
| 750 | |||
| 751 | // Keep relative paths relative. | ||
| 752 | try testResolveWindows(&[_][]const u8{"a/b"}, "a\\b"); | ||
| 784 | } | 753 | } |
| 785 | 754 | ||
| 786 | test "resolvePosix" { | 755 | test "resolvePosix" { |
| 787 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | ||
| 788 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); | ||
| 789 | |||
| 790 | try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); | 756 | try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); |
| 791 | try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e"); | 757 | try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e"); |
| 792 | try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a"); | 758 | try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a"); |
| ... | @@ -797,18 +763,21 @@ test "resolvePosix" { | ... | @@ -797,18 +763,21 @@ test "resolvePosix" { |
| 797 | try testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }, "/file"); | 763 | try testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }, "/file"); |
| 798 | try testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }, "/absolute"); | 764 | try testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }, "/absolute"); |
| 799 | try testResolvePosix(&[_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }, "/foo/tmp.3/cycles/root.js"); | 765 | try testResolvePosix(&[_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }, "/foo/tmp.3/cycles/root.js"); |
| 766 | |||
| 767 | // Keep relative paths relative. | ||
| 768 | try testResolvePosix(&[_][]const u8{"a/b"}, "a/b"); | ||
| 800 | } | 769 | } |
| 801 | 770 | ||
| 802 | fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void { | 771 | fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void { |
| 803 | const actual = try resolveWindows(testing.allocator, paths); | 772 | const actual = try resolveWindows(testing.allocator, paths); |
| 804 | defer testing.allocator.free(actual); | 773 | defer testing.allocator.free(actual); |
| 805 | try testing.expect(mem.eql(u8, actual, expected)); | 774 | try testing.expectEqualStrings(expected, actual); |
| 806 | } | 775 | } |
| 807 | 776 | ||
| 808 | fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void { | 777 | fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void { |
| 809 | const actual = try resolvePosix(testing.allocator, paths); | 778 | const actual = try resolvePosix(testing.allocator, paths); |
| 810 | defer testing.allocator.free(actual); | 779 | defer testing.allocator.free(actual); |
| 811 | try testing.expect(mem.eql(u8, actual, expected)); | 780 | try testing.expectEqualStrings(expected, actual); |
| 812 | } | 781 | } |
| 813 | 782 | ||
| 814 | /// Strip the last component from a file path. | 783 | /// Strip the last component from a file path. |
| ... | @@ -1089,13 +1058,15 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! | ... | @@ -1089,13 +1058,15 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1089 | if (parsed_from.kind != parsed_to.kind) { | 1058 | if (parsed_from.kind != parsed_to.kind) { |
| 1090 | break :x true; | 1059 | break :x true; |
| 1091 | } else switch (parsed_from.kind) { | 1060 | } else switch (parsed_from.kind) { |
| 1092 | WindowsPath.Kind.NetworkShare => { | 1061 | .NetworkShare => { |
| 1093 | break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator); | 1062 | break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator); |
| 1094 | }, | 1063 | }, |
| 1095 | WindowsPath.Kind.Drive => { | 1064 | .Drive => { |
| 1096 | break :x ascii.toUpper(parsed_from.disk_designator[0]) != ascii.toUpper(parsed_to.disk_designator[0]); | 1065 | break :x ascii.toUpper(parsed_from.disk_designator[0]) != ascii.toUpper(parsed_to.disk_designator[0]); |
| 1097 | }, | 1066 | }, |
| 1098 | else => unreachable, | 1067 | .None => { |
| 1068 | break :x false; | ||
| 1069 | }, | ||
| 1099 | } | 1070 | } |
| 1100 | }; | 1071 | }; |
| 1101 | 1072 | ||
| ... | @@ -1194,13 +1165,6 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![] | ... | @@ -1194,13 +1165,6 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![] |
| 1194 | } | 1165 | } |
| 1195 | 1166 | ||
| 1196 | test "relative" { | 1167 | test "relative" { |
| 1197 | if (builtin.target.cpu.arch == .aarch64) { | ||
| 1198 | // TODO https://github.com/ziglang/zig/issues/3288 | ||
| 1199 | return error.SkipZigTest; | ||
| 1200 | } | ||
| 1201 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | ||
| 1202 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); | ||
| 1203 | |||
| 1204 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); | 1168 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); |
| 1205 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); | 1169 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 1206 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); | 1170 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| ... | @@ -1226,6 +1190,10 @@ test "relative" { | ... | @@ -1226,6 +1190,10 @@ test "relative" { |
| 1226 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); | 1190 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| 1227 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); | 1191 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); |
| 1228 | 1192 | ||
| 1193 | try testRelativeWindows("a/b/c", "a\\b", ".."); | ||
| 1194 | try testRelativeWindows("a/b/c", "a", "..\\.."); | ||
| 1195 | try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); | ||
| 1196 | |||
| 1229 | try testRelativePosix("/var/lib", "/var", ".."); | 1197 | try testRelativePosix("/var/lib", "/var", ".."); |
| 1230 | try testRelativePosix("/var/lib", "/bin", "../../bin"); | 1198 | try testRelativePosix("/var/lib", "/bin", "../../bin"); |
| 1231 | try testRelativePosix("/var/lib", "/var/lib", ""); | 1199 | try testRelativePosix("/var/lib", "/var/lib", ""); |
| ... | @@ -1243,13 +1211,13 @@ test "relative" { | ... | @@ -1243,13 +1211,13 @@ test "relative" { |
| 1243 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { | 1211 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { |
| 1244 | const result = try relativePosix(testing.allocator, from, to); | 1212 | const result = try relativePosix(testing.allocator, from, to); |
| 1245 | defer testing.allocator.free(result); | 1213 | defer testing.allocator.free(result); |
| 1246 | try testing.expectEqualSlices(u8, expected_output, result); | 1214 | try testing.expectEqualStrings(expected_output, result); |
| 1247 | } | 1215 | } |
| 1248 | 1216 | ||
| 1249 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { | 1217 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { |
| 1250 | const result = try relativeWindows(testing.allocator, from, to); | 1218 | const result = try relativeWindows(testing.allocator, from, to); |
| 1251 | defer testing.allocator.free(result); | 1219 | defer testing.allocator.free(result); |
| 1252 | try testing.expectEqualSlices(u8, expected_output, result); | 1220 | try testing.expectEqualStrings(expected_output, result); |
| 1253 | } | 1221 | } |
| 1254 | 1222 | ||
| 1255 | /// Returns the extension of the file name (if any). | 1223 | /// Returns the extension of the file name (if any). |
lib/std/fs/test.zig+3-1| ... | @@ -1095,7 +1095,9 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { | ... | @@ -1095,7 +1095,9 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 1095 | 1095 | ||
| 1096 | const allocator = testing.allocator; | 1096 | const allocator = testing.allocator; |
| 1097 | 1097 | ||
| 1098 | const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"}; | 1098 | const cwd = try std.process.getCwdAlloc(allocator); |
| 1099 | defer allocator.free(cwd); | ||
| 1100 | const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" }; | ||
| 1099 | const filename = try fs.path.resolve(allocator, &file_paths); | 1101 | const filename = try fs.path.resolve(allocator, &file_paths); |
| 1100 | defer allocator.free(filename); | 1102 | defer allocator.free(filename); |
| 1101 | 1103 |
lib/std/fs/wasi.zig+1-4| ... | @@ -202,10 +202,7 @@ pub const PreopenList = struct { | ... | @@ -202,10 +202,7 @@ pub const PreopenList = struct { |
| 202 | // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "." | 202 | // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "." |
| 203 | const path = if (cwd_root) |cwd| blk: { | 203 | const path = if (cwd_root) |cwd| blk: { |
| 204 | const resolve_paths: []const []const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path }; | 204 | const resolve_paths: []const []const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path }; |
| 205 | break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) { | 205 | break :blk try fs.path.resolve(self.buffer.allocator, resolve_paths); |
| 206 | error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried | ||
| 207 | else => |e| return e, | ||
| 208 | }; | ||
| 209 | } else blk: { | 206 | } else blk: { |
| 210 | // If we were provided no CWD root, we preserve the preopen dir without resolving | 207 | // If we were provided no CWD root, we preserve the preopen dir without resolving |
| 211 | break :blk try self.buffer.allocator.dupe(u8, raw_path); | 208 | break :blk try self.buffer.allocator.dupe(u8, raw_path); |
src/Cache.zig+139-39| ... | @@ -1,3 +1,7 @@ | ... | @@ -1,3 +1,7 @@ |
| 1 | //! Manages `zig-cache` directories. | ||
| 2 | //! This is not a general-purpose cache. It is designed to be fast and simple, | ||
| 3 | //! not to withstand attacks using specially-crafted input. | ||
| 4 | |||
| 1 | gpa: Allocator, | 5 | gpa: Allocator, |
| 2 | manifest_dir: fs.Dir, | 6 | manifest_dir: fs.Dir, |
| 3 | hash: HashHelper = .{}, | 7 | hash: HashHelper = .{}, |
| ... | @@ -5,6 +9,14 @@ hash: HashHelper = .{}, | ... | @@ -5,6 +9,14 @@ hash: HashHelper = .{}, |
| 5 | recent_problematic_timestamp: i128 = 0, | 9 | recent_problematic_timestamp: i128 = 0, |
| 6 | mutex: std.Thread.Mutex = .{}, | 10 | mutex: std.Thread.Mutex = .{}, |
| 7 | 11 | ||
| 12 | /// A set of strings such as the zig library directory or project source root, which | ||
| 13 | /// are stripped from the file paths before putting into the cache. They | ||
| 14 | /// are replaced with single-character indicators. This is not to save | ||
| 15 | /// space but to eliminate absolute file paths. This improves portability | ||
| 16 | /// and usefulness of the cache for advanced use cases. | ||
| 17 | prefixes_buffer: [3]Compilation.Directory = undefined, | ||
| 18 | prefixes_len: usize = 0, | ||
| 19 | |||
| 8 | const Cache = @This(); | 20 | const Cache = @This(); |
| 9 | const std = @import("std"); | 21 | const std = @import("std"); |
| 10 | const builtin = @import("builtin"); | 22 | const builtin = @import("builtin"); |
| ... | @@ -18,6 +30,14 @@ const Allocator = std.mem.Allocator; | ... | @@ -18,6 +30,14 @@ const Allocator = std.mem.Allocator; |
| 18 | const Compilation = @import("Compilation.zig"); | 30 | const Compilation = @import("Compilation.zig"); |
| 19 | const log = std.log.scoped(.cache); | 31 | const log = std.log.scoped(.cache); |
| 20 | 32 | ||
| 33 | pub fn addPrefix(cache: *Cache, directory: Compilation.Directory) void { | ||
| 34 | if (directory.path) |p| { | ||
| 35 | log.debug("Cache.addPrefix {d} {s}", .{ cache.prefixes_len, p }); | ||
| 36 | } | ||
| 37 | cache.prefixes_buffer[cache.prefixes_len] = directory; | ||
| 38 | cache.prefixes_len += 1; | ||
| 39 | } | ||
| 40 | |||
| 21 | /// Be sure to call `Manifest.deinit` after successful initialization. | 41 | /// Be sure to call `Manifest.deinit` after successful initialization. |
| 22 | pub fn obtain(cache: *Cache) Manifest { | 42 | pub fn obtain(cache: *Cache) Manifest { |
| 23 | return Manifest{ | 43 | return Manifest{ |
| ... | @@ -29,6 +49,48 @@ pub fn obtain(cache: *Cache) Manifest { | ... | @@ -29,6 +49,48 @@ pub fn obtain(cache: *Cache) Manifest { |
| 29 | }; | 49 | }; |
| 30 | } | 50 | } |
| 31 | 51 | ||
| 52 | pub fn prefixes(cache: *const Cache) []const Compilation.Directory { | ||
| 53 | return cache.prefixes_buffer[0..cache.prefixes_len]; | ||
| 54 | } | ||
| 55 | |||
| 56 | const PrefixedPath = struct { | ||
| 57 | prefix: u8, | ||
| 58 | sub_path: []u8, | ||
| 59 | }; | ||
| 60 | |||
| 61 | fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { | ||
| 62 | const gpa = cache.gpa; | ||
| 63 | const resolved_path = try fs.path.resolve(gpa, &[_][]const u8{file_path}); | ||
| 64 | errdefer gpa.free(resolved_path); | ||
| 65 | return findPrefixResolved(cache, resolved_path); | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Takes ownership of `resolved_path` on success. | ||
| 69 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { | ||
| 70 | const gpa = cache.gpa; | ||
| 71 | const prefixes_slice = cache.prefixes(); | ||
| 72 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. | ||
| 73 | while (i < prefixes_slice.len) : (i += 1) { | ||
| 74 | const p = prefixes_slice[i].path.?; | ||
| 75 | if (mem.startsWith(u8, resolved_path, p)) { | ||
| 76 | // +1 to skip over the path separator here | ||
| 77 | const sub_path = try gpa.dupe(u8, resolved_path[p.len + 1 ..]); | ||
| 78 | gpa.free(resolved_path); | ||
| 79 | return PrefixedPath{ | ||
| 80 | .prefix = @intCast(u8, i), | ||
| 81 | .sub_path = sub_path, | ||
| 82 | }; | ||
| 83 | } else { | ||
| 84 | log.debug("'{s}' does not start with '{s}'", .{ resolved_path, p }); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | return PrefixedPath{ | ||
| 89 | .prefix = 0, | ||
| 90 | .sub_path = resolved_path, | ||
| 91 | }; | ||
| 92 | } | ||
| 93 | |||
| 32 | /// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 | 94 | /// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 |
| 33 | pub const bin_digest_len = 16; | 95 | pub const bin_digest_len = 16; |
| 34 | pub const hex_digest_len = bin_digest_len * 2; | 96 | pub const hex_digest_len = bin_digest_len * 2; |
| ... | @@ -45,7 +107,7 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); | ... | @@ -45,7 +107,7 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); |
| 45 | pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length); | 107 | pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length); |
| 46 | 108 | ||
| 47 | pub const File = struct { | 109 | pub const File = struct { |
| 48 | path: ?[]const u8, | 110 | prefixed_path: ?PrefixedPath, |
| 49 | max_file_size: ?usize, | 111 | max_file_size: ?usize, |
| 50 | stat: Stat, | 112 | stat: Stat, |
| 51 | bin_digest: BinDigest, | 113 | bin_digest: BinDigest, |
| ... | @@ -57,13 +119,13 @@ pub const File = struct { | ... | @@ -57,13 +119,13 @@ pub const File = struct { |
| 57 | mtime: i128, | 119 | mtime: i128, |
| 58 | }; | 120 | }; |
| 59 | 121 | ||
| 60 | pub fn deinit(self: *File, allocator: Allocator) void { | 122 | pub fn deinit(self: *File, gpa: Allocator) void { |
| 61 | if (self.path) |owned_slice| { | 123 | if (self.prefixed_path) |pp| { |
| 62 | allocator.free(owned_slice); | 124 | gpa.free(pp.sub_path); |
| 63 | self.path = null; | 125 | self.prefixed_path = null; |
| 64 | } | 126 | } |
| 65 | if (self.contents) |contents| { | 127 | if (self.contents) |contents| { |
| 66 | allocator.free(contents); | 128 | gpa.free(contents); |
| 67 | self.contents = null; | 129 | self.contents = null; |
| 68 | } | 130 | } |
| 69 | self.* = undefined; | 131 | self.* = undefined; |
| ... | @@ -175,9 +237,6 @@ pub const Lock = struct { | ... | @@ -175,9 +237,6 @@ pub const Lock = struct { |
| 175 | } | 237 | } |
| 176 | }; | 238 | }; |
| 177 | 239 | ||
| 178 | /// Manifest manages project-local `zig-cache` directories. | ||
| 179 | /// This is not a general-purpose cache. | ||
| 180 | /// It is designed to be fast and simple, not to withstand attacks using specially-crafted input. | ||
| 181 | pub const Manifest = struct { | 240 | pub const Manifest = struct { |
| 182 | cache: *Cache, | 241 | cache: *Cache, |
| 183 | /// Current state for incremental hashing. | 242 | /// Current state for incremental hashing. |
| ... | @@ -220,21 +279,27 @@ pub const Manifest = struct { | ... | @@ -220,21 +279,27 @@ pub const Manifest = struct { |
| 220 | pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize { | 279 | pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize { |
| 221 | assert(self.manifest_file == null); | 280 | assert(self.manifest_file == null); |
| 222 | 281 | ||
| 223 | try self.files.ensureUnusedCapacity(self.cache.gpa, 1); | 282 | const gpa = self.cache.gpa; |
| 224 | const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path}); | 283 | try self.files.ensureUnusedCapacity(gpa, 1); |
| 284 | const prefixed_path = try self.cache.findPrefix(file_path); | ||
| 285 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 286 | |||
| 287 | log.debug("Manifest.addFile {s} -> {d} {s}", .{ | ||
| 288 | file_path, prefixed_path.prefix, prefixed_path.sub_path, | ||
| 289 | }); | ||
| 225 | 290 | ||
| 226 | const idx = self.files.items.len; | ||
| 227 | self.files.addOneAssumeCapacity().* = .{ | 291 | self.files.addOneAssumeCapacity().* = .{ |
| 228 | .path = resolved_path, | 292 | .prefixed_path = prefixed_path, |
| 229 | .contents = null, | 293 | .contents = null, |
| 230 | .max_file_size = max_file_size, | 294 | .max_file_size = max_file_size, |
| 231 | .stat = undefined, | 295 | .stat = undefined, |
| 232 | .bin_digest = undefined, | 296 | .bin_digest = undefined, |
| 233 | }; | 297 | }; |
| 234 | 298 | ||
| 235 | self.hash.addBytes(resolved_path); | 299 | self.hash.add(prefixed_path.prefix); |
| 300 | self.hash.addBytes(prefixed_path.sub_path); | ||
| 236 | 301 | ||
| 237 | return idx; | 302 | return self.files.items.len - 1; |
| 238 | } | 303 | } |
| 239 | 304 | ||
| 240 | pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void { | 305 | pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void { |
| ... | @@ -281,6 +346,7 @@ pub const Manifest = struct { | ... | @@ -281,6 +346,7 @@ pub const Manifest = struct { |
| 281 | /// option, one may call `toOwnedLock` to obtain a smaller object which can represent | 346 | /// option, one may call `toOwnedLock` to obtain a smaller object which can represent |
| 282 | /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called. | 347 | /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called. |
| 283 | pub fn hit(self: *Manifest) !bool { | 348 | pub fn hit(self: *Manifest) !bool { |
| 349 | const gpa = self.cache.gpa; | ||
| 284 | assert(self.manifest_file == null); | 350 | assert(self.manifest_file == null); |
| 285 | 351 | ||
| 286 | self.failed_file_index = null; | 352 | self.failed_file_index = null; |
| ... | @@ -362,8 +428,8 @@ pub const Manifest = struct { | ... | @@ -362,8 +428,8 @@ pub const Manifest = struct { |
| 362 | 428 | ||
| 363 | self.want_refresh_timestamp = true; | 429 | self.want_refresh_timestamp = true; |
| 364 | 430 | ||
| 365 | const file_contents = try self.manifest_file.?.reader().readAllAlloc(self.cache.gpa, manifest_file_size_max); | 431 | const file_contents = try self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max); |
| 366 | defer self.cache.gpa.free(file_contents); | 432 | defer gpa.free(file_contents); |
| 367 | 433 | ||
| 368 | const input_file_count = self.files.items.len; | 434 | const input_file_count = self.files.items.len; |
| 369 | var any_file_changed = false; | 435 | var any_file_changed = false; |
| ... | @@ -373,9 +439,9 @@ pub const Manifest = struct { | ... | @@ -373,9 +439,9 @@ pub const Manifest = struct { |
| 373 | defer idx += 1; | 439 | defer idx += 1; |
| 374 | 440 | ||
| 375 | const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: { | 441 | const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: { |
| 376 | const new = try self.files.addOne(self.cache.gpa); | 442 | const new = try self.files.addOne(gpa); |
| 377 | new.* = .{ | 443 | new.* = .{ |
| 378 | .path = null, | 444 | .prefixed_path = null, |
| 379 | .contents = null, | 445 | .contents = null, |
| 380 | .max_file_size = null, | 446 | .max_file_size = null, |
| 381 | .stat = undefined, | 447 | .stat = undefined, |
| ... | @@ -389,27 +455,35 @@ pub const Manifest = struct { | ... | @@ -389,27 +455,35 @@ pub const Manifest = struct { |
| 389 | const inode = iter.next() orelse return error.InvalidFormat; | 455 | const inode = iter.next() orelse return error.InvalidFormat; |
| 390 | const mtime_nsec_str = iter.next() orelse return error.InvalidFormat; | 456 | const mtime_nsec_str = iter.next() orelse return error.InvalidFormat; |
| 391 | const digest_str = iter.next() orelse return error.InvalidFormat; | 457 | const digest_str = iter.next() orelse return error.InvalidFormat; |
| 458 | const prefix_str = iter.next() orelse return error.InvalidFormat; | ||
| 392 | const file_path = iter.rest(); | 459 | const file_path = iter.rest(); |
| 393 | 460 | ||
| 394 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; | 461 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; |
| 395 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; | 462 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; |
| 396 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; | 463 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; |
| 397 | _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; | 464 | _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; |
| 465 | const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; | ||
| 466 | if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; | ||
| 398 | 467 | ||
| 399 | if (file_path.len == 0) { | 468 | if (file_path.len == 0) { |
| 400 | return error.InvalidFormat; | 469 | return error.InvalidFormat; |
| 401 | } | 470 | } |
| 402 | if (cache_hash_file.path) |p| { | 471 | if (cache_hash_file.prefixed_path) |pp| { |
| 403 | if (!mem.eql(u8, file_path, p)) { | 472 | if (pp.prefix != prefix or !mem.eql(u8, file_path, pp.sub_path)) { |
| 404 | return error.InvalidFormat; | 473 | return error.InvalidFormat; |
| 405 | } | 474 | } |
| 406 | } | 475 | } |
| 407 | 476 | ||
| 408 | if (cache_hash_file.path == null) { | 477 | if (cache_hash_file.prefixed_path == null) { |
| 409 | cache_hash_file.path = try self.cache.gpa.dupe(u8, file_path); | 478 | cache_hash_file.prefixed_path = .{ |
| 479 | .prefix = prefix, | ||
| 480 | .sub_path = try gpa.dupe(u8, file_path), | ||
| 481 | }; | ||
| 410 | } | 482 | } |
| 411 | 483 | ||
| 412 | const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .mode = .read_only }) catch |err| switch (err) { | 484 | const pp = cache_hash_file.prefixed_path.?; |
| 485 | const dir = self.cache.prefixes()[pp.prefix].handle; | ||
| 486 | const this_file = dir.openFile(pp.sub_path, .{ .mode = .read_only }) catch |err| switch (err) { | ||
| 413 | error.FileNotFound => { | 487 | error.FileNotFound => { |
| 414 | try self.upgradeToExclusiveLock(); | 488 | try self.upgradeToExclusiveLock(); |
| 415 | return false; | 489 | return false; |
| ... | @@ -535,8 +609,9 @@ pub const Manifest = struct { | ... | @@ -535,8 +609,9 @@ pub const Manifest = struct { |
| 535 | } | 609 | } |
| 536 | 610 | ||
| 537 | fn populateFileHash(self: *Manifest, ch_file: *File) !void { | 611 | fn populateFileHash(self: *Manifest, ch_file: *File) !void { |
| 538 | log.debug("populateFileHash {s}", .{ch_file.path.?}); | 612 | const pp = ch_file.prefixed_path.?; |
| 539 | const file = try fs.cwd().openFile(ch_file.path.?, .{}); | 613 | const dir = self.cache.prefixes()[pp.prefix].handle; |
| 614 | const file = try dir.openFile(pp.sub_path, .{}); | ||
| 540 | defer file.close(); | 615 | defer file.close(); |
| 541 | 616 | ||
| 542 | const actual_stat = try file.stat(); | 617 | const actual_stat = try file.stat(); |
| ... | @@ -588,12 +663,17 @@ pub const Manifest = struct { | ... | @@ -588,12 +663,17 @@ pub const Manifest = struct { |
| 588 | pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 { | 663 | pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 { |
| 589 | assert(self.manifest_file != null); | 664 | assert(self.manifest_file != null); |
| 590 | 665 | ||
| 591 | const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path}); | 666 | const gpa = self.cache.gpa; |
| 592 | errdefer self.cache.gpa.free(resolved_path); | 667 | const prefixed_path = try self.cache.findPrefix(file_path); |
| 668 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 669 | |||
| 670 | log.debug("Manifest.addFilePostFetch {s} -> {d} {s}", .{ | ||
| 671 | file_path, prefixed_path.prefix, prefixed_path.sub_path, | ||
| 672 | }); | ||
| 593 | 673 | ||
| 594 | const new_ch_file = try self.files.addOne(self.cache.gpa); | 674 | const new_ch_file = try self.files.addOne(gpa); |
| 595 | new_ch_file.* = .{ | 675 | new_ch_file.* = .{ |
| 596 | .path = resolved_path, | 676 | .prefixed_path = prefixed_path, |
| 597 | .max_file_size = max_file_size, | 677 | .max_file_size = max_file_size, |
| 598 | .stat = undefined, | 678 | .stat = undefined, |
| 599 | .bin_digest = undefined, | 679 | .bin_digest = undefined, |
| ... | @@ -613,12 +693,17 @@ pub const Manifest = struct { | ... | @@ -613,12 +693,17 @@ pub const Manifest = struct { |
| 613 | pub fn addFilePost(self: *Manifest, file_path: []const u8) !void { | 693 | pub fn addFilePost(self: *Manifest, file_path: []const u8) !void { |
| 614 | assert(self.manifest_file != null); | 694 | assert(self.manifest_file != null); |
| 615 | 695 | ||
| 616 | const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path}); | 696 | const gpa = self.cache.gpa; |
| 617 | errdefer self.cache.gpa.free(resolved_path); | 697 | const prefixed_path = try self.cache.findPrefix(file_path); |
| 698 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 699 | |||
| 700 | log.debug("Manifest.addFilePost {s} -> {d} {s}", .{ | ||
| 701 | file_path, prefixed_path.prefix, prefixed_path.sub_path, | ||
| 702 | }); | ||
| 618 | 703 | ||
| 619 | const new_ch_file = try self.files.addOne(self.cache.gpa); | 704 | const new_ch_file = try self.files.addOne(gpa); |
| 620 | new_ch_file.* = .{ | 705 | new_ch_file.* = .{ |
| 621 | .path = resolved_path, | 706 | .prefixed_path = prefixed_path, |
| 622 | .max_file_size = null, | 707 | .max_file_size = null, |
| 623 | .stat = undefined, | 708 | .stat = undefined, |
| 624 | .bin_digest = undefined, | 709 | .bin_digest = undefined, |
| ... | @@ -633,17 +718,27 @@ pub const Manifest = struct { | ... | @@ -633,17 +718,27 @@ pub const Manifest = struct { |
| 633 | /// On success, cache takes ownership of `resolved_path`. | 718 | /// On success, cache takes ownership of `resolved_path`. |
| 634 | pub fn addFilePostContents( | 719 | pub fn addFilePostContents( |
| 635 | self: *Manifest, | 720 | self: *Manifest, |
| 636 | resolved_path: []const u8, | 721 | resolved_path: []u8, |
| 637 | bytes: []const u8, | 722 | bytes: []const u8, |
| 638 | stat: File.Stat, | 723 | stat: File.Stat, |
| 639 | ) error{OutOfMemory}!void { | 724 | ) error{OutOfMemory}!void { |
| 640 | assert(self.manifest_file != null); | 725 | assert(self.manifest_file != null); |
| 726 | const gpa = self.cache.gpa; | ||
| 641 | 727 | ||
| 642 | const ch_file = try self.files.addOne(self.cache.gpa); | 728 | const ch_file = try self.files.addOne(gpa); |
| 643 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); | 729 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); |
| 644 | 730 | ||
| 731 | log.debug("Manifest.addFilePostContents resolved_path={s}", .{resolved_path}); | ||
| 732 | |||
| 733 | const prefixed_path = try self.cache.findPrefixResolved(resolved_path); | ||
| 734 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 735 | |||
| 736 | log.debug("Manifest.addFilePostContents -> {d} {s}", .{ | ||
| 737 | prefixed_path.prefix, prefixed_path.sub_path, | ||
| 738 | }); | ||
| 739 | |||
| 645 | ch_file.* = .{ | 740 | ch_file.* = .{ |
| 646 | .path = resolved_path, | 741 | .prefixed_path = prefixed_path, |
| 647 | .max_file_size = null, | 742 | .max_file_size = null, |
| 648 | .stat = stat, | 743 | .stat = stat, |
| 649 | .bin_digest = undefined, | 744 | .bin_digest = undefined, |
| ... | @@ -742,12 +837,13 @@ pub const Manifest = struct { | ... | @@ -742,12 +837,13 @@ pub const Manifest = struct { |
| 742 | "{s}", | 837 | "{s}", |
| 743 | .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, | 838 | .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, |
| 744 | ) catch unreachable; | 839 | ) catch unreachable; |
| 745 | try writer.print("{d} {d} {d} {s} {s}\n", .{ | 840 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ |
| 746 | file.stat.size, | 841 | file.stat.size, |
| 747 | file.stat.inode, | 842 | file.stat.inode, |
| 748 | file.stat.mtime, | 843 | file.stat.mtime, |
| 749 | &encoded_digest, | 844 | &encoded_digest, |
| 750 | file.path.?, | 845 | file.prefixed_path.?.prefix, |
| 846 | file.prefixed_path.?.sub_path, | ||
| 751 | }); | 847 | }); |
| 752 | } | 848 | } |
| 753 | 849 | ||
| ... | @@ -889,6 +985,7 @@ test "cache file and then recall it" { | ... | @@ -889,6 +985,7 @@ test "cache file and then recall it" { |
| 889 | .gpa = testing.allocator, | 985 | .gpa = testing.allocator, |
| 890 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 986 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), |
| 891 | }; | 987 | }; |
| 988 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 892 | defer cache.manifest_dir.close(); | 989 | defer cache.manifest_dir.close(); |
| 893 | 990 | ||
| 894 | { | 991 | { |
| ... | @@ -960,6 +1057,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -960,6 +1057,7 @@ test "check that changing a file makes cache fail" { |
| 960 | .gpa = testing.allocator, | 1057 | .gpa = testing.allocator, |
| 961 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1058 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), |
| 962 | }; | 1059 | }; |
| 1060 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 963 | defer cache.manifest_dir.close(); | 1061 | defer cache.manifest_dir.close(); |
| 964 | 1062 | ||
| 965 | { | 1063 | { |
| ... | @@ -1022,6 +1120,7 @@ test "no file inputs" { | ... | @@ -1022,6 +1120,7 @@ test "no file inputs" { |
| 1022 | .gpa = testing.allocator, | 1120 | .gpa = testing.allocator, |
| 1023 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1121 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), |
| 1024 | }; | 1122 | }; |
| 1123 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1025 | defer cache.manifest_dir.close(); | 1124 | defer cache.manifest_dir.close(); |
| 1026 | 1125 | ||
| 1027 | { | 1126 | { |
| ... | @@ -1080,6 +1179,7 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1080,6 +1179,7 @@ test "Manifest with files added after initial hash work" { |
| 1080 | .gpa = testing.allocator, | 1179 | .gpa = testing.allocator, |
| 1081 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | 1180 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), |
| 1082 | }; | 1181 | }; |
| 1182 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1083 | defer cache.manifest_dir.close(); | 1183 | defer cache.manifest_dir.close(); |
| 1084 | 1184 | ||
| 1085 | { | 1185 | { |
src/Compilation.zig+27-18| ... | @@ -201,7 +201,9 @@ pub const CRTFile = struct { | ... | @@ -201,7 +201,9 @@ pub const CRTFile = struct { |
| 201 | /// For passing to a C compiler. | 201 | /// For passing to a C compiler. |
| 202 | pub const CSourceFile = struct { | 202 | pub const CSourceFile = struct { |
| 203 | src_path: []const u8, | 203 | src_path: []const u8, |
| 204 | extra_flags: []const []const u8 = &[0][]const u8{}, | 204 | extra_flags: []const []const u8 = &.{}, |
| 205 | /// Same as extra_flags except they are not added to the Cache hash. | ||
| 206 | cache_exempt_flags: []const []const u8 = &.{}, | ||
| 205 | }; | 207 | }; |
| 206 | 208 | ||
| 207 | const Job = union(enum) { | 209 | const Job = union(enum) { |
| ... | @@ -1456,23 +1458,27 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1456,23 +1458,27 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1456 | else => @as(u8, 3), | 1458 | else => @as(u8, 3), |
| 1457 | }; | 1459 | }; |
| 1458 | 1460 | ||
| 1459 | // We put everything into the cache hash that *cannot be modified during an incremental update*. | 1461 | // We put everything into the cache hash that *cannot be modified |
| 1460 | // For example, one cannot change the target between updates, but one can change source files, | 1462 | // during an incremental update*. For example, one cannot change the |
| 1461 | // so the target goes into the cache hash, but source files do not. This is so that we can | 1463 | // target between updates, but one can change source files, so the |
| 1462 | // find the same binary and incrementally update it even if there are modified source files. | 1464 | // target goes into the cache hash, but source files do not. This is so |
| 1463 | // We do this even if outputting to the current directory because we need somewhere to store | 1465 | // that we can find the same binary and incrementally update it even if |
| 1464 | // incremental compilation metadata. | 1466 | // there are modified source files. We do this even if outputting to |
| 1467 | // the current directory because we need somewhere to store incremental | ||
| 1468 | // compilation metadata. | ||
| 1465 | const cache = try arena.create(Cache); | 1469 | const cache = try arena.create(Cache); |
| 1466 | cache.* = .{ | 1470 | cache.* = .{ |
| 1467 | .gpa = gpa, | 1471 | .gpa = gpa, |
| 1468 | .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}), | 1472 | .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}), |
| 1469 | }; | 1473 | }; |
| 1474 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1475 | cache.addPrefix(options.zig_lib_directory); | ||
| 1476 | cache.addPrefix(options.local_cache_directory); | ||
| 1470 | errdefer cache.manifest_dir.close(); | 1477 | errdefer cache.manifest_dir.close(); |
| 1471 | 1478 | ||
| 1472 | // This is shared hasher state common to zig source and all C source files. | 1479 | // This is shared hasher state common to zig source and all C source files. |
| 1473 | cache.hash.addBytes(build_options.version); | 1480 | cache.hash.addBytes(build_options.version); |
| 1474 | cache.hash.add(builtin.zig_backend); | 1481 | cache.hash.add(builtin.zig_backend); |
| 1475 | cache.hash.addBytes(options.zig_lib_directory.path orelse "."); | ||
| 1476 | cache.hash.add(options.optimize_mode); | 1482 | cache.hash.add(options.optimize_mode); |
| 1477 | cache.hash.add(options.target.cpu.arch); | 1483 | cache.hash.add(options.target.cpu.arch); |
| 1478 | cache.hash.addBytes(options.target.cpu.model.name); | 1484 | cache.hash.addBytes(options.target.cpu.model.name); |
| ... | @@ -2265,8 +2271,9 @@ pub fn update(comp: *Compilation) !void { | ... | @@ -2265,8 +2271,9 @@ pub fn update(comp: *Compilation) !void { |
| 2265 | const is_hit = man.hit() catch |err| { | 2271 | const is_hit = man.hit() catch |err| { |
| 2266 | // TODO properly bubble these up instead of emitting a warning | 2272 | // TODO properly bubble these up instead of emitting a warning |
| 2267 | const i = man.failed_file_index orelse return err; | 2273 | const i = man.failed_file_index orelse return err; |
| 2268 | const file_path = man.files.items[i].path orelse return err; | 2274 | const pp = man.files.items[i].prefixed_path orelse return err; |
| 2269 | std.log.warn("{s}: {s}", .{ @errorName(err), file_path }); | 2275 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; |
| 2276 | std.log.warn("{s}: {s}{s}", .{ @errorName(err), prefix, pp.sub_path }); | ||
| 2270 | return err; | 2277 | return err; |
| 2271 | }; | 2278 | }; |
| 2272 | if (is_hit) { | 2279 | if (is_hit) { |
| ... | @@ -3246,13 +3253,6 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3246,13 +3253,6 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3246 | 3253 | ||
| 3247 | const module = comp.bin_file.options.module.?; | 3254 | const module = comp.bin_file.options.module.?; |
| 3248 | module.semaPkg(pkg) catch |err| switch (err) { | 3255 | module.semaPkg(pkg) catch |err| switch (err) { |
| 3249 | error.CurrentWorkingDirectoryUnlinked, | ||
| 3250 | error.Unexpected, | ||
| 3251 | => comp.lockAndSetMiscFailure( | ||
| 3252 | .analyze_pkg, | ||
| 3253 | "unexpected problem analyzing package '{s}'", | ||
| 3254 | .{pkg.root_src_path}, | ||
| 3255 | ), | ||
| 3256 | error.OutOfMemory => return error.OutOfMemory, | 3256 | error.OutOfMemory => return error.OutOfMemory, |
| 3257 | error.AnalysisFail => return, | 3257 | error.AnalysisFail => return, |
| 3258 | }; | 3258 | }; |
| ... | @@ -3557,7 +3557,14 @@ pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { | ... | @@ -3557,7 +3557,14 @@ pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { |
| 3557 | man.hash.add(comp.sanitize_c); | 3557 | man.hash.add(comp.sanitize_c); |
| 3558 | man.hash.addListOfBytes(comp.clang_argv); | 3558 | man.hash.addListOfBytes(comp.clang_argv); |
| 3559 | man.hash.add(comp.bin_file.options.link_libcpp); | 3559 | man.hash.add(comp.bin_file.options.link_libcpp); |
| 3560 | man.hash.addListOfBytes(comp.libc_include_dir_list); | 3560 | |
| 3561 | // When libc_installation is null it means that Zig generated this dir list | ||
| 3562 | // based on the zig library directory alone. The zig lib directory file | ||
| 3563 | // path is purposefully either in the cache or not in the cache. The | ||
| 3564 | // decision should not be overridden here. | ||
| 3565 | if (comp.bin_file.options.libc_installation != null) { | ||
| 3566 | man.hash.addListOfBytes(comp.libc_include_dir_list); | ||
| 3567 | } | ||
| 3561 | 3568 | ||
| 3562 | return man; | 3569 | return man; |
| 3563 | } | 3570 | } |
| ... | @@ -3944,6 +3951,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -3944,6 +3951,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3944 | { | 3951 | { |
| 3945 | try comp.addCCArgs(arena, &argv, ext, null); | 3952 | try comp.addCCArgs(arena, &argv, ext, null); |
| 3946 | try argv.appendSlice(c_object.src.extra_flags); | 3953 | try argv.appendSlice(c_object.src.extra_flags); |
| 3954 | try argv.appendSlice(c_object.src.cache_exempt_flags); | ||
| 3947 | 3955 | ||
| 3948 | const out_obj_path = if (comp.bin_file.options.emit) |emit| | 3956 | const out_obj_path = if (comp.bin_file.options.emit) |emit| |
| 3949 | try emit.directory.join(arena, &.{emit.sub_path}) | 3957 | try emit.directory.join(arena, &.{emit.sub_path}) |
| ... | @@ -3985,6 +3993,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -3985,6 +3993,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3985 | try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path}); | 3993 | try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path}); |
| 3986 | try comp.addCCArgs(arena, &argv, ext, out_dep_path); | 3994 | try comp.addCCArgs(arena, &argv, ext, out_dep_path); |
| 3987 | try argv.appendSlice(c_object.src.extra_flags); | 3995 | try argv.appendSlice(c_object.src.extra_flags); |
| 3996 | try argv.appendSlice(c_object.src.cache_exempt_flags); | ||
| 3988 | 3997 | ||
| 3989 | try argv.ensureUnusedCapacity(5); | 3998 | try argv.ensureUnusedCapacity(5); |
| 3990 | switch (comp.clang_preprocessor_mode) { | 3999 | switch (comp.clang_preprocessor_mode) { |
src/glibc.zig+3| ... | @@ -653,6 +653,9 @@ pub fn buildSharedObjects(comp: *Compilation) !void { | ... | @@ -653,6 +653,9 @@ pub fn buildSharedObjects(comp: *Compilation) !void { |
| 653 | .gpa = comp.gpa, | 653 | .gpa = comp.gpa, |
| 654 | .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), | 654 | .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), |
| 655 | }; | 655 | }; |
| 656 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 657 | cache.addPrefix(comp.zig_lib_directory); | ||
| 658 | cache.addPrefix(comp.global_cache_directory); | ||
| 656 | defer cache.manifest_dir.close(); | 659 | defer cache.manifest_dir.close(); |
| 657 | 660 | ||
| 658 | var man = cache.obtain(); | 661 | var man = cache.obtain(); |
src/libcxx.zig+30-18| ... | @@ -187,15 +187,6 @@ pub fn buildLibCXX(comp: *Compilation) !void { | ... | @@ -187,15 +187,6 @@ pub fn buildLibCXX(comp: *Compilation) !void { |
| 187 | try cflags.append("-faligned-allocation"); | 187 | try cflags.append("-faligned-allocation"); |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | try cflags.append("-I"); | ||
| 191 | try cflags.append(cxx_include_path); | ||
| 192 | |||
| 193 | try cflags.append("-I"); | ||
| 194 | try cflags.append(cxxabi_include_path); | ||
| 195 | |||
| 196 | try cflags.append("-I"); | ||
| 197 | try cflags.append(cxx_src_include_path); | ||
| 198 | |||
| 199 | if (target_util.supports_fpic(target)) { | 190 | if (target_util.supports_fpic(target)) { |
| 200 | try cflags.append("-fPIC"); | 191 | try cflags.append("-fPIC"); |
| 201 | } | 192 | } |
| ... | @@ -203,9 +194,24 @@ pub fn buildLibCXX(comp: *Compilation) !void { | ... | @@ -203,9 +194,24 @@ pub fn buildLibCXX(comp: *Compilation) !void { |
| 203 | try cflags.append("-std=c++20"); | 194 | try cflags.append("-std=c++20"); |
| 204 | try cflags.append("-Wno-user-defined-literals"); | 195 | try cflags.append("-Wno-user-defined-literals"); |
| 205 | 196 | ||
| 197 | // These depend on only the zig lib directory file path, which is | ||
| 198 | // purposefully either in the cache or not in the cache. The decision | ||
| 199 | // should not be overridden here. | ||
| 200 | var cache_exempt_flags = std.ArrayList([]const u8).init(arena); | ||
| 201 | |||
| 202 | try cache_exempt_flags.append("-I"); | ||
| 203 | try cache_exempt_flags.append(cxx_include_path); | ||
| 204 | |||
| 205 | try cache_exempt_flags.append("-I"); | ||
| 206 | try cache_exempt_flags.append(cxxabi_include_path); | ||
| 207 | |||
| 208 | try cache_exempt_flags.append("-I"); | ||
| 209 | try cache_exempt_flags.append(cxx_src_include_path); | ||
| 210 | |||
| 206 | c_source_files.appendAssumeCapacity(.{ | 211 | c_source_files.appendAssumeCapacity(.{ |
| 207 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }), | 212 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }), |
| 208 | .extra_flags = cflags.items, | 213 | .extra_flags = cflags.items, |
| 214 | .cache_exempt_flags = cache_exempt_flags.items, | ||
| 209 | }); | 215 | }); |
| 210 | } | 216 | } |
| 211 | 217 | ||
| ... | @@ -340,15 +346,6 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { | ... | @@ -340,15 +346,6 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { |
| 340 | try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); | 346 | try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); |
| 341 | } | 347 | } |
| 342 | 348 | ||
| 343 | try cflags.append("-I"); | ||
| 344 | try cflags.append(cxxabi_include_path); | ||
| 345 | |||
| 346 | try cflags.append("-I"); | ||
| 347 | try cflags.append(cxx_include_path); | ||
| 348 | |||
| 349 | try cflags.append("-I"); | ||
| 350 | try cflags.append(cxx_src_include_path); | ||
| 351 | |||
| 352 | if (target_util.supports_fpic(target)) { | 349 | if (target_util.supports_fpic(target)) { |
| 353 | try cflags.append("-fPIC"); | 350 | try cflags.append("-fPIC"); |
| 354 | } | 351 | } |
| ... | @@ -357,9 +354,24 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { | ... | @@ -357,9 +354,24 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { |
| 357 | try cflags.append("-funwind-tables"); | 354 | try cflags.append("-funwind-tables"); |
| 358 | try cflags.append("-std=c++20"); | 355 | try cflags.append("-std=c++20"); |
| 359 | 356 | ||
| 357 | // These depend on only the zig lib directory file path, which is | ||
| 358 | // purposefully either in the cache or not in the cache. The decision | ||
| 359 | // should not be overridden here. | ||
| 360 | var cache_exempt_flags = std.ArrayList([]const u8).init(arena); | ||
| 361 | |||
| 362 | try cache_exempt_flags.append("-I"); | ||
| 363 | try cache_exempt_flags.append(cxxabi_include_path); | ||
| 364 | |||
| 365 | try cache_exempt_flags.append("-I"); | ||
| 366 | try cache_exempt_flags.append(cxx_include_path); | ||
| 367 | |||
| 368 | try cache_exempt_flags.append("-I"); | ||
| 369 | try cache_exempt_flags.append(cxx_src_include_path); | ||
| 370 | |||
| 360 | c_source_files.appendAssumeCapacity(.{ | 371 | c_source_files.appendAssumeCapacity(.{ |
| 361 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }), | 372 | .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }), |
| 362 | .extra_flags = cflags.items, | 373 | .extra_flags = cflags.items, |
| 374 | .cache_exempt_flags = cache_exempt_flags.items, | ||
| 363 | }); | 375 | }); |
| 364 | } | 376 | } |
| 365 | 377 |
src/main.zig+8-5| ... | @@ -2744,11 +2744,14 @@ fn buildOutputType( | ... | @@ -2744,11 +2744,14 @@ fn buildOutputType( |
| 2744 | } | 2744 | } |
| 2745 | 2745 | ||
| 2746 | const self_exe_path = try introspect.findZigExePath(arena); | 2746 | const self_exe_path = try introspect.findZigExePath(arena); |
| 2747 | var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{ | 2747 | var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |unresolved_lib_dir| l: { |
| 2748 | .path = lib_dir, | 2748 | const lib_dir = try fs.path.resolve(arena, &.{unresolved_lib_dir}); |
| 2749 | .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| { | 2749 | break :l .{ |
| 2750 | fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) }); | 2750 | .path = lib_dir, |
| 2751 | }, | 2751 | .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| { |
| 2752 | fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) }); | ||
| 2753 | }, | ||
| 2754 | }; | ||
| 2752 | } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { | 2755 | } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { |
| 2753 | fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); | 2756 | fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); |
| 2754 | }; | 2757 | }; |
src/mingw.zig+4| ... | @@ -302,6 +302,10 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { | ... | @@ -302,6 +302,10 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 302 | .gpa = comp.gpa, | 302 | .gpa = comp.gpa, |
| 303 | .manifest_dir = comp.cache_parent.manifest_dir, | 303 | .manifest_dir = comp.cache_parent.manifest_dir, |
| 304 | }; | 304 | }; |
| 305 | for (comp.cache_parent.prefixes()) |prefix| { | ||
| 306 | cache.addPrefix(prefix); | ||
| 307 | } | ||
| 308 | |||
| 305 | cache.hash.addBytes(build_options.version); | 309 | cache.hash.addBytes(build_options.version); |
| 306 | cache.hash.addOptionalBytes(comp.zig_lib_directory.path); | 310 | cache.hash.addOptionalBytes(comp.zig_lib_directory.path); |
| 307 | cache.hash.add(target.cpu.arch); | 311 | cache.hash.add(target.cpu.arch); |