| ... | @@ -56,7 +56,7 @@ package_root: Cache.Path, | ... | @@ -56,7 +56,7 @@ package_root: Cache.Path, |
| 56 | error_bundle: ErrorBundle.Wip, | 56 | error_bundle: ErrorBundle.Wip, |
| 57 | manifest: ?Manifest, | 57 | manifest: ?Manifest, |
| 58 | manifest_ast: std.zig.Ast, | 58 | manifest_ast: std.zig.Ast, |
| 59 | actual_hash: Manifest.Digest, | 59 | computed_hash: ComputedHash, |
| 60 | /// Fetch logic notices whether a package has a build.zig file and sets this flag. | 60 | /// Fetch logic notices whether a package has a build.zig file and sets this flag. |
| 61 | has_build_zig: bool, | 61 | has_build_zig: bool, |
| 62 | /// Indicates whether the task aborted due to an out-of-memory condition. | 62 | /// Indicates whether the task aborted due to an out-of-memory condition. |
| ... | @@ -116,8 +116,8 @@ pub const JobQueue = struct { | ... | @@ -116,8 +116,8 @@ pub const JobQueue = struct { |
| 116 | /// as lazy. | 116 | /// as lazy. |
| 117 | unlazy_set: UnlazySet = .{}, | 117 | unlazy_set: UnlazySet = .{}, |
| 118 | | 118 | |
| 119 | pub const Table = std.AutoArrayHashMapUnmanaged(Manifest.MultiHashHexDigest, *Fetch); | 119 | pub const Table = std.AutoArrayHashMapUnmanaged(Package.Hash, *Fetch); |
| 120 | pub const UnlazySet = std.AutoArrayHashMapUnmanaged(Manifest.MultiHashHexDigest, void); | 120 | pub const UnlazySet = std.AutoArrayHashMapUnmanaged(Package.Hash, void); |
| 121 | | 121 | |
| 122 | pub fn deinit(jq: *JobQueue) void { | 122 | pub fn deinit(jq: *JobQueue) void { |
| 123 | if (jq.all_fetches.items.len == 0) return; | 123 | if (jq.all_fetches.items.len == 0) return; |
| ... | @@ -160,22 +160,24 @@ pub const JobQueue = struct { | ... | @@ -160,22 +160,24 @@ pub const JobQueue = struct { |
| 160 | | 160 | |
| 161 | // Ensure the generated .zig file is deterministic. | 161 | // Ensure the generated .zig file is deterministic. |
| 162 | jq.table.sortUnstable(@as(struct { | 162 | jq.table.sortUnstable(@as(struct { |
| 163 | keys: []const Manifest.MultiHashHexDigest, | 163 | keys: []const Package.Hash, |
| 164 | pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { | 164 | pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { |
| 165 | return std.mem.lessThan(u8, &ctx.keys[a_index], &ctx.keys[b_index]); | 165 | return std.mem.lessThan(u8, &ctx.keys[a_index].bytes, &ctx.keys[b_index].bytes); |
| 166 | } | 166 | } |
| 167 | }, .{ .keys = keys })); | 167 | }, .{ .keys = keys })); |
| 168 | | 168 | |
| 169 | for (keys, jq.table.values()) |hash, fetch| { | 169 | for (keys, jq.table.values()) |*hash, fetch| { |
| 170 | if (fetch == jq.all_fetches.items[0]) { | 170 | if (fetch == jq.all_fetches.items[0]) { |
| 171 | // The first one is a dummy package for the current project. | 171 | // The first one is a dummy package for the current project. |
| 172 | continue; | 172 | continue; |
| 173 | } | 173 | } |
| 174 | | 174 | |
| | 175 | const hash_slice = hash.toSlice(); |
| | 176 | |
| 175 | try buf.writer().print( | 177 | try buf.writer().print( |
| 176 | \\ pub const {} = struct {{ | 178 | \\ pub const {} = struct {{ |
| 177 | \\ | 179 | \\ |
| 178 | , .{std.zig.fmtId(&hash)}); | 180 | , .{std.zig.fmtId(hash_slice)}); |
| 179 | | 181 | |
| 180 | lazy: { | 182 | lazy: { |
| 181 | switch (fetch.lazy_status) { | 183 | switch (fetch.lazy_status) { |
| ... | @@ -207,7 +209,7 @@ pub const JobQueue = struct { | ... | @@ -207,7 +209,7 @@ pub const JobQueue = struct { |
| 207 | try buf.writer().print( | 209 | try buf.writer().print( |
| 208 | \\ pub const build_zig = @import("{}"); | 210 | \\ pub const build_zig = @import("{}"); |
| 209 | \\ | 211 | \\ |
| 210 | , .{std.zig.fmtEscapes(&hash)}); | 212 | , .{std.zig.fmtEscapes(hash_slice)}); |
| 211 | } | 213 | } |
| 212 | | 214 | |
| 213 | if (fetch.manifest) |*manifest| { | 215 | if (fetch.manifest) |*manifest| { |
| ... | @@ -219,7 +221,7 @@ pub const JobQueue = struct { | ... | @@ -219,7 +221,7 @@ pub const JobQueue = struct { |
| 219 | const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue; | 221 | const h = depDigest(fetch.package_root, jq.global_cache, dep) orelse continue; |
| 220 | try buf.writer().print( | 222 | try buf.writer().print( |
| 221 | " .{{ \"{}\", \"{}\" }},\n", | 223 | " .{{ \"{}\", \"{}\" }},\n", |
| 222 | .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(&h) }, | 224 | .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) }, |
| 223 | ); | 225 | ); |
| 224 | } | 226 | } |
| 225 | | 227 | |
| ... | @@ -251,7 +253,7 @@ pub const JobQueue = struct { | ... | @@ -251,7 +253,7 @@ pub const JobQueue = struct { |
| 251 | const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue; | 253 | const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue; |
| 252 | try buf.writer().print( | 254 | try buf.writer().print( |
| 253 | " .{{ \"{}\", \"{}\" }},\n", | 255 | " .{{ \"{}\", \"{}\" }},\n", |
| 254 | .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(&h) }, | 256 | .{ std.zig.fmtEscapes(name), std.zig.fmtEscapes(h.toSlice()) }, |
| 255 | ); | 257 | ); |
| 256 | } | 258 | } |
| 257 | try buf.appendSlice("};\n"); | 259 | try buf.appendSlice("};\n"); |
| ... | @@ -283,7 +285,7 @@ pub const Location = union(enum) { | ... | @@ -283,7 +285,7 @@ pub const Location = union(enum) { |
| 283 | url: []const u8, | 285 | url: []const u8, |
| 284 | /// If this is null it means the user omitted the hash field from a dependency. | 286 | /// If this is null it means the user omitted the hash field from a dependency. |
| 285 | /// It will be an error but the logic should still fetch and print the discovered hash. | 287 | /// It will be an error but the logic should still fetch and print the discovered hash. |
| 286 | hash: ?Manifest.MultiHashHexDigest, | 288 | hash: ?Package.Hash, |
| 287 | }; | 289 | }; |
| 288 | }; | 290 | }; |
| 289 | | 291 | |
| ... | @@ -325,9 +327,11 @@ pub fn run(f: *Fetch) RunError!void { | ... | @@ -325,9 +327,11 @@ pub fn run(f: *Fetch) RunError!void { |
| 325 | // "p/$hash/foo", with possibly more directories after "foo". | 327 | // "p/$hash/foo", with possibly more directories after "foo". |
| 326 | // We want to fail unless the resolved relative path has a | 328 | // We want to fail unless the resolved relative path has a |
| 327 | // prefix of "p/$hash/". | 329 | // prefix of "p/$hash/". |
| 328 | const digest_len = @typeInfo(Manifest.MultiHashHexDigest).array.len; | | |
| 329 | const prefix_len: usize = if (f.job_queue.read_only) 0 else "p/".len; | 330 | const prefix_len: usize = if (f.job_queue.read_only) 0 else "p/".len; |
| 330 | const expected_prefix = f.parent_package_root.sub_path[0 .. prefix_len + digest_len]; | 331 | const parent_sub_path = f.parent_package_root.sub_path; |
| | 332 | const end = std.mem.indexOfScalarPos(u8, parent_sub_path, prefix_len, fs.path.sep) orelse |
| | 333 | parent_sub_path.len; |
| | 334 | const expected_prefix = parent_sub_path[prefix_len..end]; |
| 331 | if (!std.mem.startsWith(u8, pkg_root.sub_path, expected_prefix)) { | 335 | if (!std.mem.startsWith(u8, pkg_root.sub_path, expected_prefix)) { |
| 332 | return f.fail( | 336 | return f.fail( |
| 333 | f.location_tok, | 337 | f.location_tok, |
| ... | @@ -367,9 +371,13 @@ pub fn run(f: *Fetch) RunError!void { | ... | @@ -367,9 +371,13 @@ pub fn run(f: *Fetch) RunError!void { |
| 367 | }, | 371 | }, |
| 368 | }; | 372 | }; |
| 369 | | 373 | |
| 370 | const s = fs.path.sep_str; | | |
| 371 | if (remote.hash) |expected_hash| { | 374 | if (remote.hash) |expected_hash| { |
| 372 | const prefixed_pkg_sub_path = "p" ++ s ++ expected_hash; | 375 | var prefixed_pkg_sub_path_buffer: [100]u8 = undefined; |
| | 376 | prefixed_pkg_sub_path_buffer[0] = 'p'; |
| | 377 | prefixed_pkg_sub_path_buffer[1] = fs.path.sep; |
| | 378 | const hash_slice = expected_hash.toSlice(); |
| | 379 | @memcpy(prefixed_pkg_sub_path_buffer[2..][0..hash_slice.len], hash_slice); |
| | 380 | const prefixed_pkg_sub_path = prefixed_pkg_sub_path_buffer[0 .. 2 + hash_slice.len]; |
| 373 | const prefix_len: usize = if (f.job_queue.read_only) "p/".len else 0; | 381 | const prefix_len: usize = if (f.job_queue.read_only) "p/".len else 0; |
| 374 | const pkg_sub_path = prefixed_pkg_sub_path[prefix_len..]; | 382 | const pkg_sub_path = prefixed_pkg_sub_path[prefix_len..]; |
| 375 | if (cache_root.handle.access(pkg_sub_path, .{})) |_| { | 383 | if (cache_root.handle.access(pkg_sub_path, .{})) |_| { |
| ... | @@ -437,7 +445,7 @@ fn runResource( | ... | @@ -437,7 +445,7 @@ fn runResource( |
| 437 | f: *Fetch, | 445 | f: *Fetch, |
| 438 | uri_path: []const u8, | 446 | uri_path: []const u8, |
| 439 | resource: *Resource, | 447 | resource: *Resource, |
| 440 | remote_hash: ?Manifest.MultiHashHexDigest, | 448 | remote_hash: ?Package.Hash, |
| 441 | ) RunError!void { | 449 | ) RunError!void { |
| 442 | defer resource.deinit(); | 450 | defer resource.deinit(); |
| 443 | const arena = f.arena.allocator(); | 451 | const arena = f.arena.allocator(); |
| ... | @@ -499,7 +507,7 @@ fn runResource( | ... | @@ -499,7 +507,7 @@ fn runResource( |
| 499 | // Empty directories have already been omitted by `unpackResource`. | 507 | // Empty directories have already been omitted by `unpackResource`. |
| 500 | // Compute the package hash based on the remaining files in the temporary | 508 | // Compute the package hash based on the remaining files in the temporary |
| 501 | // directory. | 509 | // directory. |
| 502 | f.actual_hash = try computeHash(f, pkg_path, filter); | 510 | f.computed_hash = try computeHash(f, pkg_path, filter); |
| 503 | | 511 | |
| 504 | break :blk if (unpack_result.root_dir.len > 0) | 512 | break :blk if (unpack_result.root_dir.len > 0) |
| 505 | try fs.path.join(arena, &.{ tmp_dir_sub_path, unpack_result.root_dir }) | 513 | try fs.path.join(arena, &.{ tmp_dir_sub_path, unpack_result.root_dir }) |
| ... | @@ -507,6 +515,8 @@ fn runResource( | ... | @@ -507,6 +515,8 @@ fn runResource( |
| 507 | tmp_dir_sub_path; | 515 | tmp_dir_sub_path; |
| 508 | }; | 516 | }; |
| 509 | | 517 | |
| | 518 | const computed_package_hash = computedPackageHash(f); |
| | 519 | |
| 510 | // Rename the temporary directory into the global zig package cache | 520 | // Rename the temporary directory into the global zig package cache |
| 511 | // directory. If the hash already exists, delete the temporary directory | 521 | // directory. If the hash already exists, delete the temporary directory |
| 512 | // and leave the zig package cache directory untouched as it may be in use | 522 | // and leave the zig package cache directory untouched as it may be in use |
| ... | @@ -515,7 +525,7 @@ fn runResource( | ... | @@ -515,7 +525,7 @@ fn runResource( |
| 515 | | 525 | |
| 516 | f.package_root = .{ | 526 | f.package_root = .{ |
| 517 | .root_dir = cache_root, | 527 | .root_dir = cache_root, |
| 518 | .sub_path = try arena.dupe(u8, "p" ++ s ++ Manifest.hexDigest(f.actual_hash)), | 528 | .sub_path = try std.fmt.allocPrint(arena, "p" ++ s ++ "{s}", .{computed_package_hash.toSlice()}), |
| 519 | }; | 529 | }; |
| 520 | renameTmpIntoCache(cache_root.handle, package_sub_path, f.package_root.sub_path) catch |err| { | 530 | renameTmpIntoCache(cache_root.handle, package_sub_path, f.package_root.sub_path) catch |err| { |
| 521 | const src = try cache_root.join(arena, &.{tmp_dir_sub_path}); | 531 | const src = try cache_root.join(arena, &.{tmp_dir_sub_path}); |
| ... | @@ -534,13 +544,22 @@ fn runResource( | ... | @@ -534,13 +544,22 @@ fn runResource( |
| 534 | // Validate the computed hash against the expected hash. If invalid, this | 544 | // Validate the computed hash against the expected hash. If invalid, this |
| 535 | // job is done. | 545 | // job is done. |
| 536 | | 546 | |
| 537 | const actual_hex = Manifest.hexDigest(f.actual_hash); | | |
| 538 | if (remote_hash) |declared_hash| { | 547 | if (remote_hash) |declared_hash| { |
| 539 | if (!std.mem.eql(u8, &declared_hash, &actual_hex)) { | 548 | if (declared_hash.isOld()) { |
| 540 | return f.fail(f.hash_tok, try eb.printString( | 549 | const actual_hex = Package.multiHashHexDigest(f.computed_hash.digest); |
| 541 | "hash mismatch: manifest declares {s} but the fetched package has {s}", | 550 | if (!std.mem.eql(u8, declared_hash.toSlice(), &actual_hex)) { |
| 542 | .{ declared_hash, actual_hex }, | 551 | return f.fail(f.hash_tok, try eb.printString( |
| 543 | )); | 552 | "hash mismatch: manifest declares {s} but the fetched package has {s}", |
| | 553 | .{ declared_hash.toSlice(), actual_hex }, |
| | 554 | )); |
| | 555 | } |
| | 556 | } else { |
| | 557 | if (!computed_package_hash.eql(&declared_hash)) { |
| | 558 | return f.fail(f.hash_tok, try eb.printString( |
| | 559 | "hash mismatch: manifest declares {s} but the fetched package has {s}", |
| | 560 | .{ declared_hash.toSlice(), computed_package_hash.toSlice() }, |
| | 561 | )); |
| | 562 | } |
| 544 | } | 563 | } |
| 545 | } else if (!f.omit_missing_hash_error) { | 564 | } else if (!f.omit_missing_hash_error) { |
| 546 | const notes_len = 1; | 565 | const notes_len = 1; |
| ... | @@ -551,7 +570,7 @@ fn runResource( | ... | @@ -551,7 +570,7 @@ fn runResource( |
| 551 | }); | 570 | }); |
| 552 | const notes_start = try eb.reserveNotes(notes_len); | 571 | const notes_start = try eb.reserveNotes(notes_len); |
| 553 | eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{ | 572 | eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{ |
| 554 | .msg = try eb.printString("expected .hash = \"{s}\",", .{&actual_hex}), | 573 | .msg = try eb.printString("expected .hash = \"{s}\",", .{computed_package_hash.toSlice()}), |
| 555 | })); | 574 | })); |
| 556 | return error.FetchFailed; | 575 | return error.FetchFailed; |
| 557 | } | 576 | } |
| ... | @@ -562,6 +581,16 @@ fn runResource( | ... | @@ -562,6 +581,16 @@ fn runResource( |
| 562 | return queueJobsForDeps(f); | 581 | return queueJobsForDeps(f); |
| 563 | } | 582 | } |
| 564 | | 583 | |
| | 584 | pub fn computedPackageHash(f: *const Fetch) Package.Hash { |
| | 585 | const saturated_size = std.math.cast(u32, f.computed_hash.total_size) orelse std.math.maxInt(u32); |
| | 586 | if (f.manifest) |man| { |
| | 587 | var version_buffer: [32]u8 = undefined; |
| | 588 | const version: []const u8 = std.fmt.bufPrint(&version_buffer, "{}", .{man.version}) catch &version_buffer; |
| | 589 | return .init(f.computed_hash.digest, man.name, version, saturated_size); |
| | 590 | } |
| | 591 | return .initNaked(f.computed_hash.digest, saturated_size); |
| | 592 | } |
| | 593 | |
| 565 | /// `computeHash` gets a free check for the existence of `build.zig`, but when | 594 | /// `computeHash` gets a free check for the existence of `build.zig`, but when |
| 566 | /// not computing a hash, we need to do a syscall to check for it. | 595 | /// not computing a hash, we need to do a syscall to check for it. |
| 567 | fn checkBuildFileExistence(f: *Fetch) RunError!void { | 596 | fn checkBuildFileExistence(f: *Fetch) RunError!void { |
| ... | @@ -673,9 +702,8 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { | ... | @@ -673,9 +702,8 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 673 | .url = url, | 702 | .url = url, |
| 674 | .hash = h: { | 703 | .hash = h: { |
| 675 | const h = dep.hash orelse break :h null; | 704 | const h = dep.hash orelse break :h null; |
| 676 | const digest_len = @typeInfo(Manifest.MultiHashHexDigest).array.len; | 705 | const pkg_hash: Package.Hash = .fromSlice(h); |
| 677 | const multihash_digest = h[0..digest_len].*; | 706 | const gop = f.job_queue.table.getOrPutAssumeCapacity(pkg_hash); |
| 678 | const gop = f.job_queue.table.getOrPutAssumeCapacity(multihash_digest); | | |
| 679 | if (gop.found_existing) { | 707 | if (gop.found_existing) { |
| 680 | if (!dep.lazy) { | 708 | if (!dep.lazy) { |
| 681 | gop.value_ptr.*.lazy_status = .eager; | 709 | gop.value_ptr.*.lazy_status = .eager; |
| ... | @@ -683,15 +711,15 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { | ... | @@ -683,15 +711,15 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 683 | continue; | 711 | continue; |
| 684 | } | 712 | } |
| 685 | gop.value_ptr.* = new_fetch; | 713 | gop.value_ptr.* = new_fetch; |
| 686 | break :h multihash_digest; | 714 | break :h pkg_hash; |
| 687 | }, | 715 | }, |
| 688 | } }, | 716 | } }, |
| 689 | .path => |rel_path| l: { | 717 | .path => |rel_path| l: { |
| 690 | // This might produce an invalid path, which is checked for | 718 | // This might produce an invalid path, which is checked for |
| 691 | // at the beginning of run(). | 719 | // at the beginning of run(). |
| 692 | const new_root = try f.package_root.resolvePosix(parent_arena, rel_path); | 720 | const new_root = try f.package_root.resolvePosix(parent_arena, rel_path); |
| 693 | const multihash_digest = relativePathDigest(new_root, cache_root); | 721 | const pkg_hash = relativePathDigest(new_root, cache_root); |
| 694 | const gop = f.job_queue.table.getOrPutAssumeCapacity(multihash_digest); | 722 | const gop = f.job_queue.table.getOrPutAssumeCapacity(pkg_hash); |
| 695 | if (gop.found_existing) { | 723 | if (gop.found_existing) { |
| 696 | if (!dep.lazy) { | 724 | if (!dep.lazy) { |
| 697 | gop.value_ptr.*.lazy_status = .eager; | 725 | gop.value_ptr.*.lazy_status = .eager; |
| ... | @@ -724,7 +752,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { | ... | @@ -724,7 +752,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 724 | .error_bundle = undefined, | 752 | .error_bundle = undefined, |
| 725 | .manifest = null, | 753 | .manifest = null, |
| 726 | .manifest_ast = undefined, | 754 | .manifest_ast = undefined, |
| 727 | .actual_hash = undefined, | 755 | .computed_hash = undefined, |
| 728 | .has_build_zig = false, | 756 | .has_build_zig = false, |
| 729 | .oom_flag = false, | 757 | .oom_flag = false, |
| 730 | .latest_commit = null, | 758 | .latest_commit = null, |
| ... | @@ -746,11 +774,8 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { | ... | @@ -746,11 +774,8 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 746 | } | 774 | } |
| 747 | } | 775 | } |
| 748 | | 776 | |
| 749 | pub fn relativePathDigest( | 777 | pub fn relativePathDigest(pkg_root: Cache.Path, cache_root: Cache.Directory) Package.Hash { |
| 750 | pkg_root: Cache.Path, | 778 | var hasher = Package.Hash.Algo.init(.{}); |
| 751 | cache_root: Cache.Directory, | | |
| 752 | ) Manifest.MultiHashHexDigest { | | |
| 753 | var hasher = Manifest.Hash.init(.{}); | | |
| 754 | // This hash is a tuple of: | 779 | // This hash is a tuple of: |
| 755 | // * whether it relative to the global cache directory or to the root package | 780 | // * whether it relative to the global cache directory or to the root package |
| 756 | // * the relative file path from there to the build root of the package | 781 | // * the relative file path from there to the build root of the package |
| ... | @@ -759,7 +784,7 @@ pub fn relativePathDigest( | ... | @@ -759,7 +784,7 @@ pub fn relativePathDigest( |
| 759 | else | 784 | else |
| 760 | &package_hash_prefix_project); | 785 | &package_hash_prefix_project); |
| 761 | hasher.update(pkg_root.sub_path); | 786 | hasher.update(pkg_root.sub_path); |
| 762 | return Manifest.hexDigest(hasher.finalResult()); | 787 | return .fromSlice(&hasher.finalResult()); |
| 763 | } | 788 | } |
| 764 | | 789 | |
| 765 | pub fn workerRun(f: *Fetch, prog_name: []const u8) void { | 790 | pub fn workerRun(f: *Fetch, prog_name: []const u8) void { |
| ... | @@ -1387,11 +1412,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: fs.Dir, tmp_dir: fs.Dir) anyerror!void | ... | @@ -1387,11 +1412,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: fs.Dir, tmp_dir: fs.Dir) anyerror!void |
| 1387 | } | 1412 | } |
| 1388 | } | 1413 | } |
| 1389 | | 1414 | |
| 1390 | pub fn renameTmpIntoCache( | 1415 | pub fn renameTmpIntoCache(cache_dir: fs.Dir, tmp_dir_sub_path: []const u8, dest_dir_sub_path: []const u8) !void { |
| 1391 | cache_dir: fs.Dir, | | |
| 1392 | tmp_dir_sub_path: []const u8, | | |
| 1393 | dest_dir_sub_path: []const u8, | | |
| 1394 | ) !void { | | |
| 1395 | assert(dest_dir_sub_path[1] == fs.path.sep); | 1416 | assert(dest_dir_sub_path[1] == fs.path.sep); |
| 1396 | var handled_missing_dir = false; | 1417 | var handled_missing_dir = false; |
| 1397 | while (true) { | 1418 | while (true) { |
| ... | @@ -1417,16 +1438,17 @@ pub fn renameTmpIntoCache( | ... | @@ -1417,16 +1438,17 @@ pub fn renameTmpIntoCache( |
| 1417 | } | 1438 | } |
| 1418 | } | 1439 | } |
| 1419 | | 1440 | |
| | 1441 | const ComputedHash = struct { |
| | 1442 | digest: Package.Hash.Digest, |
| | 1443 | total_size: u64, |
| | 1444 | }; |
| | 1445 | |
| 1420 | /// Assumes that files not included in the package have already been filtered | 1446 | /// Assumes that files not included in the package have already been filtered |
| 1421 | /// prior to calling this function. This ensures that files not protected by | 1447 | /// prior to calling this function. This ensures that files not protected by |
| 1422 | /// the hash are not present on the file system. Empty directories are *not | 1448 | /// the hash are not present on the file system. Empty directories are *not |
| 1423 | /// hashed* and must not be present on the file system when calling this | 1449 | /// hashed* and must not be present on the file system when calling this |
| 1424 | /// function. | 1450 | /// function. |
| 1425 | fn computeHash( | 1451 | fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!ComputedHash { |
| 1426 | f: *Fetch, | | |
| 1427 | pkg_path: Cache.Path, | | |
| 1428 | filter: Filter, | | |
| 1429 | ) RunError!Manifest.Digest { | | |
| 1430 | // All the path name strings need to be in memory for sorting. | 1452 | // All the path name strings need to be in memory for sorting. |
| 1431 | const arena = f.arena.allocator(); | 1453 | const arena = f.arena.allocator(); |
| 1432 | const gpa = f.arena.child_allocator; | 1454 | const gpa = f.arena.child_allocator; |
| ... | @@ -1449,6 +1471,9 @@ fn computeHash( | ... | @@ -1449,6 +1471,9 @@ fn computeHash( |
| 1449 | var walker = try root_dir.walk(gpa); | 1471 | var walker = try root_dir.walk(gpa); |
| 1450 | defer walker.deinit(); | 1472 | defer walker.deinit(); |
| 1451 | | 1473 | |
| | 1474 | // Total number of bytes of file contents included in the package. |
| | 1475 | var total_size: u64 = 0; |
| | 1476 | |
| 1452 | { | 1477 | { |
| 1453 | // The final hash will be a hash of each file hashed independently. This | 1478 | // The final hash will be a hash of each file hashed independently. This |
| 1454 | // allows hashing in parallel. | 1479 | // allows hashing in parallel. |
| ... | @@ -1506,6 +1531,7 @@ fn computeHash( | ... | @@ -1506,6 +1531,7 @@ fn computeHash( |
| 1506 | .kind = kind, | 1531 | .kind = kind, |
| 1507 | .hash = undefined, // to be populated by the worker | 1532 | .hash = undefined, // to be populated by the worker |
| 1508 | .failure = undefined, // to be populated by the worker | 1533 | .failure = undefined, // to be populated by the worker |
| | 1534 | .size = undefined, // to be populated by the worker |
| 1509 | }; | 1535 | }; |
| 1510 | thread_pool.spawnWg(&wait_group, workerHashFile, .{ root_dir, hashed_file }); | 1536 | thread_pool.spawnWg(&wait_group, workerHashFile, .{ root_dir, hashed_file }); |
| 1511 | try all_files.append(hashed_file); | 1537 | try all_files.append(hashed_file); |
| ... | @@ -1544,7 +1570,7 @@ fn computeHash( | ... | @@ -1544,7 +1570,7 @@ fn computeHash( |
| 1544 | | 1570 | |
| 1545 | std.mem.sortUnstable(*HashedFile, all_files.items, {}, HashedFile.lessThan); | 1571 | std.mem.sortUnstable(*HashedFile, all_files.items, {}, HashedFile.lessThan); |
| 1546 | | 1572 | |
| 1547 | var hasher = Manifest.Hash.init(.{}); | 1573 | var hasher = Package.Hash.Algo.init(.{}); |
| 1548 | var any_failures = false; | 1574 | var any_failures = false; |
| 1549 | for (all_files.items) |hashed_file| { | 1575 | for (all_files.items) |hashed_file| { |
| 1550 | hashed_file.failure catch |err| { | 1576 | hashed_file.failure catch |err| { |
| ... | @@ -1556,6 +1582,7 @@ fn computeHash( | ... | @@ -1556,6 +1582,7 @@ fn computeHash( |
| 1556 | }); | 1582 | }); |
| 1557 | }; | 1583 | }; |
| 1558 | hasher.update(&hashed_file.hash); | 1584 | hasher.update(&hashed_file.hash); |
| | 1585 | total_size += hashed_file.size; |
| 1559 | } | 1586 | } |
| 1560 | for (deleted_files.items) |deleted_file| { | 1587 | for (deleted_files.items) |deleted_file| { |
| 1561 | deleted_file.failure catch |err| { | 1588 | deleted_file.failure catch |err| { |
| ... | @@ -1580,7 +1607,10 @@ fn computeHash( | ... | @@ -1580,7 +1607,10 @@ fn computeHash( |
| 1580 | }; | 1607 | }; |
| 1581 | } | 1608 | } |
| 1582 | | 1609 | |
| 1583 | return hasher.finalResult(); | 1610 | return .{ |
| | 1611 | .digest = hasher.finalResult(), |
| | 1612 | .total_size = total_size, |
| | 1613 | }; |
| 1584 | } | 1614 | } |
| 1585 | | 1615 | |
| 1586 | fn dumpHashInfo(all_files: []const *const HashedFile) !void { | 1616 | fn dumpHashInfo(all_files: []const *const HashedFile) !void { |
| ... | @@ -1609,8 +1639,9 @@ fn workerDeleteFile(dir: fs.Dir, deleted_file: *DeletedFile) void { | ... | @@ -1609,8 +1639,9 @@ fn workerDeleteFile(dir: fs.Dir, deleted_file: *DeletedFile) void { |
| 1609 | | 1639 | |
| 1610 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { | 1640 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { |
| 1611 | var buf: [8000]u8 = undefined; | 1641 | var buf: [8000]u8 = undefined; |
| 1612 | var hasher = Manifest.Hash.init(.{}); | 1642 | var hasher = Package.Hash.Algo.init(.{}); |
| 1613 | hasher.update(hashed_file.normalized_path); | 1643 | hasher.update(hashed_file.normalized_path); |
| | 1644 | var file_size: u64 = 0; |
| 1614 | | 1645 | |
| 1615 | switch (hashed_file.kind) { | 1646 | switch (hashed_file.kind) { |
| 1616 | .file => { | 1647 | .file => { |
| ... | @@ -1622,6 +1653,7 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void | ... | @@ -1622,6 +1653,7 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void |
| 1622 | while (true) { | 1653 | while (true) { |
| 1623 | const bytes_read = try file.read(&buf); | 1654 | const bytes_read = try file.read(&buf); |
| 1624 | if (bytes_read == 0) break; | 1655 | if (bytes_read == 0) break; |
| | 1656 | file_size += bytes_read; |
| 1625 | hasher.update(buf[0..bytes_read]); | 1657 | hasher.update(buf[0..bytes_read]); |
| 1626 | file_header.update(buf[0..bytes_read]); | 1658 | file_header.update(buf[0..bytes_read]); |
| 1627 | } | 1659 | } |
| ... | @@ -1641,6 +1673,7 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void | ... | @@ -1641,6 +1673,7 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void |
| 1641 | }, | 1673 | }, |
| 1642 | } | 1674 | } |
| 1643 | hasher.final(&hashed_file.hash); | 1675 | hasher.final(&hashed_file.hash); |
| | 1676 | hashed_file.size = file_size; |
| 1644 | } | 1677 | } |
| 1645 | | 1678 | |
| 1646 | fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void { | 1679 | fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void { |
| ... | @@ -1667,9 +1700,10 @@ const DeletedFile = struct { | ... | @@ -1667,9 +1700,10 @@ const DeletedFile = struct { |
| 1667 | const HashedFile = struct { | 1700 | const HashedFile = struct { |
| 1668 | fs_path: []const u8, | 1701 | fs_path: []const u8, |
| 1669 | normalized_path: []const u8, | 1702 | normalized_path: []const u8, |
| 1670 | hash: Manifest.Digest, | 1703 | hash: Package.Hash.Digest, |
| 1671 | failure: Error!void, | 1704 | failure: Error!void, |
| 1672 | kind: Kind, | 1705 | kind: Kind, |
| | 1706 | size: u64, |
| 1673 | | 1707 | |
| 1674 | const Error = | 1708 | const Error = |
| 1675 | fs.File.OpenError || | 1709 | fs.File.OpenError || |
| ... | @@ -1744,12 +1778,8 @@ const Filter = struct { | ... | @@ -1744,12 +1778,8 @@ const Filter = struct { |
| 1744 | } | 1778 | } |
| 1745 | }; | 1779 | }; |
| 1746 | | 1780 | |
| 1747 | pub fn depDigest( | 1781 | pub fn depDigest(pkg_root: Cache.Path, cache_root: Cache.Directory, dep: Manifest.Dependency) ?Package.Hash { |
| 1748 | pkg_root: Cache.Path, | 1782 | if (dep.hash) |h| return .fromSlice(h); |
| 1749 | cache_root: Cache.Directory, | | |
| 1750 | dep: Manifest.Dependency, | | |
| 1751 | ) ?Manifest.MultiHashHexDigest { | | |
| 1752 | if (dep.hash) |h| return h[0..Manifest.multihash_hex_digest_len].*; | | |
| 1753 | | 1783 | |
| 1754 | switch (dep.location) { | 1784 | switch (dep.location) { |
| 1755 | .url => return null, | 1785 | .url => return null, |
| ... | @@ -2137,7 +2167,7 @@ test "tarball with excluded duplicate paths" { | ... | @@ -2137,7 +2167,7 @@ test "tarball with excluded duplicate paths" { |
| 2137 | defer fb.deinit(); | 2167 | defer fb.deinit(); |
| 2138 | try fetch.run(); | 2168 | try fetch.run(); |
| 2139 | | 2169 | |
| 2140 | const hex_digest = Package.Manifest.hexDigest(fetch.actual_hash); | 2170 | const hex_digest = Package.multiHashHexDigest(fetch.computed_hash.digest); |
| 2141 | try std.testing.expectEqualStrings( | 2171 | try std.testing.expectEqualStrings( |
| 2142 | "12200bafe035cbb453dd717741b66e9f9d1e6c674069d06121dafa1b2e62eb6b22da", | 2172 | "12200bafe035cbb453dd717741b66e9f9d1e6c674069d06121dafa1b2e62eb6b22da", |
| 2143 | &hex_digest, | 2173 | &hex_digest, |
| ... | @@ -2181,7 +2211,7 @@ test "tarball without root folder" { | ... | @@ -2181,7 +2211,7 @@ test "tarball without root folder" { |
| 2181 | defer fb.deinit(); | 2211 | defer fb.deinit(); |
| 2182 | try fetch.run(); | 2212 | try fetch.run(); |
| 2183 | | 2213 | |
| 2184 | const hex_digest = Package.Manifest.hexDigest(fetch.actual_hash); | 2214 | const hex_digest = Package.multiHashHexDigest(fetch.computed_hash.digest); |
| 2185 | try std.testing.expectEqualStrings( | 2215 | try std.testing.expectEqualStrings( |
| 2186 | "12209f939bfdcb8b501a61bb4a43124dfa1b2848adc60eec1e4624c560357562b793", | 2216 | "12209f939bfdcb8b501a61bb4a43124dfa1b2848adc60eec1e4624c560357562b793", |
| 2187 | &hex_digest, | 2217 | &hex_digest, |
| ... | @@ -2222,7 +2252,7 @@ test "set executable bit based on file content" { | ... | @@ -2222,7 +2252,7 @@ test "set executable bit based on file content" { |
| 2222 | try fetch.run(); | 2252 | try fetch.run(); |
| 2223 | try std.testing.expectEqualStrings( | 2253 | try std.testing.expectEqualStrings( |
| 2224 | "1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3", | 2254 | "1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3", |
| 2225 | &Manifest.hexDigest(fetch.actual_hash), | 2255 | &Package.multiHashHexDigest(fetch.computed_hash.digest), |
| 2226 | ); | 2256 | ); |
| 2227 | | 2257 | |
| 2228 | var out = try fb.packageDir(); | 2258 | var out = try fb.packageDir(); |
| ... | @@ -2304,7 +2334,7 @@ const TestFetchBuilder = struct { | ... | @@ -2304,7 +2334,7 @@ const TestFetchBuilder = struct { |
| 2304 | .error_bundle = undefined, | 2334 | .error_bundle = undefined, |
| 2305 | .manifest = null, | 2335 | .manifest = null, |
| 2306 | .manifest_ast = undefined, | 2336 | .manifest_ast = undefined, |
| 2307 | .actual_hash = undefined, | 2337 | .computed_hash = undefined, |
| 2308 | .has_build_zig = false, | 2338 | .has_build_zig = false, |
| 2309 | .oom_flag = false, | 2339 | .oom_flag = false, |
| 2310 | .latest_commit = null, | 2340 | .latest_commit = null, |