authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-16 18:15:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-16 18:15:47-07:00
logba656e5c9f58e0440278918e5c6cdf0ac2fe673c
tree109651051f0077d7bc6942ec6d4e3ed2c24daf7f
parentf1a9344ffeaa9b449eb111a112df33c845a1210c

zig fetch: add `--debug-hash` argument

This argument causes zig to print verbose hashing information to stdout, which can be used to diff two different fetches and find out why the hashes do not equal each other.

2 files changed, 41 insertions(+), 3 deletions(-)

src/Package/Fetch.zig+34-3
......@@ -81,6 +81,10 @@ pub const JobQueue = struct {
8181 wait_group: WaitGroup = .{},
8282 global_cache: Cache.Directory,
8383 recursive: bool,
84 /// Dumps hash information to stdout which can be used to troubleshoot why
85 /// two hashes of the same package do not match.
86 /// If this is true, `recursive` must be false.
87 debug_hash: bool,
8488 work_around_btrfs_bug: bool,
8589
8690 pub const Table = std.AutoArrayHashMapUnmanaged(Manifest.MultiHashHexDigest, *Fetch);
......@@ -1315,7 +1319,7 @@ fn computeHash(
13151319 const kind: HashedFile.Kind = switch (entry.kind) {
13161320 .directory => unreachable,
13171321 .file => .file,
1318 .sym_link => .sym_link,
1322 .sym_link => .link,
13191323 else => return f.fail(f.location_tok, try eb.printString(
13201324 "package contains '{s}' which has illegal file type '{s}'",
13211325 .{ entry.path, @tagName(entry.kind) },
......@@ -1399,9 +1403,36 @@ fn computeHash(
13991403 }
14001404
14011405 if (any_failures) return error.FetchFailed;
1406
1407 if (f.job_queue.debug_hash) {
1408 assert(!f.job_queue.recursive);
1409 // Print something to stdout that can be text diffed to figure out why
1410 // the package hash is different.
1411 dumpHashInfo(all_files.items) catch |err| {
1412 std.debug.print("unable to write to stdout: {s}\n", .{@errorName(err)});
1413 std.process.exit(1);
1414 };
1415 }
1416
14021417 return hasher.finalResult();
14031418}
14041419
1420fn dumpHashInfo(all_files: []const *const HashedFile) !void {
1421 const stdout = std.io.getStdOut();
1422 var bw = std.io.bufferedWriter(stdout.writer());
1423 const w = bw.writer();
1424
1425 for (all_files) |hashed_file| {
1426 try w.print("{s}: {s}: {s}\n", .{
1427 @tagName(hashed_file.kind),
1428 std.fmt.fmtSliceHexLower(&hashed_file.hash),
1429 hashed_file.normalized_path,
1430 });
1431 }
1432
1433 try bw.flush();
1434}
1435
14051436fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {
14061437 defer wg.finish();
14071438 hashed_file.failure = hashFileFallible(dir, hashed_file);
......@@ -1427,7 +1458,7 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void
14271458 hasher.update(buf[0..bytes_read]);
14281459 }
14291460 },
1430 .sym_link => {
1461 .link => {
14311462 const link_name = try dir.readLink(hashed_file.fs_path, &buf);
14321463 if (fs.path.sep != canonical_sep) {
14331464 // Package hashes are intended to be consistent across
......@@ -1480,7 +1511,7 @@ const HashedFile = struct {
14801511 fs.File.StatError ||
14811512 fs.Dir.ReadLinkError;
14821513
1483 const Kind = enum { file, sym_link };
1514 const Kind = enum { file, link };
14841515
14851516 fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool {
14861517 _ = context;
src/main.zig+7
......@@ -5143,6 +5143,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
51435143 .thread_pool = &thread_pool,
51445144 .global_cache = global_cache_directory,
51455145 .recursive = true,
5146 .debug_hash = false,
51465147 .work_around_btrfs_bug = work_around_btrfs_bug,
51475148 };
51485149 defer job_queue.deinit();
......@@ -6991,6 +6992,7 @@ pub const usage_fetch =
69916992 \\Options:
69926993 \\ -h, --help Print this help and exit
69936994 \\ --global-cache-dir [path] Override path to global Zig cache directory
6995 \\ --debug-hash Print verbose hash information to stdout
69946996 \\
69956997;
69966998
......@@ -7004,6 +7006,7 @@ fn cmdFetch(
70047006 std.process.hasEnvVarConstant("ZIG_BTRFS_WORKAROUND");
70057007 var opt_path_or_url: ?[]const u8 = null;
70067008 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
7009 var debug_hash: bool = false;
70077010
70087011 {
70097012 var i: usize = 0;
......@@ -7019,6 +7022,9 @@ fn cmdFetch(
70197022 i += 1;
70207023 override_global_cache_dir = args[i];
70217024 continue;
7025 } else if (mem.eql(u8, arg, "--debug-hash")) {
7026 debug_hash = true;
7027 continue;
70227028 } else {
70237029 fatal("unrecognized parameter: '{s}'", .{arg});
70247030 }
......@@ -7057,6 +7063,7 @@ fn cmdFetch(
70577063 .thread_pool = &thread_pool,
70587064 .global_cache = global_cache_directory,
70597065 .recursive = false,
7066 .debug_hash = debug_hash,
70607067 .work_around_btrfs_bug = work_around_btrfs_bug,
70617068 };
70627069 defer job_queue.deinit();