authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-23 14:51:22-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-23 14:51:22-05:00
log81d2135ca6ebd71b8c121a19957c8fbf7f87125b
treee6ed1b33a6a02479e661b11103c5f7b8342d2345
parent32ce2f91a92c23d46c6836a6dd68ae0f08bb04c5
parent984acae12d0dd4e24577c485b90b313c5c2d2089
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13596 from ziglang/cache-path-prefixes

Cache: introduce prefixes to manifests

15 files changed, 400 insertions(+), 321 deletions(-)

ci/linux/build-aarch64.sh+1-4
......@@ -63,7 +63,4 @@ stage3-release/bin/zig build test docs \
6363tidy --drop-empty-elements no -qe ../zig-cache/langref.html
6464
6565# Produce the experimental std lib documentation.
66stage3-release/bin/zig test ../lib/std/std.zig \
67 -femit-docs \
68 -fno-emit-bin \
69 --zig-lib-dir "$(pwd)/../lib"
66stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
ci/linux/build-x86_64-debug.sh+1-4
......@@ -67,7 +67,4 @@ stage3-debug/bin/zig build test \
6767#tidy --drop-empty-elements no -qe ../zig-cache/langref.html
6868
6969# Produce the experimental std lib documentation.
70stage3-debug/bin/zig test ../lib/std/std.zig \
71 -femit-docs \
72 -fno-emit-bin \
73 --zig-lib-dir "$(pwd)/../lib"
70stage3-debug/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
ci/linux/build-x86_64-release.sh+1-4
......@@ -63,10 +63,7 @@ stage3-release/bin/zig build test docs \
6363tidy --drop-empty-elements no -qe ../zig-cache/langref.html
6464
6565# Produce the experimental std lib documentation.
66stage3-release/bin/zig test ../lib/std/std.zig \
67 -femit-docs \
68 -fno-emit-bin \
69 --zig-lib-dir "$(pwd)/../lib"
66stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
7067
7168stage3-release/bin/zig build \
7269 --prefix stage4-release \
ci/macos/build-aarch64.sh+1-5
......@@ -44,8 +44,4 @@ stage3-release/bin/zig build test docs \
4444 --search-prefix "$PREFIX"
4545
4646# Produce the experimental std lib documentation.
47mkdir -p "stage3-release/doc/std"
48stage3-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
47stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
ci/macos/build-x86_64.sh+1-5
......@@ -51,8 +51,4 @@ stage3-release/bin/zig build test docs \
5151 --search-prefix "$PREFIX"
5252
5353# Produce the experimental std lib documentation.
54mkdir -p "stage3-release/doc/std"
55stage3-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
54stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
ci/windows/build.ps1+1-3
......@@ -53,11 +53,9 @@ Write-Output " zig build test docs..."
5353CheckLastExitCode
5454
5555# Produce the experimental std lib documentation.
56mkdir "$ZIGINSTALLDIR\doc\std" -force
57
5856Write-Output "zig test std/std.zig..."
5957
6058& "$ZIGINSTALLDIR\bin\zig.exe" test "$ZIGLIBDIR\std\std.zig" `
6159 --zig-lib-dir "$ZIGLIBDIR" `
62 -femit-docs="$ZIGINSTALLDIR\doc\std" `
60 -femit-docs `
6361 -fno-emit-bin
lib/std/fs/path.zig+179-211
......@@ -467,55 +467,49 @@ pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8 {
467467/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.
468468/// Note: all usage of this function should be audited due to the existence of symlinks.
469469/// Without performing actual syscalls, resolving `..` could be incorrect.
470/// This API may break in the future: https://github.com/ziglang/zig/issues/13613
470471pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
471 if (paths.len == 0) {
472 assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
473 return process.getCwdAlloc(allocator);
474 }
472 assert(paths.len > 0);
475473
476474 // determine which disk designator we will result with, if any
477475 var result_drive_buf = "_:".*;
478 var result_disk_designator: []const u8 = "";
479 var have_drive_kind = WindowsPath.Kind.None;
476 var disk_designator: []const u8 = "";
477 var drive_kind = WindowsPath.Kind.None;
480478 var have_abs_path = false;
481479 var first_index: usize = 0;
482 var max_size: usize = 0;
483480 for (paths) |p, i| {
484481 const parsed = windowsParsePath(p);
485482 if (parsed.is_abs) {
486483 have_abs_path = true;
487484 first_index = i;
488 max_size = result_disk_designator.len;
489485 }
490486 switch (parsed.kind) {
491 WindowsPath.Kind.Drive => {
487 .Drive => {
492488 result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]);
493 result_disk_designator = result_drive_buf[0..];
494 have_drive_kind = WindowsPath.Kind.Drive;
489 disk_designator = result_drive_buf[0..];
490 drive_kind = WindowsPath.Kind.Drive;
495491 },
496 WindowsPath.Kind.NetworkShare => {
497 result_disk_designator = parsed.disk_designator;
498 have_drive_kind = WindowsPath.Kind.NetworkShare;
492 .NetworkShare => {
493 disk_designator = parsed.disk_designator;
494 drive_kind = WindowsPath.Kind.NetworkShare;
499495 },
500 WindowsPath.Kind.None => {},
496 .None => {},
501497 }
502 max_size += p.len + 1;
503498 }
504499
505500 // if we will result with a disk designator, loop again to determine
506501 // which is the last time the disk designator is absolutely specified, if any
507502 // 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) {
509504 have_abs_path = false;
510505 first_index = 0;
511 max_size = result_disk_designator.len;
512506 var correct_disk_designator = false;
513507
514508 for (paths) |p, i| {
515509 const parsed = windowsParsePath(p);
516510 if (parsed.kind != WindowsPath.Kind.None) {
517 if (parsed.kind == have_drive_kind) {
518 correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
511 if (parsed.kind == drive_kind) {
512 correct_disk_designator = compareDiskDesignators(drive_kind, disk_designator, parsed.disk_designator);
519513 } else {
520514 continue;
521515 }
......@@ -525,92 +519,51 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
525519 }
526520 if (parsed.is_abs) {
527521 first_index = i;
528 max_size = result_disk_designator.len;
529522 have_abs_path = true;
530523 }
531 max_size += p.len + 1;
532524 }
533525 }
534526
535 // Allocate result and fill in the disk designator, calling getCwd if we have to.
536 var result: []u8 = undefined;
537 var result_index: usize = 0;
538
539 if (have_abs_path) {
540 switch (have_drive_kind) {
541 WindowsPath.Kind.Drive => {
542 result = try allocator.alloc(u8, max_size);
527 // Allocate result and fill in the disk designator.
528 var result = std.ArrayList(u8).init(allocator);
529 defer result.deinit();
543530
544 mem.copy(u8, result, result_disk_designator);
545 result_index += result_disk_designator.len;
531 const disk_designator_len: usize = l: {
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;
546537 },
547 WindowsPath.Kind.NetworkShare => {
548 result = try allocator.alloc(u8, max_size);
538 .NetworkShare => {
549539 var it = mem.tokenize(u8, paths[first_index], "/\\");
550540 const server_name = it.next().?;
551541 const other_name = it.next().?;
552542
553 result[result_index] = '\\';
554 result_index += 1;
555 result[result_index] = '\\';
556 result_index += 1;
557 mem.copy(u8, result[result_index..], server_name);
558 result_index += server_name.len;
559 result[result_index] = '\\';
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];
543 try result.ensureUnusedCapacity(2 + 1 + server_name.len + other_name.len);
544 result.appendSliceAssumeCapacity("\\\\");
545 result.appendSliceAssumeCapacity(server_name);
546 result.appendAssumeCapacity('\\');
547 result.appendSliceAssumeCapacity(other_name);
548
549 break :l result.items.len;
565550 },
566 WindowsPath.Kind.None => {
567 assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
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;
551 .None => {
552 break :l 1;
579553 },
580554 }
581 } else {
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);
555 };
604556
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.
607557 var correct_disk_designator = true;
558 var negative_count: usize = 0;
559
608560 for (paths[first_index..]) |p| {
609561 const parsed = windowsParsePath(p);
610562
611 if (parsed.kind != WindowsPath.Kind.None) {
612 if (parsed.kind == have_drive_kind) {
613 correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
563 if (parsed.kind != .None) {
564 if (parsed.kind == drive_kind) {
565 const dd = result.items[0..disk_designator_len];
566 correct_disk_designator = compareDiskDesignators(drive_kind, dd, parsed.disk_designator);
614567 } else {
615568 continue;
616569 }
......@@ -619,154 +572,167 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
619572 continue;
620573 }
621574 var it = mem.tokenize(u8, p[parsed.disk_designator.len..], "/\\");
622 while (it.next()) |component| {
575 component: while (it.next()) |component| {
623576 if (mem.eql(u8, component, ".")) {
624577 continue;
625578 } else if (mem.eql(u8, component, "..")) {
626579 while (true) {
627 if (result_index == 0 or result_index == result_disk_designator.len)
628 break;
629 result_index -= 1;
630 if (result[result_index] == '\\' or result[result_index] == '/')
580 if (result.items.len == 0) {
581 negative_count += 1;
582 continue :component;
583 }
584 if (result.items.len == disk_designator_len) {
631585 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;
632593 }
594 } else if (!have_abs_path and result.items.len == 0) {
595 try result.appendSlice(component);
633596 } else {
634 result[result_index] = sep_windows;
635 result_index += 1;
636 mem.copy(u8, result[result_index..], component);
637 result_index += component.len;
597 try result.ensureUnusedCapacity(1 + component.len);
598 result.appendAssumeCapacity('\\');
599 result.appendSliceAssumeCapacity(component);
638600 }
639601 }
640602 }
641603
642 if (result_index == result_disk_designator.len) {
643 result[result_index] = '\\';
644 result_index += 1;
604 if (disk_designator_len != 0 and result.items.len == disk_designator_len) {
605 try result.append('\\');
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 }
645623 }
646624
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 }
648638}
649639
650640/// This function is like a series of `cd` statements executed one after another.
651641/// It resolves "." and "..".
652642/// 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.
654/// Note: all usage of this function should be audited due to the existence of symlinks.
655/// Without performing actual syscalls, resolving `..` could be incorrect.
656pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 {
657 if (paths.len == 0) {
658 assert(native_os != .windows); // resolvePosix called on windows can't use getCwd
659 return process.getCwdAlloc(allocator);
660 }
643/// This function does not perform any syscalls. Executing this series of path
644/// lookups on the actual filesystem may produce different results due to
645/// symlinks.
646pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) Allocator.Error![]u8 {
647 assert(paths.len > 0);
661648
662 var first_index: usize = 0;
663 var have_abs = false;
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;
649 var result = std.ArrayList(u8).init(allocator);
650 defer result.deinit();
676651
677 if (have_abs) {
678 result = try allocator.alloc(u8, max_size);
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);
652 var negative_count: usize = 0;
653 var is_abs = false;
688654
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 }
690661 var it = mem.tokenize(u8, p, "/");
691 while (it.next()) |component| {
662 component: while (it.next()) |component| {
692663 if (mem.eql(u8, component, ".")) {
693664 continue;
694665 } else if (mem.eql(u8, component, "..")) {
695666 while (true) {
696 if (result_index == 0)
697 break;
698 result_index -= 1;
699 if (result[result_index] == '/')
700 break;
667 if (result.items.len == 0) {
668 negative_count += @boolToInt(!is_abs);
669 continue :component;
670 }
671 const ends_with_slash = result.items[result.items.len - 1] == '/';
672 result.items.len -= 1;
673 if (ends_with_slash) break;
701674 }
675 } else if (result.items.len > 0 or is_abs) {
676 try result.ensureUnusedCapacity(1 + component.len);
677 result.appendAssumeCapacity('/');
678 result.appendSliceAssumeCapacity(component);
702679 } else {
703 result[result_index] = '/';
704 result_index += 1;
705 mem.copy(u8, result[result_index..], component);
706 result_index += component.len;
680 try result.appendSlice(component);
707681 }
708682 }
709683 }
710684
711 if (result_index == 0) {
712 result[0] = '/';
713 result_index += 1;
685 if (result.items.len == 0) {
686 if (is_abs) {
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 }
714702 }
715703
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 }
717717}
718718
719719test "resolve" {
720 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
721 if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
720 try testResolveWindows(&[_][]const u8{ "a\\b\\c\\", "..\\..\\.." }, "..");
721 try testResolveWindows(&[_][]const u8{"."}, ".");
722722
723 const cwd = try process.getCwdAlloc(testing.allocator);
724 defer testing.allocator.free(cwd);
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 }
723 try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, "..");
724 try testResolvePosix(&[_][]const u8{"."}, ".");
734725}
735726
736727test "resolveWindows" {
737 if (builtin.target.cpu.arch == .aarch64) {
738 // TODO https://github.com/ziglang/zig/issues/3288
739 return error.SkipZigTest;
740 }
741 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
742 if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
743 if (native_os == .windows) {
744 const cwd = try process.getCwdAlloc(testing.allocator);
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 }
728 try testResolveWindows(
729 &[_][]const u8{ "Z:\\", "/usr/local", "lib\\zig\\std\\array_list.zig" },
730 "Z:\\usr\\local\\lib\\zig\\std\\array_list.zig",
731 );
732 try testResolveWindows(
733 &[_][]const u8{ "z:\\", "usr/local", "lib\\zig" },
734 "Z:\\usr\\local\\lib\\zig",
735 );
770736
771737 try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok");
772738 try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a");
......@@ -781,12 +747,12 @@ test "resolveWindows" {
781747 try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\");
782748 try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir");
783749 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");
784753}
785754
786755test "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
790756 try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
791757 try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");
792758 try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a");
......@@ -797,18 +763,21 @@ test "resolvePosix" {
797763 try testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }, "/file");
798764 try testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }, "/absolute");
799765 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");
800769}
801770
802771fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void {
803772 const actual = try resolveWindows(testing.allocator, paths);
804773 defer testing.allocator.free(actual);
805 try testing.expect(mem.eql(u8, actual, expected));
774 try testing.expectEqualStrings(expected, actual);
806775}
807776
808777fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void {
809778 const actual = try resolvePosix(testing.allocator, paths);
810779 defer testing.allocator.free(actual);
811 try testing.expect(mem.eql(u8, actual, expected));
780 try testing.expectEqualStrings(expected, actual);
812781}
813782
814783/// Strip the last component from a file path.
......@@ -1089,13 +1058,15 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
10891058 if (parsed_from.kind != parsed_to.kind) {
10901059 break :x true;
10911060 } else switch (parsed_from.kind) {
1092 WindowsPath.Kind.NetworkShare => {
1061 .NetworkShare => {
10931062 break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator);
10941063 },
1095 WindowsPath.Kind.Drive => {
1064 .Drive => {
10961065 break :x ascii.toUpper(parsed_from.disk_designator[0]) != ascii.toUpper(parsed_to.disk_designator[0]);
10971066 },
1098 else => unreachable,
1067 .None => {
1068 break :x false;
1069 },
10991070 }
11001071 };
11011072
......@@ -1194,13 +1165,6 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]
11941165}
11951166
11961167test "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
12041168 try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
12051169 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
12061170 try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
......@@ -1226,6 +1190,10 @@ test "relative" {
12261190 try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz");
12271191 try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz");
12281192
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
12291197 try testRelativePosix("/var/lib", "/var", "..");
12301198 try testRelativePosix("/var/lib", "/bin", "../../bin");
12311199 try testRelativePosix("/var/lib", "/var/lib", "");
......@@ -1243,13 +1211,13 @@ test "relative" {
12431211fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void {
12441212 const result = try relativePosix(testing.allocator, from, to);
12451213 defer testing.allocator.free(result);
1246 try testing.expectEqualSlices(u8, expected_output, result);
1214 try testing.expectEqualStrings(expected_output, result);
12471215}
12481216
12491217fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void {
12501218 const result = try relativeWindows(testing.allocator, from, to);
12511219 defer testing.allocator.free(result);
1252 try testing.expectEqualSlices(u8, expected_output, result);
1220 try testing.expectEqualStrings(expected_output, result);
12531221}
12541222
12551223/// 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)" {
10951095
10961096 const allocator = testing.allocator;
10971097
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" };
10991101 const filename = try fs.path.resolve(allocator, &file_paths);
11001102 defer allocator.free(filename);
11011103
lib/std/fs/wasi.zig+1-4
......@@ -202,10 +202,7 @@ pub const PreopenList = struct {
202202 // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "."
203203 const path = if (cwd_root) |cwd| blk: {
204204 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) {
206 error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried
207 else => |e| return e,
208 };
205 break :blk try fs.path.resolve(self.buffer.allocator, resolve_paths);
209206 } else blk: {
210207 // If we were provided no CWD root, we preserve the preopen dir without resolving
211208 break :blk try self.buffer.allocator.dupe(u8, raw_path);
src/Cache.zig+139-39
......@@ -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
15gpa: Allocator,
26manifest_dir: fs.Dir,
37hash: HashHelper = .{},
......@@ -5,6 +9,14 @@ hash: HashHelper = .{},
59recent_problematic_timestamp: i128 = 0,
610mutex: std.Thread.Mutex = .{},
711
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.
17prefixes_buffer: [3]Compilation.Directory = undefined,
18prefixes_len: usize = 0,
19
820const Cache = @This();
921const std = @import("std");
1022const builtin = @import("builtin");
......@@ -18,6 +30,14 @@ const Allocator = std.mem.Allocator;
1830const Compilation = @import("Compilation.zig");
1931const log = std.log.scoped(.cache);
2032
33pub 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
2141/// Be sure to call `Manifest.deinit` after successful initialization.
2242pub fn obtain(cache: *Cache) Manifest {
2343 return Manifest{
......@@ -29,6 +49,48 @@ pub fn obtain(cache: *Cache) Manifest {
2949 };
3050}
3151
52pub fn prefixes(cache: *const Cache) []const Compilation.Directory {
53 return cache.prefixes_buffer[0..cache.prefixes_len];
54}
55
56const PrefixedPath = struct {
57 prefix: u8,
58 sub_path: []u8,
59};
60
61fn 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.
69fn 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
3294/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
3395pub const bin_digest_len = 16;
3496pub const hex_digest_len = bin_digest_len * 2;
......@@ -45,7 +107,7 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
45107pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length);
46108
47109pub const File = struct {
48 path: ?[]const u8,
110 prefixed_path: ?PrefixedPath,
49111 max_file_size: ?usize,
50112 stat: Stat,
51113 bin_digest: BinDigest,
......@@ -57,13 +119,13 @@ pub const File = struct {
57119 mtime: i128,
58120 };
59121
60 pub fn deinit(self: *File, allocator: Allocator) void {
61 if (self.path) |owned_slice| {
62 allocator.free(owned_slice);
63 self.path = null;
122 pub fn deinit(self: *File, gpa: Allocator) void {
123 if (self.prefixed_path) |pp| {
124 gpa.free(pp.sub_path);
125 self.prefixed_path = null;
64126 }
65127 if (self.contents) |contents| {
66 allocator.free(contents);
128 gpa.free(contents);
67129 self.contents = null;
68130 }
69131 self.* = undefined;
......@@ -175,9 +237,6 @@ pub const Lock = struct {
175237 }
176238};
177239
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.
181240pub const Manifest = struct {
182241 cache: *Cache,
183242 /// Current state for incremental hashing.
......@@ -220,21 +279,27 @@ pub const Manifest = struct {
220279 pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize {
221280 assert(self.manifest_file == null);
222281
223 try self.files.ensureUnusedCapacity(self.cache.gpa, 1);
224 const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
282 const gpa = self.cache.gpa;
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 });
225290
226 const idx = self.files.items.len;
227291 self.files.addOneAssumeCapacity().* = .{
228 .path = resolved_path,
292 .prefixed_path = prefixed_path,
229293 .contents = null,
230294 .max_file_size = max_file_size,
231295 .stat = undefined,
232296 .bin_digest = undefined,
233297 };
234298
235 self.hash.addBytes(resolved_path);
299 self.hash.add(prefixed_path.prefix);
300 self.hash.addBytes(prefixed_path.sub_path);
236301
237 return idx;
302 return self.files.items.len - 1;
238303 }
239304
240305 pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void {
......@@ -281,6 +346,7 @@ pub const Manifest = struct {
281346 /// option, one may call `toOwnedLock` to obtain a smaller object which can represent
282347 /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called.
283348 pub fn hit(self: *Manifest) !bool {
349 const gpa = self.cache.gpa;
284350 assert(self.manifest_file == null);
285351
286352 self.failed_file_index = null;
......@@ -362,8 +428,8 @@ pub const Manifest = struct {
362428
363429 self.want_refresh_timestamp = true;
364430
365 const file_contents = try self.manifest_file.?.reader().readAllAlloc(self.cache.gpa, manifest_file_size_max);
366 defer self.cache.gpa.free(file_contents);
431 const file_contents = try self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max);
432 defer gpa.free(file_contents);
367433
368434 const input_file_count = self.files.items.len;
369435 var any_file_changed = false;
......@@ -373,9 +439,9 @@ pub const Manifest = struct {
373439 defer idx += 1;
374440
375441 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);
377443 new.* = .{
378 .path = null,
444 .prefixed_path = null,
379445 .contents = null,
380446 .max_file_size = null,
381447 .stat = undefined,
......@@ -389,27 +455,35 @@ pub const Manifest = struct {
389455 const inode = iter.next() orelse return error.InvalidFormat;
390456 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
391457 const digest_str = iter.next() orelse return error.InvalidFormat;
458 const prefix_str = iter.next() orelse return error.InvalidFormat;
392459 const file_path = iter.rest();
393460
394461 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
395462 cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
396463 cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
397464 _ = 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;
398467
399468 if (file_path.len == 0) {
400469 return error.InvalidFormat;
401470 }
402 if (cache_hash_file.path) |p| {
403 if (!mem.eql(u8, file_path, p)) {
471 if (cache_hash_file.prefixed_path) |pp| {
472 if (pp.prefix != prefix or !mem.eql(u8, file_path, pp.sub_path)) {
404473 return error.InvalidFormat;
405474 }
406475 }
407476
408 if (cache_hash_file.path == null) {
409 cache_hash_file.path = try self.cache.gpa.dupe(u8, file_path);
477 if (cache_hash_file.prefixed_path == null) {
478 cache_hash_file.prefixed_path = .{
479 .prefix = prefix,
480 .sub_path = try gpa.dupe(u8, file_path),
481 };
410482 }
411483
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) {
413487 error.FileNotFound => {
414488 try self.upgradeToExclusiveLock();
415489 return false;
......@@ -535,8 +609,9 @@ pub const Manifest = struct {
535609 }
536610
537611 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
538 log.debug("populateFileHash {s}", .{ch_file.path.?});
539 const file = try fs.cwd().openFile(ch_file.path.?, .{});
612 const pp = ch_file.prefixed_path.?;
613 const dir = self.cache.prefixes()[pp.prefix].handle;
614 const file = try dir.openFile(pp.sub_path, .{});
540615 defer file.close();
541616
542617 const actual_stat = try file.stat();
......@@ -588,12 +663,17 @@ pub const Manifest = struct {
588663 pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 {
589664 assert(self.manifest_file != null);
590665
591 const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
592 errdefer self.cache.gpa.free(resolved_path);
666 const gpa = self.cache.gpa;
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 });
593673
594 const new_ch_file = try self.files.addOne(self.cache.gpa);
674 const new_ch_file = try self.files.addOne(gpa);
595675 new_ch_file.* = .{
596 .path = resolved_path,
676 .prefixed_path = prefixed_path,
597677 .max_file_size = max_file_size,
598678 .stat = undefined,
599679 .bin_digest = undefined,
......@@ -613,12 +693,17 @@ pub const Manifest = struct {
613693 pub fn addFilePost(self: *Manifest, file_path: []const u8) !void {
614694 assert(self.manifest_file != null);
615695
616 const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
617 errdefer self.cache.gpa.free(resolved_path);
696 const gpa = self.cache.gpa;
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 });
618703
619 const new_ch_file = try self.files.addOne(self.cache.gpa);
704 const new_ch_file = try self.files.addOne(gpa);
620705 new_ch_file.* = .{
621 .path = resolved_path,
706 .prefixed_path = prefixed_path,
622707 .max_file_size = null,
623708 .stat = undefined,
624709 .bin_digest = undefined,
......@@ -633,17 +718,27 @@ pub const Manifest = struct {
633718 /// On success, cache takes ownership of `resolved_path`.
634719 pub fn addFilePostContents(
635720 self: *Manifest,
636 resolved_path: []const u8,
721 resolved_path: []u8,
637722 bytes: []const u8,
638723 stat: File.Stat,
639724 ) error{OutOfMemory}!void {
640725 assert(self.manifest_file != null);
726 const gpa = self.cache.gpa;
641727
642 const ch_file = try self.files.addOne(self.cache.gpa);
728 const ch_file = try self.files.addOne(gpa);
643729 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
644730
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
645740 ch_file.* = .{
646 .path = resolved_path,
741 .prefixed_path = prefixed_path,
647742 .max_file_size = null,
648743 .stat = stat,
649744 .bin_digest = undefined,
......@@ -742,12 +837,13 @@ pub const Manifest = struct {
742837 "{s}",
743838 .{std.fmt.fmtSliceHexLower(&file.bin_digest)},
744839 ) catch unreachable;
745 try writer.print("{d} {d} {d} {s} {s}\n", .{
840 try writer.print("{d} {d} {d} {s} {d} {s}\n", .{
746841 file.stat.size,
747842 file.stat.inode,
748843 file.stat.mtime,
749844 &encoded_digest,
750 file.path.?,
845 file.prefixed_path.?.prefix,
846 file.prefixed_path.?.sub_path,
751847 });
752848 }
753849
......@@ -889,6 +985,7 @@ test "cache file and then recall it" {
889985 .gpa = testing.allocator,
890986 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
891987 };
988 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
892989 defer cache.manifest_dir.close();
893990
894991 {
......@@ -960,6 +1057,7 @@ test "check that changing a file makes cache fail" {
9601057 .gpa = testing.allocator,
9611058 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
9621059 };
1060 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
9631061 defer cache.manifest_dir.close();
9641062
9651063 {
......@@ -1022,6 +1120,7 @@ test "no file inputs" {
10221120 .gpa = testing.allocator,
10231121 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
10241122 };
1123 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
10251124 defer cache.manifest_dir.close();
10261125
10271126 {
......@@ -1080,6 +1179,7 @@ test "Manifest with files added after initial hash work" {
10801179 .gpa = testing.allocator,
10811180 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
10821181 };
1182 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
10831183 defer cache.manifest_dir.close();
10841184
10851185 {
src/Compilation.zig+27-18
......@@ -201,7 +201,9 @@ pub const CRTFile = struct {
201201/// For passing to a C compiler.
202202pub const CSourceFile = struct {
203203 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 = &.{},
205207};
206208
207209const Job = union(enum) {
......@@ -1456,23 +1458,27 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
14561458 else => @as(u8, 3),
14571459 };
14581460
1459 // We put everything into the cache hash that *cannot be modified during an incremental update*.
1460 // For example, one cannot change the target between updates, but one can change source files,
1461 // so the target goes into the cache hash, but source files do not. This is so that we can
1462 // find the same binary and incrementally update it even if there are modified source files.
1463 // We do this even if outputting to the current directory because we need somewhere to store
1464 // incremental compilation metadata.
1461 // We put everything into the cache hash that *cannot be modified
1462 // during an incremental update*. For example, one cannot change the
1463 // target between updates, but one can change source files, so the
1464 // target goes into the cache hash, but source files do not. This is so
1465 // that we can find the same binary and incrementally update it even if
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.
14651469 const cache = try arena.create(Cache);
14661470 cache.* = .{
14671471 .gpa = gpa,
14681472 .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}),
14691473 };
1474 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1475 cache.addPrefix(options.zig_lib_directory);
1476 cache.addPrefix(options.local_cache_directory);
14701477 errdefer cache.manifest_dir.close();
14711478
14721479 // This is shared hasher state common to zig source and all C source files.
14731480 cache.hash.addBytes(build_options.version);
14741481 cache.hash.add(builtin.zig_backend);
1475 cache.hash.addBytes(options.zig_lib_directory.path orelse ".");
14761482 cache.hash.add(options.optimize_mode);
14771483 cache.hash.add(options.target.cpu.arch);
14781484 cache.hash.addBytes(options.target.cpu.model.name);
......@@ -2265,8 +2271,9 @@ pub fn update(comp: *Compilation) !void {
22652271 const is_hit = man.hit() catch |err| {
22662272 // TODO properly bubble these up instead of emitting a warning
22672273 const i = man.failed_file_index orelse return err;
2268 const file_path = man.files.items[i].path orelse return err;
2269 std.log.warn("{s}: {s}", .{ @errorName(err), file_path });
2274 const pp = man.files.items[i].prefixed_path orelse return err;
2275 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
2276 std.log.warn("{s}: {s}{s}", .{ @errorName(err), prefix, pp.sub_path });
22702277 return err;
22712278 };
22722279 if (is_hit) {
......@@ -3246,13 +3253,6 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
32463253
32473254 const module = comp.bin_file.options.module.?;
32483255 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 ),
32563256 error.OutOfMemory => return error.OutOfMemory,
32573257 error.AnalysisFail => return,
32583258 };
......@@ -3557,7 +3557,14 @@ pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest {
35573557 man.hash.add(comp.sanitize_c);
35583558 man.hash.addListOfBytes(comp.clang_argv);
35593559 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 }
35613568
35623569 return man;
35633570}
......@@ -3944,6 +3951,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
39443951 {
39453952 try comp.addCCArgs(arena, &argv, ext, null);
39463953 try argv.appendSlice(c_object.src.extra_flags);
3954 try argv.appendSlice(c_object.src.cache_exempt_flags);
39473955
39483956 const out_obj_path = if (comp.bin_file.options.emit) |emit|
39493957 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
39853993 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
39863994 try comp.addCCArgs(arena, &argv, ext, out_dep_path);
39873995 try argv.appendSlice(c_object.src.extra_flags);
3996 try argv.appendSlice(c_object.src.cache_exempt_flags);
39883997
39893998 try argv.ensureUnusedCapacity(5);
39903999 switch (comp.clang_preprocessor_mode) {
src/glibc.zig+3
......@@ -653,6 +653,9 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
653653 .gpa = comp.gpa,
654654 .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
655655 };
656 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
657 cache.addPrefix(comp.zig_lib_directory);
658 cache.addPrefix(comp.global_cache_directory);
656659 defer cache.manifest_dir.close();
657660
658661 var man = cache.obtain();
src/libcxx.zig+30-18
......@@ -187,15 +187,6 @@ pub fn buildLibCXX(comp: *Compilation) !void {
187187 try cflags.append("-faligned-allocation");
188188 }
189189
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
199190 if (target_util.supports_fpic(target)) {
200191 try cflags.append("-fPIC");
201192 }
......@@ -203,9 +194,24 @@ pub fn buildLibCXX(comp: *Compilation) !void {
203194 try cflags.append("-std=c++20");
204195 try cflags.append("-Wno-user-defined-literals");
205196
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
206211 c_source_files.appendAssumeCapacity(.{
207212 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }),
208213 .extra_flags = cflags.items,
214 .cache_exempt_flags = cache_exempt_flags.items,
209215 });
210216 }
211217
......@@ -340,15 +346,6 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
340346 try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC");
341347 }
342348
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
352349 if (target_util.supports_fpic(target)) {
353350 try cflags.append("-fPIC");
354351 }
......@@ -357,9 +354,24 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
357354 try cflags.append("-funwind-tables");
358355 try cflags.append("-std=c++20");
359356
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
360371 c_source_files.appendAssumeCapacity(.{
361372 .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }),
362373 .extra_flags = cflags.items,
374 .cache_exempt_flags = cache_exempt_flags.items,
363375 });
364376 }
365377
src/main.zig+8-5
......@@ -2744,11 +2744,14 @@ fn buildOutputType(
27442744 }
27452745
27462746 const self_exe_path = try introspect.findZigExePath(arena);
2747 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
2748 .path = lib_dir,
2749 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
2750 fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
2751 },
2747 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |unresolved_lib_dir| l: {
2748 const lib_dir = try fs.path.resolve(arena, &.{unresolved_lib_dir});
2749 break :l .{
2750 .path = lib_dir,
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 };
27522755 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
27532756 fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
27542757 };
src/mingw.zig+4
......@@ -302,6 +302,10 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
302302 .gpa = comp.gpa,
303303 .manifest_dir = comp.cache_parent.manifest_dir,
304304 };
305 for (comp.cache_parent.prefixes()) |prefix| {
306 cache.addPrefix(prefix);
307 }
308
305309 cache.hash.addBytes(build_options.version);
306310 cache.hash.addOptionalBytes(comp.zig_lib_directory.path);
307311 cache.hash.add(target.cpu.arch);