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 \...@@ -63,7 +63,4 @@ stage3-release/bin/zig build test docs \
63tidy --drop-empty-elements no -qe ../zig-cache/langref.html63tidy --drop-empty-elements no -qe ../zig-cache/langref.html
6464
65# Produce the experimental std lib documentation.65# Produce the experimental std lib documentation.
66stage3-release/bin/zig test ../lib/std/std.zig \66stage3-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.html67#tidy --drop-empty-elements no -qe ../zig-cache/langref.html
6868
69# Produce the experimental std lib documentation.69# Produce the experimental std lib documentation.
70stage3-debug/bin/zig test ../lib/std/std.zig \70stage3-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 \
63tidy --drop-empty-elements no -qe ../zig-cache/langref.html63tidy --drop-empty-elements no -qe ../zig-cache/langref.html
6464
65# Produce the experimental std lib documentation.65# Produce the experimental std lib documentation.
66stage3-release/bin/zig test ../lib/std/std.zig \66stage3-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"
7067
71stage3-release/bin/zig build \68stage3-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"
4545
46# Produce the experimental std lib documentation.46# Produce the experimental std lib documentation.
47mkdir -p "stage3-release/doc/std"47stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
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
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"
5252
53# Produce the experimental std lib documentation.53# Produce the experimental std lib documentation.
54mkdir -p "stage3-release/doc/std"54stage3-release/bin/zig test ../lib/std/std.zig -femit-docs -fno-emit-bin --zig-lib-dir ../lib
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
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..."
53CheckLastExitCode53CheckLastExitCode
5454
55# Produce the experimental std lib documentation.55# Produce the experimental std lib documentation.
56mkdir "$ZIGINSTALLDIR\doc\std" -force
57
58Write-Output "zig test std/std.zig..."56Write-Output "zig test std/std.zig..."
5957
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-bin61 -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
470pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {471pub 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 }
475473
476 // determine which disk designator we will result with, if any474 // 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 }
504499
505 // if we will result with a disk designator, loop again to determine500 // 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 any501 // 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 designator502 // 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;
513507
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 }
534526
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);
543530
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().?;
552542
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 getCwd552 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);
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.
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);
610562
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 }
641603
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 }
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 }
648}638}
649639
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.
656pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 {646pub 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 }
661648
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;
676651
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);
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 }
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 }
710684
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 }
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 }
717}717}
718718
719test "resolve" {719test "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{"."}, ".");
722722
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}
735726
736test "resolveWindows" {727test "resolveWindows" {
737 if (builtin.target.cpu.arch == .aarch64) {728 try testResolveWindows(
738 // TODO https://github.com/ziglang/zig/issues/3288729 &[_][]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 }
770736
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}
785754
786test "resolvePosix" {755test "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}
801770
802fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void {771fn 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}
807776
808fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void {777fn 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}
813782
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 };
11011072
...@@ -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}
11951166
1196test "relative" {1167test "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");
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
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" {
1243fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void {1211fn 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}
12481216
1249fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void {1217fn 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}
12541222
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)" {
10951095
1096 const allocator = testing.allocator;1096 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" };
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);
11011103
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 resolving207 // 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
1gpa: Allocator,5gpa: Allocator,
2manifest_dir: fs.Dir,6manifest_dir: fs.Dir,
3hash: HashHelper = .{},7hash: HashHelper = .{},
...@@ -5,6 +9,14 @@ hash: HashHelper = .{},...@@ -5,6 +9,14 @@ hash: HashHelper = .{},
5recent_problematic_timestamp: i128 = 0,9recent_problematic_timestamp: i128 = 0,
6mutex: std.Thread.Mutex = .{},10mutex: 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
8const Cache = @This();20const Cache = @This();
9const std = @import("std");21const std = @import("std");
10const builtin = @import("builtin");22const builtin = @import("builtin");
...@@ -18,6 +30,14 @@ const Allocator = std.mem.Allocator;...@@ -18,6 +30,14 @@ const Allocator = std.mem.Allocator;
18const Compilation = @import("Compilation.zig");30const Compilation = @import("Compilation.zig");
19const log = std.log.scoped(.cache);31const 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
21/// Be sure to call `Manifest.deinit` after successful initialization.41/// Be sure to call `Manifest.deinit` after successful initialization.
22pub fn obtain(cache: *Cache) Manifest {42pub 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}
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
32/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-694/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
33pub const bin_digest_len = 16;95pub const bin_digest_len = 16;
34pub const hex_digest_len = bin_digest_len * 2;96pub 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);
45pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length);107pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length);
46108
47pub const File = struct {109pub 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 };
59121
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};
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.
181pub const Manifest = struct {240pub 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);
222281
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 });
225290
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 };
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;
238 }303 }
239304
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 represent346 /// 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);
285351
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 {
362428
363 self.want_refresh_timestamp = true;429 self.want_refresh_timestamp = true;
364430
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);
367433
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;
374440
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();
393460
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;
398467
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 }
407476
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 }
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) {
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 }
536610
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();
541616
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);
590665
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 });
593673
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);
615695
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 });
618703
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;
641727
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);
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
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 }
753849
...@@ -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();
893990
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();
9641062
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();
10261125
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();
10841184
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.
202pub const CSourceFile = struct {202pub 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};
206208
207const Job = union(enum) {209const 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 };
14581460
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 can1463 // 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 store1465 // 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();
14711478
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 warning2272 // 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 {
32463253
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 }
35613568
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);
39473955
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);
39883997
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();
657660
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 }
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
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");
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
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 }
211217
...@@ -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 }
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
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");
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
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 }
365377
src/main.zig+8-5
...@@ -2744,11 +2744,14 @@ fn buildOutputType(...@@ -2744,11 +2744,14 @@ fn buildOutputType(
2744 }2744 }
27452745
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);