From 35b0cef70aad2202208c9a9ea020f4e449f076ec Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 16 Apr 2026 17:07:37 +0200 Subject: [PATCH] chore: Update to zig 0.16.0 --- .gitignore | 1 + build.zig.zon | 2 +- src/Diagnostics.zig | 102 +++++++++++++++++++------------------- src/InstalledPackages.zig | 2 +- src/Package.zig | 8 +-- src/PackageManager.zig | 30 +++++------ src/Packages.zig | 4 +- src/Progress.zig | 37 ++++++++------ src/fs.zig | 28 ++++------- src/fuzz.zig | 18 ++++--- src/git.zig | 2 +- src/main.zig | 81 ++++++++++++++---------------- src/testing.zig | 20 ++++---- 13 files changed, 169 insertions(+), 166 deletions(-) diff --git a/.gitignore b/.gitignore index d8c8979..36addef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .zig-cache zig-out +zig-pkg diff --git a/build.zig.zon b/build.zig.zon index f74191c..dd72241 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .dipm, .version = "0.32.0", - .minimum_zig_version = "0.16.0-dev.2145+ec25b1384", + .minimum_zig_version = "0.16.0", .fingerprint = 0xce3a2bf46fc8eacf, .dependencies = .{ .spaghet = .{ diff --git a/src/Diagnostics.zig b/src/Diagnostics.zig index ed7e8ae..eb3ab24 100644 --- a/src/Diagnostics.zig +++ b/src/Diagnostics.zig @@ -1,5 +1,6 @@ +io: std.Io, arena_alloc: std.heap.ArenaAllocator, -lock: std.Thread.Mutex, +lock: std.Io.Mutex, successes: struct { donate: std.ArrayListUnmanaged(PackageDonate), @@ -25,30 +26,31 @@ failures: struct { generic_error: std.ArrayListUnmanaged(GenericError), }, -pub fn init(alloc: std.mem.Allocator) Diagnostics { +pub fn init(io: std.Io, alloc: std.mem.Allocator) Diagnostics { return .{ + .io = io, .arena_alloc = std.heap.ArenaAllocator.init(alloc), - .lock = .{}, + .lock = .init, .successes = .{ - .donate = .{}, - .installs = .{}, - .updates = .{}, - .uninstalls = .{}, + .donate = .empty, + .installs = .empty, + .updates = .empty, + .uninstalls = .empty, }, .warnings = .{ - .already_installed = .{}, - .not_installed = .{}, - .not_found = .{}, - .not_found_for_target = .{}, - .up_to_date = .{}, + .already_installed = .empty, + .not_installed = .empty, + .not_found = .empty, + .not_found_for_target = .empty, + .up_to_date = .empty, }, .failures = .{ - .no_version_found = .{}, - .hash_mismatches = .{}, - .downloads = .{}, - .downloads_with_status = .{}, - .path_already_exists = .{}, - .generic_error = .{}, + .no_version_found = .empty, + .hash_mismatches = .empty, + .downloads = .empty, + .downloads_with_status = .empty, + .path_already_exists = .empty, + .generic_error = .empty, }, }; } @@ -87,8 +89,8 @@ pub const ReportOptions = struct { }; pub fn report(diag: *Diagnostics, writer: *std.Io.Writer, opt: ReportOptions) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); const esc = opt.escapes; inline for (@typeInfo(@TypeOf(diag.successes)).@"struct".fields) |field| { @@ -121,8 +123,8 @@ pub fn hasFailed(diag: Diagnostics) bool { } pub fn donate(diag: *Diagnostics, pkg: PackageDonate) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.successes.donate.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), .version = try diag.arena().dupe(u8, pkg.version), @@ -131,8 +133,8 @@ pub fn donate(diag: *Diagnostics, pkg: PackageDonate) !void { } pub fn installSucceeded(diag: *Diagnostics, pkg: PackageInstall) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.successes.installs.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), .version = try diag.arena().dupe(u8, pkg.version), @@ -140,8 +142,8 @@ pub fn installSucceeded(diag: *Diagnostics, pkg: PackageInstall) !void { } pub fn updateSucceeded(diag: *Diagnostics, pkg: PackageFromTo) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.successes.updates.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), .from_version = try diag.arena().dupe(u8, pkg.from_version), @@ -150,8 +152,8 @@ pub fn updateSucceeded(diag: *Diagnostics, pkg: PackageFromTo) !void { } pub fn uninstallSucceeded(diag: *Diagnostics, pkg: PackageUninstall) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.successes.uninstalls.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), .version = try diag.arena().dupe(u8, pkg.version), @@ -159,32 +161,32 @@ pub fn uninstallSucceeded(diag: *Diagnostics, pkg: PackageUninstall) !void { } pub fn alreadyInstalled(diag: *Diagnostics, pkg: PackageAlreadyInstalled) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.warnings.already_installed.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), }); } pub fn notInstalled(diag: *Diagnostics, pkg: PackageNotInstalled) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.warnings.not_installed.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), }); } pub fn notFound(diag: *Diagnostics, pkg: PackageNotFound) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.warnings.not_found.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), }); } pub fn notFoundForTarget(diag: *Diagnostics, not_found: PackageTarget) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.warnings.not_found_for_target.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, not_found.name), .target = not_found.target, @@ -192,8 +194,8 @@ pub fn notFoundForTarget(diag: *Diagnostics, not_found: PackageTarget) !void { } pub fn upToDate(diag: *Diagnostics, pkg: PackageUpToDate) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.warnings.up_to_date.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), .version = try diag.arena().dupe(u8, pkg.version), @@ -201,8 +203,8 @@ pub fn upToDate(diag: *Diagnostics, pkg: PackageUpToDate) !void { } pub fn noVersionFound(diag: *Diagnostics, pkg: PackageError) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.failures.no_version_found.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, pkg.name), .err = pkg.err, @@ -210,8 +212,8 @@ pub fn noVersionFound(diag: *Diagnostics, pkg: PackageError) !void { } pub fn hashMismatch(diag: *Diagnostics, mismatch: HashMismatch) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.failures.hash_mismatches.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, mismatch.name), .version = try diag.arena().dupe(u8, mismatch.version), @@ -221,8 +223,8 @@ pub fn hashMismatch(diag: *Diagnostics, mismatch: HashMismatch) !void { } pub fn downloadFailed(diag: *Diagnostics, failure: DownloadFailed) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.failures.downloads.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, failure.name), .version = try diag.arena().dupe(u8, failure.version), @@ -232,8 +234,8 @@ pub fn downloadFailed(diag: *Diagnostics, failure: DownloadFailed) !void { } pub fn downloadFailedWithStatus(diag: *Diagnostics, failure: DownloadFailedWithStatus) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.failures.downloads_with_status.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, failure.name), .version = try diag.arena().dupe(u8, failure.version), @@ -243,8 +245,8 @@ pub fn downloadFailedWithStatus(diag: *Diagnostics, failure: DownloadFailedWithS } pub fn pathAlreadyExists(diag: *Diagnostics, failure: PathAlreadyExists) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.failures.path_already_exists.append(diag.gpa(), .{ .name = try diag.arena().dupe(u8, failure.name), .path = try diag.arena().dupe(u8, failure.path), @@ -252,8 +254,8 @@ pub fn pathAlreadyExists(diag: *Diagnostics, failure: PathAlreadyExists) !void { } pub fn genericError(diag: *Diagnostics, failure: GenericError) !void { - diag.lock.lock(); - defer diag.lock.unlock(); + try diag.lock.lock(diag.io); + defer diag.lock.unlock(diag.io); return diag.failures.generic_error.append(diag.gpa(), .{ .id = try diag.arena().dupe(u8, failure.id), .msg = try diag.arena().dupe(u8, failure.msg), diff --git a/src/InstalledPackages.zig b/src/InstalledPackages.zig index 0b97f76..f80364b 100644 --- a/src/InstalledPackages.zig +++ b/src/InstalledPackages.zig @@ -1,5 +1,5 @@ arena_alloc: std.heap.ArenaAllocator, -by_name: std.StringArrayHashMapUnmanaged(InstalledPackage), +by_name: std.array_hash_map.String(InstalledPackage), file: ?std.Io.File, diff --git a/src/Package.zig b/src/Package.zig index a148ba4..e40aa77 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -1055,7 +1055,7 @@ fn testFromGithub(options: struct { try cwd.writeFile(io, .{ .sub_path = latest_release_file_path, .data = blk: { - var out = std.ArrayList(u8){}; + var out = std.ArrayList(u8).empty; try out.appendSlice(arena, "{\"tag_name\": \""); try out.appendSlice(arena, options.tag_name); try out.appendSlice(arena, "\",\"assets\": [{"); @@ -1247,7 +1247,7 @@ fn findBinaries( ); const script_result = try std.process.run(arena, io, .{ .argv = &.{ "sh", "-c", shell_script }, - .cwd_dir = dir, + .cwd = .{ .dir = dir }, }); // Deduplicate binaries by their basename @@ -1423,7 +1423,7 @@ test findShare { } fn findManPages(io: std.Io, arena: std.mem.Allocator, dir: std.Io.Dir) ![]const []const u8 { - var res = std.StringArrayHashMap([]const u8).init(arena); + var res = std.array_hash_map.String([]const u8).empty; var walker = try dir.walk(arena); while (try walker.next(io)) |file_entry| { @@ -1431,7 +1431,7 @@ fn findManPages(io: std.Io, arena: std.mem.Allocator, dir: std.Io.Dir) ![]const continue; const section = path.isManPage(file_entry.basename) orelse continue; - const res_entry = try res.getOrPut(file_entry.basename); + const res_entry = try res.getOrPut(arena, file_entry.basename); if (!res_entry.found_existing) res_entry.key_ptr.* = try arena.dupe(u8, file_entry.basename); diff --git a/src/PackageManager.zig b/src/PackageManager.zig index 8b0ecec..dfd2e08 100644 --- a/src/PackageManager.zig +++ b/src/PackageManager.zig @@ -110,7 +110,7 @@ fn packages(pm: *PackageManager) !*const Packages { } pub fn installMany(pm: *PackageManager, pkg_names: []const []const u8) !void { - var pkgs_not_installed = std.ArrayList([]const u8){}; + var pkgs_not_installed = std.ArrayList([]const u8).empty; defer pkgs_not_installed.deinit(pm.gpa); try pkgs_not_installed.ensureTotalCapacity(pm.gpa, pkg_names.len); @@ -126,7 +126,7 @@ pub fn installMany(pm: *PackageManager, pkg_names: []const []const u8) !void { return; var pkgs_to_install = try pm.pkgsToInstall(pkgs_not_installed.items); - defer pkgs_to_install.deinit(); + defer pkgs_to_install.deinit(pm.gpa); const global_progress = switch (pkgs_to_install.count()) { 0 => return, @@ -173,14 +173,14 @@ pub fn installMany(pm: *PackageManager, pkg_names: []const []const u8) !void { try pm.installed.flush(pm.io); } -const PkgsToInstall = std.StringArrayHashMap(Package.Specific); +const PkgsToInstall = std.array_hash_map.String(Package.Specific); fn pkgsToInstall(pm: *PackageManager, pkg_names: []const []const u8) !PkgsToInstall { - var pkgs_to_install = std.StringArrayHashMap(Package.Specific).init(pm.gpa); - errdefer pkgs_to_install.deinit(); + var pkgs_to_install = std.array_hash_map.String(Package.Specific).empty; + errdefer pkgs_to_install.deinit(pm.gpa); // First, deduplicate. The value is undefined and set later - try pkgs_to_install.ensureTotalCapacity(pkg_names.len); + try pkgs_to_install.ensureTotalCapacity(pm.gpa, pkg_names.len); for (pkg_names) |pkg_name| pkgs_to_install.putAssumeCapacity(pkg_name, undefined); @@ -292,7 +292,7 @@ fn installExtractedPackage( from_dir: std.Io.Dir, pkg: Package.Specific, ) !void { - var locations = std.ArrayList([]const u8){}; + var locations = std.ArrayList([]const u8).empty; try locations.ensureUnusedCapacity(pm.installed.arena(), pkg.install.install_bin.len + pkg.install.install_lib.len + pkg.install.install_share.len); @@ -431,7 +431,7 @@ fn installFile( pub fn uninstallMany(pm: *PackageManager, pkg_names: []const []const u8) !void { var pkgs_to_uninstall = try pm.pkgsToUninstall(pkg_names); - defer pkgs_to_uninstall.deinit(); + defer pkgs_to_uninstall.deinit(pm.gpa); for (pkgs_to_uninstall.keys(), pkgs_to_uninstall.values()) |pkg_name, pkg| { try pm.uninstallOneUnchecked(pkg_name, pkg); @@ -444,11 +444,11 @@ pub fn uninstallMany(pm: *PackageManager, pkg_names: []const []const u8) !void { fn pkgsToUninstall( pm: *PackageManager, pkg_names: []const []const u8, -) !std.StringArrayHashMap(InstalledPackage) { - var pkgs_to_uninstall = std.StringArrayHashMap(InstalledPackage).init(pm.gpa); - errdefer pkgs_to_uninstall.deinit(); +) !std.array_hash_map.String(InstalledPackage) { + var pkgs_to_uninstall = std.array_hash_map.String(InstalledPackage).empty; + errdefer pkgs_to_uninstall.deinit(pm.gpa); - try pkgs_to_uninstall.ensureTotalCapacity(pkg_names.len); + try pkgs_to_uninstall.ensureTotalCapacity(pm.gpa, pkg_names.len); for (pkg_names) |pkg_name| { const pkg = pm.installed.by_name.get(pkg_name) orelse { try pm.diag.notInstalled((.{ .name = pkg_name })); @@ -502,10 +502,10 @@ fn updatePackages(pm: *PackageManager, pkg_names: []const []const u8, options: s force: bool, }) !void { var pkgs_to_uninstall = try pm.pkgsToUninstall(pkg_names); - defer pkgs_to_uninstall.deinit(); + defer pkgs_to_uninstall.deinit(pm.gpa); var pkgs_to_install = try pm.pkgsToInstall(pkgs_to_uninstall.keys()); - defer pkgs_to_install.deinit(); + defer pkgs_to_install.deinit(pm.gpa); const pkgs = try pm.packages(); if (!options.force) { @@ -583,7 +583,7 @@ fn updatePackages(pm: *PackageManager, pkg_names: []const []const u8, options: s } const DownloadAndExtractJobs = struct { - jobs: std.ArrayList(DownloadAndExtractJob) = .{}, + jobs: std.ArrayList(DownloadAndExtractJob) = .empty, fn init(options: struct { io: std.Io, diff --git a/src/Packages.zig b/src/Packages.zig index a2c1aa5..d585550 100644 --- a/src/Packages.zig +++ b/src/Packages.zig @@ -1,5 +1,5 @@ arena_alloc: std.heap.ArenaAllocator, -by_name: std.StringArrayHashMapUnmanaged(Package), +by_name: std.array_hash_map.String(Package), pub fn init(gpa: std.mem.Allocator) Packages { return .{ .arena_alloc = .init(gpa), .by_name = .{} }; @@ -219,7 +219,7 @@ fn parseInfo(pkgs: *Packages, parser: *ini.Parser) !struct { } { var parsed = parser.next(); - var donate = std.ArrayList([]const u8){}; + var donate = std.ArrayList([]const u8).empty; var version: ?[]const u8 = null; var description: []const u8 = ""; diff --git a/src/Progress.zig b/src/Progress.zig index 0471a72..9eb829e 100644 --- a/src/Progress.zig +++ b/src/Progress.zig @@ -23,9 +23,9 @@ pub const Node = enum(usize) { pub fn advance(node: Node, amount: u32) void { const state = node.unwrap() orelse return; - state.lock.lock(); + state.lock(); state.inner.curr +|= amount; - state.lock.unlock(); + state.unlock(); } pub fn set(node: Node, fields: struct { @@ -34,14 +34,14 @@ pub const Node = enum(usize) { max: ?u32 = null, }) void { const state = node.unwrap() orelse return; - state.lock.lock(); + state.lock(); if (fields.name) |name| state.inner.name = name; if (fields.max) |max| state.inner.max = max; if (fields.curr) |curr| state.inner.curr = curr; - state.lock.unlock(); + state.unlock(); } // Wraps a file reader to provide progress @@ -114,9 +114,7 @@ pub const Node = enum(usize) { const NodeState = struct { inner: Inner, - // TODO: This can probably be done in a lock free way using a certain value of `max` as a - // "locked" value, and spinning until we can get it - lock: std.Thread.Mutex, + locked: bool, pub const Inner = struct { name: ?[]const u8 = null, @@ -124,21 +122,21 @@ const NodeState = struct { max: u32 = 0, }; - const none = NodeState{ + pub const none = NodeState{ .inner = .{}, - .lock = .{}, + .locked = false, }; fn get(node: *NodeState) Inner { - node.lock.lock(); + node.lock(); const res = node.inner; - node.lock.unlock(); + node.unlock(); return res; } fn acquire(node: *NodeState, inner: Inner) bool { - node.lock.lock(); - defer node.lock.unlock(); + node.lock(); + defer node.unlock(); if (node.inner.name != null) return false; @@ -148,9 +146,18 @@ const NodeState = struct { } fn release(node: *NodeState) void { - node.lock.lock(); + node.lock(); node.inner = .{}; - node.lock.unlock(); + node.unlock(); + } + + fn lock(node: *NodeState) void { + while (@cmpxchgWeak(bool, &node.locked, false, true, .monotonic, .monotonic)) |_| {} + } + + fn unlock(node: *NodeState) void { + std.debug.assert(node.locked); + node.locked = false; } }; diff --git a/src/fs.zig b/src/fs.zig index 1087448..16e7742 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -23,7 +23,9 @@ pub const ZigCacheTmpDir = struct { }; pub fn zigCacheTmpDir(io: std.Io, open_dir_options: std.Io.Dir.OpenOptions) !ZigCacheTmpDir { - const zig_cache_path = zigCachePath(io); + var zig_cache_path_buf: [std.fs.max_path_bytes]u8 = undefined; + const zig_cache_path = zigCachePath(io, &zig_cache_path_buf); + var zig_cache_dir = try std.Io.Dir.cwd().createDirPathOpen(io, zig_cache_path, .{}); defer zig_cache_dir.close(io); @@ -49,17 +51,12 @@ pub fn zigCacheTmpDirPath(io: std.Io, gpa: std.mem.Allocator) ![:0]const u8 { return tmp_dir.path(gpa); } -var zig_cache_path_io: std.Io = undefined; -var zig_cache_path_buf: [std.fs.max_path_bytes]u8 = undefined; -var zig_cache_path_len: usize = 0; -var zig_cache_path_once = std.once(zigCachePathOnce); - -fn zigCachePathOnce() void { +pub fn zigCachePath(io: std.Io, out: []u8) []const u8 { comptime std.debug.assert(builtin.is_test); var self_exe_dir_path_buf: [std.fs.max_path_bytes]u8 = undefined; const self_exe_dir_path = blk: { - const len = std.process.executableDirPath(zig_cache_path_io, &self_exe_dir_path_buf) catch + const len = std.process.executableDirPath(io, &self_exe_dir_path_buf) catch break :blk ".zig-cache/o/aaaa"; break :blk self_exe_dir_path_buf[0..len]; }; @@ -69,14 +66,9 @@ fn zigCachePathOnce() void { const o_path = std.fs.path.dirname(self_exe_dir_path) orelse unreachable; const zig_cache_path = std.fs.path.dirname(o_path) orelse unreachable; - zig_cache_path_len = zig_cache_path.len; - @memcpy(zig_cache_path_buf[0..zig_cache_path.len], zig_cache_path); -} - -pub fn zigCachePath(io: std.Io) []const u8 { - zig_cache_path_io = io; - zig_cache_path_once.call(); - return zig_cache_path_buf[0..zig_cache_path_len]; + const len = @min(zig_cache_path.len, out.len); + @memcpy(out[0..len], zig_cache_path[0..len]); + return out[0..len]; } const tmp_dir_bytes_count = 12; @@ -262,7 +254,7 @@ fn testOneExtract(file_type: FileType, files: []const std.Io.Dir.WriteFileOption var tmp_dir = try zigCacheTmpDir(io, .{}); defer tmp_dir.deleteAndClose(io); - var args = std.ArrayList([]const u8){}; + var args = std.ArrayList([]const u8).empty; try args.ensureTotalCapacity(arena, 4 + files.len); for (files) |file| { @@ -283,7 +275,7 @@ fn testOneExtract(file_type: FileType, files: []const std.Io.Dir.WriteFileOption const res = switch (file_type) { .gz, .zip, .tar_xz, .tar_bz2, .tar_gz, .tar_zst, .tar => std.process.run(arena, io, .{ .argv = args.items, - .cwd_dir = tmp_dir.dir, + .cwd = .{ .dir = tmp_dir.dir }, }), .binary => std.process.RunResult{ .term = .{ .exited = 0 }, diff --git a/src/fuzz.zig b/src/fuzz.zig index 372a6ed..a3b1980 100644 --- a/src/fuzz.zig +++ b/src/fuzz.zig @@ -1,16 +1,20 @@ pub fn fnFromParseAndWrite( comptime parseAndWrite: fn (std.mem.Allocator, []const u8) anyerror![]u8, -) fn (void, []const u8) anyerror!void { +) fn (void, *std.testing.Smith) anyerror!void { return struct { - fn fuzz(_: void, fuzz_input: []const u8) !void { - const allocator = std.testing.allocator; + fn fuzz(_: void, smith: *std.testing.Smith) !void { + const gpa = std.testing.allocator; + + const fuzz_input = try gpa.alloc(u8, smith.value(u16)); + smith.bytes(fuzz_input); + // This fuzz test ensure that once parsed and written out once, doing so again should // yield the same result. - const stage1 = parseAndWrite(allocator, fuzz_input) catch return; - defer allocator.free(stage1); + const stage1 = parseAndWrite(gpa, fuzz_input) catch return; + defer gpa.free(stage1); - const stage2 = try parseAndWrite(allocator, stage1); - defer allocator.free(stage2); + const stage2 = try parseAndWrite(gpa, stage1); + defer gpa.free(stage2); std.testing.expectEqualStrings(stage1, stage2) catch |err| { // On failure, also do `expectEqualSlices` to get a hex diff diff --git a/src/git.zig b/src/git.zig index ea34467..7e6732a 100644 --- a/src/git.zig +++ b/src/git.zig @@ -4,7 +4,7 @@ pub fn commitFile(io: std.Io, dir: std.Io.Dir, file: []const u8, msg: []const u8 .stdin = .ignore, .stdout = .pipe, .stderr = .pipe, - .cwd_dir = dir, + .cwd = .{ .dir = dir }, }); const failed = switch (try child.wait(io)) { .exited => |code| switch (code) { diff --git a/src/main.zig b/src/main.zig index 1e11b81..9ac604c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,5 +1,5 @@ pub fn main(init: std.process.Init) !u8 { - var io_lock = std.Thread.Mutex{}; + var io_lock = std.Io.Mutex.init; const progress = &Progress.global; var stdout_buf: [std.heap.page_size_min]u8 = undefined; @@ -7,19 +7,9 @@ pub fn main(init: std.process.Init) !u8 { var stdout = std.Io.File.stdout().writer(init.io, &stdout_buf); var stderr = std.Io.File.stderr().writer(init.io, &stderr_buf); - // We don't really care to store the thread. Just let the os clean it up - if (try stderr.file.supportsAnsiEscapeCodes(init.io)) blk: { - const thread = std.Thread.spawn(.{}, renderThread, .{ - init.io, - &stderr, - progress, - &io_lock, - }) catch |err| { - try stderr.interface.print("failed to spawn rendering thread: {}\n", .{err}); - break :blk; - }; - thread.detach(); - } + // Render concurrently with the main thread. If concurrency is not supported, don't render at + // all + var render_future = init.io.concurrent(renderThread, .{ init.io, &stderr, progress, &io_lock }); const res = mainFull(init, .{ .io_lock = &io_lock, @@ -28,7 +18,9 @@ pub fn main(init: std.process.Init) !u8 { .progress = progress, }); - io_lock.lock(); + if (render_future) |*f| f.cancel(init.io) catch {} else |_| {} + + try io_lock.lock(init.io); try progress.cleanupTty(init.io, &stderr); try stdout.end(); try stderr.end(); @@ -43,24 +35,27 @@ fn renderThread( io: std.Io, stderr: *std.Io.File.Writer, progress: *Progress, - io_lock: *std.Thread.Mutex, -) void { + io_lock: *std.Io.Mutex, +) !void { + const supports_ansi = stderr.file.supportsAnsiEscapeCodes(io) catch false; + if (!supports_ansi) return; + const fps = 15; const delay = std.Io.Duration.fromNanoseconds(std.time.ns_per_s / fps); const initial_delay = std.Io.Duration.fromNanoseconds(std.time.ns_per_s / 4); - io.sleep(initial_delay, .awake) catch {}; + try io.sleep(initial_delay, .awake); while (true) { - io_lock.lock(); - progress.renderToTty(stderr) catch {}; - stderr.interface.flush() catch {}; - io_lock.unlock(); - io.sleep(delay, .awake) catch {}; + try io_lock.lock(io); + try progress.renderToTty(stderr); + try stderr.interface.flush(); + io_lock.unlock(io); + try io.sleep(delay, .awake); } } pub const MainOptions = struct { - io_lock: *std.Thread.Mutex, + io_lock: *std.Io.Mutex, stdout: *std.Io.File.Writer, stderr: *std.Io.File.Writer, @@ -69,7 +64,7 @@ pub const MainOptions = struct { }; pub fn mainFull(init: std.process.Init, options: MainOptions) !void { - var diag = Diagnostics.init(init.gpa); + var diag = Diagnostics.init(init.io, init.gpa); defer diag.deinit(); const args = try init.minimal.args.toSlice(init.arena.allocator()); @@ -89,8 +84,8 @@ pub fn mainFull(init: std.process.Init, options: MainOptions) !void { const res = prog.mainCommand(); - prog.io_lock.lock(); - defer prog.io_lock.unlock(); + try prog.io_lock.lock(prog.init.io); + defer prog.io_lock.unlock(prog.init.io); try prog.progress.cleanupTty(init.io, prog.stderr); try diag.reportToFile(prog.stderr); @@ -164,7 +159,7 @@ diag: *Diagnostics, progress: *Progress, // Ensures that only one thread can write to stdout/stderr at a time -io_lock: *std.Thread.Mutex, +io_lock: *std.Io.Mutex, stdout: *std.Io.File.Writer, stderr: *std.Io.File.Writer, @@ -216,15 +211,15 @@ fn packages(prog: *Program) !Packages { } fn stdoutWriteAllLocked(prog: Program, str: []const u8) !void { - prog.io_lock.lock(); - defer prog.io_lock.unlock(); + try prog.io_lock.lock(prog.init.io); + defer prog.io_lock.unlock(prog.init.io); return prog.stdout.interface.writeAll(str); } fn stderrWriteAllLocked(prog: Program, str: []const u8) !void { - prog.io_lock.lock(); - defer prog.io_lock.unlock(); + try prog.io_lock.lock(prog.init.io); + defer prog.io_lock.unlock(prog.init.io); return prog.stderr.interface.writeAll(str); } @@ -242,8 +237,8 @@ const donate_usage = ; fn donateCommand(prog: *Program) !void { - var pkgs_to_show_hm = std.StringArrayHashMap(void).init(prog.init.arena.allocator()); - try pkgs_to_show_hm.ensureTotalCapacity(prog.args.args.len); + var pkgs_to_show_hm = std.array_hash_map.String(void).empty; + try pkgs_to_show_hm.ensureTotalCapacity(prog.init.arena.allocator(), prog.args.args.len); while (prog.args.next()) { if (prog.args.flag(&.{ "-h", "--help" })) @@ -302,7 +297,7 @@ const install_usage = ; fn installCommand(prog: *Program) !void { - var pkgs_to_install = std.ArrayList([]const u8){}; + var pkgs_to_install = std.ArrayList([]const u8).empty; try pkgs_to_install.ensureTotalCapacity(prog.init.arena.allocator(), prog.args.args.len); while (prog.args.next()) { @@ -331,7 +326,7 @@ const uninstall_usage = ; fn uninstallCommand(prog: *Program) !void { - var pkgs_to_uninstall = std.ArrayList([]const u8){}; + var pkgs_to_uninstall = std.ArrayList([]const u8).empty; try pkgs_to_uninstall.ensureTotalCapacity(prog.init.arena.allocator(), prog.args.args.len); while (prog.args.next()) { @@ -365,7 +360,7 @@ const update_usage = fn updateCommand(prog: *Program) !void { var force_update = false; - var pkgs_to_update = std.ArrayList([]const u8){}; + var pkgs_to_update = std.ArrayList([]const u8).empty; try pkgs_to_update.ensureTotalCapacity(prog.init.arena.allocator(), prog.args.args.len); while (prog.args.next()) { @@ -443,8 +438,8 @@ fn listInstalledCommand(prog: *Program) !void { var installed = try InstalledPackages.open(prog.init.io, prog.init.gpa, try prog.prefix()); defer installed.deinit(prog.init.io); - prog.io_lock.lock(); - defer prog.io_lock.unlock(); + try prog.io_lock.lock(prog.init.io); + defer prog.io_lock.unlock(prog.init.io); for (installed.by_name.keys(), installed.by_name.values()) |name, pkg| try prog.stdout.interface.print("{s}\t{s}\n", .{ name, pkg.version }); @@ -474,8 +469,8 @@ fn listAllCommand(prog: *Program) !void { var pkgs = try prog.packages(); defer pkgs.deinit(); - prog.io_lock.lock(); - defer prog.io_lock.unlock(); + try prog.io_lock.lock(prog.init.io); + defer prog.io_lock.unlock(prog.init.io); for (pkgs.by_name.keys(), pkgs.by_name.values()) |pkg_name, pkg| try prog.stdout.interface.print("{s}\t{s}\n", .{ pkg_name, pkg.info.version }); @@ -546,7 +541,7 @@ const pkgs_update_usage = ; fn pkgsUpdateCommand(prog: *Program) !void { - var pkgs_to_update = std.StringArrayHashMap(void).init(prog.init.arena.allocator()); + var pkgs_to_update = std.array_hash_map.String(void).empty; var delay = std.Io.Duration.fromSeconds(0); var options = PackagesAddOptions{ .update_description = false, @@ -564,7 +559,7 @@ fn pkgsUpdateCommand(prog: *Program) !void { if (prog.args.flag(&.{ "-h", "--help" })) return prog.stdoutWriteAllLocked(pkgs_update_usage); if (prog.args.positional()) |url| - try pkgs_to_update.put(url, {}); + try pkgs_to_update.put(prog.init.arena.allocator(), url, {}); } const cwd = std.Io.Dir.cwd(); diff --git a/src/testing.zig b/src/testing.zig index 8b7f8ab..8dee46d 100644 --- a/src/testing.zig +++ b/src/testing.zig @@ -575,16 +575,18 @@ test "dipm install packages with shared files in lib/" { ); } -fn fuzz(_: void, fuzz_input: []const u8) !void { - const gpa = std.testing.allocator; - var args = std.ArrayList([*:0]const u8){}; - defer args.deinit(gpa); +fn fuzz(_: void, smith: *std.testing.Smith) !void { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); - var args_it = try std.process.Args.IteratorGeneral(.{}).init(std.testing.allocator, fuzz_input); - defer args_it.deinit(); + var args = std.ArrayList([*:0]const u8).empty; + while (!smith.eos()) { + const len = smith.value(u8); + const string = try arena.allocator().allocSentinel(u8, len, 0); + smith.bytes(string); - while (args_it.next()) |arg| - try args.append(gpa, arg.ptr); + try args.append(arena.allocator(), string); + } var prefix = try setupPrefix(.{ .version = "0.1.0" }); defer prefix.deinit(); @@ -715,7 +717,7 @@ const Prefix = struct { var dir = try std.Io.Dir.cwd().openDir(io, prefix.prefix, .{}); defer dir.close(io); - var io_lock = std.Thread.Mutex{}; + var io_lock = std.Io.Mutex.init; var stdout = try dir.createFile(io, "stdout", .{ .read = true }); defer stdout.close(io);