| ... | ... | @@ -325,30 +325,59 @@ pub const Manifest = struct { |
| 325 | 325 | have_stat: bool, |
| 326 | 326 | /// Determines whether `File.digest` is populated. |
| 327 | 327 | have_digest: bool, |
| 328 | | contents: packed union(u64) { |
| 329 | | status: enum(u64) { |
| 330 | | unrequested = std.math.maxInt(u64) - 1, |
| 331 | | requested = std.math.maxInt(u64), |
| 332 | | _, |
| 333 | | pub fn populated(this: @This()) bool { |
| 334 | | return switch (this) { |
| 335 | | .unrequested, .requested => false, |
| 336 | | _ => true, |
| 337 | | }; |
| 338 | | } |
| 339 | | }, |
| 340 | | populated: packed struct(u64) { |
| 328 | contents: struct { |
| 329 | pop_off: enum(u32) { unpopulated = std.math.maxInt(u32), _ }, |
| 330 | req_len: enum(u32) { unrequested = std.math.maxInt(u32) - 1, requested = std.math.maxInt(u32), _ }, |
| 331 | |
| 332 | pub const requested: @This() = .{ |
| 333 | .pop_off = .unpopulated, |
| 334 | .req_len = .requested, |
| 335 | }; |
| 336 | |
| 337 | pub const unrequested: @This() = .{ |
| 338 | .pop_off = .unpopulated, |
| 339 | .req_len = .unrequested, |
| 340 | }; |
| 341 | |
| 342 | pub fn populated(p: Populated) @This() { |
| 343 | return .{ |
| 344 | .pop_off = @fromBackingInt(p.off), |
| 345 | .req_len = @fromBackingInt(p.len), |
| 346 | }; |
| 347 | } |
| 348 | |
| 349 | pub const Unwrapped = union(enum) { |
| 350 | unrequested, |
| 351 | requested, |
| 352 | populated: Populated, |
| 353 | }; |
| 354 | |
| 355 | pub const Populated = struct { |
| 341 | 356 | off: u32, |
| 342 | 357 | len: u32, |
| 343 | | }, |
| 358 | }; |
| 359 | |
| 360 | pub fn unwrap(this: @This()) Unwrapped { |
| 361 | return switch (this.pop_off) { |
| 362 | .unpopulated => switch (this.req_len) { |
| 363 | .unrequested => .unrequested, |
| 364 | .requested => .requested, |
| 365 | _ => unreachable, |
| 366 | }, |
| 367 | _ => .{ .populated = .{ |
| 368 | .off = @backingInt(this.pop_off), |
| 369 | .len = @backingInt(this.req_len), |
| 370 | } }, |
| 371 | }; |
| 372 | } |
| 344 | 373 | |
| 345 | 374 | pub fn get(this: @This(), all_contents: []const u8) ?[]const u8 { |
| 346 | | return if (this.status.populated()) |
| 347 | | all_contents[this.status.populated.off..][0..this.status.populated.len] |
| 348 | | else |
| 349 | | null; |
| 375 | return switch (this.unwrap()) { |
| 376 | .unrequested, .requested => null, |
| 377 | .populated => |p| all_contents[p.off..][0..p.len], |
| 378 | }; |
| 350 | 379 | } |
| 351 | | } align(@alignOf(u32)), |
| 380 | }, |
| 352 | 381 | /// `have_handle` determines whether this is populated. |
| 353 | 382 | handle: union { |
| 354 | 383 | file: Io.File, |
| ... | ... | @@ -369,14 +398,13 @@ pub const Manifest = struct { |
| 369 | 398 | return manifest.input_paths.items[@backingInt(i)]; |
| 370 | 399 | } |
| 371 | 400 | |
| 401 | pub fn offset(i: @This(), manifest: *const Manifest) File.Offset { |
| 402 | return manifest.files.keys()[@backingInt(i)]; |
| 403 | } |
| 404 | |
| 372 | 405 | pub fn contents(i: @This(), manifest: *const Manifest) []const u8 { |
| 373 | 406 | const input_path = get(i, manifest); |
| 374 | | const populated = switch (input_path.contents.status) { |
| 375 | | .unrequested => unreachable, // Must request contents before calling `check`. |
| 376 | | .requested => unreachable, // Must call `check` first. |
| 377 | | _ => input_path.contents.populated, |
| 378 | | }; |
| 379 | | return manifest.all_input_content.items[populated.off..][0..populated.len]; |
| 407 | return input_path.contents.get(manifest.all_input_content.items).?; |
| 380 | 408 | } |
| 381 | 409 | }; |
| 382 | 410 | |
| ... | ... | @@ -454,7 +482,7 @@ pub const Manifest = struct { |
| 454 | 482 | comptime assert(@offsetOf(File, "path_start") - flags_off == 1); |
| 455 | 483 | const hash_start = @backingInt(off) + flags_off; |
| 456 | 484 | const hash_end = mem.findScalarPos(u8, contents, hash_start, 0).?; |
| 457 | | return contents[hash_start..hash_end]; |
| 485 | return contents[hash_start..hash_end :0]; |
| 458 | 486 | } |
| 459 | 487 | |
| 460 | 488 | pub fn pathFallible(off: File.Offset, contents: []const u8) error{InvalidFormat}![:0]const u8 { |
| ... | ... | @@ -464,7 +492,7 @@ pub const Manifest = struct { |
| 464 | 492 | } |
| 465 | 493 | |
| 466 | 494 | pub fn path(off: File.Offset, contents: []const u8) [:0]const u8 { |
| 467 | | return pathFallible(contents, off) catch unreachable; |
| 495 | return pathFallible(off, contents) catch unreachable; |
| 468 | 496 | } |
| 469 | 497 | }; |
| 470 | 498 | |
| ... | ... | @@ -501,7 +529,7 @@ pub const Manifest = struct { |
| 501 | 529 | return .{ |
| 502 | 530 | .size = file.size, |
| 503 | 531 | .inode = file.inode, |
| 504 | | .mtime = file.mtime, |
| 532 | .mtime = .fromNanoseconds(file.mtime), |
| 505 | 533 | }; |
| 506 | 534 | } |
| 507 | 535 | |
| ... | ... | @@ -664,9 +692,9 @@ pub const Manifest = struct { |
| 664 | 692 | existing_input_file.have_handle = true; |
| 665 | 693 | }, |
| 666 | 694 | } |
| 667 | | if (options.request_contents) switch (existing_input_file.contents.status) { |
| 668 | | .requested, .unrequested => existing_input_file.contents = .{ .status = .requested }, |
| 669 | | _ => {}, |
| 695 | if (options.request_contents) switch (existing_input_file.contents.unwrap()) { |
| 696 | .requested, .unrequested => existing_input_file.contents = .requested, |
| 697 | .populated => {}, |
| 670 | 698 | }; |
| 671 | 699 | const existing_header = m.files.keys()[gop.index].get(m.contents.items); |
| 672 | 700 | if (options.stat) |stat| { |
| ... | ... | @@ -689,7 +717,7 @@ pub const Manifest = struct { |
| 689 | 717 | .file => |opt_file| if (opt_file) |file| .{ .file = file } else undefined, |
| 690 | 718 | .dir => |opt_dir| if (opt_dir) |dir| .{ .dir = dir } else undefined, |
| 691 | 719 | }, |
| 692 | | .contents = .{ .status = if (options.request_contents) .requested else .unrequested }, |
| 720 | .contents = if (options.request_contents) .requested else .unrequested, |
| 693 | 721 | .have_digest = false, |
| 694 | 722 | .have_stat = options.stat != null, |
| 695 | 723 | }); |
| ... | ... | @@ -712,6 +740,7 @@ pub const Manifest = struct { |
| 712 | 740 | pub const CheckResult = union(enum) { |
| 713 | 741 | hit, |
| 714 | 742 | incomplete_manifest, |
| 743 | invalid_manifest, |
| 715 | 744 | path_deleted: File.Offset, |
| 716 | 745 | directory_status_changed: File.Offset, |
| 717 | 746 | metadata_changed: File.Offset, |
| ... | ... | @@ -858,7 +887,7 @@ pub const Manifest = struct { |
| 858 | 887 | // We've just had a miss with the shared lock, and upgraded to an exclusive lock. Someone |
| 859 | 888 | // else might have modified the digest, so we need to check again before deciding to miss. |
| 860 | 889 | // Before trying again, we must reset `man.hash.hasher` and `man.files`. |
| 861 | | // This is basically just the first half of `unhit`. |
| 890 | // This is basically just the first half of `undoCheck`. |
| 862 | 891 | man.hash.hasher = hasher_init; |
| 863 | 892 | man.hash.hasher.update(&input_digest); |
| 864 | 893 | man.shrinkFilesToInput(); |
| ... | ... | @@ -870,7 +899,7 @@ pub const Manifest = struct { |
| 870 | 899 | // Cache miss, but `checkLocked` guarantees that all input files have their digests computed, even on a |
| 871 | 900 | // cache miss, which is needed because they will be used in the manifest digest. |
| 872 | 901 | man.dirty = true; |
| 873 | | unhit(man, &input_digest); |
| 902 | undoCheck(man, &input_digest); |
| 874 | 903 | return miss_result; |
| 875 | 904 | } |
| 876 | 905 | |
| ... | ... | @@ -918,7 +947,7 @@ pub const Manifest = struct { |
| 918 | 947 | // contents on disk, and then later truncate the contents array. |
| 919 | 948 | // |
| 920 | 949 | // The file on disk already supposedly includes the extra null byte that we have to maintain. |
| 921 | | const input_contents_len = m.contents.len; |
| 950 | const input_contents_len = m.contents.items.len; |
| 922 | 951 | // After this resize, every return statement needs to modify m.contents length. |
| 923 | 952 | try m.contents.resize(gpa, input_contents_len + manifest_size); |
| 924 | 953 | errdefer m.contents.shrinkRetainingCapacity(input_contents_len); |
| ... | ... | @@ -931,7 +960,7 @@ pub const Manifest = struct { |
| 931 | 960 | if (n != manifest_size) return checkLockedMiss(m, input_contents_len, .incomplete_manifest); |
| 932 | 961 | } |
| 933 | 962 | const disk_contents = m.contents.items[0..manifest_size]; |
| 934 | | const input_contents = m.contents.items[manifest_size..input_contents_len]; |
| 963 | const input_contents = m.contents.items[manifest_size..][0..input_contents_len]; |
| 935 | 964 | |
| 936 | 965 | // First the input files section, which must match our input files, otherwise it's invalid or incomplete. |
| 937 | 966 | var off: usize = 0; |
| ... | ... | @@ -955,14 +984,16 @@ pub const Manifest = struct { |
| 955 | 984 | // Validate and check discovered files. |
| 956 | 985 | while (off + 1 < disk_contents.len) { |
| 957 | 986 | const file_off: File.Offset = @fromBackingInt(@intCast(off)); |
| 958 | | const file = try file_off.getFallible(disk_contents); |
| 987 | const file = file_off.getFallible(disk_contents) catch |
| 988 | return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 959 | 989 | if (file.flags.prefix >= m.cache.prefixes_len) |
| 960 | 990 | return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 961 | | const path = try file_off.pathFallible(disk_contents); |
| 991 | const path = file_off.pathFallible(disk_contents) catch |
| 992 | return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 962 | 993 | if (path.len == 0) return checkLockedMiss(m, input_contents_len, .invalid_manifest); |
| 963 | 994 | |
| 964 | 995 | try m.files.putContext(gpa, file_off, {}, .{ .contents = disk_contents }); |
| 965 | | const result = try checkDiscoveredPath(file_off, file_off.path(disk_contents)); |
| 996 | const result = try checkDiscoveredPath(m, file_off, disk_contents); |
| 966 | 997 | if (result != .hit) return checkLockedMiss(m, input_contents_len, result); |
| 967 | 998 | |
| 968 | 999 | off += File.sizeOf(path.len); |
| ... | ... | @@ -994,7 +1025,10 @@ pub const Manifest = struct { |
| 994 | 1025 | result: CheckResult, |
| 995 | 1026 | ) CheckError!CheckResult { |
| 996 | 1027 | const file_offs = m.files.keys(); |
| 997 | | const off = if (file_offs.len - next_file_index == 0) input_contents_len else file_offs[next_file_index]; |
| 1028 | const off = if (file_offs.len - next_file_index == 0) |
| 1029 | input_contents_len |
| 1030 | else |
| 1031 | @backingInt(file_offs[next_file_index]); |
| 998 | 1032 | const copy_len = input_contents_len - off; |
| 999 | 1033 | @memcpy( |
| 1000 | 1034 | m.contents.items[off..][0..copy_len], |
| ... | ... | @@ -1021,7 +1055,7 @@ pub const Manifest = struct { |
| 1021 | 1055 | m: *Manifest, |
| 1022 | 1056 | file_off: File.Offset, |
| 1023 | 1057 | input_path: *InputPath, |
| 1024 | | contents: []const u8, |
| 1058 | contents: []u8, |
| 1025 | 1059 | ) CheckError!void { |
| 1026 | 1060 | const cache = m.cache; |
| 1027 | 1061 | const io = cache.io; |
| ... | ... | @@ -1031,7 +1065,10 @@ pub const Manifest = struct { |
| 1031 | 1065 | |
| 1032 | 1066 | if (input_path.have_digest) return; |
| 1033 | 1067 | |
| 1034 | | const stat_path_ok = input_path.contents.status.populated() or input_file.flags.metadata_only; |
| 1068 | const stat_path_ok = switch (input_path.contents.unwrap()) { |
| 1069 | .populated => true, |
| 1070 | .unrequested, .requested => input_file.flags.metadata_only, |
| 1071 | }; |
| 1035 | 1072 | if (!input_path.have_stat and stat_path_ok) { |
| 1036 | 1073 | // Since this is an input file, FileNotFound counts as a failure, not a cache miss. |
| 1037 | 1074 | const actual_stat = switch (input_path.getHandle(input_file)) { |
| ... | ... | @@ -1074,7 +1111,7 @@ pub const Manifest = struct { |
| 1074 | 1111 | return; |
| 1075 | 1112 | } |
| 1076 | 1113 | |
| 1077 | | if (input_path.contents.get()) |file_or_dir_contents| { |
| 1114 | if (input_path.contents.get(m.all_input_content.items)) |file_or_dir_contents| { |
| 1078 | 1115 | assert(stat_path_ok); |
| 1079 | 1116 | var hasher = hasher_init; |
| 1080 | 1117 | hasher.update(file_or_dir_contents); |
| ... | ... | @@ -1122,12 +1159,14 @@ pub const Manifest = struct { |
| 1122 | 1159 | .err = e, |
| 1123 | 1160 | } }), |
| 1124 | 1161 | }; |
| 1125 | | defer if (input_path.contents.status == .requested) { |
| 1126 | | input_path.contents = .{ .populated = .{ |
| 1162 | defer switch (input_path.contents.unwrap()) { |
| 1163 | .requested => input_path.contents = .populated(.{ |
| 1127 | 1164 | .off = @intCast(dir_contents_start), |
| 1128 | 1165 | .len = @intCast(m.all_input_content.items.len - dir_contents_start), |
| 1129 | | } }; |
| 1130 | | } else m.all_input_content.shrinkRetainingCapacity(dir_contents_start); |
| 1166 | }), |
| 1167 | .unrequested => m.all_input_content.shrinkRetainingCapacity(dir_contents_start), |
| 1168 | .populated => unreachable, |
| 1169 | }; |
| 1131 | 1170 | } else { |
| 1132 | 1171 | const opened_file = if (input_path.have_handle) |
| 1133 | 1172 | input_path.handle.file |
| ... | ... | @@ -1164,7 +1203,11 @@ pub const Manifest = struct { |
| 1164 | 1203 | } }), |
| 1165 | 1204 | }; |
| 1166 | 1205 | |
| 1167 | | if (input_path.contents.status == .requested) @panic("TODO"); |
| 1206 | switch (input_path.contents.unwrap()) { |
| 1207 | .requested => @panic("TODO"), |
| 1208 | .unrequested => {}, |
| 1209 | .populated => unreachable, |
| 1210 | } |
| 1168 | 1211 | } |
| 1169 | 1212 | } |
| 1170 | 1213 | |
| ... | ... | @@ -1173,8 +1216,8 @@ pub const Manifest = struct { |
| 1173 | 1216 | m: *Manifest, |
| 1174 | 1217 | file_off: File.Offset, |
| 1175 | 1218 | input_path: *InputPath, |
| 1176 | | disk_contents: []const u8, |
| 1177 | | input_contents: []const u8, |
| 1219 | disk_contents: []u8, |
| 1220 | input_contents: []u8, |
| 1178 | 1221 | ) CheckError!CheckResult { |
| 1179 | 1222 | const cache = m.cache; |
| 1180 | 1223 | const io = cache.io; |
| ... | ... | @@ -1191,7 +1234,7 @@ pub const Manifest = struct { |
| 1191 | 1234 | } |
| 1192 | 1235 | |
| 1193 | 1236 | if (input_path.have_stat) { |
| 1194 | | const changed = try disk_file.setStatChanged(m, input_file.stat); |
| 1237 | const changed = try disk_file.setStatChanged(m, input_file.stat()); |
| 1195 | 1238 | if (!changed) return .hit; |
| 1196 | 1239 | } |
| 1197 | 1240 | |
| ... | ... | @@ -1239,7 +1282,7 @@ pub const Manifest = struct { |
| 1239 | 1282 | return .{ .metadata_changed = file_off }; |
| 1240 | 1283 | } |
| 1241 | 1284 | |
| 1242 | | if (input_path.contents.get()) |file_or_dir_contents| { |
| 1285 | if (input_path.contents.get(m.all_input_content.items)) |file_or_dir_contents| { |
| 1243 | 1286 | var hasher = hasher_init; |
| 1244 | 1287 | hasher.update(file_or_dir_contents); |
| 1245 | 1288 | hasher.final(&disk_file.digest); |
| ... | ... | @@ -1287,12 +1330,14 @@ pub const Manifest = struct { |
| 1287 | 1330 | .err = e, |
| 1288 | 1331 | } }), |
| 1289 | 1332 | }; |
| 1290 | | defer if (input_path.contents.status == .requested) { |
| 1291 | | input_path.contents = .{ .populated = .{ |
| 1333 | defer switch (input_path.contents.unwrap()) { |
| 1334 | .requested => input_path.contents = .populated(.{ |
| 1292 | 1335 | .off = @intCast(dir_contents_start), |
| 1293 | 1336 | .len = @intCast(m.all_input_content.items.len - dir_contents_start), |
| 1294 | | } }; |
| 1295 | | } else m.all_input_content.shrinkRetainingCapacity(dir_contents_start); |
| 1337 | }), |
| 1338 | .unrequested => m.all_input_content.shrinkRetainingCapacity(dir_contents_start), |
| 1339 | .populated => unreachable, |
| 1340 | }; |
| 1296 | 1341 | |
| 1297 | 1342 | if (mem.eql(u8, &disk_file.digest, &input_file.digest)) return .hit; |
| 1298 | 1343 | return .{ .contents_changed = file_off }; |
| ... | ... | @@ -1330,30 +1375,30 @@ pub const Manifest = struct { |
| 1330 | 1375 | .err = e, |
| 1331 | 1376 | } }), |
| 1332 | 1377 | }; |
| 1333 | | if (input_path.contents.status == .requested) @panic("TODO"); |
| 1378 | switch (input_path.contents.unwrap()) { |
| 1379 | .requested => @panic("TODO"), |
| 1380 | .unrequested => {}, |
| 1381 | .populated => unreachable, |
| 1382 | } |
| 1334 | 1383 | |
| 1335 | 1384 | if (mem.eql(u8, &disk_file.digest, &input_file.digest)) return .hit; |
| 1336 | 1385 | return .{ .contents_changed = file_off }; |
| 1337 | 1386 | } |
| 1338 | 1387 | } |
| 1339 | 1388 | |
| 1340 | | fn checkDiscoveredPath( |
| 1341 | | m: *Manifest, |
| 1342 | | file_off: File.Offset, |
| 1343 | | file_path: [:0]const u8, |
| 1344 | | contents: []const u8, |
| 1345 | | ) CheckError!CheckResult { |
| 1389 | fn checkDiscoveredPath(m: *Manifest, file_off: File.Offset, contents: []u8) CheckError!CheckResult { |
| 1346 | 1390 | const file = file_off.get(contents); |
| 1347 | 1391 | const cache = m.cache; |
| 1348 | 1392 | const gpa = cache.gpa; |
| 1349 | 1393 | const io = cache.io; |
| 1350 | 1394 | const parent_dir = cache.prefixes()[file.flags.prefix].handle; |
| 1395 | const file_path = file_off.path(contents); |
| 1351 | 1396 | |
| 1352 | 1397 | if (file.flags.metadata_only) { |
| 1353 | 1398 | const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) { |
| 1354 | 1399 | error.FileNotFound => return .{ .path_deleted = file_off }, |
| 1355 | 1400 | error.Canceled => |e| return e, |
| 1356 | | else => |e| return fail(m, .{ .file_stat = .{ |
| 1401 | else => |e| return fail(&m.diagnostic, .{ .file_stat = .{ |
| 1357 | 1402 | .file_offset = file_off, |
| 1358 | 1403 | .err = e, |
| 1359 | 1404 | } }), |
| ... | ... | @@ -1376,7 +1421,7 @@ pub const Manifest = struct { |
| 1376 | 1421 | error.FileNotFound => return .{ .path_deleted = file_off }, |
| 1377 | 1422 | error.NotDir => return .{ .directory_status_changed = file_off }, |
| 1378 | 1423 | error.Canceled => |e| return e, |
| 1379 | | else => |e| return fail(m, .{ .file_open = .{ |
| 1424 | else => |e| return fail(&m.diagnostic, .{ .file_open = .{ |
| 1380 | 1425 | .file_offset = file_off, |
| 1381 | 1426 | .err = e, |
| 1382 | 1427 | } }), |
| ... | ... | @@ -1385,7 +1430,7 @@ pub const Manifest = struct { |
| 1385 | 1430 | |
| 1386 | 1431 | const actual_stat = opened_dir.stat(io) catch |err| switch (err) { |
| 1387 | 1432 | error.Canceled => |e| return e, |
| 1388 | | else => |e| return fail(m, .{ .file_stat = .{ |
| 1433 | else => |e| return fail(&m.diagnostic, .{ .file_stat = .{ |
| 1389 | 1434 | .file_offset = file_off, |
| 1390 | 1435 | .err = e, |
| 1391 | 1436 | } }), |
| ... | ... | @@ -1394,9 +1439,9 @@ pub const Manifest = struct { |
| 1394 | 1439 | const prev_digest: BinDigest = file.digest; |
| 1395 | 1440 | const dir_contents_start = m.all_input_content.items.len; |
| 1396 | 1441 | defer m.all_input_content.shrinkRetainingCapacity(dir_contents_start); |
| 1397 | | hashDir(gpa, io, opened_dir, &file.digest, &m.all_input_contents) catch |err| switch (err) { |
| 1442 | hashDir(gpa, io, opened_dir, &file.digest, &m.all_input_content) catch |err| switch (err) { |
| 1398 | 1443 | error.Canceled, error.OutOfMemory => |e| return e, |
| 1399 | | else => |e| return fail(m, .{ .file_read = .{ |
| 1444 | else => |e| return fail(&m.diagnostic, .{ .file_read = .{ |
| 1400 | 1445 | .file_offset = file_off, |
| 1401 | 1446 | .err = e, |
| 1402 | 1447 | } }), |
| ... | ... | @@ -1411,7 +1456,7 @@ pub const Manifest = struct { |
| 1411 | 1456 | error.FileNotFound => return .{ .path_deleted = file_off }, |
| 1412 | 1457 | error.IsDir => return .{ .directory_status_changed = file_off }, |
| 1413 | 1458 | error.Canceled => |e| return e, |
| 1414 | | else => |e| return fail(m, .{ .file_open = .{ |
| 1459 | else => |e| return fail(&m.diagnostic, .{ .file_open = .{ |
| 1415 | 1460 | .file_offset = file_off, |
| 1416 | 1461 | .err = e, |
| 1417 | 1462 | } }), |
| ... | ... | @@ -1420,7 +1465,7 @@ pub const Manifest = struct { |
| 1420 | 1465 | |
| 1421 | 1466 | const actual_stat = opened_file.stat(io) catch |err| switch (err) { |
| 1422 | 1467 | error.Canceled => |e| return e, |
| 1423 | | else => |e| return fail(m, .{ .file_stat = .{ |
| 1468 | else => |e| return fail(&m.diagnostic, .{ .file_stat = .{ |
| 1424 | 1469 | .file_offset = file_off, |
| 1425 | 1470 | .err = e, |
| 1426 | 1471 | } }), |
| ... | ... | @@ -1430,7 +1475,7 @@ pub const Manifest = struct { |
| 1430 | 1475 | const prev_digest: BinDigest = file.digest; |
| 1431 | 1476 | hashFile(io, opened_file, &file.digest) catch |err| switch (err) { |
| 1432 | 1477 | error.Canceled => |e| return e, |
| 1433 | | else => |e| return fail(m, .{ .file_read = .{ |
| 1478 | else => |e| return fail(&m.diagnostic, .{ .file_read = .{ |
| 1434 | 1479 | .file_offset = file_off, |
| 1435 | 1480 | .err = e, |
| 1436 | 1481 | } }), |
| ... | ... | @@ -1453,7 +1498,7 @@ pub const Manifest = struct { |
| 1453 | 1498 | /// not including discovered files). |
| 1454 | 1499 | /// |
| 1455 | 1500 | /// Assumes that `digest` is populated for all input files. |
| 1456 | | pub fn unhit(man: *Manifest, input_digest: *const BinDigest) void { |
| 1501 | pub fn undoCheck(man: *Manifest, input_digest: *const BinDigest) void { |
| 1457 | 1502 | // Reset the hash. |
| 1458 | 1503 | man.hash.hasher = hasher_init; |
| 1459 | 1504 | man.hash.hasher.update(input_digest); |
| ... | ... | @@ -2117,8 +2162,7 @@ test "cache file and then recall it" { |
| 2117 | 2162 | ch.hash.addBytes("1234"); |
| 2118 | 2163 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 2119 | 2164 | |
| 2120 | | // There should be nothing in the cache |
| 2121 | | try testing.expectEqual(.miss, try ch.check(.none)); |
| 2165 | try testing.expectEqual(.incomplete_manifest, try ch.check(.none)); |
| 2122 | 2166 | |
| 2123 | 2167 | digest1 = ch.final(); |
| 2124 | 2168 | try ch.finalize(); |
| ... | ... | @@ -2185,8 +2229,7 @@ test "check that changing a file makes cache fail" { |
| 2185 | 2229 | man.hash.addBytes("1234"); |
| 2186 | 2230 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 2187 | 2231 | |
| 2188 | | // There should be nothing in the cache |
| 2189 | | try testing.expectEqual(.miss, try man.check(.none)); |
| 2232 | try testing.expectEqual(.incomplete_manifest, try man.check(.none)); |
| 2190 | 2233 | |
| 2191 | 2234 | try testing.expectEqualStrings(original_temp_file_contents, temp_file_idx.contents(&man)); |
| 2192 | 2235 | |
| ... | ... | @@ -2204,11 +2247,13 @@ test "check that changing a file makes cache fail" { |
| 2204 | 2247 | man.hash.addBytes("1234"); |
| 2205 | 2248 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 2206 | 2249 | |
| 2207 | | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 2208 | | try testing.expectEqual(.miss, try man.check(.none)); |
| 2250 | // The one input file changed. |
| 2251 | try testing.expectEqual( |
| 2252 | @as(Manifest.CheckResult, .{ .contents_changed = temp_file_idx.offset(&man) }), |
| 2253 | try man.check(.none), |
| 2254 | ); |
| 2209 | 2255 | |
| 2210 | | // The cache system does not keep the contents of re-hashed input files. |
| 2211 | | try testing.expectEqual(false, temp_file_idx.get(&man).contents.status.populated()); |
| 2256 | try testing.expectEqualStrings(updated_temp_file_contents, temp_file_idx.contents(&man)); |
| 2212 | 2257 | |
| 2213 | 2258 | digest2 = man.final(); |
| 2214 | 2259 | |
| ... | ... | @@ -2248,8 +2293,7 @@ test "no file inputs" { |
| 2248 | 2293 | |
| 2249 | 2294 | man.hash.addBytes("1234"); |
| 2250 | 2295 | |
| 2251 | | // There should be nothing in the cache |
| 2252 | | try testing.expectEqual(.miss, try man.check(.none)); |
| 2296 | try testing.expectEqual(.incomplete_manifest, try man.check(.none)); |
| 2253 | 2297 | |
| 2254 | 2298 | digest1 = man.final(); |
| 2255 | 2299 | |
| ... | ... | @@ -2306,31 +2350,30 @@ test "Manifest with files added after initial hash work" { |
| 2306 | 2350 | defer cache.manifest_dir.close(io); |
| 2307 | 2351 | |
| 2308 | 2352 | { |
| 2309 | | var ch = cache.obtain(); |
| 2310 | | defer ch.deinit(); |
| 2353 | var man = cache.obtain(); |
| 2354 | defer man.deinit(); |
| 2311 | 2355 | |
| 2312 | | ch.hash.addBytes("1234"); |
| 2313 | | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 2356 | man.hash.addBytes("1234"); |
| 2357 | _ = try man.addInputPath(.initCwd(temp_file1), .{}); |
| 2314 | 2358 | |
| 2315 | | // There should be nothing in the cache |
| 2316 | | try testing.expectEqual(.miss, try ch.check(.none)); |
| 2359 | try testing.expectEqual(.incomplete_manifest, try man.check(.none)); |
| 2317 | 2360 | |
| 2318 | | _ = try ch.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 2361 | try man.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 2319 | 2362 | |
| 2320 | | digest1 = ch.final(); |
| 2321 | | try ch.finalize(); |
| 2363 | digest1 = man.final(); |
| 2364 | try man.finalize(); |
| 2322 | 2365 | } |
| 2323 | 2366 | { |
| 2324 | | var ch = cache.obtain(); |
| 2325 | | defer ch.deinit(); |
| 2367 | var man = cache.obtain(); |
| 2368 | defer man.deinit(); |
| 2326 | 2369 | |
| 2327 | | ch.hash.addBytes("1234"); |
| 2328 | | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 2370 | man.hash.addBytes("1234"); |
| 2371 | _ = try man.addInputPath(.initCwd(temp_file1), .{}); |
| 2329 | 2372 | |
| 2330 | | try testing.expect(.hit == try ch.check(.none)); |
| 2331 | | digest2 = ch.final(); |
| 2373 | try testing.expect(.hit == try man.check(.none)); |
| 2374 | digest2 = man.final(); |
| 2332 | 2375 | |
| 2333 | | try testing.expectEqual(false, ch.have_exclusive_lock); |
| 2376 | try testing.expectEqual(false, man.have_exclusive_lock); |
| 2334 | 2377 | } |
| 2335 | 2378 | try testing.expect(mem.eql(u8, &digest1, &digest2)); |
| 2336 | 2379 | |
| ... | ... | @@ -2344,20 +2387,24 @@ test "Manifest with files added after initial hash work" { |
| 2344 | 2387 | } |
| 2345 | 2388 | |
| 2346 | 2389 | { |
| 2347 | | var ch = cache.obtain(); |
| 2348 | | defer ch.deinit(); |
| 2390 | var man = cache.obtain(); |
| 2391 | defer man.deinit(); |
| 2349 | 2392 | |
| 2350 | | ch.hash.addBytes("1234"); |
| 2351 | | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 2393 | man.hash.addBytes("1234"); |
| 2394 | _ = try man.addInputPath(.initCwd(temp_file1), .{}); |
| 2352 | 2395 | |
| 2353 | | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 2354 | | try testing.expectEqual(.miss, try ch.check(.none)); |
| 2396 | switch (try man.check(.none)) { |
| 2397 | .contents_changed => |off| { |
| 2398 | try testing.expectEqualStrings(temp_file2, off.path(man.contents.items)); |
| 2399 | }, |
| 2400 | else => return error.TestFailed, |
| 2401 | } |
| 2355 | 2402 | |
| 2356 | | _ = try ch.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 2403 | try man.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 2357 | 2404 | |
| 2358 | | digest3 = ch.final(); |
| 2405 | digest3 = man.final(); |
| 2359 | 2406 | |
| 2360 | | try ch.finalize(); |
| 2407 | try man.finalize(); |
| 2361 | 2408 | } |
| 2362 | 2409 | |
| 2363 | 2410 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); |