From 69d07472a12c8ec8f83a43ed63c1ab7e2ab71c14 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Dec 2025 16:02:44 -0800 Subject: [PATCH] std lib tests passing on linux --- lib/std/Build/Step.zig | 19 ++++++------- lib/std/Io.zig | 5 ---- lib/std/Io/Threaded.zig | 50 ++++++++++++++-------------------- lib/std/debug/ElfFile.zig | 12 ++++---- lib/std/debug/SelfInfo/Elf.zig | 4 +-- lib/std/process/Child.zig | 2 +- lib/std/process/Environ.zig | 4 +-- lib/std/std.zig | 5 ++++ 8 files changed, 46 insertions(+), 55 deletions(-) diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 2699e3f01b..93d6b9cdd6 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -448,7 +448,10 @@ pub fn evalZigProcess( try handleChildProcUnsupported(s); try handleVerbose(s.owner, null, argv); - var child = std.process.spawn(io, .{ + const zp = try gpa.create(ZigProcess); + defer if (!watch) gpa.destroy(zp); + + zp.child = std.process.spawn(io, .{ .argv = argv, .env_map = &b.graph.env_map, .stdin = .pipe, @@ -457,22 +460,18 @@ pub fn evalZigProcess( .request_resource_usage_statistics = true, .progress_node = prog_node, }) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err }); - defer if (!watch) child.kill(io); + defer if (!watch) zp.child.kill(io); - const zp = try gpa.create(ZigProcess); zp.* = .{ - .child = child, + .child = zp.child, .poller = Io.poll(gpa, ZigProcess.StreamEnum, .{ - .stdout = child.stdout.?, - .stderr = child.stderr.?, + .stdout = zp.child.stdout.?, + .stderr = zp.child.stderr.?, }), .progress_ipc_fd = if (std.Progress.have_ipc) prog_node.getIpcFd() else {}, }; if (watch) s.setZigProcess(zp); - defer if (!watch) { - zp.poller.deinit(); - gpa.destroy(zp); - }; + defer if (!watch) zp.poller.deinit(); const result = try zigProcessUpdate(s, zp, watch, web_server, gpa); diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 2f1a180fc5..a1e38135bd 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -2240,8 +2240,3 @@ pub fn tryLockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancel pub fn unlockStderr(io: Io) void { return io.vtable.unlockStderr(io.userdata); } - -pub fn environ(io: Io, name: []const u8) ?[]const u8 { - _ = io; - std.debug.panic("TODO: environ query: {s}", .{name}); -} diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 8bb3201bb8..a94b20ab79 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -101,6 +101,9 @@ pub const Environ = struct { .windows, .wasi => struct {}, else => struct { PATH: ?[:0]const u8 = null, + DEBUGINFOD_CACHE_PATH: ?[:0]const u8 = null, + XDG_CACHE_HOME: ?[:0]const u8 = null, + HOME: ?[:0]const u8 = null, }, }; }; @@ -12674,27 +12677,6 @@ fn scanEnviron(t: *Threaded) void { } comptime assert(@sizeOf(Environ.String) == 0); } - } else if (builtin.link_libc) { - var ptr = std.c.environ; - while (ptr[0]) |line| : (ptr += 1) { - var line_i: usize = 0; - while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} - const key = line[0..line_i]; - - var end_i: usize = line_i; - while (line[end_i] != 0) : (end_i += 1) {} - const value = line[line_i + 1 .. end_i :0]; - - if (std.mem.eql(u8, key, "NO_COLOR")) { - t.environ.exist.NO_COLOR = true; - } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) { - t.environ.exist.CLICOLOR_FORCE = true; - } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) { - t.environ.string.PATH = value; - } else if (std.mem.eql(u8, key, "ZIG_PROGRESS")) { - t.environ.zig_progress_handle = std.fmt.parseInt(u31, value, 10) catch error.UnrecognizedFormat; - } - } } else { for (t.environ.block) |opt_line| { const line = opt_line.?; @@ -12710,10 +12692,10 @@ fn scanEnviron(t: *Threaded) void { t.environ.exist.NO_COLOR = true; } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) { t.environ.exist.CLICOLOR_FORCE = true; - } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) { - t.environ.string.PATH = value; } else if (std.mem.eql(u8, key, "ZIG_PROGRESS")) { t.environ.zig_progress_handle = std.fmt.parseInt(u31, value, 10) catch error.UnrecognizedFormat; + } else inline for (@typeInfo(Environ.String).@"struct".fields) |field| { + if (std.mem.eql(u8, key, field.name)) @field(t.environ.string, field.name) = value; } } } @@ -12966,12 +12948,10 @@ fn childKill(userdata: ?*anyopaque, child: *std.process.Child) void { if (is_windows) { childKillWindows(t, child, 1) catch { childCleanupStreams(child); - child.id = null; }; } else { childKillPosix(t, child) catch { childCleanupStreams(child); - child.id = null; }; } } @@ -13012,7 +12992,6 @@ fn childWaitWindows(t: *Threaded, child: *process.Child) process.Child.WaitError posix.close(child.id); posix.close(child.thread_handle); childCleanupStreams(child); - child.id = null; return term; } @@ -13028,10 +13007,8 @@ fn childWaitPosix(t: *Threaded, child: *process.Child) process.Child.WaitError!p } break :res posix.waitpid(pid, 0); }; - const status = res.status; childCleanupStreams(child); - child.id = null; - return statusToTerm(status); + return statusToTerm(res.status); } fn statusToTerm(status: u32) process.Child.Term { @@ -13046,7 +13023,14 @@ fn statusToTerm(status: u32) process.Child.Term { } fn childKillPosix(t: *Threaded, child: *process.Child) !void { - try posix.kill(child.id.?, posix.SIG.TERM); + while (true) switch (posix.errno(posix.system.kill(child.id.?, .TERM))) { + .SUCCESS => break, + .INTR => continue, + .PERM => return error.PermissionDenied, + .INVAL => |err| return errnoBug(err), + .SRCH => |err| return errnoBug(err), + else => |err| return posix.unexpectedErrno(err), + }; _ = try childWaitPosix(t, child); } @@ -13063,6 +13047,7 @@ fn childCleanupStreams(child: *process.Child) void { posix.close(stderr.handle); child.stderr = null; } + child.id = null; } /// Errors that can occur between fork() and execv() @@ -14367,6 +14352,11 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { } }; } +pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { + t.scanEnviron(); + return @field(t.environ.string, name); +} + test { _ = @import("Threaded/test.zig"); } diff --git a/lib/std/debug/ElfFile.zig b/lib/std/debug/ElfFile.zig index e17c518271..53db1c755a 100644 --- a/lib/std/debug/ElfFile.zig +++ b/lib/std/debug/ElfFile.zig @@ -66,16 +66,17 @@ pub const DebugInfoSearchPaths = struct { .exe_dir = null, }; - pub fn native(exe_path: []const u8, io: Io) DebugInfoSearchPaths { - return .{ + pub fn native(exe_path: []const u8) DebugInfoSearchPaths { + if (std.options.elf_debug_info_search_paths) |f| return f(exe_path); + if (std.Options.debug_threaded_io) |t| return .{ .debuginfod_client = p: { - if (io.environ("DEBUGINFOD_CACHE_PATH")) |p| { + if (t.environString("DEBUGINFOD_CACHE_PATH")) |p| { break :p .{ p, "" }; } - if (io.environ("XDG_CACHE_HOME")) |cache_path| { + if (t.environString("XDG_CACHE_HOME")) |cache_path| { break :p .{ cache_path, "/debuginfod_client" }; } - if (io.environ("HOME")) |home_path| { + if (t.environString("HOME")) |home_path| { break :p .{ home_path, "/.cache/debuginfod_client" }; } break :p null; @@ -85,6 +86,7 @@ pub const DebugInfoSearchPaths = struct { }, .exe_dir = std.fs.path.dirname(exe_path) orelse ".", }; + @compileError("std.Options.elf_debug_info_search_paths must be provided"); } }; diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index c62c2df4b8..ffcb4dfd26 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -327,7 +327,7 @@ const Module = struct { const load_result = if (mod.name.len > 0) res: { var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo; defer file.close(io); - break :res std.debug.ElfFile.load(gpa, io, file, mod.build_id, &.native(mod.name, io)); + break :res std.debug.ElfFile.load(gpa, io, file, mod.build_id, &.native(mod.name)); } else res: { const path = std.process.executablePathAlloc(io, gpa) catch |err| switch (err) { error.OutOfMemory => |e| return e, @@ -336,7 +336,7 @@ const Module = struct { defer gpa.free(path); var file = Io.Dir.cwd().openFile(io, path, .{}) catch return error.MissingDebugInfo; defer file.close(io); - break :res std.debug.ElfFile.load(gpa, io, file, mod.build_id, &.native(path, io)); + break :res std.debug.ElfFile.load(gpa, io, file, mod.build_id, &.native(path)); }; var elf_file = load_result catch |err| switch (err) { diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 6a170116ce..176405cc8b 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -106,7 +106,7 @@ pub const Term = union(enum) { /// /// Uncancelable. Ignores unexpected errors from the operating system. pub fn kill(child: *Child, io: Io) void { - if (child.id != null) { + if (child.id == null) { assert(child.stdin == null); assert(child.stdout == null); assert(child.stderr == null); diff --git a/lib/std/process/Environ.zig b/lib/std/process/Environ.zig index 831ba5a468..4601d389ed 100644 --- a/lib/std/process/Environ.zig +++ b/lib/std/process/Environ.zig @@ -792,6 +792,6 @@ test "convert from Environ to Map and back again" { var map2 = try environ.createMap(gpa); defer map2.deinit(); - try testing.expectEqualSlices([]const u8, map.keys(), map2.keys()); - try testing.expectEqualSlices([]const u8, map.values(), map2.values()); + try testing.expectEqualDeep(map.keys(), map2.keys()); + try testing.expectEqualDeep(map.values(), map2.values()); } diff --git a/lib/std/std.zig b/lib/std/std.zig index c4cb3b1537..627e7de6dd 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -173,6 +173,11 @@ pub const Options = struct { /// stack traces will just print an error to the relevant `Io.Writer` and return. allow_stack_tracing: bool = !@import("builtin").strip_debug_info, + elf_debug_info_search_paths: ?fn (exe_path: []const u8) switch (@import("builtin").object_format) { + .elf => debug.ElfFile.DebugInfoSearchPaths, + else => void, + } = null, + pub const debug_threaded_io: ?*Io.Threaded = if (@hasDecl(root, "std_options_debug_threaded_io")) root.std_options_debug_threaded_io else