authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-19 13:48:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-22 20:57:56-07:00
log21bd13626d66c36c327bb317bd09cad979d92327
tree0339aef23b4655448e6a71cdfca1840f66c69092
parent32ce2f91a92c23d46c6836a6dd68ae0f08bb04c5

Cache: introduce prefixes to manifests

Before, cache manifest files would have absolute file paths. This is problematic for two reasons: * Absolute file paths are not portable. Some operating systems such as WASI have trouble with them. The files themselves are less portable; they cannot be migrated from one user's home directory to another's. And finally they can break due to file paths exceeding maximum path component size. * They would prevent some advanced use cases of Zig, where the lib dir has a different path in a different invocation but is ultimately the same Zig version and lib directory as before. This commit adds a new column that specifies the prefix directory for each file. 0 is an escape hatch and has the previous behavior. The other two prefixes introduced are zig lib directory, and the cache directory. This means files in zig-cache manifests can reference files local to these directories. In practice, this means it is possible to use a different file path for the zig lib directory in a subsequent run of zig and have it still take advantage of the global cache, provided that the files inside remain unchanged. closes #13050

4 files changed, 157 insertions(+), 48 deletions(-)

src/Cache.zig+136-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,11 @@ const Allocator = std.mem.Allocator;...@@ -18,6 +30,11 @@ 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 cache.prefixes_buffer[cache.prefixes_len] = directory;
35 cache.prefixes_len += 1;
36}
37
21/// Be sure to call `Manifest.deinit` after successful initialization.38/// Be sure to call `Manifest.deinit` after successful initialization.
22pub fn obtain(cache: *Cache) Manifest {39pub fn obtain(cache: *Cache) Manifest {
23 return Manifest{40 return Manifest{
...@@ -29,6 +46,48 @@ pub fn obtain(cache: *Cache) Manifest {...@@ -29,6 +46,48 @@ pub fn obtain(cache: *Cache) Manifest {
29 };46 };
30}47}
3148
49pub fn prefixes(cache: *const Cache) []const Compilation.Directory {
50 return cache.prefixes_buffer[0..cache.prefixes_len];
51}
52
53const PrefixedPath = struct {
54 prefix: u8,
55 sub_path: []u8,
56};
57
58fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath {
59 const gpa = cache.gpa;
60 const resolved_path = try fs.path.resolve(gpa, &[_][]const u8{file_path});
61 errdefer gpa.free(resolved_path);
62 return findPrefixResolved(cache, resolved_path);
63}
64
65/// Takes ownership of `resolved_path` on success.
66fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath {
67 const gpa = cache.gpa;
68 const prefixes_slice = cache.prefixes();
69 var i: u8 = 1; // Start at 1 to skip over checking the null prefix.
70 while (i < prefixes_slice.len) : (i += 1) {
71 const p = prefixes_slice[i].path.?;
72 if (mem.startsWith(u8, resolved_path, p)) {
73 // +1 to skip over the path separator here
74 const sub_path = try gpa.dupe(u8, resolved_path[p.len + 1 ..]);
75 gpa.free(resolved_path);
76 return PrefixedPath{
77 .prefix = @intCast(u8, i),
78 .sub_path = sub_path,
79 };
80 } else {
81 log.debug("'{s}' does not start with '{s}'", .{ resolved_path, p });
82 }
83 }
84
85 return PrefixedPath{
86 .prefix = 0,
87 .sub_path = resolved_path,
88 };
89}
90
32/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-691/// 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;92pub const bin_digest_len = 16;
34pub const hex_digest_len = bin_digest_len * 2;93pub const hex_digest_len = bin_digest_len * 2;
...@@ -45,7 +104,7 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);...@@ -45,7 +104,7 @@ pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
45pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length);104pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length);
46105
47pub const File = struct {106pub const File = struct {
48 path: ?[]const u8,107 prefixed_path: ?PrefixedPath,
49 max_file_size: ?usize,108 max_file_size: ?usize,
50 stat: Stat,109 stat: Stat,
51 bin_digest: BinDigest,110 bin_digest: BinDigest,
...@@ -57,13 +116,13 @@ pub const File = struct {...@@ -57,13 +116,13 @@ pub const File = struct {
57 mtime: i128,116 mtime: i128,
58 };117 };
59118
60 pub fn deinit(self: *File, allocator: Allocator) void {119 pub fn deinit(self: *File, gpa: Allocator) void {
61 if (self.path) |owned_slice| {120 if (self.prefixed_path) |pp| {
62 allocator.free(owned_slice);121 gpa.free(pp.sub_path);
63 self.path = null;122 self.prefixed_path = null;
64 }123 }
65 if (self.contents) |contents| {124 if (self.contents) |contents| {
66 allocator.free(contents);125 gpa.free(contents);
67 self.contents = null;126 self.contents = null;
68 }127 }
69 self.* = undefined;128 self.* = undefined;
...@@ -175,9 +234,6 @@ pub const Lock = struct {...@@ -175,9 +234,6 @@ pub const Lock = struct {
175 }234 }
176};235};
177236
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 {237pub const Manifest = struct {
182 cache: *Cache,238 cache: *Cache,
183 /// Current state for incremental hashing.239 /// Current state for incremental hashing.
...@@ -220,21 +276,27 @@ pub const Manifest = struct {...@@ -220,21 +276,27 @@ pub const Manifest = struct {
220 pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize {276 pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize {
221 assert(self.manifest_file == null);277 assert(self.manifest_file == null);
222278
223 try self.files.ensureUnusedCapacity(self.cache.gpa, 1);279 const gpa = self.cache.gpa;
224 const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});280 try self.files.ensureUnusedCapacity(gpa, 1);
281 const prefixed_path = try self.cache.findPrefix(file_path);
282 errdefer gpa.free(prefixed_path.sub_path);
283
284 log.debug("Manifest.addFile {s} -> {d} {s}", .{
285 file_path, prefixed_path.prefix, prefixed_path.sub_path,
286 });
225287
226 const idx = self.files.items.len;
227 self.files.addOneAssumeCapacity().* = .{288 self.files.addOneAssumeCapacity().* = .{
228 .path = resolved_path,289 .prefixed_path = prefixed_path,
229 .contents = null,290 .contents = null,
230 .max_file_size = max_file_size,291 .max_file_size = max_file_size,
231 .stat = undefined,292 .stat = undefined,
232 .bin_digest = undefined,293 .bin_digest = undefined,
233 };294 };
234295
235 self.hash.addBytes(resolved_path);296 self.hash.add(prefixed_path.prefix);
297 self.hash.addBytes(prefixed_path.sub_path);
236298
237 return idx;299 return self.files.items.len - 1;
238 }300 }
239301
240 pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void {302 pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void {
...@@ -281,6 +343,7 @@ pub const Manifest = struct {...@@ -281,6 +343,7 @@ pub const Manifest = struct {
281 /// option, one may call `toOwnedLock` to obtain a smaller object which can represent343 /// 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.344 /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called.
283 pub fn hit(self: *Manifest) !bool {345 pub fn hit(self: *Manifest) !bool {
346 const gpa = self.cache.gpa;
284 assert(self.manifest_file == null);347 assert(self.manifest_file == null);
285348
286 self.failed_file_index = null;349 self.failed_file_index = null;
...@@ -362,8 +425,8 @@ pub const Manifest = struct {...@@ -362,8 +425,8 @@ pub const Manifest = struct {
362425
363 self.want_refresh_timestamp = true;426 self.want_refresh_timestamp = true;
364427
365 const file_contents = try self.manifest_file.?.reader().readAllAlloc(self.cache.gpa, manifest_file_size_max);428 const file_contents = try self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max);
366 defer self.cache.gpa.free(file_contents);429 defer gpa.free(file_contents);
367430
368 const input_file_count = self.files.items.len;431 const input_file_count = self.files.items.len;
369 var any_file_changed = false;432 var any_file_changed = false;
...@@ -373,9 +436,9 @@ pub const Manifest = struct {...@@ -373,9 +436,9 @@ pub const Manifest = struct {
373 defer idx += 1;436 defer idx += 1;
374437
375 const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: {438 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);439 const new = try self.files.addOne(gpa);
377 new.* = .{440 new.* = .{
378 .path = null,441 .prefixed_path = null,
379 .contents = null,442 .contents = null,
380 .max_file_size = null,443 .max_file_size = null,
381 .stat = undefined,444 .stat = undefined,
...@@ -389,27 +452,35 @@ pub const Manifest = struct {...@@ -389,27 +452,35 @@ pub const Manifest = struct {
389 const inode = iter.next() orelse return error.InvalidFormat;452 const inode = iter.next() orelse return error.InvalidFormat;
390 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;453 const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
391 const digest_str = iter.next() orelse return error.InvalidFormat;454 const digest_str = iter.next() orelse return error.InvalidFormat;
455 const prefix_str = iter.next() orelse return error.InvalidFormat;
392 const file_path = iter.rest();456 const file_path = iter.rest();
393457
394 cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;458 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;459 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;460 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;461 _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
462 const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat;
463 if (prefix >= self.cache.prefixes_len) return error.InvalidFormat;
398464
399 if (file_path.len == 0) {465 if (file_path.len == 0) {
400 return error.InvalidFormat;466 return error.InvalidFormat;
401 }467 }
402 if (cache_hash_file.path) |p| {468 if (cache_hash_file.prefixed_path) |pp| {
403 if (!mem.eql(u8, file_path, p)) {469 if (pp.prefix != prefix or !mem.eql(u8, file_path, pp.sub_path)) {
404 return error.InvalidFormat;470 return error.InvalidFormat;
405 }471 }
406 }472 }
407473
408 if (cache_hash_file.path == null) {474 if (cache_hash_file.prefixed_path == null) {
409 cache_hash_file.path = try self.cache.gpa.dupe(u8, file_path);475 cache_hash_file.prefixed_path = .{
476 .prefix = prefix,
477 .sub_path = try gpa.dupe(u8, file_path),
478 };
410 }479 }
411480
412 const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .mode = .read_only }) catch |err| switch (err) {481 const pp = cache_hash_file.prefixed_path.?;
482 const dir = self.cache.prefixes()[pp.prefix].handle;
483 const this_file = dir.openFile(pp.sub_path, .{ .mode = .read_only }) catch |err| switch (err) {
413 error.FileNotFound => {484 error.FileNotFound => {
414 try self.upgradeToExclusiveLock();485 try self.upgradeToExclusiveLock();
415 return false;486 return false;
...@@ -535,8 +606,9 @@ pub const Manifest = struct {...@@ -535,8 +606,9 @@ pub const Manifest = struct {
535 }606 }
536607
537 fn populateFileHash(self: *Manifest, ch_file: *File) !void {608 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
538 log.debug("populateFileHash {s}", .{ch_file.path.?});609 const pp = ch_file.prefixed_path.?;
539 const file = try fs.cwd().openFile(ch_file.path.?, .{});610 const dir = self.cache.prefixes()[pp.prefix].handle;
611 const file = try dir.openFile(pp.sub_path, .{});
540 defer file.close();612 defer file.close();
541613
542 const actual_stat = try file.stat();614 const actual_stat = try file.stat();
...@@ -588,12 +660,17 @@ pub const Manifest = struct {...@@ -588,12 +660,17 @@ pub const Manifest = struct {
588 pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 {660 pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 {
589 assert(self.manifest_file != null);661 assert(self.manifest_file != null);
590662
591 const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});663 const gpa = self.cache.gpa;
592 errdefer self.cache.gpa.free(resolved_path);664 const prefixed_path = try self.cache.findPrefix(file_path);
665 errdefer gpa.free(prefixed_path.sub_path);
666
667 log.debug("Manifest.addFilePostFetch {s} -> {d} {s}", .{
668 file_path, prefixed_path.prefix, prefixed_path.sub_path,
669 });
593670
594 const new_ch_file = try self.files.addOne(self.cache.gpa);671 const new_ch_file = try self.files.addOne(gpa);
595 new_ch_file.* = .{672 new_ch_file.* = .{
596 .path = resolved_path,673 .prefixed_path = prefixed_path,
597 .max_file_size = max_file_size,674 .max_file_size = max_file_size,
598 .stat = undefined,675 .stat = undefined,
599 .bin_digest = undefined,676 .bin_digest = undefined,
...@@ -613,12 +690,17 @@ pub const Manifest = struct {...@@ -613,12 +690,17 @@ pub const Manifest = struct {
613 pub fn addFilePost(self: *Manifest, file_path: []const u8) !void {690 pub fn addFilePost(self: *Manifest, file_path: []const u8) !void {
614 assert(self.manifest_file != null);691 assert(self.manifest_file != null);
615692
616 const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});693 const gpa = self.cache.gpa;
617 errdefer self.cache.gpa.free(resolved_path);694 const prefixed_path = try self.cache.findPrefix(file_path);
695 errdefer gpa.free(prefixed_path.sub_path);
696
697 log.debug("Manifest.addFilePost {s} -> {d} {s}", .{
698 file_path, prefixed_path.prefix, prefixed_path.sub_path,
699 });
618700
619 const new_ch_file = try self.files.addOne(self.cache.gpa);701 const new_ch_file = try self.files.addOne(gpa);
620 new_ch_file.* = .{702 new_ch_file.* = .{
621 .path = resolved_path,703 .prefixed_path = prefixed_path,
622 .max_file_size = null,704 .max_file_size = null,
623 .stat = undefined,705 .stat = undefined,
624 .bin_digest = undefined,706 .bin_digest = undefined,
...@@ -633,17 +715,27 @@ pub const Manifest = struct {...@@ -633,17 +715,27 @@ pub const Manifest = struct {
633 /// On success, cache takes ownership of `resolved_path`.715 /// On success, cache takes ownership of `resolved_path`.
634 pub fn addFilePostContents(716 pub fn addFilePostContents(
635 self: *Manifest,717 self: *Manifest,
636 resolved_path: []const u8,718 resolved_path: []u8,
637 bytes: []const u8,719 bytes: []const u8,
638 stat: File.Stat,720 stat: File.Stat,
639 ) error{OutOfMemory}!void {721 ) error{OutOfMemory}!void {
640 assert(self.manifest_file != null);722 assert(self.manifest_file != null);
723 const gpa = self.cache.gpa;
641724
642 const ch_file = try self.files.addOne(self.cache.gpa);725 const ch_file = try self.files.addOne(gpa);
643 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);726 errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
644727
728 log.debug("Manifest.addFilePostContents resolved_path={s}", .{resolved_path});
729
730 const prefixed_path = try self.cache.findPrefixResolved(resolved_path);
731 errdefer gpa.free(prefixed_path.sub_path);
732
733 log.debug("Manifest.addFilePostContents -> {d} {s}", .{
734 prefixed_path.prefix, prefixed_path.sub_path,
735 });
736
645 ch_file.* = .{737 ch_file.* = .{
646 .path = resolved_path,738 .prefixed_path = prefixed_path,
647 .max_file_size = null,739 .max_file_size = null,
648 .stat = stat,740 .stat = stat,
649 .bin_digest = undefined,741 .bin_digest = undefined,
...@@ -742,12 +834,13 @@ pub const Manifest = struct {...@@ -742,12 +834,13 @@ pub const Manifest = struct {
742 "{s}",834 "{s}",
743 .{std.fmt.fmtSliceHexLower(&file.bin_digest)},835 .{std.fmt.fmtSliceHexLower(&file.bin_digest)},
744 ) catch unreachable;836 ) catch unreachable;
745 try writer.print("{d} {d} {d} {s} {s}\n", .{837 try writer.print("{d} {d} {d} {s} {d} {s}\n", .{
746 file.stat.size,838 file.stat.size,
747 file.stat.inode,839 file.stat.inode,
748 file.stat.mtime,840 file.stat.mtime,
749 &encoded_digest,841 &encoded_digest,
750 file.path.?,842 file.prefixed_path.?.prefix,
843 file.prefixed_path.?.sub_path,
751 });844 });
752 }845 }
753846
...@@ -889,6 +982,7 @@ test "cache file and then recall it" {...@@ -889,6 +982,7 @@ test "cache file and then recall it" {
889 .gpa = testing.allocator,982 .gpa = testing.allocator,
890 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),983 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
891 };984 };
985 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
892 defer cache.manifest_dir.close();986 defer cache.manifest_dir.close();
893987
894 {988 {
...@@ -960,6 +1054,7 @@ test "check that changing a file makes cache fail" {...@@ -960,6 +1054,7 @@ test "check that changing a file makes cache fail" {
960 .gpa = testing.allocator,1054 .gpa = testing.allocator,
961 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),1055 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
962 };1056 };
1057 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
963 defer cache.manifest_dir.close();1058 defer cache.manifest_dir.close();
9641059
965 {1060 {
...@@ -1022,6 +1117,7 @@ test "no file inputs" {...@@ -1022,6 +1117,7 @@ test "no file inputs" {
1022 .gpa = testing.allocator,1117 .gpa = testing.allocator,
1023 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),1118 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1024 };1119 };
1120 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1025 defer cache.manifest_dir.close();1121 defer cache.manifest_dir.close();
10261122
1027 {1123 {
...@@ -1080,6 +1176,7 @@ test "Manifest with files added after initial hash work" {...@@ -1080,6 +1176,7 @@ test "Manifest with files added after initial hash work" {
1080 .gpa = testing.allocator,1176 .gpa = testing.allocator,
1081 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),1177 .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
1082 };1178 };
1179 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1083 defer cache.manifest_dir.close();1180 defer cache.manifest_dir.close();
10841181
1085 {1182 {
src/Compilation.zig+14-9
...@@ -1456,23 +1456,27 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1456,23 +1456,27 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1456 else => @as(u8, 3),1456 else => @as(u8, 3),
1457 };1457 };
14581458
1459 // We put everything into the cache hash that *cannot be modified during an incremental update*.1459 // 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,1460 // 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 can1461 // 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.1462 // 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 store1463 // that we can find the same binary and incrementally update it even if
1464 // incremental compilation metadata.1464 // there are modified source files. We do this even if outputting to
1465 // the current directory because we need somewhere to store incremental
1466 // compilation metadata.
1465 const cache = try arena.create(Cache);1467 const cache = try arena.create(Cache);
1466 cache.* = .{1468 cache.* = .{
1467 .gpa = gpa,1469 .gpa = gpa,
1468 .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}),1470 .manifest_dir = try options.local_cache_directory.handle.makeOpenPath("h", .{}),
1469 };1471 };
1472 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
1473 cache.addPrefix(options.zig_lib_directory);
1474 cache.addPrefix(options.local_cache_directory);
1470 errdefer cache.manifest_dir.close();1475 errdefer cache.manifest_dir.close();
14711476
1472 // This is shared hasher state common to zig source and all C source files.1477 // This is shared hasher state common to zig source and all C source files.
1473 cache.hash.addBytes(build_options.version);1478 cache.hash.addBytes(build_options.version);
1474 cache.hash.add(builtin.zig_backend);1479 cache.hash.add(builtin.zig_backend);
1475 cache.hash.addBytes(options.zig_lib_directory.path orelse ".");
1476 cache.hash.add(options.optimize_mode);1480 cache.hash.add(options.optimize_mode);
1477 cache.hash.add(options.target.cpu.arch);1481 cache.hash.add(options.target.cpu.arch);
1478 cache.hash.addBytes(options.target.cpu.model.name);1482 cache.hash.addBytes(options.target.cpu.model.name);
...@@ -2265,8 +2269,9 @@ pub fn update(comp: *Compilation) !void {...@@ -2265,8 +2269,9 @@ pub fn update(comp: *Compilation) !void {
2265 const is_hit = man.hit() catch |err| {2269 const is_hit = man.hit() catch |err| {
2266 // TODO properly bubble these up instead of emitting a warning2270 // TODO properly bubble these up instead of emitting a warning
2267 const i = man.failed_file_index orelse return err;2271 const i = man.failed_file_index orelse return err;
2268 const file_path = man.files.items[i].path orelse return err;2272 const pp = man.files.items[i].prefixed_path orelse return err;
2269 std.log.warn("{s}: {s}", .{ @errorName(err), file_path });2273 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
2274 std.log.warn("{s}: {s}{s}", .{ @errorName(err), prefix, pp.sub_path });
2270 return err;2275 return err;
2271 };2276 };
2272 if (is_hit) {2277 if (is_hit) {
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/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);