From d1d2c37af26902f953b2b72335b326c4b01e3bb2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 3 Dec 2025 20:37:43 -0800 Subject: std: all Dir functions moved to std.Io --- lib/std/debug.zig | 80 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 34 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index feea5f9a41..7c5993dfcf 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1104,7 +1104,14 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { return ptr; } -fn printSourceAtAddress(gpa: Allocator, io: Io, debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) Writer.Error!void { +fn printSourceAtAddress( + gpa: Allocator, + io: Io, + debug_info: *SelfInfo, + writer: *Writer, + address: usize, + tty_config: tty.Config, +) Writer.Error!void { const symbol: Symbol = debug_info.getSymbol(gpa, io, address) catch |err| switch (err) { error.MissingDebugInfo, error.UnsupportedDebugInfo, @@ -1125,6 +1132,7 @@ fn printSourceAtAddress(gpa: Allocator, io: Io, debug_info: *SelfInfo, writer: * }; defer if (symbol.source_location) |sl| gpa.free(sl.file_name); return printLineInfo( + io, writer, symbol.source_location, address, @@ -1134,6 +1142,7 @@ fn printSourceAtAddress(gpa: Allocator, io: Io, debug_info: *SelfInfo, writer: * ); } fn printLineInfo( + io: Io, writer: *Writer, source_location: ?SourceLocation, address: usize, @@ -1159,7 +1168,7 @@ fn printLineInfo( // Show the matching source code line if possible if (source_location) |sl| { - if (printLineFromFile(writer, sl)) { + if (printLineFromFile(io, writer, sl)) { if (sl.column > 0) { // The caret already takes one char const space_needed = @as(usize, @intCast(sl.column - 1)); @@ -1177,16 +1186,17 @@ fn printLineInfo( } } } -fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void { +fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) !void { // Allow overriding the target-agnostic source line printing logic by exposing `root.debug.printLineFromFile`. if (@hasDecl(root, "debug") and @hasDecl(root.debug, "printLineFromFile")) { - return root.debug.printLineFromFile(writer, source_location); + return root.debug.printLineFromFile(io, writer, source_location); } // Need this to always block even in async I/O mode, because this could potentially // be called from e.g. the event loop code crashing. - var f = try fs.cwd().openFile(source_location.file_name, .{}); - defer f.close(); + const cwd: Io.Dir = .cwd(); + var f = try cwd.openFile(io, source_location.file_name, .{}); + defer f.close(io); // TODO fstat and make sure that the file has the correct size var buf: [4096]u8 = undefined; @@ -1237,11 +1247,13 @@ fn printLineFromFile(writer: *Writer, source_location: SourceLocation) !void { } test printLineFromFile { - var aw: Writer.Allocating = .init(std.testing.allocator); + const io = std.testing.io; + const gpa = std.testing.allocator; + + var aw: Writer.Allocating = .init(gpa); defer aw.deinit(); const output_stream = &aw.writer; - const allocator = std.testing.allocator; const join = std.fs.path.join; const expectError = std.testing.expectError; const expectEqualStrings = std.testing.expectEqualStrings; @@ -1249,24 +1261,24 @@ test printLineFromFile { var test_dir = std.testing.tmpDir(.{}); defer test_dir.cleanup(); // Relies on testing.tmpDir internals which is not ideal, but SourceLocation requires paths. - const test_dir_path = try join(allocator, &.{ ".zig-cache", "tmp", test_dir.sub_path[0..] }); - defer allocator.free(test_dir_path); + const test_dir_path = try join(gpa, &.{ ".zig-cache", "tmp", test_dir.sub_path[0..] }); + defer gpa.free(test_dir_path); // Cases { - const path = try join(allocator, &.{ test_dir_path, "one_line.zig" }); - defer allocator.free(path); + const path = try join(gpa, &.{ test_dir_path, "one_line.zig" }); + defer gpa.free(path); try test_dir.dir.writeFile(.{ .sub_path = "one_line.zig", .data = "no new lines in this file, but one is printed anyway" }); - try expectError(error.EndOfFile, printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); + try expectError(error.EndOfFile, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 })); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.written()); aw.clearRetainingCapacity(); } { - const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" }); - defer allocator.free(path); + const path = try fs.path.join(gpa, &.{ test_dir_path, "three_lines.zig" }); + defer gpa.free(path); try test_dir.dir.writeFile(.{ .sub_path = "three_lines.zig", .data = @@ -1276,19 +1288,19 @@ test printLineFromFile { , }); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try expectEqualStrings("1\n", aw.written()); aw.clearRetainingCapacity(); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 3, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 3, .column = 0 }); try expectEqualStrings("3\n", aw.written()); aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); defer file.close(); - const path = try fs.path.join(allocator, &.{ test_dir_path, "line_overlaps_page_boundary.zig" }); - defer allocator.free(path); + const path = try fs.path.join(gpa, &.{ test_dir_path, "line_overlaps_page_boundary.zig" }); + defer gpa.free(path); const overlap = 10; var buf: [16]u8 = undefined; @@ -1299,55 +1311,55 @@ test printLineFromFile { try writer.splatByteAll('a', overlap); try writer.flush(); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 }); try expectEqualStrings(("a" ** overlap) ++ "\n", aw.written()); aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); defer file.close(); - const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); - defer allocator.free(path); + const path = try fs.path.join(gpa, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); + defer gpa.free(path); var file_writer = file.writer(&.{}); const writer = &file_writer.interface; try writer.splatByteAll('a', std.heap.page_size_max); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", aw.written()); aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); defer file.close(); - const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); - defer allocator.free(path); + const path = try fs.path.join(gpa, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); + defer gpa.free(path); var file_writer = file.writer(&.{}); const writer = &file_writer.interface; try writer.splatByteAll('a', 3 * std.heap.page_size_max); - try expectError(error.EndOfFile, printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 })); + try expectError(error.EndOfFile, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 })); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.written()); aw.clearRetainingCapacity(); try writer.writeAll("a\na"); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 1, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", aw.written()); aw.clearRetainingCapacity(); - try printLineFromFile(output_stream, .{ .file_name = path, .line = 2, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 }); try expectEqualStrings("a\n", aw.written()); aw.clearRetainingCapacity(); } { const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); defer file.close(); - const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" }); - defer allocator.free(path); + const path = try fs.path.join(gpa, &.{ test_dir_path, "file_of_newlines.zig" }); + defer gpa.free(path); var file_writer = file.writer(&.{}); const writer = &file_writer.interface; @@ -1355,11 +1367,11 @@ test printLineFromFile { try writer.splatByteAll('\n', real_file_start); try writer.writeAll("abc\ndef"); - try printLineFromFile(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 }); try expectEqualStrings("abc\n", aw.written()); aw.clearRetainingCapacity(); - try printLineFromFile(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 }); + try printLineFromFile(io, output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 }); try expectEqualStrings("def\n", aw.written()); aw.clearRetainingCapacity(); } -- cgit v1.2.3 From aafddc2ea13e40a8262d9378aeca2e097a37ac03 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Dec 2025 19:08:37 -0800 Subject: update all occurrences of close() to close(io) --- build.zig | 4 +- lib/compiler/aro/aro/Compilation.zig | 8 +- lib/compiler/aro/aro/Driver.zig | 12 +- lib/compiler/aro/aro/Driver/Filesystem.zig | 28 +-- lib/compiler/aro/aro/Toolchain.zig | 3 +- lib/compiler/objcopy.zig | 4 +- lib/compiler/resinator/cli.zig | 4 +- lib/compiler/resinator/compile.zig | 34 ++-- lib/compiler/resinator/errors.zig | 4 +- lib/compiler/resinator/main.zig | 14 +- lib/compiler/resinator/utils.zig | 9 +- lib/compiler/std-docs.zig | 33 ++-- lib/compiler/translate-c/main.zig | 5 +- lib/std/Build/Cache/Directory.zig | 4 +- lib/std/Build/Fuzz.zig | 4 +- lib/std/Build/Step.zig | 3 +- lib/std/Build/Step/InstallArtifact.zig | 3 +- lib/std/Build/Step/InstallDir.zig | 2 +- lib/std/Build/Step/Run.zig | 20 ++- lib/std/Build/Step/WriteFile.zig | 10 +- lib/std/Build/Watch/FsEvents.zig | 9 +- lib/std/Build/WebServer.zig | 7 +- lib/std/Io/Dir.zig | 41 +++-- lib/std/Io/Writer.zig | 6 +- lib/std/Io/net/test.zig | 10 +- lib/std/Io/test.zig | 16 +- lib/std/Thread.zig | 7 +- lib/std/crypto/Certificate/Bundle.zig | 6 +- lib/std/crypto/codecs/asn1/test.zig | 2 +- lib/std/debug.zig | 8 +- lib/std/debug/ElfFile.zig | 26 ++- lib/std/debug/Info.zig | 21 ++- lib/std/debug/MachOFile.zig | 18 +- lib/std/debug/SelfInfo/Elf.zig | 10 +- lib/std/debug/SelfInfo/MachO.zig | 4 +- lib/std/debug/SelfInfo/Windows.zig | 6 +- lib/std/dynamic_library.zig | 34 ++-- lib/std/fs.zig | 4 +- lib/std/fs/test.zig | 270 ++++++++++++++++++----------- lib/std/http/Client.zig | 4 +- lib/std/os/linux/IoUring.zig | 85 ++++++--- lib/std/os/linux/test.zig | 12 +- lib/std/posix/test.zig | 54 ++++-- lib/std/process.zig | 12 +- lib/std/process/Child.zig | 43 ++--- lib/std/tar.zig | 71 ++++---- lib/std/tar/test.zig | 8 +- lib/std/testing.zig | 6 +- lib/std/zig/LibCInstallation.zig | 71 ++++---- lib/std/zig/WindowsSdk.zig | 67 +++---- lib/std/zig/llvm/Builder.zig | 19 +- lib/std/zig/system.zig | 4 +- lib/std/zig/system/linux.zig | 2 +- lib/std/zip.zig | 6 +- src/Compilation.zig | 64 +++---- src/Package/Fetch.zig | 72 ++++---- src/Package/Fetch/git.zig | 26 +-- src/Zcu.zig | 10 +- src/Zcu/PerThread.zig | 6 +- src/codegen/llvm.zig | 5 +- src/fmt.zig | 10 +- src/introspect.zig | 28 +-- src/libs/freebsd.zig | 4 +- src/libs/glibc.zig | 4 +- src/libs/mingw.zig | 6 +- src/libs/netbsd.zig | 4 +- src/link.zig | 83 +++++---- src/link/C.zig | 6 +- src/link/Elf.zig | 6 +- src/link/Lld.zig | 2 +- src/link/MachO.zig | 16 +- src/link/MachO/DebugSymbols.zig | 50 +++--- src/link/Wasm.zig | 4 +- src/main.zig | 135 ++++++++------- src/print_targets.zig | 3 +- 75 files changed, 1014 insertions(+), 707 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/build.zig b/build.zig index ade4825927..7ea515de3e 100644 --- a/build.zig +++ b/build.zig @@ -1604,12 +1604,12 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { b.build_root, @errorName(err), }); }; - defer dir.close(); + defer dir.close(io); var wf = b.addWriteFiles(); var it = dir.iterateAssumeFirstIteration(); - while (it.next() catch @panic("failed to read dir")) |entry| { + while (it.next(io) catch @panic("failed to read dir")) |entry| { if (std.mem.startsWith(u8, entry.name, ".") or entry.kind != .file) continue; diff --git a/lib/compiler/aro/aro/Compilation.zig b/lib/compiler/aro/aro/Compilation.zig index d5f4ebe2d9..9fb8123146 100644 --- a/lib/compiler/aro/aro/Compilation.zig +++ b/lib/compiler/aro/aro/Compilation.zig @@ -1639,8 +1639,10 @@ fn addSourceFromPathExtra(comp: *Compilation, path: []const u8, kind: Source.Kin return error.FileNotFound; } + const io = comp.io; + const file = try comp.cwd.openFile(path, .{}); - defer file.close(); + defer file.close(io); return comp.addSourceFromFile(file, path, kind); } @@ -1971,8 +1973,10 @@ fn getPathContents(comp: *Compilation, path: []const u8, limit: Io.Limit) ![]u8 return error.FileNotFound; } + const io = comp.io; + const file = try comp.cwd.openFile(path, .{}); - defer file.close(); + defer file.close(io); return comp.getFileContents(file, limit); } diff --git a/lib/compiler/aro/aro/Driver.zig b/lib/compiler/aro/aro/Driver.zig index 6a399ece39..888ade2be4 100644 --- a/lib/compiler/aro/aro/Driver.zig +++ b/lib/compiler/aro/aro/Driver.zig @@ -1286,6 +1286,8 @@ fn processSource( d.comp.generated_buf.items.len = 0; const prev_total = d.diagnostics.errors; + const io = d.comp.io; + var pp = try Preprocessor.initDefault(d.comp); defer pp.deinit(); @@ -1328,7 +1330,7 @@ fn processSource( return d.fatal("unable to create dependency file '{s}': {s}", .{ path, errorDescription(er) }) else std.fs.File.stdout(); - defer if (dep_file_name != null) file.close(); + defer if (dep_file_name != null) file.close(io); var file_writer = file.writer(&writer_buf); dep_file.write(&file_writer.interface) catch @@ -1353,7 +1355,7 @@ fn processSource( return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) }) else std.fs.File.stdout(); - defer if (d.output_name != null) file.close(); + defer if (d.output_name != null) file.close(io); var file_writer = file.writer(&writer_buf); pp.prettyPrintTokens(&file_writer.interface, dump_mode) catch @@ -1404,7 +1406,7 @@ fn processSource( if (d.only_preprocess_and_compile) { const out_file = d.comp.cwd.createFile(out_file_name, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ out_file_name, errorDescription(er) }); - defer out_file.close(); + defer out_file.close(io); assembly.writeToFile(out_file) catch |er| return d.fatal("unable to write to output file '{s}': {s}", .{ out_file_name, errorDescription(er) }); @@ -1418,7 +1420,7 @@ fn processSource( const assembly_out_file_name = try d.getRandomFilename(&assembly_name_buf, ".s"); const out_file = d.comp.cwd.createFile(assembly_out_file_name, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ assembly_out_file_name, errorDescription(er) }); - defer out_file.close(); + defer out_file.close(io); assembly.writeToFile(out_file) catch |er| return d.fatal("unable to write to output file '{s}': {s}", .{ assembly_out_file_name, errorDescription(er) }); try d.invokeAssembler(tc, assembly_out_file_name, out_file_name); @@ -1454,7 +1456,7 @@ fn processSource( const out_file = d.comp.cwd.createFile(out_file_name, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ out_file_name, errorDescription(er) }); - defer out_file.close(); + defer out_file.close(io); var file_writer = out_file.writer(&writer_buf); obj.finish(&file_writer.interface) catch diff --git a/lib/compiler/aro/aro/Driver/Filesystem.zig b/lib/compiler/aro/aro/Driver/Filesystem.zig index 87092cb235..19ac9bfe41 100644 --- a/lib/compiler/aro/aro/Driver/Filesystem.zig +++ b/lib/compiler/aro/aro/Driver/Filesystem.zig @@ -1,8 +1,10 @@ -const std = @import("std"); -const mem = std.mem; const builtin = @import("builtin"); const is_windows = builtin.os.tag == .windows; +const std = @import("std"); +const Io = std.Io; +const mem = std.std.mem; + fn readFileFake(entries: []const Filesystem.Entry, path: []const u8, buf: []u8) ?[]const u8 { @branchHint(.cold); for (entries) |entry| { @@ -96,7 +98,7 @@ fn findProgramByNamePosix(name: []const u8, path: ?[]const u8, buf: []u8) ?[]con } pub const Filesystem = union(enum) { - real: std.fs.Dir, + real: std.Io.Dir, fake: []const Entry, const Entry = struct { @@ -121,7 +123,7 @@ pub const Filesystem = union(enum) { base: []const u8, i: usize = 0, - fn next(self: *@This()) !?std.fs.Dir.Entry { + fn next(self: *@This()) !?std.Io.Dir.Entry { while (self.i < self.entries.len) { const entry = self.entries[self.i]; self.i += 1; @@ -130,7 +132,7 @@ pub const Filesystem = union(enum) { const remaining = entry.path[self.base.len + 1 ..]; if (std.mem.indexOfScalar(u8, remaining, std.fs.path.sep) != null) continue; const extension = std.fs.path.extension(remaining); - const kind: std.fs.Dir.Entry.Kind = if (extension.len == 0) .directory else .file; + const kind: std.Io.Dir.Entry.Kind = if (extension.len == 0) .directory else .file; return .{ .name = remaining, .kind = kind }; } } @@ -140,7 +142,7 @@ pub const Filesystem = union(enum) { }; const Dir = union(enum) { - dir: std.fs.Dir, + dir: std.Io.Dir, fake: FakeDir, pub fn iterate(self: Dir) Iterator { @@ -150,19 +152,19 @@ pub const Filesystem = union(enum) { }; } - pub fn close(self: *Dir) void { + pub fn close(self: *Dir, io: Io) void { switch (self.*) { - .dir => |*d| d.close(), + .dir => |*d| d.close(io), .fake => {}, } } }; const Iterator = union(enum) { - iterator: std.fs.Dir.Iterator, + iterator: std.Io.Dir.Iterator, fake: FakeDir.Iterator, - pub fn next(self: *Iterator) std.fs.Dir.Iterator.Error!?std.fs.Dir.Entry { + pub fn next(self: *Iterator) std.Io.Dir.Iterator.Error!?std.Io.Dir.Entry { return switch (self.*) { .iterator => |*it| it.next(), .fake => |*it| it.next(), @@ -208,11 +210,11 @@ pub const Filesystem = union(enum) { /// Read the file at `path` into `buf`. /// Returns null if any errors are encountered /// Otherwise returns a slice of `buf`. If the file is larger than `buf` partial contents are returned - pub fn readFile(fs: Filesystem, path: []const u8, buf: []u8) ?[]const u8 { + pub fn readFile(fs: Filesystem, io: Io, path: []const u8, buf: []u8) ?[]const u8 { return switch (fs) { .real => |cwd| { const file = cwd.openFile(path, .{}) catch return null; - defer file.close(); + defer file.close(io); const bytes_read = file.readAll(buf) catch return null; return buf[0..bytes_read]; @@ -221,7 +223,7 @@ pub const Filesystem = union(enum) { }; } - pub fn openDir(fs: Filesystem, dir_name: []const u8) std.fs.Dir.OpenError!Dir { + pub fn openDir(fs: Filesystem, dir_name: []const u8) std.Io.Dir.OpenError!Dir { return switch (fs) { .real => |cwd| .{ .dir = try cwd.openDir(dir_name, .{ .access_sub_paths = false, .iterate = true }) }, .fake => |entries| .{ .fake = .{ .entries = entries, .path = dir_name } }, diff --git a/lib/compiler/aro/aro/Toolchain.zig b/lib/compiler/aro/aro/Toolchain.zig index 326278cc38..ae84369205 100644 --- a/lib/compiler/aro/aro/Toolchain.zig +++ b/lib/compiler/aro/aro/Toolchain.zig @@ -497,6 +497,7 @@ pub fn addBuiltinIncludeDir(tc: *const Toolchain) !void { const comp = d.comp; const gpa = comp.gpa; const arena = comp.arena; + const io = comp.io; try d.includes.ensureUnusedCapacity(gpa, 1); if (d.resource_dir) |resource_dir| { const path = try std.fs.path.join(arena, &.{ resource_dir, "include" }); @@ -509,7 +510,7 @@ pub fn addBuiltinIncludeDir(tc: *const Toolchain) !void { var search_path = d.aro_name; while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) { var base_dir = d.comp.cwd.openDir(dirname, .{}) catch continue; - defer base_dir.close(); + defer base_dir.close(io); base_dir.access("include/stddef.h", .{}) catch continue; const path = try std.fs.path.join(arena, &.{ dirname, "include" }); diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index 7cf0f14e42..1608c121b1 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -152,7 +152,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void const io = threaded.io(); const input_file = fs.cwd().openFile(input, .{}) catch |err| fatal("failed to open {s}: {t}", .{ input, err }); - defer input_file.close(); + defer input_file.close(io); const stat = input_file.stat() catch |err| fatal("failed to stat {s}: {t}", .{ input, err }); @@ -180,7 +180,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void const mode = if (out_fmt != .elf or only_keep_debug) fs.File.default_mode else stat.mode; var output_file = try fs.cwd().createFile(output, .{ .mode = mode }); - defer output_file.close(); + defer output_file.close(io); var out = output_file.writer(&output_buffer); diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index 59568e9cef..ffaa62e7ca 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -1991,6 +1991,8 @@ test "parse: input and output formats" { } test "maybeAppendRC" { + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); @@ -2001,7 +2003,7 @@ test "maybeAppendRC" { // Create the file so that it's found. In this scenario, .rc should not get // appended. var file = try tmp.dir.createFile("foo", .{}); - file.close(); + file.close(io); try options.maybeAppendRC(tmp.dir); try std.testing.expectEqualStrings("foo", options.input_source.filename); diff --git a/lib/compiler/resinator/compile.zig b/lib/compiler/resinator/compile.zig index 08e161e505..7dc77e5ee1 100644 --- a/lib/compiler/resinator/compile.zig +++ b/lib/compiler/resinator/compile.zig @@ -34,7 +34,7 @@ const code_pages = @import("code_pages.zig"); const errors = @import("errors.zig"); pub const CompileOptions = struct { - cwd: std.fs.Dir, + cwd: std.Io.Dir, diagnostics: *Diagnostics, source_mappings: ?*SourceMappings = null, /// List of paths (absolute or relative to `cwd`) for every file that the resources within the .rc file depend on. @@ -107,7 +107,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io // the cwd so we don't need to add it as a distinct search path. if (std.fs.path.dirname(root_path)) |root_dir_path| { var root_dir = try options.cwd.openDir(root_dir_path, .{}); - errdefer root_dir.close(); + errdefer root_dir.close(io); try search_dirs.append(allocator, .{ .dir = root_dir, .path = try allocator.dupe(u8, root_dir_path) }); } } @@ -136,7 +136,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io // TODO: maybe a warning that the search path is skipped? continue; }; - errdefer dir.close(); + errdefer dir.close(io); try search_dirs.append(allocator, .{ .dir = dir, .path = try allocator.dupe(u8, extra_include_path) }); } for (options.system_include_paths) |system_include_path| { @@ -144,7 +144,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io // TODO: maybe a warning that the search path is skipped? continue; }; - errdefer dir.close(); + errdefer dir.close(io); try search_dirs.append(allocator, .{ .dir = dir, .path = try allocator.dupe(u8, system_include_path) }); } if (!options.ignore_include_env_var) { @@ -160,7 +160,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io var it = std.mem.tokenizeScalar(u8, INCLUDE, delimiter); while (it.next()) |search_path| { var dir = openSearchPathDir(options.cwd, search_path) catch continue; - errdefer dir.close(); + errdefer dir.close(io); try search_dirs.append(allocator, .{ .dir = dir, .path = try allocator.dupe(u8, search_path) }); } } @@ -196,7 +196,7 @@ pub const Compiler = struct { arena: Allocator, allocator: Allocator, io: Io, - cwd: std.fs.Dir, + cwd: std.Io.Dir, state: State = .{}, diagnostics: *Diagnostics, dependencies: ?*Dependencies, @@ -388,7 +388,9 @@ pub const Compiler = struct { /// matching file is invalid. That is, it does not do the `cmd` PATH searching /// thing of continuing to look for matching files until it finds a valid /// one if a matching file is invalid. - fn searchForFile(self: *Compiler, path: []const u8) !std.fs.File { + fn searchForFile(self: *Compiler, path: []const u8) !std.Io.File { + const io = self.io; + // If the path is absolute, then it is not resolved relative to any search // paths, so there's no point in checking them. // @@ -405,7 +407,7 @@ pub const Compiler = struct { // an absolute path. if (std.fs.path.isAbsolute(path)) { const file = try utils.openFileNotDir(std.fs.cwd(), path, .{}); - errdefer file.close(); + errdefer file.close(io); if (self.dependencies) |dependencies| { const duped_path = try dependencies.allocator.dupe(u8, path); @@ -414,10 +416,10 @@ pub const Compiler = struct { } } - var first_error: ?(std.fs.File.OpenError || std.fs.File.StatError) = null; + var first_error: ?(std.Io.File.OpenError || std.Io.File.StatError) = null; for (self.search_dirs) |search_dir| { if (utils.openFileNotDir(search_dir.dir, path, .{})) |file| { - errdefer file.close(); + errdefer file.close(io); if (self.dependencies) |dependencies| { const searched_file_path = try std.fs.path.join(dependencies.allocator, &.{ @@ -587,7 +589,7 @@ pub const Compiler = struct { }); }, }; - defer file_handle.close(); + defer file_handle.close(io); var file_buffer: [2048]u8 = undefined; var file_reader = file_handle.reader(io, &file_buffer); @@ -2892,9 +2894,9 @@ pub const Compiler = struct { } }; -pub const OpenSearchPathError = std.fs.Dir.OpenError; +pub const OpenSearchPathError = std.Io.Dir.OpenError; -fn openSearchPathDir(dir: std.fs.Dir, path: []const u8) OpenSearchPathError!std.fs.Dir { +fn openSearchPathDir(dir: std.Io.Dir, path: []const u8) OpenSearchPathError!std.Io.Dir { // Validate the search path to avoid possible unreachable on invalid paths, // see https://github.com/ziglang/zig/issues/15607 for why this is currently necessary. try validateSearchPath(path); @@ -2927,11 +2929,11 @@ fn validateSearchPath(path: []const u8) error{BadPathName}!void { } pub const SearchDir = struct { - dir: std.fs.Dir, + dir: std.Io.Dir, path: ?[]const u8, - pub fn deinit(self: *SearchDir, allocator: Allocator) void { - self.dir.close(); + pub fn deinit(self: *SearchDir, allocator: Allocator, io: Io) void { + self.dir.close(io); if (self.path) |path| { allocator.free(path); } diff --git a/lib/compiler/resinator/errors.zig b/lib/compiler/resinator/errors.zig index 0060990ab6..8509aa610f 100644 --- a/lib/compiler/resinator/errors.zig +++ b/lib/compiler/resinator/errors.zig @@ -1221,8 +1221,8 @@ const CorrespondingLines = struct { }; } - pub fn deinit(self: *CorrespondingLines) void { - self.file.close(); + pub fn deinit(self: *CorrespondingLines, io: Io) void { + self.file.close(io); } }; diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 6d6819f45a..42308a8987 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -296,7 +296,7 @@ pub fn main() !void { error.ParseError, error.CompileError => { try error_handler.emitDiagnostics(gpa, std.fs.cwd(), final_input, &diagnostics, mapping_results.mappings); // Delete the output file on error - res_stream.cleanupAfterError(); + res_stream.cleanupAfterError(io); std.process.exit(1); }, else => |e| return e, @@ -315,7 +315,7 @@ pub fn main() !void { try error_handler.emitMessage(gpa, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) }); std.process.exit(1); }; - defer depfile.close(); + defer depfile.close(io); var depfile_buffer: [1024]u8 = undefined; var depfile_writer = depfile.writer(&depfile_buffer); @@ -402,7 +402,7 @@ pub fn main() !void { }, } // Delete the output file on error - coff_stream.cleanupAfterError(); + coff_stream.cleanupAfterError(io); std.process.exit(1); }; @@ -434,11 +434,11 @@ const IoStream = struct { self.source.deinit(allocator); } - pub fn cleanupAfterError(self: *IoStream) void { + pub fn cleanupAfterError(self: *IoStream, io: Io) void { switch (self.source) { .file => |file| { // Delete the output file on error - file.close(); + file.close(io); // Failing to delete is not really a big deal, so swallow any errors std.fs.cwd().deleteFile(self.name) catch {}; }, @@ -465,9 +465,9 @@ const IoStream = struct { } } - pub fn deinit(self: *Source, allocator: Allocator) void { + pub fn deinit(self: *Source, allocator: Allocator, io: Io) void { switch (self.*) { - .file => |file| file.close(), + .file => |file| file.close(io), .stdio => {}, .memory => |*list| list.deinit(allocator), .closed => {}, diff --git a/lib/compiler/resinator/utils.zig b/lib/compiler/resinator/utils.zig index 021b8cf4de..f8080539cb 100644 --- a/lib/compiler/resinator/utils.zig +++ b/lib/compiler/resinator/utils.zig @@ -1,6 +1,8 @@ -const std = @import("std"); const builtin = @import("builtin"); +const std = @import("std"); +const Io = std.Io; + pub const UncheckedSliceWriter = struct { const Self = @This(); @@ -28,11 +30,12 @@ pub const UncheckedSliceWriter = struct { /// TODO: Remove once https://github.com/ziglang/zig/issues/5732 is addressed. pub fn openFileNotDir( cwd: std.fs.Dir, + io: Io, path: []const u8, flags: std.fs.File.OpenFlags, ) (std.fs.File.OpenError || std.fs.File.StatError)!std.fs.File { - const file = try cwd.openFile(path, flags); - errdefer file.close(); + const file = try cwd.openFile(io, path, flags); + errdefer file.close(io); // https://github.com/ziglang/zig/issues/5732 if (builtin.os.tag != .windows) { const stat = try file.stat(); diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index 12825146a2..15c17ee59b 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -1,12 +1,14 @@ const builtin = @import("builtin"); + const std = @import("std"); +const Io = std.Io; const mem = std.mem; const Allocator = std.mem.Allocator; const assert = std.debug.assert; const Cache = std.Build.Cache; fn usage() noreturn { - std.fs.File.stdout().writeAll( + std.Io.File.stdout().writeAll( \\Usage: zig std [options] \\ \\Options: @@ -27,6 +29,10 @@ pub fn main() !void { var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; const gpa = general_purpose_allocator.allocator(); + var threaded: std.Io.Threaded = .init(gpa); + defer threaded.deinit(); + const io = threaded.io(); + var argv = try std.process.argsWithAllocator(arena); defer argv.deinit(); assert(argv.skip()); @@ -35,7 +41,7 @@ pub fn main() !void { const global_cache_path = argv.next().?; var lib_dir = try std.fs.cwd().openDir(zig_lib_directory, .{}); - defer lib_dir.close(); + defer lib_dir.close(io); var listen_port: u16 = 0; var force_open_browser: ?bool = null; @@ -64,7 +70,7 @@ pub fn main() !void { }); const port = http_server.listen_address.in.getPort(); const url_with_newline = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/\n", .{port}); - std.fs.File.stdout().writeAll(url_with_newline) catch {}; + std.Io.File.stdout().writeAll(url_with_newline) catch {}; if (should_open_browser) { openBrowserTab(gpa, url_with_newline[0 .. url_with_newline.len - 1 :'\n']) catch |err| { std.log.err("unable to open browser: {s}", .{@errorName(err)}); @@ -73,6 +79,7 @@ pub fn main() !void { var context: Context = .{ .gpa = gpa, + .io = io, .zig_exe_path = zig_exe_path, .global_cache_path = global_cache_path, .lib_dir = lib_dir, @@ -83,14 +90,15 @@ pub fn main() !void { const connection = try http_server.accept(); _ = std.Thread.spawn(.{}, accept, .{ &context, connection }) catch |err| { std.log.err("unable to accept connection: {s}", .{@errorName(err)}); - connection.stream.close(); + connection.stream.close(io); continue; }; } } fn accept(context: *Context, connection: std.net.Server.Connection) void { - defer connection.stream.close(); + const io = context.io; + defer connection.stream.close(io); var recv_buffer: [4000]u8 = undefined; var send_buffer: [4000]u8 = undefined; @@ -124,6 +132,7 @@ fn accept(context: *Context, connection: std.net.Server.Connection) void { const Context = struct { gpa: Allocator, + io: Io, lib_dir: std.fs.Dir, zig_lib_directory: []const u8, zig_exe_path: []const u8, @@ -185,6 +194,7 @@ fn serveDocsFile( fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { const gpa = context.gpa; + const io = context.io; var send_buffer: [0x4000]u8 = undefined; var response = try request.respondStreaming(&send_buffer, .{ @@ -197,7 +207,7 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { }); var std_dir = try context.lib_dir.openDir("std", .{ .iterate = true }); - defer std_dir.close(); + defer std_dir.close(io); var walker = try std_dir.walk(gpa); defer walker.deinit(); @@ -216,11 +226,11 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { else => continue, } var file = try entry.dir.openFile(entry.basename, .{}); - defer file.close(); + defer file.close(io); const stat = try file.stat(); - var file_reader: std.fs.File.Reader = .{ + var file_reader: std.Io.File.Reader = .{ .file = file, - .interface = std.fs.File.Reader.initInterface(&.{}), + .interface = std.Io.File.Reader.initInterface(&.{}), .size = stat.size, }; try archiver.writeFile(entry.path, &file_reader, stat.mtime); @@ -283,6 +293,7 @@ fn buildWasmBinary( optimize_mode: std.builtin.OptimizeMode, ) !Cache.Path { const gpa = context.gpa; + const io = context.io; var argv: std.ArrayList([]const u8) = .empty; @@ -371,7 +382,7 @@ fn buildWasmBinary( } // Send EOF to stdin. - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; switch (try child.wait()) { @@ -410,7 +421,7 @@ fn buildWasmBinary( }; } -fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { +fn sendMessage(file: std.Io.File, tag: std.zig.Client.Message.Tag) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 0, diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig index b0d7a5d9bd..0c72298b30 100644 --- a/lib/compiler/translate-c/main.zig +++ b/lib/compiler/translate-c/main.zig @@ -121,6 +121,7 @@ pub const usage = fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration: bool) !void { const gpa = d.comp.gpa; + const io = d.comp.io; const aro_args = args: { var i: usize = 0; @@ -228,7 +229,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration return d.fatal("unable to create dependency file '{s}': {s}", .{ path, aro.Driver.errorDescription(er) }) else std.fs.File.stdout(); - defer if (dep_file_name != null) file.close(); + defer if (dep_file_name != null) file.close(io); var file_writer = file.writer(&out_buf); dep_file.write(&file_writer.interface) catch @@ -246,7 +247,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration var close_out_file = false; var out_file_path: []const u8 = ""; var out_file: std.fs.File = .stdout(); - defer if (close_out_file) out_file.close(); + defer if (close_out_file) out_file.close(io); if (d.output_name) |path| blk: { if (std.mem.eql(u8, path, "-")) break :blk; diff --git a/lib/std/Build/Cache/Directory.zig b/lib/std/Build/Cache/Directory.zig index 305ef25361..ce5f5b02bb 100644 --- a/lib/std/Build/Cache/Directory.zig +++ b/lib/std/Build/Cache/Directory.zig @@ -52,8 +52,8 @@ pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ! /// Whether or not the handle should be closed, or the path should be freed /// is determined by usage, however this function is provided for convenience /// if it happens to be what the caller needs. -pub fn closeAndFree(self: *Directory, gpa: Allocator) void { - self.handle.close(); +pub fn closeAndFree(self: *Directory, gpa: Allocator, io: Io) void { + self.handle.close(io); if (self.path) |p| gpa.free(p); self.* = undefined; } diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 2897b29969..db83f393fd 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -411,7 +411,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO }); return error.AlreadyReported; }; - defer coverage_file.close(); + defer coverage_file.close(io); const file_size = coverage_file.getEndPos() catch |err| { log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err }); @@ -533,7 +533,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void { cov.run.step.name, coverage_file_path, err, }); }; - defer coverage_file.close(); + defer coverage_file.close(io); const fuzz_abi = std.Build.abi.fuzz; var rbuf: [0x1000]u8 = undefined; diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 33fe755c2b..acde47071d 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -441,6 +441,7 @@ pub fn evalZigProcess( assert(argv.len != 0); const b = s.owner; const arena = b.allocator; + const io = b.graph.io; try handleChildProcUnsupported(s); try handleVerbose(s.owner, null, argv); @@ -474,7 +475,7 @@ pub fn evalZigProcess( if (!watch) { // Send EOF to stdin. - zp.child.stdin.?.close(); + zp.child.stdin.?.close(io); zp.child.stdin = null; const term = zp.child.wait() catch |err| { diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index c203ae924b..1cdb232770 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -119,6 +119,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { _ = options; const install_artifact: *InstallArtifact = @fieldParentPtr("step", step); const b = step.owner; + const io = b.graph.io; var all_cached = true; @@ -168,7 +169,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { src_dir_path, @errorName(err), }); }; - defer src_dir.close(); + defer src_dir.close(io); var it = try src_dir.walk(b.allocator); next_entry: while (try it.next()) |entry| { diff --git a/lib/std/Build/Step/InstallDir.zig b/lib/std/Build/Step/InstallDir.zig index ecb0959cc7..788d5565a7 100644 --- a/lib/std/Build/Step/InstallDir.zig +++ b/lib/std/Build/Step/InstallDir.zig @@ -68,7 +68,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err }); }; - defer src_dir.close(); + defer src_dir.close(io); var it = try src_dir.walk(arena); var all_cached = true; next_entry: while (try it.next()) |entry| { diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 28c09e1faf..e66e30cc79 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -851,7 +851,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { .{ file_path, err }, ); }; - defer file.close(); + defer file.close(io); var buf: [1024]u8 = undefined; var file_reader = file.reader(io, &buf); @@ -1111,7 +1111,7 @@ pub fn rerunInFuzzMode( result.writer.writeAll(file_plp.prefix) catch return error.OutOfMemory; const file = try file_path.root_dir.handle.openFile(file_path.subPathOrDot(), .{}); - defer file.close(); + defer file.close(io); var buf: [1024]u8 = undefined; var file_reader = file.reader(io, &buf); @@ -1671,8 +1671,10 @@ fn evalZigTest( options: Step.MakeOptions, fuzz_context: ?FuzzContext, ) !EvalZigTestResult { - const gpa = run.step.owner.allocator; - const arena = run.step.owner.allocator; + const step_owner = run.step.owner; + const gpa = step_owner.allocator; + const arena = step_owner.allocator; + const io = step_owner.graph.io; // We will update this every time a child runs. run.step.result_peak_rss = 0; @@ -1724,7 +1726,7 @@ fn evalZigTest( run.step.result_stderr = try arena.dupe(u8, poller.reader(.stderr).buffered()); // Clean up everything and wait for the child to exit. - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; poller.deinit(); child_killed = true; @@ -1744,7 +1746,7 @@ fn evalZigTest( poller.reader(.stderr).tossBuffered(); // Clean up everything and wait for the child to exit. - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; poller.deinit(); child_killed = true; @@ -2177,7 +2179,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { child.stdin.?.writeAll(bytes) catch |err| { return run.step.fail("unable to write stdin: {s}", .{@errorName(err)}); }; - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; }, .lazy_path => |lazy_path| { @@ -2185,7 +2187,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { const file = path.root_dir.handle.openFile(path.subPathOrDot(), .{}) catch |err| { return run.step.fail("unable to open stdin file: {s}", .{@errorName(err)}); }; - defer file.close(); + defer file.close(io); // TODO https://github.com/ziglang/zig/issues/23955 var read_buffer: [1024]u8 = undefined; var file_reader = file.reader(io, &read_buffer); @@ -2204,7 +2206,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { stdin_writer.err.?, }), }; - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; }, .none => {}, diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 030c7c6811..201b132271 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -206,7 +206,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } } - const open_dir_cache = try arena.alloc(fs.Dir, write_file.directories.items.len); + const open_dir_cache = try arena.alloc(Io.Dir, write_file.directories.items.len); var open_dirs_count: usize = 0; defer closeDirs(open_dir_cache[0..open_dirs_count]); @@ -264,7 +264,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { b.cache_root, cache_path, @errorName(err), }); }; - defer cache_dir.close(); + defer cache_dir.close(io); for (write_file.files.items) |file| { if (fs.path.dirname(file.sub_path)) |dirname| { @@ -342,6 +342,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try step.writeManifest(&man); } -fn closeDirs(dirs: []fs.Dir) void { - for (dirs) |*d| d.close(); +fn closeDirs(io: Io, dirs: []Io.Dir) void { + var group: Io.Group = .init; + defer group.wait(); + for (dirs) |d| group.async(Io.Dir.close, .{ d, io }); } diff --git a/lib/std/Build/Watch/FsEvents.zig b/lib/std/Build/Watch/FsEvents.zig index 6131663993..59238c8725 100644 --- a/lib/std/Build/Watch/FsEvents.zig +++ b/lib/std/Build/Watch/FsEvents.zig @@ -78,10 +78,10 @@ const ResolvedSymbols = struct { kCFAllocatorUseContext: *const CFAllocatorRef, }; -pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { +pub fn init(io: Io) error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch return error.OpenFrameworkFailed; - errdefer core_services.close(); + errdefer core_services.close(io); var resolved_symbols: ResolvedSymbols = undefined; inline for (@typeInfo(ResolvedSymbols).@"struct".fields) |f| { @@ -102,10 +102,10 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { }; } -pub fn deinit(fse: *FsEvents, gpa: Allocator) void { +pub fn deinit(fse: *FsEvents, gpa: Allocator, io: Io) void { dispatch_release(fse.waiting_semaphore); dispatch_release(fse.dispatch_queue); - fse.core_services.close(); + fse.core_services.close(io); gpa.free(fse.watch_roots); fse.watch_paths.deinit(gpa); @@ -487,6 +487,7 @@ const FSEventStreamEventFlags = packed struct(u32) { }; const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const watch_log = std.log.scoped(.watch); diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 2c865a8889..f91075b444 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -129,6 +129,7 @@ pub fn init(opts: Options) WebServer { } pub fn deinit(ws: *WebServer) void { const gpa = ws.gpa; + const io = ws.graph.io; gpa.free(ws.step_names_trailing); gpa.free(ws.step_status_bits); @@ -139,7 +140,7 @@ pub fn deinit(ws: *WebServer) void { gpa.free(ws.time_report_update_times); if (ws.serve_thread) |t| { - if (ws.tcp_server) |*s| s.stream.close(); + if (ws.tcp_server) |*s| s.stream.close(io); t.join(); } if (ws.tcp_server) |*s| s.deinit(); @@ -507,7 +508,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons log.err("failed to open '{f}': {s}", .{ path, @errorName(err) }); continue; }; - defer file.close(); + defer file.close(io); const stat = try file.stat(); var read_buffer: [1024]u8 = undefined; var file_reader: Io.File.Reader = .initSize(file.adaptToNewApi(), io, &read_buffer, stat.size); @@ -634,7 +635,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim } // Send EOF to stdin. - child.stdin.?.close(); + child.stdin.?.close(io); child.stdin = null; switch (try child.wait()) { diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 348950e20a..5a74c8ca72 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -131,7 +131,7 @@ pub const SelectiveWalker = struct { /// After each call to this function, and on deinit(), the memory returned /// from this function becomes invalid. A copy must be made in order to keep /// a reference to the path. - pub fn next(self: *SelectiveWalker) Error!?Walker.Entry { + pub fn next(self: *SelectiveWalker, io: Io) Error!?Walker.Entry { while (self.stack.items.len > 0) { const top = &self.stack.items[self.stack.items.len - 1]; var dirname_len = top.dirname_len; @@ -142,7 +142,7 @@ pub const SelectiveWalker = struct { // likely just fail with the same error. var item = self.stack.pop().?; if (self.stack.items.len != 0) { - item.iter.dir.close(); + item.iter.dir.close(io); } return err; }) |entry| { @@ -164,7 +164,7 @@ pub const SelectiveWalker = struct { } else { var item = self.stack.pop().?; if (self.stack.items.len != 0) { - item.iter.dir.close(); + item.iter.dir.close(io); } } } @@ -172,7 +172,7 @@ pub const SelectiveWalker = struct { } /// Traverses into the directory, continuing walking one level down. - pub fn enter(self: *SelectiveWalker, entry: Walker.Entry) !void { + pub fn enter(self: *SelectiveWalker, io: Io, entry: Walker.Entry) !void { if (entry.kind != .directory) { @branchHint(.cold); return; @@ -184,7 +184,7 @@ pub const SelectiveWalker = struct { else => |e| return e, } }; - errdefer new_dir.close(); + errdefer new_dir.close(io); try self.stack.append(self.allocator, .{ .iter = new_dir.iterateAssumeFirstIteration(), @@ -200,11 +200,11 @@ pub const SelectiveWalker = struct { /// Leaves the current directory, continuing walking one level up. /// If the current entry is a directory entry, then the "current directory" /// will pertain to that entry if `enter` is called before `leave`. - pub fn leave(self: *SelectiveWalker) void { + pub fn leave(self: *SelectiveWalker, io: Io) void { var item = self.stack.pop().?; if (self.stack.items.len != 0) { @branchHint(.likely); - item.iter.dir.close(); + item.iter.dir.close(io); } } }; @@ -558,7 +558,8 @@ pub fn makeDir(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions) pub const MakePathError = MakeError || StatPathError; -/// Creates parent directories as necessary to ensure `sub_path` exists as a directory. +/// Creates parent directories with default permissions as necessary to ensure +/// `sub_path` exists as a directory. /// /// Returns success if the path already exists and is a directory. /// @@ -579,8 +580,11 @@ pub const MakePathError = MakeError || StatPathError; /// - On other platforms, `..` are not resolved before the path is passed to `mkdirat`, /// meaning a `sub_path` like "first/../second" will create both a `./first` /// and a `./second` directory. -pub fn makePath(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions) MakePathError!void { - _ = try io.vtable.dirMakePath(io.userdata, dir, sub_path, permissions); +/// +/// See also: +/// * `makePathStatus` +pub fn makePath(dir: Dir, io: Io, sub_path: []const u8) MakePathError!void { + _ = try io.vtable.dirMakePath(io.userdata, dir, sub_path, .default_dir); } pub const MakePathStatus = enum { existed, created }; @@ -593,6 +597,11 @@ pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8, permissions: Permi pub const MakeOpenPathError = MakeError || OpenError || StatPathError; +pub const MakeOpenPathOptions = struct { + open_options: OpenOptions = .{}, + permissions: Permissions = .default_dir, +}; + /// Performs the equivalent of `makePath` followed by `openDir`, atomically if possible. /// /// When this operation is canceled, it may leave the file system in a @@ -601,8 +610,8 @@ pub const MakeOpenPathError = MakeError || OpenError || StatPathError; /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). /// On WASI, `sub_path` should be encoded as valid UTF-8. /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. -pub fn makeOpenPath(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions, options: OpenOptions) MakeOpenPathError!Dir { - return io.vtable.dirMakeOpenPath(io.userdata, dir, sub_path, permissions, options); +pub fn makeOpenPath(dir: Dir, io: Io, sub_path: []const u8, options: MakeOpenPathOptions) MakeOpenPathError!Dir { + return io.vtable.dirMakeOpenPath(io.userdata, dir, sub_path, options.permissions, options.open_options); } pub const Stat = File.Stat; @@ -1266,10 +1275,10 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, start_over: while (true) { var dir = (try parent.deleteTreeOpenInitialSubpath(io, sub_path, kind_hint)) orelse return; var cleanup_dir_parent: ?Dir = null; - defer if (cleanup_dir_parent) |*d| d.close(); + defer if (cleanup_dir_parent) |*d| d.close(io); var cleanup_dir = true; - defer if (cleanup_dir) dir.close(); + defer if (cleanup_dir) dir.close(io); // Valid use of max_path_bytes because dir_name_buf will only // ever store a single path component that was returned from the @@ -1315,7 +1324,7 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, error.Canceled, => |e| return e, }; - if (cleanup_dir_parent) |*d| d.close(); + if (cleanup_dir_parent) |*d| d.close(io); cleanup_dir_parent = dir; dir = new_dir; const result = dir_name_buf[0..entry.name.len]; @@ -1354,7 +1363,7 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, } // Reached the end of the directory entries, which means we successfully deleted all of them. // Now to remove the directory itself. - dir.close(); + dir.close(io); cleanup_dir = false; if (cleanup_dir_parent) |d| { diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index f49ef8eb67..5601293cfb 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -2835,7 +2835,7 @@ test "discarding sendFile" { defer tmp_dir.cleanup(); const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); var r_buffer: [256]u8 = undefined; var file_writer: std.fs.File.Writer = .init(file, &r_buffer); try file_writer.interface.writeByte('h'); @@ -2857,7 +2857,7 @@ test "allocating sendFile" { defer tmp_dir.cleanup(); const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); var r_buffer: [2]u8 = undefined; var file_writer: std.fs.File.Writer = .init(file, &r_buffer); try file_writer.interface.writeAll("abcd"); @@ -2881,7 +2881,7 @@ test sendFileReading { defer tmp_dir.cleanup(); const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); var r_buffer: [2]u8 = undefined; var file_writer: std.fs.File.Writer = .init(file, &r_buffer); try file_writer.interface.writeAll("abcd"); diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig index e234a9edde..5818f6c3f7 100644 --- a/lib/std/Io/net/test.zig +++ b/lib/std/Io/net/test.zig @@ -232,8 +232,10 @@ test "listen on an in use port" { fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyerror!void { if (builtin.os.tag == .wasi) return error.SkipZigTest; + const io = testing.io; + const connection = try net.tcpConnectToHost(allocator, name, port); - defer connection.close(); + defer connection.close(io); var buf: [100]u8 = undefined; const len = try connection.read(&buf); @@ -244,8 +246,10 @@ fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyer fn testClient(addr: net.IpAddress) anyerror!void { if (builtin.os.tag == .wasi) return error.SkipZigTest; + const io = testing.io; + const socket_file = try net.tcpConnectToAddress(addr); - defer socket_file.close(); + defer socket_file.close(io); var buf: [100]u8 = undefined; const len = try socket_file.read(&buf); @@ -330,7 +334,7 @@ test "non-blocking tcp server" { try testing.expectError(error.WouldBlock, accept_err); const socket_file = try net.tcpConnectToAddress(server.socket.address); - defer socket_file.close(); + defer socket_file.close(io); var stream = try server.accept(io); defer stream.close(io); diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index f7965ed14e..9ea2d48ee5 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -28,7 +28,7 @@ test "write a file, read it, then delete it" { const tmp_file_name = "temp_test_file.txt"; { var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); var file_writer = file.writer(&.{}); const st = &file_writer.interface; @@ -45,7 +45,7 @@ test "write a file, read it, then delete it" { { var file = try tmp.dir.openFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); const file_size = try file.getEndPos(); const expected_file_size: u64 = "begin".len + data.len + "end".len; @@ -67,9 +67,11 @@ test "File seek ops" { var tmp = tmpDir(.{}); defer tmp.cleanup(); + const io = testing.io; + const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); try file.writeAll(&([_]u8{0x55} ** 8192)); @@ -88,12 +90,14 @@ test "File seek ops" { } test "setEndPos" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); // Verify that the file size changes and the file offset is not moved try expect((try file.getEndPos()) == 0); @@ -111,12 +115,14 @@ test "setEndPos" { } test "updateTimes" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "just_a_temporary_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{ .read = true }); - defer file.close(); + defer file.close(io); const stat_old = try file.stat(); // Set atime and mtime to 5s before diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 35b268b349..9f532c3bec 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -7,6 +7,7 @@ const target = builtin.target; const native_os = builtin.os.tag; const std = @import("std.zig"); +const Io = std.Io; const math = std.math; const assert = std.debug.assert; const posix = std.posix; @@ -176,7 +177,7 @@ pub const SetNameError = error{ InvalidWtf8, } || posix.PrctlError || posix.WriteError || std.fs.File.OpenError || std.fmt.BufPrintError; -pub fn setName(self: Thread, name: []const u8) SetNameError!void { +pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { if (name.len > max_name_len) return error.NameTooLong; const name_with_terminator = blk: { @@ -208,7 +209,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); const file = try std.fs.cwd().openFile(path, .{ .mode = .write_only }); - defer file.close(); + defer file.close(io); try file.writeAll(name); return; @@ -325,7 +326,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co const io = threaded.ioBasic(); const file = try std.fs.cwd().openFile(path, .{}); - defer file.close(); + defer file.close(io); var file_reader = file.readerStreaming(io, &.{}); const data_len = file_reader.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) { diff --git a/lib/std/crypto/Certificate/Bundle.zig b/lib/std/crypto/Certificate/Bundle.zig index 53fb638250..9541e01db5 100644 --- a/lib/std/crypto/Certificate/Bundle.zig +++ b/lib/std/crypto/Certificate/Bundle.zig @@ -181,7 +181,7 @@ pub fn addCertsFromDirPath( sub_dir_path: []const u8, ) AddCertsFromDirPathError!void { var iterable_dir = try dir.openDir(sub_dir_path, .{ .iterate = true }); - defer iterable_dir.close(); + defer iterable_dir.close(io); return addCertsFromDir(cb, gpa, io, iterable_dir); } @@ -194,7 +194,7 @@ pub fn addCertsFromDirPathAbsolute( ) AddCertsFromDirPathError!void { assert(fs.path.isAbsolute(abs_dir_path)); var iterable_dir = try fs.openDirAbsolute(abs_dir_path, .{ .iterate = true }); - defer iterable_dir.close(); + defer iterable_dir.close(io); return addCertsFromDir(cb, gpa, io, now, iterable_dir); } @@ -222,7 +222,7 @@ pub fn addCertsFromFilePathAbsolute( abs_file_path: []const u8, ) AddCertsFromFilePathError!void { var file = try fs.openFileAbsolute(abs_file_path, .{}); - defer file.close(); + defer file.close(io); var file_reader = file.reader(io, &.{}); return addCertsFromFile(cb, gpa, &file_reader, now.toSeconds()); } diff --git a/lib/std/crypto/codecs/asn1/test.zig b/lib/std/crypto/codecs/asn1/test.zig index fe12cba819..ff854fcbde 100644 --- a/lib/std/crypto/codecs/asn1/test.zig +++ b/lib/std/crypto/codecs/asn1/test.zig @@ -75,6 +75,6 @@ test AllTypes { // Use this to update test file. // const dir = try std.fs.cwd().openDir("lib/std/crypto/asn1", .{}); // var file = try dir.createFile(path, .{}); - // defer file.close(); + // defer file.close(io); // try file.writeAll(buf); } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 7c5993dfcf..0cb96ed593 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1298,7 +1298,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "line_overlaps_page_boundary.zig" }); defer gpa.free(path); @@ -1317,7 +1317,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); defer gpa.free(path); @@ -1331,7 +1331,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); defer gpa.free(path); @@ -1357,7 +1357,7 @@ test printLineFromFile { } { const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); - defer file.close(); + defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "file_of_newlines.zig" }); defer gpa.free(path); diff --git a/lib/std/debug/ElfFile.zig b/lib/std/debug/ElfFile.zig index e81943ab49..92bcca1bcf 100644 --- a/lib/std/debug/ElfFile.zig +++ b/lib/std/debug/ElfFile.zig @@ -1,5 +1,13 @@ //! A helper type for loading an ELF file and collecting its DWARF debug information, unwind //! information, and symbol table. +const ElfFile = @This(); + +const std = @import("std"); +const Io = std.Io; +const Endian = std.builtin.Endian; +const Dwarf = std.debug.Dwarf; +const Allocator = std.mem.Allocator; +const elf = std.elf; is_64: bool, endian: Endian, @@ -358,10 +366,17 @@ const Section = struct { const Array = std.enums.EnumArray(Section.Id, ?Section); }; -fn loadSeparateDebugFile(arena: Allocator, main_loaded: *LoadInnerResult, opt_crc: ?u32, comptime fmt: []const u8, args: anytype) Allocator.Error!?[]align(std.heap.page_size_min) const u8 { +fn loadSeparateDebugFile( + arena: Allocator, + io: Io, + main_loaded: *LoadInnerResult, + opt_crc: ?u32, + comptime fmt: []const u8, + args: anytype, +) Allocator.Error!?[]align(std.heap.page_size_min) const u8 { const path = try std.fmt.allocPrint(arena, fmt, args); const elf_file = std.fs.cwd().openFile(path, .{}) catch return null; - defer elf_file.close(); + defer elf_file.close(io); const result = loadInner(arena, elf_file, opt_crc) catch |err| switch (err) { error.OutOfMemory => |e| return e, @@ -529,10 +544,3 @@ fn loadInner( .mapped_mem = mapped_mem, }; } - -const std = @import("std"); -const Endian = std.builtin.Endian; -const Dwarf = std.debug.Dwarf; -const ElfFile = @This(); -const Allocator = std.mem.Allocator; -const elf = std.elf; diff --git a/lib/std/debug/Info.zig b/lib/std/debug/Info.zig index 921cd36ab8..9268ca0247 100644 --- a/lib/std/debug/Info.zig +++ b/lib/std/debug/Info.zig @@ -5,19 +5,18 @@ //! Unlike `std.debug.SelfInfo`, this API does not assume the debug information //! in question happens to match the host CPU architecture, OS, or other target //! properties. +const Info = @This(); const std = @import("../std.zig"); +const Io = std.Io; const Allocator = std.mem.Allocator; const Path = std.Build.Cache.Path; const assert = std.debug.assert; const Coverage = std.debug.Coverage; const SourceLocation = std.debug.Coverage.SourceLocation; - const ElfFile = std.debug.ElfFile; const MachOFile = std.debug.MachOFile; -const Info = @This(); - impl: union(enum) { elf: ElfFile, macho: MachOFile, @@ -25,13 +24,23 @@ impl: union(enum) { /// Externally managed, outlives this `Info` instance. coverage: *Coverage, -pub const LoadError = std.fs.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError || error{ MissingDebugInfo, UnsupportedDebugInfo }; +pub const LoadError = error{ + MissingDebugInfo, + UnsupportedDebugInfo, +} || std.fs.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError; -pub fn load(gpa: Allocator, path: Path, coverage: *Coverage, format: std.Target.ObjectFormat, arch: std.Target.Cpu.Arch) LoadError!Info { +pub fn load( + gpa: Allocator, + io: Io, + path: Path, + coverage: *Coverage, + format: std.Target.ObjectFormat, + arch: std.Target.Cpu.Arch, +) LoadError!Info { switch (format) { .elf => { var file = try path.root_dir.handle.openFile(path.sub_path, .{}); - defer file.close(); + defer file.close(io); var elf_file: ElfFile = try .load(gpa, file, null, &.none); errdefer elf_file.deinit(gpa); diff --git a/lib/std/debug/MachOFile.zig b/lib/std/debug/MachOFile.zig index 3be1b1daff..3f0f620a90 100644 --- a/lib/std/debug/MachOFile.zig +++ b/lib/std/debug/MachOFile.zig @@ -27,13 +27,13 @@ pub fn deinit(mf: *MachOFile, gpa: Allocator) void { posix.munmap(mf.mapped_memory); } -pub fn load(gpa: Allocator, path: []const u8, arch: std.Target.Cpu.Arch) Error!MachOFile { +pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch) Error!MachOFile { switch (arch) { .x86_64, .aarch64 => {}, else => unreachable, } - const all_mapped_memory = try mapDebugInfoFile(path); + const all_mapped_memory = try mapDebugInfoFile(io, path); errdefer posix.munmap(all_mapped_memory); // In most cases, the file we just mapped is a Mach-O binary. However, it could be a "universal @@ -239,7 +239,7 @@ pub fn load(gpa: Allocator, path: []const u8, arch: std.Target.Cpu.Arch) Error!M .text_vmaddr = text_vmaddr, }; } -pub fn getDwarfForAddress(mf: *MachOFile, gpa: Allocator, vaddr: u64) !struct { *Dwarf, u64 } { +pub fn getDwarfForAddress(mf: *MachOFile, gpa: Allocator, io: Io, vaddr: u64) !struct { *Dwarf, u64 } { const symbol = Symbol.find(mf.symbols, vaddr) orelse return error.MissingDebugInfo; if (symbol.ofile == Symbol.unknown_ofile) return error.MissingDebugInfo; @@ -254,7 +254,7 @@ pub fn getDwarfForAddress(mf: *MachOFile, gpa: Allocator, vaddr: u64) !struct { const gop = try mf.ofiles.getOrPut(gpa, symbol.ofile); if (!gop.found_existing) { const name = mem.sliceTo(mf.strings[symbol.ofile..], 0); - gop.value_ptr.* = loadOFile(gpa, name); + gop.value_ptr.* = loadOFile(gpa, io, name); } const of = &(gop.value_ptr.* catch |err| return err); @@ -356,7 +356,7 @@ test { _ = Symbol; } -fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { +fn loadOFile(gpa: Allocator, io: Io, o_file_name: []const u8) !OFile { const all_mapped_memory, const mapped_ofile = map: { const open_paren = paren: { if (std.mem.endsWith(u8, o_file_name, ")")) { @@ -365,7 +365,7 @@ fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { } } // Not an archive, just a normal path to a .o file - const m = try mapDebugInfoFile(o_file_name); + const m = try mapDebugInfoFile(io, o_file_name); break :map .{ m, m }; }; @@ -373,7 +373,7 @@ fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { const archive_path = o_file_name[0..open_paren]; const target_name_in_archive = o_file_name[open_paren + 1 .. o_file_name.len - 1]; - const mapped_archive = try mapDebugInfoFile(archive_path); + const mapped_archive = try mapDebugInfoFile(io, archive_path); errdefer posix.munmap(mapped_archive); var ar_reader: Io.Reader = .fixed(mapped_archive); @@ -511,12 +511,12 @@ fn loadOFile(gpa: Allocator, o_file_name: []const u8) !OFile { } /// Uses `mmap` to map the file at `path` into memory. -fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { +fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) const u8 { const file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, else => return error.ReadFailed, }; - defer file.close(); + defer file.close(io); const file_len = std.math.cast( usize, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 59c0b42451..155dac6fb8 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -319,14 +319,14 @@ const Module = struct { } /// Assumes we already hold an exclusive lock. - fn getLoadedElf(mod: *Module, gpa: Allocator) Error!*LoadedElf { - if (mod.loaded_elf == null) mod.loaded_elf = loadElf(mod, gpa); + fn getLoadedElf(mod: *Module, gpa: Allocator, io: Io) Error!*LoadedElf { + if (mod.loaded_elf == null) mod.loaded_elf = loadElf(mod, gpa, io); return if (mod.loaded_elf.?) |*elf| elf else |err| err; } - fn loadElf(mod: *Module, gpa: Allocator) Error!LoadedElf { + fn loadElf(mod: *Module, gpa: Allocator, io: Io) Error!LoadedElf { const load_result = if (mod.name.len > 0) res: { var file = std.fs.cwd().openFile(mod.name, .{}) catch return error.MissingDebugInfo; - defer file.close(); + defer file.close(io); break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(mod.name)); } else res: { const path = std.fs.selfExePathAlloc(gpa) catch |err| switch (err) { @@ -335,7 +335,7 @@ const Module = struct { }; defer gpa.free(path); var file = std.fs.cwd().openFile(path, .{}) catch return error.MissingDebugInfo; - defer file.close(); + defer file.close(io); break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(path)); }; diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index dd11b4c8bf..2491cf416c 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -615,12 +615,12 @@ test { } /// Uses `mmap` to map the file at `path` into memory. -fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { +fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) const u8 { const file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, else => return error.ReadFailed, }; - defer file.close(); + defer file.close(io); const file_end_pos = file.getEndPos() catch |err| switch (err) { error.Unexpected => |e| return e, diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index 0e923a0f16..557f3901eb 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -207,11 +207,11 @@ const Module = struct { file: fs.File, section_handle: windows.HANDLE, section_view: []const u8, - fn deinit(mf: *const MappedFile) void { + fn deinit(mf: *const MappedFile, io: Io) void { const process_handle = windows.GetCurrentProcess(); assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(mf.section_view.ptr)) == .SUCCESS); windows.CloseHandle(mf.section_handle); - mf.file.close(); + mf.file.close(io); } }; @@ -447,7 +447,7 @@ const Module = struct { error.FileNotFound, error.IsDir => break :pdb null, else => return error.ReadFailed, }; - errdefer pdb_file.close(); + errdefer pdb_file.close(io); const pdb_reader = try arena.create(Io.File.Reader); pdb_reader.* = pdb_file.reader(io, try arena.alloc(u8, 4096)); diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index a3490ed7db..c91056b0ab 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -1,10 +1,12 @@ -const std = @import("std.zig"); const builtin = @import("builtin"); +const native_os = builtin.os.tag; + +const std = @import("std.zig"); +const Io = std.Io; const mem = std.mem; const testing = std.testing; const elf = std.elf; const windows = std.os.windows; -const native_os = builtin.os.tag; const posix = std.posix; /// Cross-platform dynamic library loading and symbol lookup. @@ -38,8 +40,8 @@ pub const DynLib = struct { } /// Trusts the file. - pub fn close(self: *DynLib) void { - return self.inner.close(); + pub fn close(self: *DynLib, io: Io) void { + return self.inner.close(io); } pub fn lookup(self: *DynLib, comptime T: type, name: [:0]const u8) ?T { @@ -155,23 +157,23 @@ pub const ElfDynLib = struct { dt_gnu_hash: *elf.gnu_hash.Header, }; - fn openPath(path: []const u8) !std.fs.Dir { + fn openPath(path: []const u8, io: Io) !std.fs.Dir { if (path.len == 0) return error.NotDir; var parts = std.mem.tokenizeScalar(u8, path, '/'); var parent = if (path[0] == '/') try std.fs.cwd().openDir("/", .{}) else std.fs.cwd(); while (parts.next()) |part| { const child = try parent.openDir(part, .{}); - parent.close(); + parent.close(io); parent = child; } return parent; } - fn resolveFromSearchPath(search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { + fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { var paths = std.mem.tokenizeScalar(u8, search_path, delim); while (paths.next()) |p| { var dir = openPath(p) catch continue; - defer dir.close(); + defer dir.close(io); const fd = posix.openat(dir.fd, file_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true, @@ -181,9 +183,9 @@ pub const ElfDynLib = struct { return null; } - fn resolveFromParent(dir_path: []const u8, file_name: []const u8) ?posix.fd_t { + fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?posix.fd_t { var dir = std.fs.cwd().openDir(dir_path, .{}) catch return null; - defer dir.close(); + defer dir.close(io); return posix.openat(dir.fd, file_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true, @@ -195,7 +197,7 @@ pub const ElfDynLib = struct { // - DT_RPATH of the calling binary is not used as a search path // - DT_RUNPATH of the calling binary is not used as a search path // - /etc/ld.so.cache is not read - fn resolveFromName(path_or_name: []const u8) !posix.fd_t { + fn resolveFromName(io: Io, path_or_name: []const u8) !posix.fd_t { // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| { return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); @@ -206,21 +208,21 @@ pub const ElfDynLib = struct { std.os.linux.getegid() == std.os.linux.getgid()) { if (posix.getenvZ("LD_LIBRARY_PATH")) |ld_library_path| { - if (resolveFromSearchPath(ld_library_path, path_or_name, ':')) |fd| { + if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| { return fd; } } } // Lastly the directories /lib and /usr/lib are searched (in this exact order) - if (resolveFromParent("/lib", path_or_name)) |fd| return fd; - if (resolveFromParent("/usr/lib", path_or_name)) |fd| return fd; + if (resolveFromParent(io, "/lib", path_or_name)) |fd| return fd; + if (resolveFromParent(io, "/usr/lib", path_or_name)) |fd| return fd; return error.FileNotFound; } /// Trusts the file. Malicious file will be able to execute arbitrary code. - pub fn open(path: []const u8) Error!ElfDynLib { - const fd = try resolveFromName(path); + pub fn open(io: Io, path: []const u8) Error!ElfDynLib { + const fd = try resolveFromName(io, path); defer posix.close(fd); const file: std.fs.File = .{ .handle = fd }; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 72fab9c7c2..9472e5d2a5 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -227,7 +227,7 @@ pub fn deleteFileAbsolute(absolute_path: []const u8) Dir.DeleteFileError!void { /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). /// On WASI, `absolute_path` should be encoded as valid UTF-8. /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. -pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { +pub fn deleteTreeAbsolute(io: Io, absolute_path: []const u8) !void { assert(path.isAbsolute(absolute_path)); const dirname = path.dirname(absolute_path) orelse return error{ /// Attempt to remove the root file system path. @@ -236,7 +236,7 @@ pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { }.CannotDeleteRootDirectory; var dir = try cwd().openDir(dirname, .{}); - defer dir.close(); + defer dir.close(io); return dir.deleteTree(path.basename(absolute_path)); } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 52baa0699d..15b8e9b558 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -178,6 +178,8 @@ fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlag } test "Dir.readLink" { + const io = testing.io; + try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { // Create some targets @@ -208,7 +210,7 @@ test "Dir.readLink" { const parent_file = ".." ++ fs.path.sep_str ++ "target.txt"; const canonical_parent_file = try ctx.toCanonicalPathSep(parent_file); var subdir = try ctx.dir.makeOpenPath("subdir", .{}); - defer subdir.close(); + defer subdir.close(io); try setupSymlink(subdir, canonical_parent_file, "relative-link.txt", .{}); try testReadLink(subdir, canonical_parent_file, "relative-link.txt"); if (builtin.os.tag == .windows) { @@ -268,6 +270,8 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void } test "File.stat on a File that is a symlink returns Kind.sym_link" { + const io = testing.io; + // This test requires getting a file descriptor of a symlink which // is not possible on all targets switch (builtin.target.os.tag) { @@ -302,7 +306,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { .SecurityDescriptor = null, .SecurityQualityOfService = null, }; - var io: windows.IO_STATUS_BLOCK = undefined; + var io_status_block: windows.IO_STATUS_BLOCK = undefined; const rc = windows.ntdll.NtCreateFile( &handle, .{ @@ -317,7 +321,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { }, }, &attr, - &io, + &io_status_block, null, .{ .NORMAL = true }, .VALID_FLAGS, @@ -352,7 +356,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { }, else => unreachable, }; - defer symlink.close(); + defer symlink.close(io); const stat = try symlink.stat(); try testing.expectEqual(File.Kind.sym_link, stat.kind); @@ -361,6 +365,8 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { } test "openDir" { + const io = testing.io; + try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { const allocator = ctx.arena.allocator(); @@ -370,7 +376,7 @@ test "openDir" { for ([_][]const u8{ "", ".", ".." }) |sub_path| { const dir_path = try fs.path.join(allocator, &.{ subdir_path, sub_path }); var dir = try ctx.dir.openDir(dir_path, .{}); - defer dir.close(); + defer dir.close(io); } } }.impl); @@ -393,6 +399,8 @@ test "openDirAbsolute" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -404,7 +412,7 @@ test "openDirAbsolute" { // Can open sub_path var tmp_sub = try fs.openDirAbsolute(sub_path, .{}); - defer tmp_sub.close(); + defer tmp_sub.close(io); const sub_ino = (try tmp_sub.stat()).inode; @@ -414,7 +422,7 @@ test "openDirAbsolute" { defer testing.allocator.free(dir_path); var dir = try fs.openDirAbsolute(dir_path, .{}); - defer dir.close(); + defer dir.close(io); const ino = (try dir.stat()).inode; try testing.expectEqual(tmp_ino, ino); @@ -426,7 +434,7 @@ test "openDirAbsolute" { defer testing.allocator.free(dir_path); var dir = try fs.openDirAbsolute(dir_path, .{}); - defer dir.close(); + defer dir.close(io); const ino = (try dir.stat()).inode; try testing.expectEqual(sub_ino, ino); @@ -438,7 +446,7 @@ test "openDirAbsolute" { defer testing.allocator.free(dir_path); var dir = try fs.openDirAbsolute(dir_path, .{}); - defer dir.close(); + defer dir.close(io); const ino = (try dir.stat()).inode; try testing.expectEqual(tmp_ino, ino); @@ -446,13 +454,15 @@ test "openDirAbsolute" { } test "openDir cwd parent '..'" { + const io = testing.io; + var dir = fs.cwd().openDir("..", .{}) catch |err| { if (native_os == .wasi and err == error.PermissionDenied) { return; // This is okay. WASI disallows escaping from the fs sandbox } return err; }; - defer dir.close(); + defer dir.close(io); } test "openDir non-cwd parent '..'" { @@ -461,14 +471,16 @@ test "openDir non-cwd parent '..'" { else => {}, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var subdir = try tmp.dir.makeOpenPath("subdir", .{}); - defer subdir.close(); + defer subdir.close(io); var dir = try subdir.openDir("..", .{}); - defer dir.close(); + defer dir.close(io); const expected_path = try tmp.dir.realpathAlloc(testing.allocator, "."); defer testing.allocator.free(expected_path); @@ -516,12 +528,14 @@ test "readLinkAbsolute" { } test "Dir.Iterator" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. const file = try tmp_dir.dir.createFile("some_file", .{}); - file.close(); + file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -546,6 +560,8 @@ test "Dir.Iterator" { } test "Dir.Iterator many entries" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); @@ -555,7 +571,7 @@ test "Dir.Iterator many entries" { while (i < num) : (i += 1) { const name = try std.fmt.bufPrint(&buf, "{}", .{i}); const file = try tmp_dir.dir.createFile(name, .{}); - file.close(); + file.close(io); } var arena = ArenaAllocator.init(testing.allocator); @@ -581,12 +597,14 @@ test "Dir.Iterator many entries" { } test "Dir.Iterator twice" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. const file = try tmp_dir.dir.createFile("some_file", .{}); - file.close(); + file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -614,12 +632,14 @@ test "Dir.Iterator twice" { } test "Dir.Iterator reset" { + const io = testing.io; + var tmp_dir = tmpDir(.{ .iterate = true }); defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. const file = try tmp_dir.dir.createFile("some_file", .{}); - file.close(); + file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -650,12 +670,14 @@ test "Dir.Iterator reset" { } test "Dir.Iterator but dir is deleted during iteration" { + const io = testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); // Create directory and setup an iterator for it var subdir = try tmp.dir.makeOpenPath("subdir", .{ .iterate = true }); - defer subdir.close(); + defer subdir.close(io); var iterator = subdir.iterate(); @@ -742,11 +764,13 @@ test "Dir.realpath smoke test" { } test "readFileAlloc" { + const io = testing.io; + var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); var file = try tmp_dir.dir.createFile("test_file", .{ .read = true }); - defer file.close(); + defer file.close(io); const buf1 = try tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(1024)); defer testing.allocator.free(buf1); @@ -815,10 +839,12 @@ test "statFile on dangling symlink" { test "directory operations on files" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; + const test_file_name = try ctx.transformPath("test_file"); var file = try ctx.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try testing.expectError(error.PathAlreadyExists, ctx.dir.makeDir(test_file_name)); try testing.expectError(error.NotDir, ctx.dir.openDir(test_file_name, .{})); @@ -833,7 +859,7 @@ test "directory operations on files" { file = try ctx.dir.openFile(test_file_name, .{}); const stat = try file.stat(); try testing.expectEqual(File.Kind.file, stat.kind); - file.close(); + file.close(io); } }.impl); } @@ -842,6 +868,8 @@ test "file operations on directories" { // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759 if (native_os == .freebsd) return error.SkipZigTest; + const io = testing.io; + try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { const test_dir_name = try ctx.transformPath("test_dir"); @@ -869,7 +897,7 @@ test "file operations on directories" { if (native_os == .wasi and builtin.link_libc) { // wasmtime unexpectedly succeeds here, see https://github.com/ziglang/zig/issues/20747 const handle = try ctx.dir.openFile(test_dir_name, .{ .mode = .read_write }); - handle.close(); + handle.close(io); } else { // Note: The `.mode = .read_write` is necessary to ensure the error occurs on all platforms. // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732 @@ -883,21 +911,23 @@ test "file operations on directories" { // ensure the directory still exists as a sanity check var dir = try ctx.dir.openDir(test_dir_name, .{}); - dir.close(); + dir.close(io); } }.impl); } test "makeOpenPath parent dirs do not exist" { + const io = testing.io; + var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); var dir = try tmp_dir.dir.makeOpenPath("root_dir/parent_dir/some_dir", .{}); - dir.close(); + dir.close(io); // double check that the full directory structure was created var dir_verification = try tmp_dir.dir.openDir("root_dir/parent_dir/some_dir", .{}); - dir_verification.close(); + dir_verification.close(io); } test "deleteDir" { @@ -924,6 +954,7 @@ test "deleteDir" { test "Dir.rename files" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; // Rename on Windows can hit intermittent AccessDenied errors // when certain conditions are true about the host system. // For now, skip this test when the path type is UNC to avoid them. @@ -939,13 +970,13 @@ test "Dir.rename files" { const test_file_name = try ctx.transformPath("test_file"); const renamed_test_file_name = try ctx.transformPath("test_file_renamed"); var file = try ctx.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try ctx.dir.rename(test_file_name, renamed_test_file_name); // Ensure the file was renamed try testing.expectError(error.FileNotFound, ctx.dir.openFile(test_file_name, .{})); file = try ctx.dir.openFile(renamed_test_file_name, .{}); - file.close(); + file.close(io); // Rename to self succeeds try ctx.dir.rename(renamed_test_file_name, renamed_test_file_name); @@ -953,12 +984,12 @@ test "Dir.rename files" { // Rename to existing file succeeds const existing_file_path = try ctx.transformPath("existing_file"); var existing_file = try ctx.dir.createFile(existing_file_path, .{ .read = true }); - existing_file.close(); + existing_file.close(io); try ctx.dir.rename(renamed_test_file_name, existing_file_path); try testing.expectError(error.FileNotFound, ctx.dir.openFile(renamed_test_file_name, .{})); file = try ctx.dir.openFile(existing_file_path, .{}); - file.close(); + file.close(io); } }.impl); } @@ -966,6 +997,8 @@ test "Dir.rename files" { test "Dir.rename directories" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; + // Rename on Windows can hit intermittent AccessDenied errors // when certain conditions are true about the host system. // For now, skip this test when the path type is UNC to avoid them. @@ -985,8 +1018,8 @@ test "Dir.rename directories" { // Put a file in the directory var file = try dir.createFile("test_file", .{ .read = true }); - file.close(); - dir.close(); + file.close(io); + dir.close(io); const test_dir_renamed_again_path = try ctx.transformPath("test_dir_renamed_again"); try ctx.dir.rename(test_dir_renamed_path, test_dir_renamed_again_path); @@ -995,8 +1028,8 @@ test "Dir.rename directories" { try testing.expectError(error.FileNotFound, ctx.dir.openDir(test_dir_renamed_path, .{})); dir = try ctx.dir.openDir(test_dir_renamed_again_path, .{}); file = try dir.openFile("test_file", .{}); - file.close(); - dir.close(); + file.close(io); + dir.close(io); } }.impl); } @@ -1007,6 +1040,8 @@ test "Dir.rename directory onto empty dir" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; + const test_dir_path = try ctx.transformPath("test_dir"); const target_dir_path = try ctx.transformPath("target_dir_path"); @@ -1017,7 +1052,7 @@ test "Dir.rename directory onto empty dir" { // Ensure the directory was renamed try testing.expectError(error.FileNotFound, ctx.dir.openDir(test_dir_path, .{})); var dir = try ctx.dir.openDir(target_dir_path, .{}); - dir.close(); + dir.close(io); } }.impl); } @@ -1028,6 +1063,7 @@ test "Dir.rename directory onto non-empty dir" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const test_dir_path = try ctx.transformPath("test_dir"); const target_dir_path = try ctx.transformPath("target_dir_path"); @@ -1035,15 +1071,15 @@ test "Dir.rename directory onto non-empty dir" { var target_dir = try ctx.dir.makeOpenPath(target_dir_path, .{}); var file = try target_dir.createFile("test_file", .{ .read = true }); - file.close(); - target_dir.close(); + file.close(io); + target_dir.close(io); // Rename should fail with PathAlreadyExists if target_dir is non-empty try testing.expectError(error.PathAlreadyExists, ctx.dir.rename(test_dir_path, target_dir_path)); // Ensure the directory was not renamed var dir = try ctx.dir.openDir(test_dir_path, .{}); - dir.close(); + dir.close(io); } }.impl); } @@ -1054,11 +1090,12 @@ test "Dir.rename file <-> dir" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const test_file_path = try ctx.transformPath("test_file"); const test_dir_path = try ctx.transformPath("test_dir"); var file = try ctx.dir.createFile(test_file_path, .{ .read = true }); - file.close(); + file.close(io); try ctx.dir.makeDir(test_dir_path); try testing.expectError(error.IsDir, ctx.dir.rename(test_file_path, test_dir_path)); try testing.expectError(error.NotDir, ctx.dir.rename(test_dir_path, test_file_path)); @@ -1067,6 +1104,8 @@ test "Dir.rename file <-> dir" { } test "rename" { + const io = testing.io; + var tmp_dir1 = tmpDir(.{}); defer tmp_dir1.cleanup(); @@ -1077,19 +1116,21 @@ test "rename" { const test_file_name = "test_file"; const renamed_test_file_name = "test_file_renamed"; var file = try tmp_dir1.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try fs.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name); // ensure the file was renamed try testing.expectError(error.FileNotFound, tmp_dir1.dir.openFile(test_file_name, .{})); file = try tmp_dir2.dir.openFile(renamed_test_file_name, .{}); - file.close(); + file.close(io); } test "renameAbsolute" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); @@ -1109,7 +1150,7 @@ test "renameAbsolute" { const test_file_name = "test_file"; const renamed_test_file_name = "test_file_renamed"; var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true }); - file.close(); + file.close(io); try fs.renameAbsolute( try fs.path.join(allocator, &.{ base_path, test_file_name }), try fs.path.join(allocator, &.{ base_path, renamed_test_file_name }), @@ -1120,7 +1161,7 @@ test "renameAbsolute" { file = try tmp_dir.dir.openFile(renamed_test_file_name, .{}); const stat = try file.stat(); try testing.expectEqual(File.Kind.file, stat.kind); - file.close(); + file.close(io); // Renaming directories const test_dir_name = "test_dir"; @@ -1134,14 +1175,16 @@ test "renameAbsolute" { // ensure the directory was renamed try testing.expectError(error.FileNotFound, tmp_dir.dir.openDir(test_dir_name, .{})); var dir = try tmp_dir.dir.openDir(renamed_test_dir_name, .{}); - dir.close(); + dir.close(io); } test "openSelfExe" { if (native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + const self_exe_file = try std.fs.openSelfExe(.{}); - self_exe_file.close(); + self_exe_file.close(io); } test "selfExePath" { @@ -1155,13 +1198,15 @@ test "selfExePath" { } test "deleteTree does not follow symlinks" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.makePath("b"); { var a = try tmp.dir.makeOpenPath("a", .{}); - defer a.close(); + defer a.close(io); try setupSymlink(a, "../b", "b", .{ .is_directory = true }); } @@ -1257,27 +1302,31 @@ test "makePath but sub_path contains pre-existing file" { try testing.expectError(error.NotDir, tmp.dir.makePath("foo/bar/baz")); } -fn expectDir(dir: Dir, path: []const u8) !void { +fn expectDir(io: Io, dir: Dir, path: []const u8) !void { var d = try dir.openDir(path, .{}); - d.close(); + d.close(io); } test "makepath existing directories" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.makeDir("A"); var tmpA = try tmp.dir.openDir("A", .{}); - defer tmpA.close(); + defer tmpA.close(io); try tmpA.makeDir("B"); const testPath = "A" ++ fs.path.sep_str ++ "B" ++ fs.path.sep_str ++ "C"; try tmp.dir.makePath(testPath); - try expectDir(tmp.dir, testPath); + try expectDir(io, tmp.dir, testPath); } test "makepath through existing valid symlink" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1286,10 +1335,12 @@ test "makepath through existing valid symlink" { try tmp.dir.makePath("working-symlink" ++ fs.path.sep_str ++ "in-realfolder"); - try expectDir(tmp.dir, "realfolder" ++ fs.path.sep_str ++ "in-realfolder"); + try expectDir(io, tmp.dir, "realfolder" ++ fs.path.sep_str ++ "in-realfolder"); } test "makepath relative walks" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1305,21 +1356,23 @@ test "makepath relative walks" { .windows => { // On Windows, .. is resolved before passing the path to NtCreateFile, // meaning everything except `first/C` drops out. - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "C"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C"); try testing.expectError(error.FileNotFound, tmp.dir.access("second", .{})); try testing.expectError(error.FileNotFound, tmp.dir.access("third", .{})); }, else => { - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "A"); - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "B"); - try expectDir(tmp.dir, "first" ++ fs.path.sep_str ++ "C"); - try expectDir(tmp.dir, "second"); - try expectDir(tmp.dir, "third"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "A"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "B"); + try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C"); + try expectDir(io, tmp.dir, "second"); + try expectDir(io, tmp.dir, "third"); }, } } test "makepath ignores '.'" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1337,14 +1390,14 @@ test "makepath ignores '.'" { try tmp.dir.makePath(dotPath); - try expectDir(tmp.dir, expectedPath); + try expectDir(io, tmp.dir, expectedPath); } -fn testFilenameLimits(iterable_dir: Dir, maxed_filename: []const u8) !void { +fn testFilenameLimits(io: Io, iterable_dir: Dir, maxed_filename: []const u8) !void { // setup, create a dir and a nested file both with maxed filenames, and walk the dir { var maxed_dir = try iterable_dir.makeOpenPath(maxed_filename, .{}); - defer maxed_dir.close(); + defer maxed_dir.close(io); try maxed_dir.writeFile(.{ .sub_path = maxed_filename, .data = "" }); @@ -1364,6 +1417,8 @@ fn testFilenameLimits(iterable_dir: Dir, maxed_filename: []const u8) !void { } test "max file name component lengths" { + const io = testing.io; + var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -1371,16 +1426,16 @@ test "max file name component lengths" { // U+FFFF is the character with the largest code point that is encoded as a single // UTF-16 code unit, so Windows allows for NAME_MAX of them. const maxed_windows_filename = ("\u{FFFF}".*) ** windows.NAME_MAX; - try testFilenameLimits(tmp.dir, &maxed_windows_filename); + try testFilenameLimits(io, tmp.dir, &maxed_windows_filename); } else if (native_os == .wasi) { // On WASI, the maxed filename depends on the host OS, so in order for this test to // work on any host, we need to use a length that will work for all platforms // (i.e. the minimum max_name_bytes of all supported platforms). const maxed_wasi_filename = [_]u8{'1'} ** 255; - try testFilenameLimits(tmp.dir, &maxed_wasi_filename); + try testFilenameLimits(io, tmp.dir, &maxed_wasi_filename); } else { const maxed_ascii_filename = [_]u8{'1'} ** std.fs.max_name_bytes; - try testFilenameLimits(tmp.dir, &maxed_ascii_filename); + try testFilenameLimits(io, tmp.dir, &maxed_ascii_filename); } } @@ -1399,7 +1454,7 @@ test "writev, readv" { var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); var writer = src_file.writerStreaming(&.{}); @@ -1429,7 +1484,7 @@ test "pwritev, preadv" { var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); var writer = src_file.writer(&.{}); @@ -1459,7 +1514,7 @@ test "setEndPos" { const file_name = "afile.txt"; try tmp.dir.writeFile(.{ .sub_path = file_name, .data = "ninebytes" }); const f = try tmp.dir.openFile(file_name, .{ .mode = .read_write }); - defer f.close(); + defer f.close(io); const initial_size = try f.getEndPos(); var buffer: [32]u8 = undefined; @@ -1522,21 +1577,21 @@ test "sendfile" { try tmp.dir.makePath("os_test_tmp"); var dir = try tmp.dir.openDir("os_test_tmp", .{}); - defer dir.close(); + defer dir.close(io); const line1 = "line1\n"; const line2 = "second line\n"; var vecs = [_][]const u8{ line1, line2 }; var src_file = try dir.createFile("sendfile1.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); { var fw = src_file.writer(&.{}); try fw.interface.writeVecAll(&vecs); } var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true }); - defer dest_file.close(); + defer dest_file.close(io); const header1 = "header1\n"; const header2 = "second header\n"; @@ -1569,15 +1624,15 @@ test "sendfile with buffered data" { try tmp.dir.makePath("os_test_tmp"); var dir = try tmp.dir.openDir("os_test_tmp", .{}); - defer dir.close(); + defer dir.close(io); var src_file = try dir.createFile("sendfile1.txt", .{ .read = true }); - defer src_file.close(); + defer src_file.close(io); try src_file.writeAll("AAAABBBB"); var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true }); - defer dest_file.close(); + defer dest_file.close(io); var src_buffer: [32]u8 = undefined; var file_reader = src_file.reader(io, &src_buffer); @@ -1659,10 +1714,11 @@ test "open file with exclusive nonblocking lock twice" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); const file1 = try ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); - defer file1.close(); + defer file1.close(io); const file2 = ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); @@ -1675,10 +1731,11 @@ test "open file with shared and exclusive nonblocking lock" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); const file1 = try ctx.dir.createFile(filename, .{ .lock = .shared, .lock_nonblocking = true }); - defer file1.close(); + defer file1.close(io); const file2 = ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); @@ -1691,10 +1748,11 @@ test "open file with exclusive and shared nonblocking lock" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); const file1 = try ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); - defer file1.close(); + defer file1.close(io); const file2 = ctx.dir.createFile(filename, .{ .lock = .shared, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); @@ -1707,10 +1765,11 @@ test "open file with exclusive lock twice, make sure second lock waits" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("file_lock_test.txt"); const file = try ctx.dir.createFile(filename, .{ .lock = .exclusive }); - errdefer file.close(); + errdefer file.close(io); const S = struct { fn checkFn(dir: *fs.Dir, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void { @@ -1718,7 +1777,7 @@ test "open file with exclusive lock twice, make sure second lock waits" { const file1 = try dir.createFile(path, .{ .lock = .exclusive }); locked.set(); - file1.close(); + file1.close(io); } }; @@ -1739,7 +1798,7 @@ test "open file with exclusive lock twice, make sure second lock waits" { try testing.expectError(error.Timeout, locked.timedWait(10 * std.time.ns_per_ms)); // Release the file lock which should unlock the thread to lock it and set the locked event. - file.close(); + file.close(io); locked.wait(); } }.impl); @@ -1748,6 +1807,8 @@ test "open file with exclusive lock twice, make sure second lock waits" { test "open file with exclusive nonblocking lock twice (absolute paths)" { if (native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var random_bytes: [12]u8 = undefined; std.crypto.random.bytes(&random_bytes); @@ -1774,18 +1835,19 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { .lock = .exclusive, .lock_nonblocking = true, }); - file1.close(); + file1.close(io); try testing.expectError(error.WouldBlock, file2); } test "read from locked file" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const filename = try ctx.transformPath("read_lock_file_test.txt"); { const f = try ctx.dir.createFile(filename, .{ .read = true }); - defer f.close(); + defer f.close(io); var buffer: [1]u8 = undefined; _ = try f.read(&buffer); } @@ -1794,9 +1856,9 @@ test "read from locked file" { .read = true, .lock = .exclusive, }); - defer f.close(); + defer f.close(io); const f2 = try ctx.dir.openFile(filename, .{}); - defer f2.close(); + defer f2.close(io); var buffer: [1]u8 = undefined; if (builtin.os.tag == .windows) { try std.testing.expectError(error.LockViolation, f2.read(&buffer)); @@ -1809,6 +1871,8 @@ test "read from locked file" { } test "walker" { + const io = testing.io; + var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -1857,13 +1921,15 @@ test "walker" { }; // make sure that the entry.dir is the containing dir var entry_dir = try entry.dir.openDir(entry.basename, .{}); - defer entry_dir.close(); + defer entry_dir.close(io); num_walked += 1; } try testing.expectEqual(expected_paths.kvs.len, num_walked); } test "selective walker, skip entries that start with ." { + const io = testing.io; + var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -1923,7 +1989,7 @@ test "selective walker, skip entries that start with ." { // make sure that the entry.dir is the containing dir var entry_dir = try entry.dir.openDir(entry.basename, .{}); - defer entry_dir.close(); + defer entry_dir.close(io); num_walked += 1; } try testing.expectEqual(expected_paths.kvs.len, num_walked); @@ -1968,16 +2034,16 @@ test "'.' and '..' in fs.Dir functions" { try ctx.dir.makeDir(subdir_path); try ctx.dir.access(subdir_path, .{}); var created_subdir = try ctx.dir.openDir(subdir_path, .{}); - created_subdir.close(); + created_subdir.close(io); const created_file = try ctx.dir.createFile(file_path, .{}); - created_file.close(); + created_file.close(io); try ctx.dir.access(file_path, .{}); try ctx.dir.copyFile(file_path, ctx.dir, copy_path, .{}); try ctx.dir.rename(copy_path, rename_path); const renamed_file = try ctx.dir.openFile(rename_path, .{}); - renamed_file.close(); + renamed_file.close(io); try ctx.dir.deleteFile(rename_path); try ctx.dir.writeFile(.{ .sub_path = update_path, .data = "something" }); @@ -1994,6 +2060,8 @@ test "'.' and '..' in absolute functions" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -2007,11 +2075,11 @@ test "'.' and '..' in absolute functions" { try fs.makeDirAbsolute(subdir_path); try fs.accessAbsolute(subdir_path, .{}); var created_subdir = try fs.openDirAbsolute(subdir_path, .{}); - created_subdir.close(); + created_subdir.close(io); const created_file_path = try fs.path.join(allocator, &.{ subdir_path, "../file" }); const created_file = try fs.createFileAbsolute(created_file_path, .{}); - created_file.close(); + created_file.close(io); try fs.accessAbsolute(created_file_path, .{}); const copied_file_path = try fs.path.join(allocator, &.{ subdir_path, "../copy" }); @@ -2019,7 +2087,7 @@ test "'.' and '..' in absolute functions" { const renamed_file_path = try fs.path.join(allocator, &.{ subdir_path, "../rename" }); try fs.renameAbsolute(copied_file_path, renamed_file_path); const renamed_file = try fs.openFileAbsolute(renamed_file_path, .{}); - renamed_file.close(); + renamed_file.close(io); try fs.deleteFileAbsolute(renamed_file_path); try fs.deleteDirAbsolute(subdir_path); @@ -2029,11 +2097,13 @@ test "chmod" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const file = try tmp.dir.createFile("test_file", .{ .mode = 0o600 }); - defer file.close(); + defer file.close(io); try testing.expectEqual(@as(File.Mode, 0o600), (try file.stat()).mode & 0o7777); try file.chmod(0o644); @@ -2041,7 +2111,7 @@ test "chmod" { try tmp.dir.makeDir("test_dir"); var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true }); - defer dir.close(); + defer dir.close(io); try dir.chmod(0o700); try testing.expectEqual(@as(File.Mode, 0o700), (try dir.stat()).mode & 0o7777); @@ -2051,17 +2121,19 @@ test "chown" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const file = try tmp.dir.createFile("test_file", .{}); - defer file.close(); + defer file.close(io); try file.chown(null, null); try tmp.dir.makeDir("test_dir"); var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true }); - defer dir.close(); + defer dir.close(io); try dir.chown(null, null); } @@ -2157,7 +2229,7 @@ test "read file non vectored" { const contents = "hello, world!\n"; const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); { var file_writer: std.fs.File.Writer = .init(file, &.{}); try file_writer.interface.writeAll(contents); @@ -2189,7 +2261,7 @@ test "seek keeping partial buffer" { const contents = "0123456789"; const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); - defer file.close(); + defer file.close(io); { var file_writer: std.fs.File.Writer = .init(file, &.{}); try file_writer.interface.writeAll(contents); @@ -2231,7 +2303,7 @@ test "seekBy" { try tmp_dir.dir.writeFile(.{ .sub_path = "blah.txt", .data = "let's test seekBy" }); const f = try tmp_dir.dir.openFile("blah.txt", .{ .mode = .read_only }); - defer f.close(); + defer f.close(io); var reader = f.readerStreaming(io, &.{}); try reader.seekBy(2); @@ -2250,7 +2322,7 @@ test "seekTo flushes buffered data" { const contents = "data"; const file = try tmp.dir.createFile("seek.bin", .{ .read = true }); - defer file.close(); + defer file.close(io); { var buf: [16]u8 = undefined; var file_writer = std.fs.File.writer(file, &buf); @@ -2277,9 +2349,9 @@ test "File.Writer sendfile with buffered contents" { { try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" }); const in = try tmp_dir.dir.openFile("a", .{}); - defer in.close(); + defer in.close(io); const out = try tmp_dir.dir.createFile("b", .{}); - defer out.close(); + defer out.close(io); var in_buf: [2]u8 = undefined; var in_r = in.reader(io, &in_buf); @@ -2294,7 +2366,7 @@ test "File.Writer sendfile with buffered contents" { } var check = try tmp_dir.dir.openFile("b", .{}); - defer check.close(); + defer check.close(io); var check_buf: [4]u8 = undefined; var check_r = check.reader(io, &check_buf); try testing.expectEqualStrings("abcd", try check_r.interface.take(4)); diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 10ab23f476..7f8c9827ef 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1473,6 +1473,8 @@ pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{N /// /// This function is threadsafe. pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connection { + const io = client.io; + if (client.connection_pool.findConnection(.{ .host = path, .port = 0, @@ -1485,7 +1487,7 @@ pub fn connectUnix(client: *Client, path: []const u8) ConnectUnixError!*Connecti conn.* = .{ .data = undefined }; const stream = try Io.net.connectUnixSocket(path); - errdefer stream.close(); + errdefer stream.close(io); conn.data = .{ .stream = stream, diff --git a/lib/std/os/linux/IoUring.zig b/lib/std/os/linux/IoUring.zig index c927dab376..e4a5bd3738 100644 --- a/lib/std/os/linux/IoUring.zig +++ b/lib/std/os/linux/IoUring.zig @@ -1,13 +1,16 @@ const IoUring = @This(); -const std = @import("std"); + const builtin = @import("builtin"); +const is_linux = builtin.os.tag == .linux; + +const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const mem = std.mem; const net = std.Io.net; const posix = std.posix; const linux = std.os.linux; const testing = std.testing; -const is_linux = builtin.os.tag == .linux; const page_size_min = std.heap.page_size_min; fd: linux.fd_t = -1, @@ -1975,6 +1978,8 @@ test "readv" { test "writev/fsync/readv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(4, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -1987,7 +1992,7 @@ test "writev/fsync/readv" { const path = "test_io_uring_writev_fsync_readv"; const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); - defer file.close(); + defer file.close(io); const fd = file.handle; const buffer_write = [_]u8{42} ** 128; @@ -2045,6 +2050,8 @@ test "writev/fsync/readv" { test "write/read" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(2, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2056,7 +2063,7 @@ test "write/read" { defer tmp.cleanup(); const path = "test_io_uring_write_read"; const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); - defer file.close(); + defer file.close(io); const fd = file.handle; const buffer_write = [_]u8{97} ** 20; @@ -2092,6 +2099,8 @@ test "write/read" { test "splice/read" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(4, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2102,12 +2111,12 @@ test "splice/read" { var tmp = std.testing.tmpDir(.{}); const path_src = "test_io_uring_splice_src"; const file_src = try tmp.dir.createFile(path_src, .{ .read = true, .truncate = true }); - defer file_src.close(); + defer file_src.close(io); const fd_src = file_src.handle; const path_dst = "test_io_uring_splice_dst"; const file_dst = try tmp.dir.createFile(path_dst, .{ .read = true, .truncate = true }); - defer file_dst.close(); + defer file_dst.close(io); const fd_dst = file_dst.handle; const buffer_write = [_]u8{97} ** 20; @@ -2163,6 +2172,8 @@ test "splice/read" { test "write_fixed/read_fixed" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(2, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2175,7 +2186,7 @@ test "write_fixed/read_fixed" { const path = "test_io_uring_write_read_fixed"; const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); - defer file.close(); + defer file.close(io); const fd = file.handle; var raw_buffers: [2][11]u8 = undefined; @@ -2282,6 +2293,8 @@ test "openat" { test "close" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2294,7 +2307,7 @@ test "close" { const path = "test_io_uring_close"; const file = try tmp.dir.createFile(path, .{}); - errdefer file.close(); + errdefer file.close(io); const sqe_close = try ring.close(0x44444444, file.handle); try testing.expectEqual(linux.IORING_OP.CLOSE, sqe_close.opcode); @@ -2313,6 +2326,8 @@ test "close" { test "accept/connect/send/recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2321,7 +2336,7 @@ test "accept/connect/send/recv" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); const buffer_send = [_]u8{ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 }; var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; @@ -2573,6 +2588,8 @@ test "timeout_remove" { test "accept/connect/recv/link_timeout" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2581,7 +2598,7 @@ test "accept/connect/recv/link_timeout" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; @@ -2622,6 +2639,8 @@ test "accept/connect/recv/link_timeout" { test "fallocate" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2634,7 +2653,7 @@ test "fallocate" { const path = "test_io_uring_fallocate"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2668,6 +2687,8 @@ test "fallocate" { test "statx" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2679,7 +2700,7 @@ test "statx" { defer tmp.cleanup(); const path = "test_io_uring_statx"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2725,6 +2746,8 @@ test "statx" { test "accept/connect/recv/cancel" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2733,7 +2756,7 @@ test "accept/connect/recv/cancel" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; @@ -2929,6 +2952,8 @@ test "shutdown" { test "renameat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -2945,7 +2970,7 @@ test "renameat" { // Write old file with data const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 }); - defer old_file.close(); + defer old_file.close(io); try old_file.writeAll("hello"); // Submit renameat @@ -2987,6 +3012,8 @@ test "renameat" { test "unlinkat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3002,7 +3029,7 @@ test "unlinkat" { // Write old file with data const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); // Submit unlinkat @@ -3083,6 +3110,8 @@ test "mkdirat" { test "symlinkat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3097,7 +3126,7 @@ test "symlinkat" { const link_path = "test_io_uring_symlinkat_link"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); // Submit symlinkat @@ -3131,6 +3160,8 @@ test "symlinkat" { test "linkat" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(1, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3147,7 +3178,7 @@ test "linkat" { // Write file with data const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 }); - defer first_file.close(); + defer first_file.close(io); try first_file.writeAll("hello"); // Submit linkat @@ -3407,6 +3438,8 @@ test "remove_buffers" { test "provide_buffers: accept/connect/send/recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3443,7 +3476,7 @@ test "provide_buffers: accept/connect/send/recv" { } const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); // Do 4 send on the socket @@ -3696,6 +3729,8 @@ test "accept multishot" { test "accept/connect/send_zc/recv" { try skipKernelLessThan(.{ .major = 6, .minor = 0, .patch = 0 }); + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -3704,7 +3739,7 @@ test "accept/connect/send_zc/recv" { defer ring.deinit(); const socket_test_harness = try createSocketTestHarness(&ring); - defer socket_test_harness.close(); + defer socket_test_harness.close(io); const buffer_send = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe }; var buffer_recv = [_]u8{0} ** 10; @@ -4105,6 +4140,8 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void { test BufferGroup { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + // Init IoUring var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, @@ -4132,7 +4169,7 @@ test BufferGroup { // Create client/server fds const fds = try createSocketTestHarness(&ring); - defer fds.close(); + defer fds.close(io); const data = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe }; // Client sends data @@ -4170,6 +4207,8 @@ test BufferGroup { test "ring mapped buffers recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -4196,7 +4235,7 @@ test "ring mapped buffers recv" { // create client/server fds const fds = try createSocketTestHarness(&ring); - defer fds.close(); + defer fds.close(io); // for random user_data in sqe/cqe var Rnd = std.Random.DefaultPrng.init(std.testing.random_seed); @@ -4259,6 +4298,8 @@ test "ring mapped buffers recv" { test "ring mapped buffers multishot recv" { if (!is_linux) return error.SkipZigTest; + const io = testing.io; + var ring = IoUring.init(16, 0) catch |err| switch (err) { error.SystemOutdated => return error.SkipZigTest, error.PermissionDenied => return error.SkipZigTest, @@ -4285,7 +4326,7 @@ test "ring mapped buffers multishot recv" { // create client/server fds const fds = try createSocketTestHarness(&ring); - defer fds.close(); + defer fds.close(io); // for random user_data in sqe/cqe var Rnd = std.Random.DefaultPrng.init(std.testing.random_seed); diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 7399907477..39606ddfac 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -12,12 +12,14 @@ const fs = std.fs; test "fallocate" { if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/30220 + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const path = "test_fallocate"; const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); - defer file.close(); + defer file.close(io); try expect((try file.stat()).size == 0); @@ -77,12 +79,14 @@ test "timer" { } test "statx" { + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "just_a_temporary_file.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); var buf: linux.Statx = undefined; switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .BASIC_STATS, &buf))) { @@ -111,12 +115,14 @@ test "user and group ids" { } test "fadvise" { + const io = std.testing.io; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const tmp_file_name = "temp_posix_fadvise.txt"; var file = try tmp.dir.createFile(tmp_file_name, .{}); - defer file.close(); + defer file.close(io); var buf: [2048]u8 = undefined; try file.writeAll(&buf); diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 3bb5e64c73..8889e50ea3 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -148,6 +148,8 @@ test "linkat with different directories" { else => return error.SkipZigTest, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -163,10 +165,10 @@ test "linkat with different directories" { try posix.linkat(tmp.dir.fd, target_name, subdir.fd, link_name, 0); const efd = try tmp.dir.openFile(target_name, .{}); - defer efd.close(); + defer efd.close(io); const nfd = try subdir.openFile(link_name, .{}); - defer nfd.close(); + defer nfd.close(io); { const eino, _ = try getLinkInfo(efd.handle); @@ -381,6 +383,8 @@ test "mmap" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -413,7 +417,7 @@ test "mmap" { // Create a file used for testing mmap() calls with a file descriptor { const file = try tmp.dir.createFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); var stream = file.writer(&.{}); @@ -426,7 +430,7 @@ test "mmap" { // Map the whole file { const file = try tmp.dir.openFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); const data = try posix.mmap( null, @@ -451,7 +455,7 @@ test "mmap" { // Map the upper half of the file { const file = try tmp.dir.openFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); const data = try posix.mmap( null, @@ -476,13 +480,15 @@ test "fcntl" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); // Note: The test assumes createFile opens the file with CLOEXEC { @@ -526,12 +532,14 @@ test "fsync" { else => return error.SkipZigTest, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); const test_out_file = "os_tmp_test"; const file = try tmp.dir.createFile(test_out_file, .{}); - defer file.close(); + defer file.close(io); try posix.fsync(file.handle); try posix.fdatasync(file.handle); @@ -646,22 +654,24 @@ test "dup & dup2" { else => return error.SkipZigTest, } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); { var file = try tmp.dir.createFile("os_dup_test", .{}); - defer file.close(); + defer file.close(io); var duped = std.fs.File{ .handle = try posix.dup(file.handle) }; - defer duped.close(); + defer duped.close(io); try duped.writeAll("dup"); // Tests aren't run in parallel so using the next fd shouldn't be an issue. const new_fd = duped.handle + 1; try posix.dup2(file.handle, new_fd); var dup2ed = std.fs.File{ .handle = new_fd }; - defer dup2ed.close(); + defer dup2ed.close(io); try dup2ed.writeAll("dup2"); } @@ -687,11 +697,13 @@ test "getppid" { test "writev longer than IOV_MAX" { if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("pwritev", .{}); - defer file.close(); + defer file.close(io); const iovecs = [_]posix.iovec_const{.{ .base = "a", .len = 1 }} ** (posix.IOV_MAX + 1); const amt = try file.writev(&iovecs); @@ -709,12 +721,14 @@ test "POSIX file locking with fcntl" { return error.SkipZigTest; } + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); // Create a temporary lock file var file = try tmp.dir.createFile("lock", .{ .read = true }); - defer file.close(); + defer file.close(io); try file.setEndPos(2); const fd = file.handle; @@ -905,21 +919,25 @@ test "timerfd" { } test "isatty" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("foo", .{}); - defer file.close(); + defer file.close(io); try expectEqual(posix.isatty(file.handle), false); } test "pread with empty buffer" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("pread_empty", .{ .read = true }); - defer file.close(); + defer file.close(io); const bytes = try a.alloc(u8, 0); defer a.free(bytes); @@ -929,11 +947,13 @@ test "pread with empty buffer" { } test "write with empty buffer" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("write_empty", .{}); - defer file.close(); + defer file.close(io); const bytes = try a.alloc(u8, 0); defer a.free(bytes); @@ -943,11 +963,13 @@ test "write with empty buffer" { } test "pwrite with empty buffer" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); var file = try tmp.dir.createFile("pwrite_empty", .{}); - defer file.close(); + defer file.close(io); const bytes = try a.alloc(u8, 0); defer a.free(bytes); diff --git a/lib/std/process.zig b/lib/std/process.zig index a0a26c766f..a8dede6ad4 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1,12 +1,14 @@ -const std = @import("std.zig"); const builtin = @import("builtin"); +const native_os = builtin.os.tag; + +const std = @import("std.zig"); +const Io = std.Io; const fs = std.fs; const mem = std.mem; const math = std.math; -const Allocator = mem.Allocator; +const Allocator = std.mem.Allocator; const assert = std.debug.assert; const testing = std.testing; -const native_os = builtin.os.tag; const posix = std.posix; const windows = std.os.windows; const unicode = std.unicode; @@ -1571,9 +1573,9 @@ pub fn getUserInfo(name: []const u8) !UserInfo { /// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else /// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. -pub fn posixGetUserInfo(name: []const u8) !UserInfo { +pub fn posixGetUserInfo(io: Io, name: []const u8) !UserInfo { const file = try std.fs.openFileAbsolute("/etc/passwd", .{}); - defer file.close(); + defer file.close(io); var buffer: [4096]u8 = undefined; var file_reader = file.reader(&buffer); return posixGetUserInfoPasswdStream(name, &file_reader.interface) catch |err| switch (err) { diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 6dad72df44..be3026ff10 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -4,6 +4,7 @@ const builtin = @import("builtin"); const native_os = builtin.os.tag; const std = @import("../std.zig"); +const Io = std.Io; const unicode = std.unicode; const fs = std.fs; const process = std.process; @@ -277,17 +278,17 @@ pub fn spawnAndWait(self: *ChildProcess) SpawnError!Term { } /// Forcibly terminates child process and then cleans up all resources. -pub fn kill(self: *ChildProcess) !Term { +pub fn kill(self: *ChildProcess, io: Io) !Term { if (native_os == .windows) { - return self.killWindows(1); + return self.killWindows(io, 1); } else { - return self.killPosix(); + return self.killPosix(io); } } -pub fn killWindows(self: *ChildProcess, exit_code: windows.UINT) !Term { +pub fn killWindows(self: *ChildProcess, io: Io, exit_code: windows.UINT) !Term { if (self.term) |term| { - self.cleanupStreams(); + self.cleanupStreams(io); return term; } @@ -303,20 +304,20 @@ pub fn killWindows(self: *ChildProcess, exit_code: windows.UINT) !Term { }, else => return err, }; - try self.waitUnwrappedWindows(); + try self.waitUnwrappedWindows(io); return self.term.?; } -pub fn killPosix(self: *ChildProcess) !Term { +pub fn killPosix(self: *ChildProcess, io: Io) !Term { if (self.term) |term| { - self.cleanupStreams(); + self.cleanupStreams(io); return term; } posix.kill(self.id, posix.SIG.TERM) catch |err| switch (err) { error.ProcessNotFound => return error.AlreadyTerminated, else => return err, }; - self.waitUnwrappedPosix(); + self.waitUnwrappedPosix(io); return self.term.?; } @@ -354,15 +355,15 @@ pub fn waitForSpawn(self: *ChildProcess) SpawnError!void { } /// Blocks until child process terminates and then cleans up all resources. -pub fn wait(self: *ChildProcess) WaitError!Term { +pub fn wait(self: *ChildProcess, io: Io) WaitError!Term { try self.waitForSpawn(); // report spawn errors if (self.term) |term| { - self.cleanupStreams(); + self.cleanupStreams(io); return term; } switch (native_os) { - .windows => try self.waitUnwrappedWindows(), - else => self.waitUnwrappedPosix(), + .windows => try self.waitUnwrappedWindows(io), + else => self.waitUnwrappedPosix(io), } self.id = undefined; return self.term.?; @@ -474,7 +475,7 @@ pub fn run(args: struct { }; } -fn waitUnwrappedWindows(self: *ChildProcess) WaitError!void { +fn waitUnwrappedWindows(self: *ChildProcess, io: Io) WaitError!void { const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false); self.term = @as(SpawnError!Term, x: { @@ -492,11 +493,11 @@ fn waitUnwrappedWindows(self: *ChildProcess) WaitError!void { posix.close(self.id); posix.close(self.thread_handle); - self.cleanupStreams(); + self.cleanupStreams(io); return result; } -fn waitUnwrappedPosix(self: *ChildProcess) void { +fn waitUnwrappedPosix(self: *ChildProcess, io: Io) void { const res: posix.WaitPidResult = res: { if (self.request_resource_usage_statistics) { switch (native_os) { @@ -527,7 +528,7 @@ fn waitUnwrappedPosix(self: *ChildProcess) void { break :res posix.waitpid(self.id, 0); }; const status = res.status; - self.cleanupStreams(); + self.cleanupStreams(io); self.handleWaitResult(status); } @@ -535,17 +536,17 @@ fn handleWaitResult(self: *ChildProcess, status: u32) void { self.term = statusToTerm(status); } -fn cleanupStreams(self: *ChildProcess) void { +fn cleanupStreams(self: *ChildProcess, io: Io) void { if (self.stdin) |*stdin| { - stdin.close(); + stdin.close(io); self.stdin = null; } if (self.stdout) |*stdout| { - stdout.close(); + stdout.close(io); self.stdout = null; } if (self.stderr) |*stderr| { - stderr.close(); + stderr.close(io); self.stderr = null; } } diff --git a/lib/std/tar.zig b/lib/std/tar.zig index bf96aed35c..d861314fec 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -16,6 +16,7 @@ //! pax reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13 const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const testing = std.testing; @@ -302,7 +303,7 @@ pub const FileKind = enum { /// Iterator over entries in the tar file represented by reader. pub const Iterator = struct { - reader: *std.Io.Reader, + reader: *Io.Reader, diagnostics: ?*Diagnostics = null, // buffers for heeader and file attributes @@ -328,7 +329,7 @@ pub const Iterator = struct { /// Iterates over files in tar archive. /// `next` returns each file in tar archive. - pub fn init(reader: *std.Io.Reader, options: Options) Iterator { + pub fn init(reader: *Io.Reader, options: Options) Iterator { return .{ .reader = reader, .diagnostics = options.diagnostics, @@ -473,7 +474,7 @@ pub const Iterator = struct { return null; } - pub fn streamRemaining(it: *Iterator, file: File, w: *std.Io.Writer) std.Io.Reader.StreamError!void { + pub fn streamRemaining(it: *Iterator, file: File, w: *Io.Writer) Io.Reader.StreamError!void { try it.reader.streamExact64(w, file.size); it.unread_file_bytes = 0; } @@ -499,14 +500,14 @@ const pax_max_size_attr_len = 64; pub const PaxIterator = struct { size: usize, // cumulative size of all pax attributes - reader: *std.Io.Reader, + reader: *Io.Reader, const Self = @This(); const Attribute = struct { kind: PaxAttributeKind, len: usize, // length of the attribute value - reader: *std.Io.Reader, // reader positioned at value start + reader: *Io.Reader, // reader positioned at value start // Copies pax attribute value into destination buffer. // Must be called with destination buffer of size at least Attribute.len. @@ -573,13 +574,13 @@ pub const PaxIterator = struct { } // Checks that each record ends with new line. - fn validateAttributeEnding(reader: *std.Io.Reader) !void { + fn validateAttributeEnding(reader: *Io.Reader) !void { if (try reader.takeByte() != '\n') return error.PaxInvalidAttributeEnd; } }; /// Saves tar file content to the file systems. -pub fn pipeToFileSystem(dir: std.fs.Dir, reader: *std.Io.Reader, options: PipeOptions) !void { +pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOptions) !void { var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined; var file_contents_buffer: [1024]u8 = undefined; @@ -610,7 +611,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: *std.Io.Reader, options: PipeOp }, .file => { if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| { - defer fs_file.close(); + defer fs_file.close(io); var file_writer = fs_file.writer(&file_contents_buffer); try it.streamRemaining(file, &file_writer.interface); try file_writer.interface.flush(); @@ -637,7 +638,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: *std.Io.Reader, options: PipeOp } } -fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8, mode: std.fs.File.Mode) !std.fs.File { +fn createDirAndFile(dir: Io.Dir, file_name: []const u8, mode: Io.File.Mode) !Io.File { const fs_file = dir.createFile(file_name, .{ .exclusive = true, .mode = mode }) catch |err| { if (err == error.FileNotFound) { if (std.fs.path.dirname(file_name)) |dir_name| { @@ -651,7 +652,7 @@ fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8, mode: std.fs.File.Mo } // Creates a symbolic link at path `file_name` which points to `link_name`. -fn createDirAndSymlink(dir: std.fs.Dir, link_name: []const u8, file_name: []const u8) !void { +fn createDirAndSymlink(dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void { dir.symLink(link_name, file_name, .{}) catch |err| { if (err == error.FileNotFound) { if (std.fs.path.dirname(file_name)) |dir_name| { @@ -783,7 +784,7 @@ test PaxIterator { var buffer: [1024]u8 = undefined; outer: for (cases) |case| { - var reader: std.Io.Reader = .fixed(case.data); + var reader: Io.Reader = .fixed(case.data); var it: PaxIterator = .{ .size = case.data.len, .reader = &reader, @@ -874,13 +875,15 @@ test "header parse mode" { } test "create file and symlink" { + const io = testing.io; + var root = testing.tmpDir(.{}); defer root.cleanup(); var file = try createDirAndFile(root.dir, "file1", default_mode); - file.close(); + file.close(io); file = try createDirAndFile(root.dir, "a/b/c/file2", default_mode); - file.close(); + file.close(io); createDirAndSymlink(root.dir, "a/b/c/file2", "symlink1") catch |err| { // On Windows when developer mode is not enabled @@ -892,7 +895,7 @@ test "create file and symlink" { // Danglink symlnik, file created later try createDirAndSymlink(root.dir, "../../../g/h/i/file4", "j/k/l/symlink3"); file = try createDirAndFile(root.dir, "g/h/i/file4", default_mode); - file.close(); + file.close(io); } test Iterator { @@ -916,7 +919,7 @@ test Iterator { // example/empty/ const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); // User provided buffers to the iterator var file_name_buffer: [std.fs.max_path_bytes]u8 = undefined; @@ -942,7 +945,7 @@ test Iterator { .file => { try testing.expectEqualStrings("example/a/file", file.name); var buf: [16]u8 = undefined; - var w: std.Io.Writer = .fixed(&buf); + var w: Io.Writer = .fixed(&buf); try it.streamRemaining(file, &w); try testing.expectEqualStrings("content\n", w.buffered()); }, @@ -955,6 +958,7 @@ test Iterator { } test pipeToFileSystem { + const io = testing.io; // Example tar file is created from this tree structure: // $ tree example // example @@ -975,14 +979,14 @@ test pipeToFileSystem { // example/empty/ const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .follow_symlinks = false }); defer tmp.cleanup(); const dir = tmp.dir; // Save tar from reader to the file system `dir` - pipeToFileSystem(dir, &reader, .{ + pipeToFileSystem(io, dir, &reader, .{ .mode_mode = .ignore, .strip_components = 1, .exclude_empty_directories = true, @@ -1005,8 +1009,9 @@ test pipeToFileSystem { } test "pipeToFileSystem root_dir" { + const io = testing.io; const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); // with strip_components = 1 { @@ -1015,7 +1020,7 @@ test "pipeToFileSystem root_dir" { var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 1, .diagnostics = &diagnostics, }) catch |err| { @@ -1037,7 +1042,7 @@ test "pipeToFileSystem root_dir" { var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 0, .diagnostics = &diagnostics, }) catch |err| { @@ -1053,43 +1058,46 @@ test "pipeToFileSystem root_dir" { } test "findRoot with single file archive" { + const io = testing.io; const data = @embedFile("tar/testdata/22752.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - try pipeToFileSystem(tmp.dir, &reader, .{ .diagnostics = &diagnostics }); + try pipeToFileSystem(io, tmp.dir, &reader, .{ .diagnostics = &diagnostics }); try testing.expectEqualStrings("", diagnostics.root_dir); } test "findRoot without explicit root dir" { + const io = testing.io; const data = @embedFile("tar/testdata/19820.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{}); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - try pipeToFileSystem(tmp.dir, &reader, .{ .diagnostics = &diagnostics }); + try pipeToFileSystem(io, tmp.dir, &reader, .{ .diagnostics = &diagnostics }); try testing.expectEqualStrings("root", diagnostics.root_dir); } test "pipeToFileSystem strip_components" { + const io = testing.io; const data = @embedFile("tar/testdata/example.tar"); - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .follow_symlinks = false }); defer tmp.cleanup(); var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 3, .diagnostics = &diagnostics, }) catch |err| { @@ -1110,10 +1118,10 @@ fn normalizePath(bytes: []u8) []u8 { return bytes; } -const default_mode = std.fs.File.default_mode; +const default_mode = Io.File.default_mode; // File system mode based on tar header mode and mode_mode options. -fn fileMode(mode: u32, options: PipeOptions) std.fs.File.Mode { +fn fileMode(mode: u32, options: PipeOptions) Io.File.Mode { if (!std.fs.has_executable_bit or options.mode_mode == .ignore) return default_mode; @@ -1139,16 +1147,17 @@ test fileMode { test "executable bit" { if (!std.fs.has_executable_bit) return error.SkipZigTest; + const io = testing.io; const S = std.posix.S; const data = @embedFile("tar/testdata/example.tar"); for ([_]PipeOptions.ModeMode{ .ignore, .executable_bit_only }) |opt| { - var reader: std.Io.Reader = .fixed(data); + var reader: Io.Reader = .fixed(data); var tmp = testing.tmpDir(.{ .follow_symlinks = false }); //defer tmp.cleanup(); - pipeToFileSystem(tmp.dir, &reader, .{ + pipeToFileSystem(io, tmp.dir, &reader, .{ .strip_components = 1, .exclude_empty_directories = true, .mode_mode = opt, diff --git a/lib/std/tar/test.zig b/lib/std/tar/test.zig index 780e2b844c..b3fbd8f4b3 100644 --- a/lib/std/tar/test.zig +++ b/lib/std/tar/test.zig @@ -424,6 +424,7 @@ test "insufficient buffer in Header name filed" { } test "should not overwrite existing file" { + const io = testing.io; // Starting from this folder structure: // $ tree root // root @@ -469,17 +470,18 @@ test "should not overwrite existing file" { defer root.cleanup(); try testing.expectError( error.PathAlreadyExists, - tar.pipeToFileSystem(root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }), + tar.pipeToFileSystem(io, root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }), ); // Unpack with strip_components = 0 should pass r = .fixed(data); var root2 = std.testing.tmpDir(.{}); defer root2.cleanup(); - try tar.pipeToFileSystem(root2.dir, &r, .{ .mode_mode = .ignore, .strip_components = 0 }); + try tar.pipeToFileSystem(io, root2.dir, &r, .{ .mode_mode = .ignore, .strip_components = 0 }); } test "case sensitivity" { + const io = testing.io; // Mimicking issue #18089, this tar contains, same file name in two case // sensitive name version. Should fail on case insensitive file systems. // @@ -495,7 +497,7 @@ test "case sensitivity" { var root = std.testing.tmpDir(.{}); defer root.cleanup(); - tar.pipeToFileSystem(root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }) catch |err| { + tar.pipeToFileSystem(io, root.dir, &r, .{ .mode_mode = .ignore, .strip_components = 1 }) catch |err| { // on case insensitive fs we fail on overwrite existing file try testing.expectEqual(error.PathAlreadyExists, err); return; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 186cafad59..63131d771c 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -613,9 +613,9 @@ pub const TmpDir = struct { const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); pub fn cleanup(self: *TmpDir) void { - self.dir.close(); + self.dir.close(io); self.parent_dir.deleteTree(&self.sub_path) catch {}; - self.parent_dir.close(); + self.parent_dir.close(io); self.* = undefined; } }; @@ -629,7 +629,7 @@ pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir { const cwd = std.fs.cwd(); var cache_dir = cwd.makeOpenPath(".zig-cache", .{}) catch @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); - defer cache_dir.close(); + defer cache_dir.close(io); const parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); const dir = parent_dir.makeOpenPath(&sub_path, opts) catch diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index 2ab4e48570..c8bde2ab02 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -1,4 +1,18 @@ //! See the render function implementation for documentation of the fields. +const LibCInstallation = @This(); + +const builtin = @import("builtin"); +const is_darwin = builtin.target.os.tag.isDarwin(); +const is_windows = builtin.target.os.tag == .windows; +const is_haiku = builtin.target.os.tag == .haiku; + +const std = @import("std"); +const Io = std.Io; +const Target = std.Target; +const fs = std.fs; +const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; +const log = std.log.scoped(.libc_installation); include_dir: ?[]const u8 = null, sys_include_dir: ?[]const u8 = null, @@ -157,6 +171,7 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { pub const FindNativeOptions = struct { allocator: Allocator, + io: Io, target: *const std.Target, /// If enabled, will print human-friendly errors to stderr. @@ -165,29 +180,32 @@ pub const FindNativeOptions = struct { /// Finds the default, native libc. pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation { + const gpa = args.allocator; + const io = args.io; + var self: LibCInstallation = .{}; if (is_darwin and args.target.os.tag.isDarwin()) { - if (!std.zig.system.darwin.isSdkInstalled(args.allocator)) + if (!std.zig.system.darwin.isSdkInstalled(gpa)) return error.DarwinSdkNotFound; - const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse + const sdk = std.zig.system.darwin.getSdk(gpa, args.target) orelse return error.DarwinSdkNotFound; - defer args.allocator.free(sdk); + defer gpa.free(sdk); - self.include_dir = try fs.path.join(args.allocator, &.{ + self.include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include", }); - self.sys_include_dir = try fs.path.join(args.allocator, &.{ + self.sys_include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include", }); return self; } else if (is_windows) { - const sdk = std.zig.WindowsSdk.find(args.allocator, args.target.cpu.arch) catch |err| switch (err) { + const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch) catch |err| switch (err) { error.NotFound => return error.WindowsSdkNotFound, error.PathTooLong => return error.WindowsSdkNotFound, error.OutOfMemory => return error.OutOfMemory, }; - defer sdk.free(args.allocator); + defer sdk.free(gpa); try self.findNativeMsvcIncludeDir(args, sdk); try self.findNativeMsvcLibDir(args, sdk); @@ -197,16 +215,16 @@ pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation { } else if (is_haiku) { try self.findNativeIncludeDirPosix(args); try self.findNativeGccDirHaiku(args); - self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); + self.crt_dir = try gpa.dupeZ(u8, "/system/develop/lib"); } else if (builtin.target.os.tag == .illumos) { // There is only one libc, and its headers/libraries are always in the same spot. - self.include_dir = try args.allocator.dupeZ(u8, "/usr/include"); - self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include"); - self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64"); + self.include_dir = try gpa.dupeZ(u8, "/usr/include"); + self.sys_include_dir = try gpa.dupeZ(u8, "/usr/include"); + self.crt_dir = try gpa.dupeZ(u8, "/usr/lib/64"); } else if (std.process.can_spawn) { try self.findNativeIncludeDirPosix(args); switch (builtin.target.os.tag) { - .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"), + .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try gpa.dupeZ(u8, "/usr/lib"), .linux => try self.findNativeCrtDirPosix(args), else => {}, } @@ -229,6 +247,7 @@ pub fn deinit(self: *LibCInstallation, allocator: Allocator) void { fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void { const allocator = args.allocator; + const io = args.io; // Detect infinite loops. var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) { @@ -326,7 +345,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) F else => return error.FileSystem, }; - defer search_dir.close(); + defer search_dir.close(io); if (self.include_dir == null) { if (search_dir.access(include_dir_example_file, .{})) |_| { @@ -361,6 +380,7 @@ fn findNativeIncludeDirWindows( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); @@ -380,7 +400,7 @@ fn findNativeIncludeDirWindows( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("stdlib.h", .{}) catch |err| switch (err) { error.FileNotFound => continue, @@ -400,6 +420,7 @@ fn findNativeCrtDirWindows( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); @@ -427,7 +448,7 @@ fn findNativeCrtDirWindows( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("ucrt.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, @@ -467,6 +488,7 @@ fn findNativeKernel32LibDir( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; var install_buf: [2]std.zig.WindowsSdk.Installation = undefined; const installs = fillInstallations(&install_buf, sdk); @@ -494,7 +516,7 @@ fn findNativeKernel32LibDir( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("kernel32.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, @@ -513,6 +535,7 @@ fn findNativeMsvcIncludeDir( sdk: std.zig.WindowsSdk, ) FindError!void { const allocator = args.allocator; + const io = args.io; const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound; const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound; @@ -529,7 +552,7 @@ fn findNativeMsvcIncludeDir( else => return error.FileSystem, }; - defer dir.close(); + defer dir.close(io); dir.access("vcruntime.h", .{}) catch |err| switch (err) { error.FileNotFound => return error.LibCStdLibHeaderNotFound, @@ -1015,17 +1038,3 @@ pub fn resolveCrtPaths( }, } } - -const LibCInstallation = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const Target = std.Target; -const fs = std.fs; -const Allocator = std.mem.Allocator; -const Path = std.Build.Cache.Path; - -const is_darwin = builtin.target.os.tag.isDarwin(); -const is_windows = builtin.target.os.tag == .windows; -const is_haiku = builtin.target.os.tag == .haiku; - -const log = std.log.scoped(.libc_installation); diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig index 89d608633c..a7b16e1bed 100644 --- a/lib/std/zig/WindowsSdk.zig +++ b/lib/std/zig/WindowsSdk.zig @@ -1,7 +1,10 @@ const WindowsSdk = @This(); const builtin = @import("builtin"); + const std = @import("std"); +const Io = std.Io; const Writer = std.Io.Writer; +const Allocator = std.mem.Allocator; windows10sdk: ?Installation, windows81sdk: ?Installation, @@ -20,7 +23,7 @@ const product_version_max_length = version_major_minor_max_length + ".65535".len /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory. /// Caller owns the result's fields. /// After finishing work, call `free(allocator)`. -pub fn find(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk { +pub fn find(allocator: Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk { if (builtin.os.tag != .windows) return error.NotFound; //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed @@ -58,7 +61,7 @@ pub fn find(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutO }; } -pub fn free(sdk: WindowsSdk, allocator: std.mem.Allocator) void { +pub fn free(sdk: WindowsSdk, allocator: Allocator) void { if (sdk.windows10sdk) |*w10sdk| { w10sdk.free(allocator); } @@ -75,7 +78,7 @@ pub fn free(sdk: WindowsSdk, allocator: std.mem.Allocator) void { /// Caller owns result. fn iterateAndFilterByVersion( iterator: *std.fs.Dir.Iterator, - allocator: std.mem.Allocator, + allocator: Allocator, prefix: []const u8, ) error{OutOfMemory}![][]const u8 { const Version = struct { @@ -174,7 +177,7 @@ const RegistryWtf8 = struct { /// Get string from registry. /// Caller owns result. - pub fn getString(reg: RegistryWtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 { + pub fn getString(reg: RegistryWtf8, allocator: Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 { const subkey_wtf16le: [:0]const u16 = subkey_wtf16le: { var subkey_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined; const subkey_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(subkey_wtf16le_buf[0..], subkey) catch unreachable; @@ -282,7 +285,7 @@ const RegistryWtf16Le = struct { } /// Get string ([:0]const u16) from registry. - fn getString(reg: RegistryWtf16Le, allocator: std.mem.Allocator, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]const u16 { + fn getString(reg: RegistryWtf16Le, allocator: Allocator, subkey_wtf16le: [:0]const u16, value_name_wtf16le: [:0]const u16) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]const u16 { var actual_type: windows.ULONG = undefined; // Calculating length to allocate @@ -416,7 +419,7 @@ pub const Installation = struct { /// Caller owns the result's fields. /// After finishing work, call `free(allocator)`. fn find( - allocator: std.mem.Allocator, + allocator: Allocator, roots_key: RegistryWtf8, roots_subkey: []const u8, prefix: []const u8, @@ -437,7 +440,8 @@ pub const Installation = struct { } fn findFromRoot( - allocator: std.mem.Allocator, + allocator: Allocator, + io: Io, roots_key: RegistryWtf8, roots_subkey: []const u8, prefix: []const u8, @@ -478,7 +482,7 @@ pub const Installation = struct { error.NameTooLong => return error.PathTooLong, else => return error.InstallationNotFound, }; - defer sdk_lib_dir.close(); + defer sdk_lib_dir.close(io); var iterator = sdk_lib_dir.iterate(); const versions = try iterateAndFilterByVersion(&iterator, allocator, prefix); @@ -495,7 +499,7 @@ pub const Installation = struct { } fn findFromInstallationFolder( - allocator: std.mem.Allocator, + allocator: Allocator, version_key_name: []const u8, ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation { var key_name_buf: [RegistryWtf16Le.key_name_max_len]u8 = undefined; @@ -597,14 +601,14 @@ pub const Installation = struct { return (reg_value == 1); } - fn free(install: Installation, allocator: std.mem.Allocator) void { + fn free(install: Installation, allocator: Allocator) void { allocator.free(install.path); allocator.free(install.version); } }; const MsvcLibDir = struct { - fn findInstancesDirViaSetup(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { + fn findInstancesDirViaSetup(allocator: Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { const vs_setup_key_path = "SOFTWARE\\Microsoft\\VisualStudio\\Setup"; const vs_setup_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, vs_setup_key_path, .{}) catch |err| switch (err) { error.KeyNotFound => return error.PathNotFound, @@ -629,7 +633,7 @@ const MsvcLibDir = struct { return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound; } - fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { + fn findInstancesDirViaCLSID(allocator: Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}"; const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid, .{}) catch |err| switch (err) { error.KeyNotFound => return error.PathNotFound, @@ -665,7 +669,7 @@ const MsvcLibDir = struct { return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound; } - fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { + fn findInstancesDir(allocator: Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { // First, try getting the packages cache path from the registry. // This only seems to exist when the path is different from the default. method1: { @@ -748,13 +752,13 @@ const MsvcLibDir = struct { /// /// The logic in this function is intended to match what ISetupConfiguration does /// under-the-hood, as verified using Procmon. - fn findViaCOM(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn findViaCOM(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances` // This will contain directories with names of instance IDs like 80a758ca, // which will contain `state.json` files that have the version and // installation directory. var instances_dir = try findInstancesDir(allocator); - defer instances_dir.close(); + defer instances_dir.close(io); var state_subpath_buf: [std.fs.max_name_bytes + 32]u8 = undefined; var latest_version_lib_dir: std.ArrayList(u8) = .empty; @@ -791,7 +795,7 @@ const MsvcLibDir = struct { const installation_path = parsed.value.object.get("installationPath") orelse continue; if (installation_path != .string) continue; - const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string, arch) catch |err| switch (err) { + const lib_dir_path = libDirFromInstallationPath(allocator, io, installation_path.string, arch) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.PathNotFound => continue, }; @@ -806,7 +810,12 @@ const MsvcLibDir = struct { return latest_version_lib_dir.toOwnedSlice(allocator); } - fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn libDirFromInstallationPath( + allocator: Allocator, + io: Io, + installation_path: []const u8, + arch: std.Target.Cpu.Arch, + ) error{ OutOfMemory, PathNotFound }![]const u8 { var lib_dir_buf = try std.array_list.Managed(u8).initCapacity(allocator, installation_path.len + 64); errdefer lib_dir_buf.deinit(); @@ -837,7 +846,7 @@ const MsvcLibDir = struct { else => unreachable, }); - if (!verifyLibDir(lib_dir_buf.items)) { + if (!verifyLibDir(io, lib_dir_buf.items)) { return error.PathNotFound; } @@ -845,7 +854,7 @@ const MsvcLibDir = struct { } // https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance - fn findViaRegistry(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn findViaRegistry(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { // %localappdata%\Microsoft\VisualStudio\ // %appdata%\Local\Microsoft\VisualStudio\ @@ -859,7 +868,7 @@ const MsvcLibDir = struct { var visualstudio_folder = std.fs.openDirAbsolute(visualstudio_folder_path, .{ .iterate = true, }) catch return error.PathNotFound; - defer visualstudio_folder.close(); + defer visualstudio_folder.close(io); var iterator = visualstudio_folder.iterate(); break :vs_versions try iterateAndFilterByVersion(&iterator, allocator, ""); @@ -926,14 +935,14 @@ const MsvcLibDir = struct { }; errdefer allocator.free(msvc_dir); - if (!verifyLibDir(msvc_dir)) { + if (!verifyLibDir(io, msvc_dir)) { return error.PathNotFound; } return msvc_dir; } - fn findViaVs7Key(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn findViaVs7Key(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { var base_path: std.array_list.Managed(u8) = base_path: { try_env: { var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) { @@ -989,7 +998,7 @@ const MsvcLibDir = struct { else => unreachable, }); - if (!verifyLibDir(base_path.items)) { + if (!verifyLibDir(io, base_path.items)) { return error.PathNotFound; } @@ -997,11 +1006,11 @@ const MsvcLibDir = struct { return full_path; } - fn verifyLibDir(lib_dir_path: []const u8) bool { + fn verifyLibDir(io: Io, lib_dir_path: []const u8) bool { std.debug.assert(std.fs.path.isAbsolute(lib_dir_path)); // should be already handled in `findVia*` var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false; - defer dir.close(); + defer dir.close(io); const stat = dir.statFile("vcruntime.lib") catch return false; if (stat.kind != .file) @@ -1012,12 +1021,12 @@ const MsvcLibDir = struct { /// Find path to MSVC's `lib/` directory. /// Caller owns the result. - pub fn find(allocator: std.mem.Allocator, arch: std.Target.Cpu.Arch) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 { - const full_path = MsvcLibDir.findViaCOM(allocator, arch) catch |err1| switch (err1) { + pub fn find(allocator: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 { + const full_path = MsvcLibDir.findViaCOM(allocator, io, arch) catch |err1| switch (err1) { error.OutOfMemory => return error.OutOfMemory, - error.PathNotFound => MsvcLibDir.findViaRegistry(allocator, arch) catch |err2| switch (err2) { + error.PathNotFound => MsvcLibDir.findViaRegistry(allocator, io, arch) catch |err2| switch (err2) { error.OutOfMemory => return error.OutOfMemory, - error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator, arch) catch |err3| switch (err3) { + error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator, io, arch) catch |err3| switch (err3) { error.OutOfMemory => return error.OutOfMemory, error.PathNotFound => return error.MsvcLibDirNotFound, }, diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 587ac82c70..9a52ae2c81 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -1,14 +1,17 @@ +const builtin = @import("builtin"); +const Builder = @This(); + const std = @import("../../std.zig"); +const Io = std.Io; const Allocator = std.mem.Allocator; const assert = std.debug.assert; -const bitcode_writer = @import("bitcode_writer.zig"); -const Builder = @This(); -const builtin = @import("builtin"); const DW = std.dwarf; -const ir = @import("ir.zig"); const log = std.log.scoped(.llvm); const Writer = std.Io.Writer; +const bitcode_writer = @import("bitcode_writer.zig"); +const ir = @import("ir.zig"); + gpa: Allocator, strip: bool, @@ -9579,11 +9582,11 @@ pub fn dump(b: *Builder) void { b.printToFile(stderr, &buffer) catch {}; } -pub fn printToFilePath(b: *Builder, dir: std.fs.Dir, path: []const u8) !void { +pub fn printToFilePath(b: *Builder, io: Io, dir: std.fs.Dir, path: []const u8) !void { var buffer: [4000]u8 = undefined; - const file = try dir.createFile(path, .{}); - defer file.close(); - try b.printToFile(file, &buffer); + const file = try dir.createFile(io, path, .{}); + defer file.close(io); + try b.printToFile(io, file, &buffer); } pub fn printToFile(b: *Builder, file: std.fs.File, buffer: []u8) !void { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 290cd8d388..4f0c11797d 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -847,7 +847,7 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion { error.Unexpected => |e| return e, error.Canceled => |e| return e, }; - defer file.close(); + defer file.close(io); // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system. var buffer: [8000]u8 = undefined; @@ -1051,7 +1051,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ else => |e| return e, }; var is_elf_file = false; - defer if (!is_elf_file) file.close(); + defer if (!is_elf_file) file.close(io); file_reader = .initAdapted(file, io, &file_reader_buffer); file_name = undefined; // it aliases file_reader_buffer diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index 6b4f0cf6f9..668e5a1d99 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -447,7 +447,7 @@ pub fn detectNativeCpuAndFeatures(io: Io) ?Target.Cpu { var file = fs.openFileAbsolute("/proc/cpuinfo", .{}) catch |err| switch (err) { else => return null, }; - defer file.close(); + defer file.close(io); var buffer: [4096]u8 = undefined; // "flags" lines can get pretty long. var file_reader = file.reader(io, &buffer); diff --git a/lib/std/zip.zig b/lib/std/zip.zig index 583377e00a..c2dbaf5b81 100644 --- a/lib/std/zip.zig +++ b/lib/std/zip.zig @@ -554,17 +554,19 @@ pub const Iterator = struct { return; } + const io = stream.io; + const out_file = blk: { if (std.fs.path.dirname(filename)) |dirname| { var parent_dir = try dest.makeOpenPath(dirname, .{}); - defer parent_dir.close(); + defer parent_dir.close(io); const basename = std.fs.path.basename(filename); break :blk try parent_dir.createFile(basename, .{ .exclusive = true }); } break :blk try dest.createFile(filename, .{ .exclusive = true }); }; - defer out_file.close(); + defer out_file.close(io); var out_file_buffer: [1024]u8 = undefined; var file_writer = out_file.writer(&out_file_buffer); const local_data_file_offset: u64 = diff --git a/src/Compilation.zig b/src/Compilation.zig index 931a0b2d14..df64dee19f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -721,13 +721,13 @@ pub const Directories = struct { /// This may be the same as `global_cache`. local_cache: Cache.Directory, - pub fn deinit(dirs: *Directories) void { + pub fn deinit(dirs: *Directories, io: Io) void { // The local and global caches could be the same. const close_local = dirs.local_cache.handle.fd != dirs.global_cache.handle.fd; - dirs.global_cache.handle.close(); - if (close_local) dirs.local_cache.handle.close(); - dirs.zig_lib.handle.close(); + dirs.global_cache.handle.close(io); + if (close_local) dirs.local_cache.handle.close(io); + dirs.zig_lib.handle.close(io); } /// Returns a `Directories` where `local_cache` is replaced with `global_cache`, intended for @@ -1105,7 +1105,7 @@ pub const CObject = struct { if (diag.src_loc.offset == 0 or diag.src_loc.column == 0) break :source_line 0; const file = fs.cwd().openFile(file_name, .{}) catch break :source_line 0; - defer file.close(); + defer file.close(io); var buffer: [1024]u8 = undefined; var file_reader = file.reader(io, &buffer); file_reader.seekTo(diag.src_loc.offset + 1 - diag.src_loc.column) catch break :source_line 0; @@ -1180,7 +1180,7 @@ pub const CObject = struct { var buffer: [1024]u8 = undefined; const file = try fs.cwd().openFile(path, .{}); - defer file.close(); + defer file.close(io); var file_reader = file.reader(io, &buffer); var bc = std.zig.llvm.BitcodeReader.init(gpa, .{ .reader = &file_reader.interface }); defer bc.deinit(); @@ -1617,13 +1617,13 @@ const CacheUse = union(CacheMode) { } }; - fn deinit(cu: CacheUse) void { + fn deinit(cu: CacheUse, io: Io) void { switch (cu) { .none => |none| { assert(none.tmp_artifact_directory == null); }, .incremental => |incremental| { - incremental.artifact_directory.handle.close(); + incremental.artifact_directory.handle.close(io); }, .whole => |whole| { assert(whole.tmp_artifact_directory == null); @@ -2113,7 +2113,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, cache.addPrefix(options.dirs.zig_lib); cache.addPrefix(options.dirs.local_cache); cache.addPrefix(options.dirs.global_cache); - errdefer cache.manifest_dir.close(); + errdefer cache.manifest_dir.close(io); // This is shared hasher state common to zig source and all C source files. cache.hash.addBytes(build_options.version); @@ -2157,7 +2157,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, var local_zir_dir = options.dirs.local_cache.handle.makeOpenPath(zir_sub_dir, .{}) catch |err| { return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = zir_sub_dir, .err = err } }); }; - errdefer local_zir_dir.close(); + errdefer local_zir_dir.close(io); const local_zir_cache: Cache.Directory = .{ .handle = local_zir_dir, .path = try options.dirs.local_cache.join(arena, &.{zir_sub_dir}), @@ -2165,7 +2165,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, var global_zir_dir = options.dirs.global_cache.handle.makeOpenPath(zir_sub_dir, .{}) catch |err| { return diag.fail(.{ .create_cache_path = .{ .which = .global, .sub = zir_sub_dir, .err = err } }); }; - errdefer global_zir_dir.close(); + errdefer global_zir_dir.close(io); const global_zir_cache: Cache.Directory = .{ .handle = global_zir_dir, .path = try options.dirs.global_cache.join(arena, &.{zir_sub_dir}), @@ -2436,7 +2436,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, var artifact_dir = options.dirs.local_cache.handle.makeOpenPath(artifact_sub_dir, .{}) catch |err| { return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = artifact_sub_dir, .err = err } }); }; - errdefer artifact_dir.close(); + errdefer artifact_dir.close(io); const artifact_directory: Cache.Directory = .{ .handle = artifact_dir, .path = try options.dirs.local_cache.join(arena, &.{artifact_sub_dir}), @@ -2689,6 +2689,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, pub fn destroy(comp: *Compilation) void { const gpa = comp.gpa; + const io = comp.io; if (comp.bin_file) |lf| lf.destroy(); if (comp.zcu) |zcu| zcu.deinit(); @@ -2760,7 +2761,7 @@ pub fn destroy(comp: *Compilation) void { comp.clearMiscFailures(); - comp.cache_parent.manifest_dir.close(); + comp.cache_parent.manifest_dir.close(io); } pub fn clearMiscFailures(comp: *Compilation) void { @@ -2791,10 +2792,12 @@ pub fn hotCodeSwap( } fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { + const io = comp.io; + switch (comp.cache_use) { .none => |none| { if (none.tmp_artifact_directory) |*tmp_dir| { - tmp_dir.handle.close(); + tmp_dir.handle.close(io); none.tmp_artifact_directory = null; if (dev.env == .bootstrap) { // zig1 uses `CacheMode.none`, but it doesn't need to know how to delete @@ -2834,7 +2837,7 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { comp.bin_file = null; } if (whole.tmp_artifact_directory) |*tmp_dir| { - tmp_dir.handle.close(); + tmp_dir.handle.close(io); whole.tmp_artifact_directory = null; const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| { @@ -3152,7 +3155,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE // the file handle and re-open it in the follow up call to // `makeWritable`. if (lf.file) |f| { - f.close(); + f.close(io); lf.file = null; if (lf.closeDebugInfo()) break :w .lf_and_debug; @@ -3165,7 +3168,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE // Rename the temporary directory into place. // Close tmp dir and link.File to avoid open handle during rename. - whole.tmp_artifact_directory.?.handle.close(); + whole.tmp_artifact_directory.?.handle.close(io); whole.tmp_artifact_directory = null; const s = fs.path.sep_str; const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int); @@ -5258,6 +5261,7 @@ fn workerDocsCopy(comp: *Compilation) void { fn docsCopyFallible(comp: *Compilation) anyerror!void { const zcu = comp.zcu orelse return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{}); + const io = comp.io; const docs_path = comp.resolveEmitPath(comp.emit_docs.?); var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| { @@ -5267,7 +5271,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { .{ docs_path, @errorName(err) }, ); }; - defer out_dir.close(); + defer out_dir.close(io); for (&[_][]const u8{ "docs/main.js", "docs/index.html" }) |sub_path| { const basename = fs.path.basename(sub_path); @@ -5287,7 +5291,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { .{ docs_path, @errorName(err) }, ); }; - defer tar_file.close(); + defer tar_file.close(io); var buffer: [1024]u8 = undefined; var tar_file_writer = tar_file.writer(&buffer); @@ -5331,7 +5335,7 @@ fn docsCopyModule( } catch |err| { return comp.lockAndSetMiscFailure(.docs_copy, "unable to open directory '{f}': {t}", .{ root.fmt(comp), err }); }; - defer mod_dir.close(); + defer mod_dir.close(io); var walker = try mod_dir.walk(comp.gpa); defer walker.deinit(); @@ -5355,7 +5359,7 @@ fn docsCopyModule( root.fmt(comp), entry.path, err, }); }; - defer file.close(); + defer file.close(io); const stat = try file.stat(); var file_reader: fs.File.Reader = .initSize(file.adaptToNewApi(), io, &buffer, stat.size); @@ -5510,7 +5514,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU ); return error.AlreadyReported; }; - defer out_dir.close(); + defer out_dir.close(io); crt_file.full_object_path.root_dir.handle.copyFile( crt_file.full_object_path.sub_path, @@ -5693,7 +5697,7 @@ pub fn translateC( const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename; const cache_dir = comp.dirs.local_cache.handle; var cache_tmp_dir = try cache_dir.makeOpenPath(tmp_sub_path, .{}); - defer cache_tmp_dir.close(); + defer cache_tmp_dir.close(io); const translated_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, translated_basename }); const source_path = switch (source) { @@ -6268,7 +6272,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr // so we need a temporary filename. const out_obj_path = try comp.tmpFilePath(arena, o_basename); var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath("tmp", .{}); - defer zig_cache_tmp_dir.close(); + defer zig_cache_tmp_dir.close(io); const out_diag_path = if (comp.clang_passthrough_mode or !ext.clangSupportsDiagnostics()) null @@ -6433,7 +6437,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr const digest = man.final(); const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{}); - defer o_dir.close(); + defer o_dir.close(io); const tmp_basename = fs.path.basename(out_obj_path); try fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename); break :blk digest; @@ -6477,8 +6481,6 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 const tracy_trace = trace(@src()); defer tracy_trace.end(); - const io = comp.io; - const src_path = switch (win32_resource.src) { .rc => |rc_src| rc_src.src_path, .manifest => |src_path| src_path, @@ -6487,6 +6489,8 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 log.debug("updating win32 resource: {s}", .{src_path}); + const io = comp.io; + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -6522,7 +6526,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 const o_sub_path = try fs.path.join(arena, &.{ "o", &digest }); var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{}); - defer o_dir.close(); + defer o_dir.close(io); const in_rc_path = try comp.dirs.local_cache.join(comp.gpa, &.{ o_sub_path, rc_basename, @@ -6610,7 +6614,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 const digest = if (try man.hit()) man.final() else blk: { var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath("tmp", .{}); - defer zig_cache_tmp_dir.close(); + defer zig_cache_tmp_dir.close(io); const res_filename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext}); @@ -6681,7 +6685,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 const digest = man.final(); const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{}); - defer o_dir.close(); + defer o_dir.close(io); const tmp_basename = fs.path.basename(out_res_path); try fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, res_filename); break :blk digest; diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 9b7c83cc39..58f970abe5 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -513,7 +513,7 @@ fn runResource( break :handle dir; }, }; - defer tmp_directory.handle.close(); + defer tmp_directory.handle.close(io); // Fetch and unpack a resource into a temporary directory. var unpack_result = try unpackResource(f, resource, uri_path, tmp_directory); @@ -523,7 +523,7 @@ fn runResource( // Apply btrfs workaround if needed. Reopen tmp_directory. if (native_os == .linux and f.job_queue.work_around_btrfs_bug) { // https://github.com/ziglang/zig/issues/17095 - pkg_path.root_dir.handle.close(); + pkg_path.root_dir.handle.close(io); pkg_path.root_dir.handle = cache_root.handle.makeOpenPath(tmp_dir_sub_path, .{ .iterate = true, }) catch @panic("btrfs workaround failed"); @@ -885,7 +885,7 @@ const Resource = union(enum) { file: fs.File.Reader, http_request: HttpRequest, git: Git, - dir: fs.Dir, + dir: Io.Dir, const Git = struct { session: git.Session, @@ -908,7 +908,7 @@ const Resource = union(enum) { .git => |*git_resource| { git_resource.fetch_stream.deinit(); }, - .dir => |*dir| dir.close(), + .dir => |*dir| dir.close(io), } resource.* = undefined; } @@ -1247,13 +1247,14 @@ fn unpackResource( } } -fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: *Io.Reader) RunError!UnpackResult { +fn unpackTarball(f: *Fetch, out_dir: Io.Dir, reader: *Io.Reader) RunError!UnpackResult { const eb = &f.error_bundle; const arena = f.arena.allocator(); + const io = f.job_queue.io; var diagnostics: std.tar.Diagnostics = .{ .allocator = arena }; - std.tar.pipeToFileSystem(out_dir, reader, .{ + std.tar.pipeToFileSystem(io, out_dir, reader, .{ .diagnostics = &diagnostics, .strip_components = 0, .mode_mode = .ignore, @@ -1280,7 +1281,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: *Io.Reader) RunError!Unpack fn unzip( f: *Fetch, - out_dir: fs.Dir, + out_dir: Io.Dir, reader: *Io.Reader, ) error{ ReadFailed, OutOfMemory, Canceled, FetchFailed }!UnpackResult { // We write the entire contents to a file first because zip files @@ -1314,7 +1315,7 @@ fn unzip( ), }; }; - defer zip_file.close(); + defer zip_file.close(io); var zip_file_buffer: [4096]u8 = undefined; var zip_file_reader = b: { var zip_file_writer = zip_file.writer(&zip_file_buffer); @@ -1349,7 +1350,7 @@ fn unzip( return .{ .root_dir = diagnostics.root_dir }; } -fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!UnpackResult { +fn unpackGitPack(f: *Fetch, out_dir: Io.Dir, resource: *Resource.Git) anyerror!UnpackResult { const io = f.job_queue.io; const arena = f.arena.allocator(); // TODO don't try to get a gpa from an arena. expose this dependency higher up @@ -1363,9 +1364,9 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U // directory, since that isn't relevant for fetching a package. { var pack_dir = try out_dir.makeOpenPath(".git", .{}); - defer pack_dir.close(); + defer pack_dir.close(io); var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true }); - defer pack_file.close(); + defer pack_file.close(io); var pack_file_buffer: [4096]u8 = undefined; var pack_file_reader = b: { var pack_file_writer = pack_file.writer(&pack_file_buffer); @@ -1376,7 +1377,7 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U }; var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true }); - defer index_file.close(); + defer index_file.close(io); var index_file_buffer: [2000]u8 = undefined; var index_file_writer = index_file.writer(&index_file_buffer); { @@ -1393,7 +1394,7 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U try repository.init(gpa, object_format, &pack_file_reader, &index_file_reader); defer repository.deinit(); var diagnostics: git.Diagnostics = .{ .allocator = arena }; - try repository.checkout(out_dir, resource.want_oid, &diagnostics); + try repository.checkout(io, out_dir, resource.want_oid, &diagnostics); if (diagnostics.errors.items.len > 0) { try res.allocErrors(arena, diagnostics.errors.items.len, "unable to unpack packfile"); @@ -1411,7 +1412,7 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U return res; } -fn recursiveDirectoryCopy(f: *Fetch, dir: fs.Dir, tmp_dir: fs.Dir) anyerror!void { +fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void { const gpa = f.arena.child_allocator; // Recursive directory copy. var it = try dir.walk(gpa); @@ -1451,7 +1452,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: fs.Dir, tmp_dir: fs.Dir) anyerror!void } } -pub fn renameTmpIntoCache(cache_dir: fs.Dir, tmp_dir_sub_path: []const u8, dest_dir_sub_path: []const u8) !void { +pub fn renameTmpIntoCache(cache_dir: Io.Dir, tmp_dir_sub_path: []const u8, dest_dir_sub_path: []const u8) !void { assert(dest_dir_sub_path[1] == fs.path.sep); var handled_missing_dir = false; while (true) { @@ -1660,15 +1661,15 @@ fn dumpHashInfo(all_files: []const *const HashedFile) !void { try w.flush(); } -fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile) void { +fn workerHashFile(dir: Io.Dir, hashed_file: *HashedFile) void { hashed_file.failure = hashFileFallible(dir, hashed_file); } -fn workerDeleteFile(dir: fs.Dir, deleted_file: *DeletedFile) void { +fn workerDeleteFile(dir: Io.Dir, deleted_file: *DeletedFile) void { deleted_file.failure = deleteFileFallible(dir, deleted_file); } -fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { +fn hashFileFallible(io: Io, dir: Io.Dir, hashed_file: *HashedFile) HashedFile.Error!void { var buf: [8000]u8 = undefined; var hasher = Package.Hash.Algo.init(.{}); hasher.update(hashed_file.normalized_path); @@ -1677,7 +1678,7 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void switch (hashed_file.kind) { .file => { var file = try dir.openFile(hashed_file.fs_path, .{}); - defer file.close(); + defer file.close(io); // Hard-coded false executable bit: https://github.com/ziglang/zig/issues/17463 hasher.update(&.{ 0, 0 }); var file_header: FileHeader = .{}; @@ -1707,7 +1708,7 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void hashed_file.size = file_size; } -fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void { +fn deleteFileFallible(dir: Io.Dir, deleted_file: *DeletedFile) DeletedFile.Error!void { try dir.deleteFile(deleted_file.fs_path); } @@ -1724,8 +1725,8 @@ const DeletedFile = struct { failure: Error!void, const Error = - fs.Dir.DeleteFileError || - fs.Dir.DeleteDirError; + Io.Dir.DeleteFileError || + Io.Dir.DeleteDirError; }; const HashedFile = struct { @@ -1741,7 +1742,7 @@ const HashedFile = struct { fs.File.ReadError || fs.File.StatError || fs.File.ChmodError || - fs.Dir.ReadLinkError; + Io.Dir.ReadLinkError; const Kind = enum { file, link }; @@ -2074,7 +2075,7 @@ test "tarball with duplicate paths" { defer tmp.cleanup(); const tarball_name = "duplicate_paths.tar.gz"; - try saveEmbedFile(tarball_name, tmp.dir); + try saveEmbedFile(io, tarball_name, tmp.dir); const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); defer gpa.free(tarball_path); @@ -2107,7 +2108,7 @@ test "tarball with excluded duplicate paths" { defer tmp.cleanup(); const tarball_name = "duplicate_paths_excluded.tar.gz"; - try saveEmbedFile(tarball_name, tmp.dir); + try saveEmbedFile(io, tarball_name, tmp.dir); const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); defer gpa.free(tarball_path); @@ -2153,7 +2154,7 @@ test "tarball without root folder" { defer tmp.cleanup(); const tarball_name = "no_root.tar.gz"; - try saveEmbedFile(tarball_name, tmp.dir); + try saveEmbedFile(io, tarball_name, tmp.dir); const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); defer gpa.free(tarball_path); @@ -2186,7 +2187,7 @@ test "set executable bit based on file content" { defer tmp.cleanup(); const tarball_name = "executables.tar.gz"; - try saveEmbedFile(tarball_name, tmp.dir); + try saveEmbedFile(io, tarball_name, tmp.dir); const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); defer gpa.free(tarball_path); @@ -2210,7 +2211,7 @@ test "set executable bit based on file content" { ); var out = try fb.packageDir(); - defer out.close(); + defer out.close(io); const S = std.posix.S; // expect executable bit not set try std.testing.expect((try out.statFile("file1")).mode & S.IXUSR == 0); @@ -2231,11 +2232,11 @@ test "set executable bit based on file content" { // -rwxrwxr-x 1 17 Apr script_with_shebang_without_exec_bit } -fn saveEmbedFile(comptime tarball_name: []const u8, dir: fs.Dir) !void { +fn saveEmbedFile(io: Io, comptime tarball_name: []const u8, dir: Io.Dir) !void { //const tarball_name = "duplicate_paths_excluded.tar.gz"; const tarball_content = @embedFile("Fetch/testdata/" ++ tarball_name); var tmp_file = try dir.createFile(tarball_name, .{}); - defer tmp_file.close(); + defer tmp_file.close(io); try tmp_file.writeAll(tarball_content); } @@ -2250,7 +2251,7 @@ const TestFetchBuilder = struct { self: *TestFetchBuilder, allocator: std.mem.Allocator, io: Io, - cache_parent_dir: std.fs.Dir, + cache_parent_dir: std.Io.Dir, path_or_url: []const u8, ) !*Fetch { const cache_dir = try cache_parent_dir.makeOpenPath("zig-global-cache", .{}); @@ -2301,14 +2302,15 @@ const TestFetchBuilder = struct { } fn deinit(self: *TestFetchBuilder) void { + const io = self.job_queue.io; self.fetch.deinit(); self.job_queue.deinit(); self.fetch.prog_node.end(); - self.global_cache_directory.handle.close(); + self.global_cache_directory.handle.close(io); self.http_client.deinit(); } - fn packageDir(self: *TestFetchBuilder) !fs.Dir { + fn packageDir(self: *TestFetchBuilder) !Io.Dir { const root = self.fetch.package_root; return try root.root_dir.handle.openDir(root.sub_path, .{ .iterate = true }); } @@ -2316,8 +2318,10 @@ const TestFetchBuilder = struct { // Test helper, asserts thet package dir constains expected_files. // expected_files must be sorted. fn expectPackageFiles(self: *TestFetchBuilder, expected_files: []const []const u8) !void { + const io = self.job_queue.io; + var package_dir = try self.packageDir(); - defer package_dir.close(); + defer package_dir.close(io); var actual_files: std.ArrayList([]u8) = .empty; defer actual_files.deinit(std.testing.allocator); diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index a2ea870c3f..864865bd19 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -213,6 +213,7 @@ pub const Repository = struct { /// Checks out the repository at `commit_oid` to `worktree`. pub fn checkout( repository: *Repository, + io: Io, worktree: std.fs.Dir, commit_oid: Oid, diagnostics: *Diagnostics, @@ -223,12 +224,13 @@ pub const Repository = struct { if (commit_object.type != .commit) return error.NotACommit; break :tree_oid try getCommitTree(repository.odb.format, commit_object.data); }; - try repository.checkoutTree(worktree, tree_oid, "", diagnostics); + try repository.checkoutTree(io, worktree, tree_oid, "", diagnostics); } /// Checks out the tree at `tree_oid` to `worktree`. fn checkoutTree( repository: *Repository, + io: Io, dir: std.fs.Dir, tree_oid: Oid, current_path: []const u8, @@ -253,10 +255,10 @@ pub const Repository = struct { .directory => { try dir.makeDir(entry.name); var subdir = try dir.openDir(entry.name, .{}); - defer subdir.close(); + defer subdir.close(io); const sub_path = try std.fs.path.join(repository.odb.allocator, &.{ current_path, entry.name }); defer repository.odb.allocator.free(sub_path); - try repository.checkoutTree(subdir, entry.oid, sub_path, diagnostics); + try repository.checkoutTree(io, subdir, entry.oid, sub_path, diagnostics); }, .file => { try repository.odb.seekOid(entry.oid); @@ -271,7 +273,7 @@ pub const Repository = struct { } }); continue; }; - defer file.close(); + defer file.close(io); try file.writeAll(file_object.data); }, .symlink => { @@ -1583,14 +1585,14 @@ fn runRepositoryTest(io: Io, comptime format: Oid.Format, head_commit: []const u var git_dir = testing.tmpDir(.{}); defer git_dir.cleanup(); var pack_file = try git_dir.dir.createFile("testrepo.pack", .{ .read = true }); - defer pack_file.close(); + defer pack_file.close(io); try pack_file.writeAll(testrepo_pack); var pack_file_buffer: [2000]u8 = undefined; var pack_file_reader = pack_file.reader(io, &pack_file_buffer); var index_file = try git_dir.dir.createFile("testrepo.idx", .{ .read = true }); - defer index_file.close(); + defer index_file.close(io); var index_file_buffer: [2000]u8 = undefined; var index_file_writer = index_file.writer(&index_file_buffer); try indexPack(testing.allocator, format, &pack_file_reader, &index_file_writer); @@ -1621,7 +1623,7 @@ fn runRepositoryTest(io: Io, comptime format: Oid.Format, head_commit: []const u var diagnostics: Diagnostics = .{ .allocator = testing.allocator }; defer diagnostics.deinit(); - try repository.checkout(worktree.dir, commit_id, &diagnostics); + try repository.checkout(io, worktree.dir, commit_id, &diagnostics); try testing.expect(diagnostics.errors.items.len == 0); const expected_files: []const []const u8 = &.{ @@ -1713,20 +1715,20 @@ pub fn main() !void { const format = std.meta.stringToEnum(Oid.Format, args[1]) orelse return error.InvalidFormat; var pack_file = try std.fs.cwd().openFile(args[2], .{}); - defer pack_file.close(); + defer pack_file.close(io); var pack_file_buffer: [4096]u8 = undefined; var pack_file_reader = pack_file.reader(io, &pack_file_buffer); const commit = try Oid.parse(format, args[3]); var worktree = try std.fs.cwd().makeOpenPath(args[4], .{}); - defer worktree.close(); + defer worktree.close(io); var git_dir = try worktree.makeOpenPath(".git", .{}); - defer git_dir.close(); + defer git_dir.close(io); std.debug.print("Starting index...\n", .{}); var index_file = try git_dir.createFile("idx", .{ .read = true }); - defer index_file.close(); + defer index_file.close(io); var index_file_buffer: [4096]u8 = undefined; var index_file_writer = index_file.writer(&index_file_buffer); try indexPack(allocator, format, &pack_file_reader, &index_file_writer); @@ -1738,7 +1740,7 @@ pub fn main() !void { defer repository.deinit(); var diagnostics: Diagnostics = .{ .allocator = allocator }; defer diagnostics.deinit(); - try repository.checkout(worktree, commit, &diagnostics); + try repository.checkout(io, worktree, commit, &diagnostics); for (diagnostics.errors.items) |err| { std.debug.print("Diagnostic: {}\n", .{err}); diff --git a/src/Zcu.zig b/src/Zcu.zig index 137b4d8b59..58d884afe3 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -1078,7 +1078,7 @@ pub const File = struct { const dir, const sub_path = file.path.openInfo(zcu.comp.dirs); break :f try dir.openFile(sub_path, .{}); }; - defer f.close(); + defer f.close(io); const stat = f.stat() catch |err| switch (err) { error.Streaming => { @@ -2813,8 +2813,8 @@ pub fn init(zcu: *Zcu, gpa: Allocator, io: Io, thread_count: usize) !void { pub fn deinit(zcu: *Zcu) void { const comp = zcu.comp; - const gpa = comp.gpa; const io = comp.io; + const gpa = zcu.gpa; { const pt: Zcu.PerThread = .activate(zcu, .main); defer pt.deactivate(); @@ -2835,8 +2835,8 @@ pub fn deinit(zcu: *Zcu) void { } zcu.embed_table.deinit(gpa); - zcu.local_zir_cache.handle.close(); - zcu.global_zir_cache.handle.close(); + zcu.local_zir_cache.handle.close(io); + zcu.global_zir_cache.handle.close(io); for (zcu.failed_analysis.values()) |value| value.destroy(gpa); for (zcu.failed_codegen.values()) |value| value.destroy(gpa); @@ -2900,7 +2900,7 @@ pub fn deinit(zcu: *Zcu) void { if (zcu.resolved_references) |*r| r.deinit(gpa); - if (zcu.comp.debugIncremental()) { + if (comp.debugIncremental()) { zcu.incremental_debug_state.deinit(gpa); } } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 2ad5bac01c..d2ca004058 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -96,7 +96,7 @@ pub fn updateFile( const dir, const sub_path = file.path.openInfo(comp.dirs); break :f try dir.openFile(sub_path, .{}); }; - defer source_file.close(); + defer source_file.close(io); const stat = try source_file.stat(); @@ -215,7 +215,7 @@ pub fn updateFile( else => |e| return e, // Retryable errors are handled at callsite. }; }; - defer cache_file.close(); + defer cache_file.close(io); // Under `--time-report`, ignore cache hits; do the work anyway for those juicy numbers. const ignore_hit = comp.time_report != null; @@ -2468,7 +2468,7 @@ fn updateEmbedFileInner( const dir, const sub_path = ef.path.openInfo(zcu.comp.dirs); break :f try dir.openFile(sub_path, .{}); }; - defer file.close(); + defer file.close(io); const stat: Cache.File.Stat = .fromFs(try file.stat()); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5dc55b74f6..cb4fe0459f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -799,6 +799,7 @@ pub const Object = struct { pub fn emit(o: *Object, pt: Zcu.PerThread, options: EmitOptions) error{ LinkFailure, OutOfMemory }!void { const zcu = pt.zcu; const comp = zcu.comp; + const io = comp.io; const diags = &comp.link_diags; { @@ -979,7 +980,7 @@ pub const Object = struct { if (options.pre_bc_path) |path| { var file = std.fs.cwd().createFile(path, .{}) catch |err| return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) }); - defer file.close(); + defer file.close(io); const ptr: [*]const u8 = @ptrCast(bitcode.ptr); file.writeAll(ptr[0..(bitcode.len * 4)]) catch |err| @@ -992,7 +993,7 @@ pub const Object = struct { if (options.post_bc_path) |path| { var file = std.fs.cwd().createFile(path, .{}) catch |err| return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) }); - defer file.close(); + defer file.close(io); const ptr: [*]const u8 = @ptrCast(bitcode.ptr); file.writeAll(ptr[0..(bitcode.len * 4)]) catch |err| diff --git a/src/fmt.zig b/src/fmt.zig index 80925200d6..907c7885ad 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -187,7 +187,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! // On Windows, statFile does not work for directories error.IsDir => dir: { var dir = try fs.cwd().openDir(file_path, .{}); - defer dir.close(); + defer dir.close(io); break :dir try dir.stat(); }, else => |e| return e, @@ -222,8 +222,10 @@ fn fmtPathDir( parent_dir: fs.Dir, parent_sub_path: []const u8, ) !void { + const io = fmt.io; + var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true }); - defer dir.close(); + defer dir.close(io); const stat = try dir.stat(); if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; @@ -262,7 +264,7 @@ fn fmtPathFile( const source_file = try dir.openFile(sub_path, .{}); var file_closed = false; - errdefer if (!file_closed) source_file.close(); + errdefer if (!file_closed) source_file.close(io); const stat = try source_file.stat(); @@ -280,7 +282,7 @@ fn fmtPathFile( }; defer gpa.free(source_code); - source_file.close(); + source_file.close(io); file_closed = true; // Add to set after no longer possible to get error.IsDir. diff --git a/src/introspect.zig b/src/introspect.zig index 8467b566c6..9b6797e7d8 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -1,18 +1,21 @@ -const std = @import("std"); const builtin = @import("builtin"); +const build_options = @import("build_options"); + +const std = @import("std"); +const Io = std.Io; const mem = std.mem; -const Allocator = mem.Allocator; +const Allocator = std.mem.Allocator; const os = std.os; const fs = std.fs; const Cache = std.Build.Cache; + const Compilation = @import("Compilation.zig"); const Package = @import("Package.zig"); -const build_options = @import("build_options"); /// Returns the sub_path that worked, or `null` if none did. /// The path of the returned Directory is relative to `base`. /// The handle of the returned Directory is open. -fn testZigInstallPrefix(base_dir: fs.Dir) ?Cache.Directory { +fn testZigInstallPrefix(io: Io, base_dir: Io.Dir) ?Cache.Directory { const test_index_file = "std" ++ fs.path.sep_str ++ "std.zig"; zig_dir: { @@ -20,31 +23,31 @@ fn testZigInstallPrefix(base_dir: fs.Dir) ?Cache.Directory { const lib_zig = "lib" ++ fs.path.sep_str ++ "zig"; var test_zig_dir = base_dir.openDir(lib_zig, .{}) catch break :zig_dir; const file = test_zig_dir.openFile(test_index_file, .{}) catch { - test_zig_dir.close(); + test_zig_dir.close(io); break :zig_dir; }; - file.close(); + file.close(io); return .{ .handle = test_zig_dir, .path = lib_zig }; } // Try lib/std/std.zig var test_zig_dir = base_dir.openDir("lib", .{}) catch return null; const file = test_zig_dir.openFile(test_index_file, .{}) catch { - test_zig_dir.close(); + test_zig_dir.close(io); return null; }; - file.close(); + file.close(io); return .{ .handle = test_zig_dir, .path = "lib" }; } /// Both the directory handle and the path are newly allocated resources which the caller now owns. -pub fn findZigLibDir(gpa: Allocator) !Cache.Directory { +pub fn findZigLibDir(gpa: Allocator, io: Io) !Cache.Directory { const cwd_path = try getResolvedCwd(gpa); defer gpa.free(cwd_path); const self_exe_path = try fs.selfExePathAlloc(gpa); defer gpa.free(self_exe_path); - return findZigLibDirFromSelfExe(gpa, cwd_path, self_exe_path); + return findZigLibDirFromSelfExe(gpa, io, cwd_path, self_exe_path); } /// Like `std.process.getCwdAlloc`, but also resolves the path with `std.fs.path.resolve`. This @@ -73,6 +76,7 @@ pub fn getResolvedCwd(gpa: Allocator) error{ /// Both the directory handle and the path are newly allocated resources which the caller now owns. pub fn findZigLibDirFromSelfExe( allocator: Allocator, + io: Io, /// The return value of `getResolvedCwd`. /// Passed as an argument to avoid pointlessly repeating the call. cwd_path: []const u8, @@ -82,9 +86,9 @@ pub fn findZigLibDirFromSelfExe( var cur_path: []const u8 = self_exe_path; while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) { var base_dir = cwd.openDir(dirname, .{}) catch continue; - defer base_dir.close(); + defer base_dir.close(io); - const sub_directory = testZigInstallPrefix(base_dir) orelse continue; + const sub_directory = testZigInstallPrefix(io, base_dir) orelse continue; const p = try fs.path.join(allocator, &.{ dirname, sub_directory.path.? }); defer allocator.free(p); diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index afeb5b3282..8c5e0afe4b 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -449,7 +449,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); - defer cache.manifest_dir.close(); + defer cache.manifest_dir.close(io); var man = cache.obtain(); defer man.deinit(); @@ -480,7 +480,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .handle = try comp.dirs.global_cache.handle.makeOpenPath(o_sub_path, .{}), .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}), }; - defer o_directory.handle.close(); + defer o_directory.handle.close(io); const abilists_contents = man.files.keys()[abilists_index].contents.?; const metadata = try loadMetaData(gpa, abilists_contents); diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index 64d0fdbeac..bec20ff3d4 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -684,7 +684,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); - defer cache.manifest_dir.close(); + defer cache.manifest_dir.close(io); var man = cache.obtain(); defer man.deinit(); @@ -715,7 +715,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .handle = try comp.dirs.global_cache.handle.makeOpenPath(o_sub_path, .{}), .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}), }; - defer o_directory.handle.close(); + defer o_directory.handle.close(io); const abilists_contents = man.files.keys()[abilists_index].contents.?; const metadata = try loadMetaData(gpa, abilists_contents); diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index b3c018996a..005696e1fc 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -262,7 +262,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); - defer cache.manifest_dir.close(); + defer cache.manifest_dir.close(io); cache.hash.addBytes(build_options.version); cache.hash.addOptionalBytes(comp.dirs.zig_lib.path); @@ -297,7 +297,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { const digest = man.final(); const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); var o_dir = try comp.dirs.global_cache.handle.makeOpenPath(o_sub_path, .{}); - defer o_dir.close(); + defer o_dir.close(io); const aro = @import("aro"); var diagnostics: aro.Diagnostics = .{ @@ -377,7 +377,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { { const lib_final_file = try o_dir.createFile(final_lib_basename, .{ .truncate = true }); - defer lib_final_file.close(); + defer lib_final_file.close(io); var buffer: [1024]u8 = undefined; var file_writer = lib_final_file.writer(&buffer); try implib.writeCoffArchive(gpa, &file_writer.interface, members); diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 8d35e3bd71..67e6a2f903 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -390,7 +390,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); - defer cache.manifest_dir.close(); + defer cache.manifest_dir.close(io); var man = cache.obtain(); defer man.deinit(); @@ -421,7 +421,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .handle = try comp.dirs.global_cache.handle.makeOpenPath(o_sub_path, .{}), .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}), }; - defer o_directory.handle.close(); + defer o_directory.handle.close(io); const abilists_contents = man.files.keys()[abilists_index].contents.?; const metadata = try loadMetaData(gpa, abilists_contents); diff --git a/src/link.zig b/src/link.zig index 6ac96504c7..ef095987c9 100644 --- a/src/link.zig +++ b/src/link.zig @@ -687,7 +687,7 @@ pub const File = struct { .lld => assert(base.file == null), .elf => if (base.file) |f| { dev.check(.elf_linker); - f.close(); + f.close(io); base.file = null; if (base.child_pid) |pid| { @@ -701,7 +701,7 @@ pub const File = struct { }, .macho, .wasm => if (base.file) |f| { dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker }); - f.close(); + f.close(io); base.file = null; if (base.child_pid) |pid| { @@ -866,8 +866,9 @@ pub const File = struct { } pub fn destroy(base: *File) void { + const io = base.comp.io; base.releaseLock(); - if (base.file) |f| f.close(); + if (base.file) |f| f.close(io); switch (base.tag) { .plan9 => unreachable, inline else => |tag| { @@ -1060,9 +1061,10 @@ pub const File = struct { /// Opens a path as an object file and parses it into the linker. fn openLoadObject(base: *File, path: Path) anyerror!void { if (base.tag == .lld) return; + const io = base.comp.io; const diags = &base.comp.link_diags; - const input = try openObjectInput(diags, path); - errdefer input.object.file.close(); + const input = try openObjectInput(io, diags, path); + errdefer input.object.file.close(io); try loadInput(base, input); } @@ -1070,21 +1072,22 @@ pub const File = struct { /// If `query` is non-null, allows GNU ld scripts. fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void { if (base.tag == .lld) return; + const io = base.comp.io; if (opt_query) |query| { - const archive = try openObject(path, query.must_link, query.hidden); - errdefer archive.file.close(); + const archive = try openObject(io, path, query.must_link, query.hidden); + errdefer archive.file.close(io); loadInput(base, .{ .archive = archive }) catch |err| switch (err) { error.BadMagic, error.UnexpectedEndOfFile => { if (base.tag != .elf and base.tag != .elf2) return err; try loadGnuLdScript(base, path, query, archive.file); - archive.file.close(); + archive.file.close(io); return; }, else => return err, }; } else { - const archive = try openObject(path, false, false); - errdefer archive.file.close(); + const archive = try openObject(io, path, false, false); + errdefer archive.file.close(io); try loadInput(base, .{ .archive = archive }); } } @@ -1093,13 +1096,14 @@ pub const File = struct { /// Handles GNU ld scripts. fn openLoadDso(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void { if (base.tag == .lld) return; - const dso = try openDso(path, query.needed, query.weak, query.reexport); - errdefer dso.file.close(); + const io = base.comp.io; + const dso = try openDso(io, path, query.needed, query.weak, query.reexport); + errdefer dso.file.close(io); loadInput(base, .{ .dso = dso }) catch |err| switch (err) { error.BadMagic, error.UnexpectedEndOfFile => { if (base.tag != .elf and base.tag != .elf2) return err; try loadGnuLdScript(base, path, query, dso.file); - dso.file.close(); + dso.file.close(io); return; }, else => return err, @@ -1735,6 +1739,7 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void { pub fn resolveInputs( gpa: Allocator, arena: Allocator, + io: Io, target: *const std.Target, /// This function mutates this array but does not take ownership. /// Allocated with `gpa`. @@ -1784,6 +1789,7 @@ pub fn resolveInputs( for (lib_directories) |lib_directory| switch (try resolveLibInput( gpa, arena, + io, unresolved_inputs, resolved_inputs, &checked_paths, @@ -1810,6 +1816,7 @@ pub fn resolveInputs( for (lib_directories) |lib_directory| switch (try resolveLibInput( gpa, arena, + io, unresolved_inputs, resolved_inputs, &checked_paths, @@ -1837,6 +1844,7 @@ pub fn resolveInputs( switch (try resolveLibInput( gpa, arena, + io, unresolved_inputs, resolved_inputs, &checked_paths, @@ -1855,6 +1863,7 @@ pub fn resolveInputs( switch (try resolveLibInput( gpa, arena, + io, unresolved_inputs, resolved_inputs, &checked_paths, @@ -1886,6 +1895,7 @@ pub fn resolveInputs( if (try resolvePathInput( gpa, arena, + io, unresolved_inputs, resolved_inputs, &ld_script_bytes, @@ -1903,6 +1913,7 @@ pub fn resolveInputs( switch ((try resolvePathInput( gpa, arena, + io, unresolved_inputs, resolved_inputs, &ld_script_bytes, @@ -1930,6 +1941,7 @@ pub fn resolveInputs( if (try resolvePathInput( gpa, arena, + io, unresolved_inputs, resolved_inputs, &ld_script_bytes, @@ -1969,6 +1981,7 @@ const fatal = std.process.fatal; fn resolveLibInput( gpa: Allocator, arena: Allocator, + io: Io, /// Allocated via `gpa`. unresolved_inputs: *std.ArrayList(UnresolvedInput), /// Allocated via `gpa`. @@ -1998,7 +2011,7 @@ fn resolveLibInput( error.FileNotFound => break :tbd, else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }), }; - errdefer file.close(); + errdefer file.close(io); return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); } @@ -2013,7 +2026,7 @@ fn resolveLibInput( }), }; try checked_paths.print(gpa, "\n {f}", .{test_path}); - switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{ + switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{ .path = test_path, .query = name_query.query, }, link_mode, color)) { @@ -2036,7 +2049,7 @@ fn resolveLibInput( test_path, @errorName(e), }), }; - errdefer file.close(); + errdefer file.close(io); return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); } @@ -2052,7 +2065,7 @@ fn resolveLibInput( error.FileNotFound => break :mingw, else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }), }; - errdefer file.close(); + errdefer file.close(io); return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); } @@ -2087,6 +2100,7 @@ fn finishResolveLibInput( fn resolvePathInput( gpa: Allocator, arena: Allocator, + io: Io, /// Allocated with `gpa`. unresolved_inputs: *std.ArrayList(UnresolvedInput), /// Allocated with `gpa`. @@ -2098,12 +2112,12 @@ fn resolvePathInput( color: std.zig.Color, ) Allocator.Error!?ResolveLibInputResult { switch (Compilation.classifyFileExt(pq.path.sub_path)) { - .static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color), - .shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color), + .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color), + .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color), .object => { var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err| fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) }); - errdefer file.close(); + errdefer file.close(io); try resolved_inputs.append(gpa, .{ .object = .{ .path = pq.path, .file = file, @@ -2115,7 +2129,7 @@ fn resolvePathInput( .res => { var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err| fatal("failed to open windows resource {f}: {s}", .{ pq.path, @errorName(err) }); - errdefer file.close(); + errdefer file.close(io); try resolved_inputs.append(gpa, .{ .res = .{ .path = pq.path, .file = file, @@ -2129,6 +2143,7 @@ fn resolvePathInput( fn resolvePathInputLib( gpa: Allocator, arena: Allocator, + io: Io, /// Allocated with `gpa`. unresolved_inputs: *std.ArrayList(UnresolvedInput), /// Allocated with `gpa`. @@ -2155,7 +2170,7 @@ fn resolvePathInputLib( @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e), }), }; - errdefer file.close(); + errdefer file.close(io); try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len)); const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{f}': {s}", .{ std.fmt.alt(test_path, .formatEscapeChar), @errorName(err), @@ -2223,7 +2238,7 @@ fn resolvePathInputLib( } }); } } - file.close(); + file.close(io); return .ok; } @@ -2233,13 +2248,13 @@ fn resolvePathInputLib( @tagName(link_mode), test_path, @errorName(e), }), }; - errdefer file.close(); + errdefer file.close(io); return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); } -pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object { +pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object { var file = try path.root_dir.handle.openFile(path.sub_path, .{}); - errdefer file.close(); + errdefer file.close(io); return .{ .path = path, .file = file, @@ -2248,9 +2263,9 @@ pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object { }; } -pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso { +pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso { var file = try path.root_dir.handle.openFile(path.sub_path, .{}); - errdefer file.close(); + errdefer file.close(io); return .{ .path = path, .file = file, @@ -2260,20 +2275,20 @@ pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso }; } -pub fn openObjectInput(diags: *Diags, path: Path) error{LinkFailure}!Input { - return .{ .object = openObject(path, false, false) catch |err| { +pub fn openObjectInput(io: Io, diags: *Diags, path: Path) error{LinkFailure}!Input { + return .{ .object = openObject(io, path, false, false) catch |err| { return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); } }; } -pub fn openArchiveInput(diags: *Diags, path: Path, must_link: bool, hidden: bool) error{LinkFailure}!Input { - return .{ .archive = openObject(path, must_link, hidden) catch |err| { +pub fn openArchiveInput(io: Io, diags: *Diags, path: Path, must_link: bool, hidden: bool) error{LinkFailure}!Input { + return .{ .archive = openObject(io, path, must_link, hidden) catch |err| { return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); } }; } -pub fn openDsoInput(diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{LinkFailure}!Input { - return .{ .dso = openDso(path, needed, weak, reexport) catch |err| { +pub fn openDsoInput(io: Io, diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{LinkFailure}!Input { + return .{ .dso = openDso(io, path, needed, weak, reexport) catch |err| { return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); } }; } diff --git a/src/link/C.zig b/src/link/C.zig index ce48e85851..04c92443e5 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -124,6 +124,7 @@ pub fn createEmpty( emit: Path, options: link.File.OpenOptions, ) !*C { + const io = comp.io; const target = &comp.root_mod.resolved_target.result; assert(target.ofmt == .c); const optimize_mode = comp.root_mod.optimize_mode; @@ -139,7 +140,7 @@ pub fn createEmpty( // Truncation is done on `flush`. .truncate = false, }); - errdefer file.close(); + errdefer file.close(io); const c_file = try arena.create(C); @@ -763,6 +764,7 @@ pub fn flushEmitH(zcu: *Zcu) !void { if (true) return; // emit-h is regressed const emit_h = zcu.emit_h orelse return; + const io = zcu.comp.io; // We collect a list of buffers to write, and write them all at once with pwritev 😎 const num_buffers = emit_h.decl_table.count() + 1; @@ -795,7 +797,7 @@ pub fn flushEmitH(zcu: *Zcu) !void { // make it easier on the file system by doing 1 reallocation instead of two. .truncate = false, }); - defer file.close(); + defer file.close(io); try file.setEndPos(file_size); try file.pwritevAll(all_buffers.items, 0); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 69acbe034b..ae7d631f09 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -406,10 +406,12 @@ pub fn open( } pub fn deinit(self: *Elf) void { - const gpa = self.base.comp.gpa; + const comp = self.base.comp; + const gpa = comp.gpa; + const io = comp.io; for (self.file_handles.items) |fh| { - fh.close(); + fh.close(io); } self.file_handles.deinit(gpa); diff --git a/src/link/Lld.zig b/src/link/Lld.zig index 2345090482..66b032e0a9 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -1628,7 +1628,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi defer comp.dirs.local_cache.handle.deleteFileZ(rsp_path) catch |err| log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); { - defer rsp_file.close(); + defer rsp_file.close(io); var rsp_file_buffer: [1024]u8 = undefined; var rsp_file_writer = rsp_file.writer(&rsp_file_buffer); const rsp_writer = &rsp_file_writer.interface; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2c4ffd6632..471465cea1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -267,14 +267,16 @@ pub fn open( } pub fn deinit(self: *MachO) void { - const gpa = self.base.comp.gpa; + const comp = self.base.comp; + const gpa = comp.gpa; + const io = comp.io; if (self.d_sym) |*d_sym| { d_sym.deinit(); } for (self.file_handles.items) |handle| { - handle.close(); + handle.close(io); } self.file_handles.deinit(gpa); @@ -3257,8 +3259,10 @@ const InitMetadataOptions = struct { }; pub fn closeDebugInfo(self: *MachO) bool { + const comp = self.base.comp; + const io = comp.io; const d_sym = &(self.d_sym orelse return false); - d_sym.file.?.close(); + d_sym.file.?.close(io); d_sym.file = null; return true; } @@ -3269,7 +3273,9 @@ pub fn reopenDebugInfo(self: *MachO) !void { assert(!self.base.comp.config.use_llvm); assert(self.base.comp.config.debug_format == .dwarf); - const gpa = self.base.comp.gpa; + const comp = self.base.comp; + const io = comp.io; + const gpa = comp.gpa; const sep = fs.path.sep_str; const d_sym_path = try std.fmt.allocPrint( gpa, @@ -3279,7 +3285,7 @@ pub fn reopenDebugInfo(self: *MachO) !void { defer gpa.free(d_sym_path); var d_sym_bundle = try self.base.emit.root_dir.handle.makeOpenPath(d_sym_path, .{}); - defer d_sym_bundle.close(); + defer d_sym_bundle.close(io); self.d_sym.?.file = try d_sym_bundle.createFile(fs.path.basename(self.base.emit.sub_path), .{ .truncate = false, diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 5d7b9b88c3..8a97f2844f 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -1,5 +1,28 @@ +const DebugSymbols = @This(); + +const std = @import("std"); +const Io = std.Io; +const assert = std.debug.assert; +const fs = std.fs; +const log = std.log.scoped(.link_dsym); +const macho = std.macho; +const makeStaticString = MachO.makeStaticString; +const math = std.math; +const mem = std.mem; +const Writer = std.Io.Writer; +const Allocator = std.mem.Allocator; + +const link = @import("../../link.zig"); +const MachO = @import("../MachO.zig"); +const StringTable = @import("../StringTable.zig"); +const Type = @import("../../Type.zig"); +const trace = @import("../../tracy.zig").trace; +const load_commands = @import("load_commands.zig"); +const padToIdeal = MachO.padToIdeal; + +io: Io, allocator: Allocator, -file: ?fs.File, +file: ?Io.File, symtab_cmd: macho.symtab_command = .{}, uuid_cmd: macho.uuid_command = .{ .uuid = [_]u8{0} ** 16 }, @@ -208,7 +231,8 @@ pub fn flush(self: *DebugSymbols, macho_file: *MachO) !void { pub fn deinit(self: *DebugSymbols) void { const gpa = self.allocator; - if (self.file) |file| file.close(); + const io = self.io; + if (self.file) |file| file.close(io); self.segments.deinit(gpa); self.sections.deinit(gpa); self.relocs.deinit(gpa); @@ -443,25 +467,3 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 { assert(sect < self.sections.items.len); return self.sections.items[sect]; } - -const DebugSymbols = @This(); - -const std = @import("std"); -const build_options = @import("build_options"); -const assert = std.debug.assert; -const fs = std.fs; -const link = @import("../../link.zig"); -const load_commands = @import("load_commands.zig"); -const log = std.log.scoped(.link_dsym); -const macho = std.macho; -const makeStaticString = MachO.makeStaticString; -const math = std.math; -const mem = std.mem; -const padToIdeal = MachO.padToIdeal; -const trace = @import("../../tracy.zig").trace; -const Writer = std.Io.Writer; - -const Allocator = mem.Allocator; -const MachO = @import("../MachO.zig"); -const StringTable = @import("../StringTable.zig"); -const Type = @import("../../Type.zig"); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 92307ec40c..160e6cdcc6 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -3032,7 +3032,7 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void { const io = wasm.base.comp.io; const gc_sections = wasm.base.gc_sections; - defer obj.file.close(); + defer obj.file.close(io); var file_reader = obj.file.reader(io, &.{}); @@ -3060,7 +3060,7 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void { const io = wasm.base.comp.io; const gc_sections = wasm.base.gc_sections; - defer obj.file.close(); + defer obj.file.close(io); var file_reader = obj.file.reader(io, &.{}); diff --git a/src/main.zig b/src/main.zig index a897f2a847..3ca64881f8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -328,21 +328,21 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { .prepend_global_cache_path = true, }); } else if (mem.eql(u8, cmd, "init")) { - return cmdInit(gpa, arena, cmd_args); + return cmdInit(gpa, arena, io, cmd_args); } else if (mem.eql(u8, cmd, "targets")) { dev.check(.targets_command); const host = std.zig.resolveTargetQueryOrFatal(io, .{}); - var stdout_writer = fs.File.stdout().writer(&stdout_buffer); - try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); + try @import("print_targets.zig").cmdTargets(arena, io, cmd_args, &stdout_writer.interface, &host); return stdout_writer.interface.flush(); } else if (mem.eql(u8, cmd, "version")) { dev.check(.version_command); - try fs.File.stdout().writeAll(build_options.version ++ "\n"); + try Io.File.stdout().writeAll(build_options.version ++ "\n"); return; } else if (mem.eql(u8, cmd, "env")) { dev.check(.env_command); const host = std.zig.resolveTargetQueryOrFatal(io, .{}); - var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); try @import("print_env.zig").cmdEnv( arena, &stdout_writer.interface, @@ -358,10 +358,10 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { }); } else if (mem.eql(u8, cmd, "zen")) { dev.check(.zen_command); - return fs.File.stdout().writeAll(info_zen); + return Io.File.stdout().writeAll(info_zen); } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) { dev.check(.help_command); - return fs.File.stdout().writeAll(usage); + return Io.File.stdout().writeAll(usage); } else if (mem.eql(u8, cmd, "ast-check")) { return cmdAstCheck(arena, io, cmd_args); } else if (mem.eql(u8, cmd, "detect-cpu")) { @@ -698,7 +698,7 @@ const Emit = union(enum) { yes: []const u8, const OutputToCacheReason = enum { listen, @"zig run", @"zig test" }; - fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: ?OutputToCacheReason) Compilation.CreateOptions.Emit { + fn resolve(io: Io, emit: Emit, default_basename: []const u8, output_to_cache: ?OutputToCacheReason) Compilation.CreateOptions.Emit { return switch (emit) { .no => .no, .yes_default_path => if (output_to_cache != null) .yes_cache else .{ .yes_path = default_basename }, @@ -716,7 +716,7 @@ const Emit = union(enum) { var dir = fs.cwd().openDir(dir_path, .{}) catch |err| { fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) }); }; - dir.close(); + dir.close(io); } break :e .{ .yes_path = path }; }, @@ -1034,7 +1034,7 @@ fn buildOutputType( }; } else if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try fs.File.stdout().writeAll(usage_build_generic); + try Io.File.stdout().writeAll(usage_build_generic); return cleanExit(); } else if (mem.eql(u8, arg, "--")) { if (arg_mode == .run) { @@ -2834,9 +2834,9 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-V")) { warn("ignoring request for supported emulations: unimplemented", .{}); } else if (mem.eql(u8, arg, "-v")) { - try fs.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n"); + try Io.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n"); } else if (mem.eql(u8, arg, "--version")) { - try fs.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n"); + try Io.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n"); process.exit(0); } else { fatal("unsupported linker arg: {s}", .{arg}); @@ -3251,8 +3251,8 @@ fn buildOutputType( } } - var cleanup_emit_bin_dir: ?fs.Dir = null; - defer if (cleanup_emit_bin_dir) |*dir| dir.close(); + var cleanup_emit_bin_dir: ?Io.Dir = null; + defer if (cleanup_emit_bin_dir) |*dir| dir.close(io); // For `zig run` and `zig test`, we don't want to put the binary in the cwd by default. So, if // the binary is requested with no explicit path (as is the default), we emit to the cache. @@ -3307,7 +3307,7 @@ fn buildOutputType( var dir = fs.cwd().openDir(dir_path, .{}) catch |err| { fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) }); }; - dir.close(); + dir.close(io); } break :emit .{ .yes_path = path }; }, @@ -3390,7 +3390,7 @@ fn buildOutputType( // will be a hash of its contents — so multiple invocations of // `zig cc -` will result in the same temp file name. var f = try dirs.local_cache.handle.createFile(dump_path, .{}); - defer f.close(); + defer f.close(io); // Re-using the hasher from Cache, since the functional requirements // for the hashing algorithm here and in the cache are the same. @@ -3399,7 +3399,7 @@ fn buildOutputType( var file_writer = f.writer(&.{}); var buffer: [1000]u8 = undefined; var hasher = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"), &buffer); - var stdin_reader = fs.File.stdin().readerStreaming(io, &.{}); + var stdin_reader = Io.File.stdin().readerStreaming(io, &.{}); _ = hasher.writer.sendFileAll(&stdin_reader, .unlimited) catch |err| switch (err) { error.WriteFailed => fatal("failed to write {s}: {t}", .{ dump_path, file_writer.err.? }), else => fatal("failed to pipe stdin to {s}: {t}", .{ dump_path, err }), @@ -3630,13 +3630,13 @@ fn buildOutputType( if (show_builtin) { const builtin_opts = comp.root_mod.getBuiltinOptions(comp.config); const source = try builtin_opts.generate(arena); - return fs.File.stdout().writeAll(source); + return Io.File.stdout().writeAll(source); } switch (listen) { .none => {}, .stdio => { - var stdin_reader = fs.File.stdin().reader(io, &stdin_buffer); - var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); try serve( comp, &stdin_reader.interface, @@ -4034,6 +4034,7 @@ fn createModule( link.resolveInputs( gpa, arena, + io, target, &unresolved_link_inputs, &create_module.link_inputs, @@ -4689,8 +4690,8 @@ fn cmdTranslateC( @errorName(err), }); }; - defer zig_file.close(); - var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + defer zig_file.close(io); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); var file_reader = zig_file.reader(io, &.{}); _ = try stdout_writer.interface.sendFileAll(&file_reader, .unlimited); try stdout_writer.interface.flush(); @@ -4728,7 +4729,7 @@ const usage_init = \\ ; -fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { +fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { dev.check(.init_command); var template: enum { example, minimal } = .example; @@ -4740,7 +4741,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { if (mem.eql(u8, arg, "-m") or mem.eql(u8, arg, "--minimal")) { template = .minimal; } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try fs.File.stdout().writeAll(usage_init); + try Io.File.stdout().writeAll(usage_init); return cleanExit(); } else { fatal("unrecognized parameter: '{s}'", .{arg}); @@ -4759,7 +4760,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { switch (template) { .example => { - var templates = findTemplates(gpa, arena); + var templates = findTemplates(gpa, arena, io); defer templates.deinit(); const s = fs.path.sep_str; @@ -4789,7 +4790,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { return cleanExit(); }, .minimal => { - writeSimpleTemplateFile(Package.Manifest.basename, + writeSimpleTemplateFile(io, Package.Manifest.basename, \\.{{ \\ .name = .{s}, \\ .version = "0.0.1", @@ -4806,7 +4807,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { else => fatal("failed to create '{s}': {s}", .{ Package.Manifest.basename, @errorName(err) }), error.PathAlreadyExists => fatal("refusing to overwrite '{s}'", .{Package.Manifest.basename}), }; - writeSimpleTemplateFile(Package.build_zig_basename, + writeSimpleTemplateFile(io, Package.build_zig_basename, \\const std = @import("std"); \\ \\pub fn build(b: *std.Build) void {{ @@ -5203,8 +5204,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) .parent = root_mod, }); - var cleanup_build_dir: ?fs.Dir = null; - defer if (cleanup_build_dir) |*dir| dir.close(); + var cleanup_build_dir: ?Io.Dir = null; + defer if (cleanup_build_dir) |*dir| dir.close(io); if (dev.env.supports(.fetch_command)) { const fetch_prog_node = root_prog_node.start("Fetch Packages", 0); @@ -5296,6 +5297,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) try job_queue.createDependenciesSource(&source_buf); const deps_mod = try createDependenciesModule( arena, + io, source_buf.items, root_mod, dirs, @@ -5357,6 +5359,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) } } else try createEmptyDependenciesModule( arena, + io, root_mod, dirs, config, @@ -5623,7 +5626,7 @@ fn jitCmd( defer comp.destroy(); if (options.server) { - var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); var server: std.zig.Server = .{ .out = &stdout_writer.interface, .in = undefined, // won't be receiving messages @@ -6156,7 +6159,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try fs.File.stdout().writeAll(usage_ast_check); + try Io.File.stdout().writeAll(usage_ast_check); return cleanExit(); } else if (mem.eql(u8, arg, "-t")) { want_output_text = true; @@ -6187,9 +6190,9 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void { break :file fs.cwd().openFile(p, .{}) catch |err| { fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) }); }; - } else fs.File.stdin(); - defer if (zig_source_path != null) f.close(); - var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer); + } else Io.File.stdin(); + defer if (zig_source_path != null) f.close(io); + var file_reader: Io.File.Reader = f.reader(io, &stdin_buffer); break :s std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err| { fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) }); }; @@ -6207,7 +6210,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void { const tree = try Ast.parse(arena, source, mode); - var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); const stdout_bw = &stdout_writer.interface; switch (mode) { .zig => { @@ -6330,7 +6333,7 @@ fn cmdDetectCpu(io: Io, args: []const []const u8) !void { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try fs.File.stdout().writeAll(detect_cpu_usage); + try Io.File.stdout().writeAll(detect_cpu_usage); return cleanExit(); } else if (mem.eql(u8, arg, "--llvm")) { use_llvm = true; @@ -6422,7 +6425,7 @@ fn detectNativeCpuWithLLVM( } fn printCpu(cpu: std.Target.Cpu) !void { - var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); const stdout_bw = &stdout_writer.interface; if (cpu.model.llvm_name) |llvm_name| { @@ -6471,7 +6474,7 @@ fn cmdDumpLlvmInts( const dl = tm.createTargetDataLayout(); const context = llvm.Context.create(); - var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); const stdout_bw = &stdout_writer.interface; for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| { const int_type = context.intType(bits); @@ -6494,10 +6497,10 @@ fn cmdDumpZir(arena: Allocator, io: Io, args: []const []const u8) !void { var f = fs.cwd().openFile(cache_file, .{}) catch |err| { fatal("unable to open zir cache file for dumping '{s}': {s}", .{ cache_file, @errorName(err) }); }; - defer f.close(); + defer f.close(io); const zir = try Zcu.loadZirCache(arena, io, f); - var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); const stdout_bw = &stdout_writer.interface; { const instruction_bytes = zir.instructions.len * @@ -6540,16 +6543,16 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void { const old_source = source: { var f = fs.cwd().openFile(old_source_path, .{}) catch |err| fatal("unable to open old source file '{s}': {s}", .{ old_source_path, @errorName(err) }); - defer f.close(); - var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer); + defer f.close(io); + var file_reader: Io.File.Reader = f.reader(io, &stdin_buffer); break :source std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err| fatal("unable to read old source file '{s}': {s}", .{ old_source_path, @errorName(err) }); }; const new_source = source: { var f = fs.cwd().openFile(new_source_path, .{}) catch |err| fatal("unable to open new source file '{s}': {s}", .{ new_source_path, @errorName(err) }); - defer f.close(); - var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer); + defer f.close(io); + var file_reader: Io.File.Reader = f.reader(io, &stdin_buffer); break :source std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err| fatal("unable to read new source file '{s}': {s}", .{ new_source_path, @errorName(err) }); }; @@ -6581,7 +6584,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void { var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty; try Zcu.mapOldZirToNew(arena, old_zir, new_zir, &inst_map); - var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); const stdout_bw = &stdout_writer.interface; { try stdout_bw.print("Instruction mappings:\n", .{}); @@ -6912,7 +6915,7 @@ fn cmdFetch( const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try fs.File.stdout().writeAll(usage_fetch); + try Io.File.stdout().writeAll(usage_fetch); return cleanExit(); } else if (mem.eql(u8, arg, "--global-cache-dir")) { if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); @@ -6958,7 +6961,7 @@ fn cmdFetch( .path = p, }; }; - defer global_cache_directory.handle.close(); + defer global_cache_directory.handle.close(io); var job_queue: Package.Fetch.JobQueue = .{ .io = io, @@ -7021,7 +7024,7 @@ fn cmdFetch( const name = switch (save) { .no => { - var stdout = fs.File.stdout().writerStreaming(&stdout_buffer); + var stdout = Io.File.stdout().writerStreaming(&stdout_buffer); try stdout.interface.print("{s}\n", .{package_hash_slice}); try stdout.interface.flush(); return cleanExit(); @@ -7043,7 +7046,7 @@ fn cmdFetch( // The name to use in case the manifest file needs to be created now. const init_root_name = fs.path.basename(build_root.directory.path orelse cwd_path); - var manifest, var ast = try loadManifest(gpa, arena, .{ + var manifest, var ast = try loadManifest(gpa, arena, io, .{ .root_name = try sanitizeExampleName(arena, init_root_name), .dir = build_root.directory.handle, .color = color, @@ -7168,6 +7171,7 @@ fn cmdFetch( fn createEmptyDependenciesModule( arena: Allocator, + io: Io, main_mod: *Package.Module, dirs: Compilation.Directories, global_options: Compilation.Config, @@ -7176,6 +7180,7 @@ fn createEmptyDependenciesModule( try Package.Fetch.JobQueue.createEmptyDependenciesSource(&source); _ = try createDependenciesModule( arena, + io, source.items, main_mod, dirs, @@ -7187,6 +7192,7 @@ fn createEmptyDependenciesModule( /// build runner to obtain via `@import("@dependencies")`. fn createDependenciesModule( arena: Allocator, + io: Io, source: []const u8, main_mod: *Package.Module, dirs: Compilation.Directories, @@ -7198,7 +7204,7 @@ fn createDependenciesModule( const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); { var tmp_dir = try dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}); - defer tmp_dir.close(); + defer tmp_dir.close(io); try tmp_dir.writeFile(.{ .sub_path = basename, .data = source }); } @@ -7232,10 +7238,10 @@ fn createDependenciesModule( const BuildRoot = struct { directory: Cache.Directory, build_zig_basename: []const u8, - cleanup_build_dir: ?fs.Dir, + cleanup_build_dir: ?Io.Dir, - fn deinit(br: *BuildRoot) void { - if (br.cleanup_build_dir) |*dir| dir.close(); + fn deinit(br: *BuildRoot, io: Io) void { + if (br.cleanup_build_dir) |*dir| dir.close(io); br.* = undefined; } }; @@ -7304,13 +7310,14 @@ fn findBuildRoot(arena: Allocator, options: FindBuildRootOptions) !BuildRoot { const LoadManifestOptions = struct { root_name: []const u8, - dir: fs.Dir, + dir: Io.Dir, color: Color, }; fn loadManifest( gpa: Allocator, arena: Allocator, + io: Io, options: LoadManifestOptions, ) !struct { Package.Manifest, Ast } { const manifest_bytes = while (true) { @@ -7322,7 +7329,7 @@ fn loadManifest( 0, ) catch |err| switch (err) { error.FileNotFound => { - writeSimpleTemplateFile(Package.Manifest.basename, + writeSimpleTemplateFile(io, Package.Manifest.basename, \\.{{ \\ .name = .{s}, \\ .version = "{s}", @@ -7374,12 +7381,12 @@ fn loadManifest( const Templates = struct { zig_lib_directory: Cache.Directory, - dir: fs.Dir, + dir: Io.Dir, buffer: std.array_list.Managed(u8), - fn deinit(templates: *Templates) void { - templates.zig_lib_directory.handle.close(); - templates.dir.close(); + fn deinit(templates: *Templates, io: Io) void { + templates.zig_lib_directory.handle.close(io); + templates.dir.close(io); templates.buffer.deinit(); templates.* = undefined; } @@ -7387,7 +7394,7 @@ const Templates = struct { fn write( templates: *Templates, arena: Allocator, - out_dir: fs.Dir, + out_dir: Io.Dir, root_name: []const u8, template_path: []const u8, fingerprint: Package.Fingerprint, @@ -7435,23 +7442,23 @@ const Templates = struct { }); } }; -fn writeSimpleTemplateFile(file_name: []const u8, comptime fmt: []const u8, args: anytype) !void { +fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime fmt: []const u8, args: anytype) !void { const f = try fs.cwd().createFile(file_name, .{ .exclusive = true }); - defer f.close(); + defer f.close(io); var buf: [4096]u8 = undefined; var fw = f.writer(&buf); try fw.interface.print(fmt, args); try fw.interface.flush(); } -fn findTemplates(gpa: Allocator, arena: Allocator) Templates { +fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates { const cwd_path = introspect.getResolvedCwd(arena) catch |err| { fatal("unable to get cwd: {s}", .{@errorName(err)}); }; const self_exe_path = fs.selfExePathAlloc(arena) catch |err| { fatal("unable to find self exe path: {s}", .{@errorName(err)}); }; - var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, cwd_path, self_exe_path) catch |err| { + var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, io, cwd_path, self_exe_path) catch |err| { fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) }); }; diff --git a/src/print_targets.zig b/src/print_targets.zig index d9118b901b..2f80187de1 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -12,6 +12,7 @@ const introspect = @import("introspect.zig"); pub fn cmdTargets( allocator: Allocator, + io: Io, args: []const []const u8, out: *std.Io.Writer, native_target: *const Target, @@ -20,7 +21,7 @@ pub fn cmdTargets( var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| { fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; - defer zig_lib_directory.handle.close(); + defer zig_lib_directory.handle.close(io); defer allocator.free(zig_lib_directory.path.?); const abilists_contents = zig_lib_directory.handle.readFileAlloc( -- cgit v1.2.3 From 3204fb756980c19b7a95534acdd7a1bba837fbc3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 6 Dec 2025 17:23:07 -0800 Subject: update all occurrences of std.fs.File to std.Io.File --- lib/compiler/aro/aro/Compilation.zig | 4 +- lib/compiler/aro/aro/Driver.zig | 15 +++---- lib/compiler/aro/aro/Preprocessor.zig | 3 +- lib/compiler/aro/backend/Assembly.zig | 3 +- lib/compiler/aro/main.zig | 3 +- lib/compiler/build_runner.zig | 6 +-- lib/compiler/libc.zig | 3 +- lib/compiler/objcopy.zig | 13 +++--- lib/compiler/reduce.zig | 3 +- lib/compiler/resinator/cli.zig | 3 +- lib/compiler/resinator/errors.zig | 8 ++-- lib/compiler/resinator/main.zig | 10 ++--- lib/compiler/resinator/utils.zig | 4 +- lib/compiler/test_runner.zig | 10 ++--- lib/compiler/translate-c/main.zig | 13 +++--- lib/std/Build.zig | 6 +-- lib/std/Build/Step.zig | 2 +- lib/std/Build/Step/ObjCopy.zig | 2 +- lib/std/Build/Step/Run.zig | 21 +++++----- lib/std/Io.zig | 2 +- lib/std/Io/Writer.zig | 9 ++-- lib/std/Io/test.zig | 2 +- lib/std/Io/tty.zig | 7 ++-- lib/std/Progress.zig | 18 ++++---- lib/std/Random/benchmark.zig | 6 ++- lib/std/Thread.zig | 4 +- lib/std/crypto/Certificate/Bundle.zig | 8 ++-- lib/std/crypto/Certificate/Bundle/macos.zig | 2 +- lib/std/crypto/benchmark.zig | 8 ++-- lib/std/debug.zig | 6 +-- lib/std/debug/ElfFile.zig | 4 +- lib/std/debug/Info.zig | 2 +- lib/std/debug/Pdb.zig | 2 +- lib/std/debug/SelfInfo/Windows.zig | 2 +- lib/std/debug/simple_panic.zig | 2 +- lib/std/dynamic_library.zig | 2 +- lib/std/fs/test.zig | 8 ++-- lib/std/hash/benchmark.zig | 5 ++- lib/std/http.zig | 2 +- lib/std/pdb.zig | 2 +- lib/std/posix/test.zig | 18 ++++---- lib/std/process/Child.zig | 4 +- lib/std/unicode/throughput_test.zig | 6 +-- lib/std/zig/Zir.zig | 8 ++-- lib/std/zig/Zoir.zig | 15 +++---- lib/std/zig/llvm/Builder.zig | 4 +- lib/std/zig/perf_test.zig | 3 +- lib/std/zip.zig | 5 ++- src/Compilation.zig | 4 +- src/Package/Fetch.zig | 16 +++---- src/Package/Fetch/git.zig | 22 +++++----- src/Zcu.zig | 8 ++-- src/Zcu/PerThread.zig | 6 +-- src/fmt.zig | 16 +++---- src/link.zig | 20 ++++----- src/link/Dwarf.zig | 53 ++++++++++++----------- src/link/Elf.zig | 5 ++- src/link/Elf/Object.zig | 65 +++++++++++++++-------------- src/link/Elf/SharedObject.zig | 37 ++++++++-------- src/link/Elf/file.zig | 35 ++++++++-------- src/link/Elf2.zig | 46 ++++++++++---------- src/link/MachO.zig | 23 +++++----- src/link/MachO/CodeSignature.zig | 8 ++-- src/link/MachO/fat.zig | 12 +++--- src/link/MachO/file.zig | 3 +- src/link/MachO/hasher.zig | 22 +++++----- src/link/MachO/uuid.zig | 19 ++++----- src/link/MappedFile.zig | 25 ++++++----- src/link/Wasm.zig | 5 ++- src/link/tapi.zig | 10 ++--- 70 files changed, 399 insertions(+), 359 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/aro/aro/Compilation.zig b/lib/compiler/aro/aro/Compilation.zig index 9fb8123146..06b27c33bb 100644 --- a/lib/compiler/aro/aro/Compilation.zig +++ b/lib/compiler/aro/aro/Compilation.zig @@ -1646,7 +1646,7 @@ fn addSourceFromPathExtra(comp: *Compilation, path: []const u8, kind: Source.Kin return comp.addSourceFromFile(file, path, kind); } -pub fn addSourceFromFile(comp: *Compilation, file: std.fs.File, path: []const u8, kind: Source.Kind) !Source { +pub fn addSourceFromFile(comp: *Compilation, file: Io.File, path: []const u8, kind: Source.Kind) !Source { const contents = try comp.getFileContents(file, .unlimited); errdefer comp.gpa.free(contents); return comp.addSourceFromOwnedBuffer(path, contents, kind); @@ -1980,7 +1980,7 @@ fn getPathContents(comp: *Compilation, path: []const u8, limit: Io.Limit) ![]u8 return comp.getFileContents(file, limit); } -fn getFileContents(comp: *Compilation, file: std.fs.File, limit: Io.Limit) ![]u8 { +fn getFileContents(comp: *Compilation, file: Io.File, limit: Io.Limit) ![]u8 { var file_buf: [4096]u8 = undefined; var file_reader = file.reader(comp.io, &file_buf); diff --git a/lib/compiler/aro/aro/Driver.zig b/lib/compiler/aro/aro/Driver.zig index 888ade2be4..f933e3ce52 100644 --- a/lib/compiler/aro/aro/Driver.zig +++ b/lib/compiler/aro/aro/Driver.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const mem = std.mem; const Allocator = mem.Allocator; const process = std.process; @@ -1061,7 +1062,7 @@ pub fn printDiagnosticsStats(d: *Driver) void { } } -pub fn detectConfig(d: *Driver, file: std.fs.File) std.Io.tty.Config { +pub fn detectConfig(d: *Driver, file: Io.File) std.Io.tty.Config { if (d.diagnostics.color == false) return .no_color; const force_color = d.diagnostics.color == true; @@ -1109,7 +1110,7 @@ pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8, comptime fast_ defer macro_buf.deinit(d.comp.gpa); var stdout_buf: [256]u8 = undefined; - var stdout = std.fs.File.stdout().writer(&stdout_buf); + var stdout = Io.File.stdout().writer(&stdout_buf); if (parseArgs(d, &stdout.interface, ¯o_buf, args) catch |er| switch (er) { error.WriteFailed => return d.fatal("failed to write to stdout: {s}", .{errorDescription(er)}), error.OutOfMemory => return error.OutOfMemory, @@ -1329,7 +1330,7 @@ fn processSource( d.comp.cwd.createFile(path, .{}) catch |er| return d.fatal("unable to create dependency file '{s}': {s}", .{ path, errorDescription(er) }) else - std.fs.File.stdout(); + Io.File.stdout(); defer if (dep_file_name != null) file.close(io); var file_writer = file.writer(&writer_buf); @@ -1354,7 +1355,7 @@ fn processSource( d.comp.cwd.createFile(some, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) }) else - std.fs.File.stdout(); + Io.File.stdout(); defer if (d.output_name != null) file.close(io); var file_writer = file.writer(&writer_buf); @@ -1369,7 +1370,7 @@ fn processSource( defer tree.deinit(); if (d.verbose_ast) { - var stdout = std.fs.File.stdout().writer(&writer_buf); + var stdout = Io.File.stdout().writer(&writer_buf); tree.dump(d.detectConfig(stdout.file), &stdout.interface) catch {}; } @@ -1433,7 +1434,7 @@ fn processSource( defer ir.deinit(gpa); if (d.verbose_ir) { - var stdout = std.fs.File.stdout().writer(&writer_buf); + var stdout = Io.File.stdout().writer(&writer_buf); ir.dump(gpa, d.detectConfig(stdout.file), &stdout.interface) catch {}; } @@ -1499,7 +1500,7 @@ pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) Compil if (d.verbose_linker_args) { var stdout_buf: [4096]u8 = undefined; - var stdout = std.fs.File.stdout().writer(&stdout_buf); + var stdout = Io.File.stdout().writer(&stdout_buf); dumpLinkerArgs(&stdout.interface, argv.items) catch { return d.fatal("unable to dump linker args: {s}", .{errorDescription(stdout.err.?)}); }; diff --git a/lib/compiler/aro/aro/Preprocessor.zig b/lib/compiler/aro/aro/Preprocessor.zig index 6bd1206aff..d47727cbf0 100644 --- a/lib/compiler/aro/aro/Preprocessor.zig +++ b/lib/compiler/aro/aro/Preprocessor.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const mem = std.mem; const Allocator = mem.Allocator; const assert = std.debug.assert; @@ -1068,7 +1069,7 @@ fn verboseLog(pp: *Preprocessor, raw: RawToken, comptime fmt: []const u8, args: const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start }); var stderr_buf: [4096]u8 = undefined; - var stderr = std.fs.File.stderr().writer(&stderr_buf); + var stderr = Io.File.stderr().writer(&stderr_buf); const w = &stderr.interface; w.print("{s}:{d}:{d}: ", .{ source.path, line_col.line_no, line_col.col }) catch return; diff --git a/lib/compiler/aro/backend/Assembly.zig b/lib/compiler/aro/backend/Assembly.zig index d0d14bdd77..80143bf97f 100644 --- a/lib/compiler/aro/backend/Assembly.zig +++ b/lib/compiler/aro/backend/Assembly.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const Allocator = std.mem.Allocator; data: []const u8, @@ -11,7 +12,7 @@ pub fn deinit(self: *const Assembly, gpa: Allocator) void { gpa.free(self.text); } -pub fn writeToFile(self: Assembly, file: std.fs.File) !void { +pub fn writeToFile(self: Assembly, file: Io.File) !void { var file_writer = file.writer(&.{}); var buffers = [_][]const u8{ self.data, self.text }; diff --git a/lib/compiler/aro/main.zig b/lib/compiler/aro/main.zig index d3655e43da..66c8add4a3 100644 --- a/lib/compiler/aro/main.zig +++ b/lib/compiler/aro/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const Allocator = mem.Allocator; const mem = std.mem; const process = std.process; @@ -50,7 +51,7 @@ pub fn main() u8 { defer gpa.free(aro_name); var stderr_buf: [1024]u8 = undefined; - var stderr = std.fs.File.stderr().writer(&stderr_buf); + var stderr = Io.File.stderr().writer(&stderr_buf); var diagnostics: Diagnostics = .{ .output = .{ .to_writer = .{ .color = .detect(stderr.file), diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 2c8d71e3e2..6e0e2d8eca 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -7,7 +7,7 @@ const assert = std.debug.assert; const fmt = std.fmt; const mem = std.mem; const process = std.process; -const File = std.fs.File; +const File = std.Io.File; const Step = std.Build.Step; const Watch = std.Build.Watch; const WebServer = std.Build.WebServer; @@ -1845,9 +1845,9 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void { } var stdio_buffer_allocation: [256]u8 = undefined; -var stdout_writer_allocation: std.fs.File.Writer = undefined; +var stdout_writer_allocation: Io.File.Writer = undefined; fn initStdoutWriter() *Writer { - stdout_writer_allocation = std.fs.File.stdout().writerStreaming(&stdio_buffer_allocation); + stdout_writer_allocation = Io.File.stdout().writerStreaming(&stdio_buffer_allocation); return &stdout_writer_allocation.interface; } diff --git a/lib/compiler/libc.zig b/lib/compiler/libc.zig index a18a7a0e06..142b87062e 100644 --- a/lib/compiler/libc.zig +++ b/lib/compiler/libc.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const mem = std.mem; const LibCInstallation = std.zig.LibCInstallation; @@ -39,7 +40,7 @@ pub fn main() !void { var input_file: ?[]const u8 = null; var target_arch_os_abi: []const u8 = "native"; var print_includes: bool = false; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; { var i: usize = 2; diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index 1608c121b1..c360ea8df0 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -1,12 +1,13 @@ const builtin = @import("builtin"); + const std = @import("std"); +const Io = std.Io; const mem = std.mem; const fs = std.fs; const elf = std.elf; const Allocator = std.mem.Allocator; -const File = std.fs.File; +const File = std.Io.File; const assert = std.debug.assert; - const fatal = std.process.fatal; const Server = std.zig.Server; @@ -56,7 +57,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void fatal("unexpected positional argument: '{s}'", .{arg}); } } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - return std.fs.File.stdout().writeAll(usage); + return Io.File.stdout().writeAll(usage); } else if (mem.eql(u8, arg, "-O") or mem.eql(u8, arg, "--output-target")) { i += 1; if (i >= args.len) fatal("expected another argument after '{s}'", .{arg}); @@ -177,7 +178,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void } }; - const mode = if (out_fmt != .elf or only_keep_debug) fs.File.default_mode else stat.mode; + const mode = if (out_fmt != .elf or only_keep_debug) Io.File.default_mode else stat.mode; var output_file = try fs.cwd().createFile(output, .{ .mode = mode }); defer output_file.close(io); @@ -221,8 +222,8 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void try out.end(); if (listen) { - var stdin_reader = fs.File.stdin().reader(io, &stdin_buffer); - var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + var stdin_reader = Io.File.stdin().reader(io, &stdin_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); var server = try Server.init(.{ .in = &stdin_reader.interface, .out = &stdout_writer.interface, diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig index 28305e801b..bbd3d172b4 100644 --- a/lib/compiler/reduce.zig +++ b/lib/compiler/reduce.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const mem = std.mem; const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -68,7 +69,7 @@ pub fn main() !void { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - const stdout = std.fs.File.stdout(); + const stdout = Io.File.stdout(); try stdout.writeAll(usage); return std.process.cleanExit(); } else if (mem.eql(u8, arg, "--")) { diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index ffaa62e7ca..17da5c7b75 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const code_pages = @import("code_pages.zig"); const SupportedCodePage = code_pages.SupportedCodePage; const lang = @import("lang.zig"); @@ -169,7 +170,7 @@ pub const Options = struct { coff_options: cvtres.CoffOptions = .{}, pub const IoSource = union(enum) { - stdio: std.fs.File, + stdio: Io.File, filename: []const u8, }; pub const AutoIncludes = enum { any, msvc, gnu, none }; diff --git a/lib/compiler/resinator/errors.zig b/lib/compiler/resinator/errors.zig index 8509aa610f..61fd00e683 100644 --- a/lib/compiler/resinator/errors.zig +++ b/lib/compiler/resinator/errors.zig @@ -169,9 +169,9 @@ pub const ErrorDetails = struct { filename_string_index: FilenameStringIndex, pub const FilenameStringIndex = std.meta.Int(.unsigned, 32 - @bitSizeOf(FileOpenErrorEnum)); - pub const FileOpenErrorEnum = std.meta.FieldEnum(std.fs.File.OpenError || std.fs.File.StatError); + pub const FileOpenErrorEnum = std.meta.FieldEnum(Io.File.OpenError || Io.File.StatError); - pub fn enumFromError(err: (std.fs.File.OpenError || std.fs.File.StatError)) FileOpenErrorEnum { + pub fn enumFromError(err: (Io.File.OpenError || Io.File.StatError)) FileOpenErrorEnum { return switch (err) { inline else => |e| @field(ErrorDetails.FileOpenError.FileOpenErrorEnum, @errorName(e)), }; @@ -1094,8 +1094,8 @@ const CorrespondingLines = struct { last_byte: u8 = 0, at_eof: bool = false, span: SourceMappings.CorrespondingSpan, - file: std.fs.File, - file_reader: std.fs.File.Reader, + file: Io.File, + file_reader: Io.File.Reader, code_page: SupportedCodePage, pub fn init( diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 42308a8987..e171d8199c 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -45,7 +45,7 @@ pub fn main() !void { } var stdout_buffer: [1024]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; var error_handler: ErrorHandler = switch (zig_integration) { true => .{ @@ -447,8 +447,8 @@ const IoStream = struct { } pub const Source = union(enum) { - file: std.fs.File, - stdio: std.fs.File, + file: Io.File, + stdio: Io.File, memory: std.ArrayList(u8), /// The source has been closed and any usage of the Source in this state is illegal (except deinit). closed: void, @@ -500,10 +500,10 @@ const IoStream = struct { } pub const Writer = union(enum) { - file: std.fs.File.Writer, + file: Io.File.Writer, allocating: std.Io.Writer.Allocating, - pub const Error = Allocator.Error || std.fs.File.WriteError; + pub const Error = Allocator.Error || Io.File.WriteError; pub fn interface(this: *@This()) *std.Io.Writer { return switch (this.*) { diff --git a/lib/compiler/resinator/utils.zig b/lib/compiler/resinator/utils.zig index f8080539cb..e8565f3af9 100644 --- a/lib/compiler/resinator/utils.zig +++ b/lib/compiler/resinator/utils.zig @@ -32,8 +32,8 @@ pub fn openFileNotDir( cwd: std.fs.Dir, io: Io, path: []const u8, - flags: std.fs.File.OpenFlags, -) (std.fs.File.OpenError || std.fs.File.StatError)!std.fs.File { + flags: Io.File.OpenFlags, +) (Io.File.OpenError || Io.File.StatError)!Io.File { const file = try cwd.openFile(io, path, flags); errdefer file.close(io); // https://github.com/ziglang/zig/issues/5732 diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index 72ed3e7677..07a6724ec0 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -74,8 +74,8 @@ pub fn main() void { fn mainServer() !void { @disableInstrumentation(); - var stdin_reader = std.fs.File.stdin().readerStreaming(runner_threaded_io.io(), &stdin_buffer); - var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); + var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io.io(), &stdin_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); var server = try std.zig.Server.init(.{ .in = &stdin_reader.interface, .out = &stdout_writer.interface, @@ -228,7 +228,7 @@ fn mainTerminal() void { .root_name = "Test", .estimated_total_items = test_fn_list.len, }); - const have_tty = std.fs.File.stderr().isTty(); + const have_tty = Io.File.stderr().isTty(); var leaks: usize = 0; for (test_fn_list, 0..) |test_fn, i| { @@ -318,7 +318,7 @@ pub fn log( /// work-in-progress backends can handle it. pub fn mainSimple() anyerror!void { @disableInstrumentation(); - // is the backend capable of calling `std.fs.File.writeAll`? + // is the backend capable of calling `Io.File.writeAll`? const enable_write = switch (builtin.zig_backend) { .stage2_aarch64, .stage2_riscv64 => true, else => false, @@ -334,7 +334,7 @@ pub fn mainSimple() anyerror!void { var failed: u64 = 0; // we don't want to bring in File and Writer if the backend doesn't support it - const stdout = if (enable_write) std.fs.File.stdout() else {}; + const stdout = if (enable_write) Io.File.stdout() else {}; for (builtin.test_functions) |test_fn| { if (enable_write) { diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig index 0c72298b30..830c70e424 100644 --- a/lib/compiler/translate-c/main.zig +++ b/lib/compiler/translate-c/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const mem = std.mem; const process = std.process; @@ -34,7 +35,7 @@ pub fn main() u8 { } var stderr_buf: [1024]u8 = undefined; - var stderr = std.fs.File.stderr().writer(&stderr_buf); + var stderr = Io.File.stderr().writer(&stderr_buf); var diagnostics: aro.Diagnostics = switch (zig_integration) { false => .{ .output = .{ .to_writer = .{ .color = .detect(stderr.file), @@ -99,7 +100,7 @@ fn serveErrorBundle(arena: std.mem.Allocator, diagnostics: *const aro.Diagnostic "translation failure", ); var stdout_buffer: [1024]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); var server: std.zig.Server = .{ .out = &stdout_writer.interface, .in = undefined, @@ -129,13 +130,13 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration args[i] = arg; if (mem.eql(u8, arg, "--help")) { var stdout_buf: [512]u8 = undefined; - var stdout = std.fs.File.stdout().writer(&stdout_buf); + var stdout = Io.File.stdout().writer(&stdout_buf); try stdout.interface.print(usage, .{args[0]}); try stdout.interface.flush(); return; } else if (mem.eql(u8, arg, "--version")) { var stdout_buf: [512]u8 = undefined; - var stdout = std.fs.File.stdout().writer(&stdout_buf); + var stdout = Io.File.stdout().writer(&stdout_buf); // TODO add version try stdout.interface.writeAll("0.0.0-dev\n"); try stdout.interface.flush(); @@ -228,7 +229,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration d.comp.cwd.createFile(path, .{}) catch |er| return d.fatal("unable to create dependency file '{s}': {s}", .{ path, aro.Driver.errorDescription(er) }) else - std.fs.File.stdout(); + Io.File.stdout(); defer if (dep_file_name != null) file.close(io); var file_writer = file.writer(&out_buf); @@ -246,7 +247,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration var close_out_file = false; var out_file_path: []const u8 = ""; - var out_file: std.fs.File = .stdout(); + var out_file: Io.File = .stdout(); defer if (close_out_file) out_file.close(io); if (d.output_name) |path| blk: { diff --git a/lib/std/Build.zig b/lib/std/Build.zig index fcd94ce134..2755b895c2 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1,3 +1,4 @@ +const Build = @This(); const builtin = @import("builtin"); const std = @import("std.zig"); @@ -9,13 +10,12 @@ const panic = std.debug.panic; const assert = debug.assert; const log = std.log; const StringHashMap = std.StringHashMap; -const Allocator = mem.Allocator; +const Allocator = std.mem.Allocator; const Target = std.Target; const process = std.process; const EnvMap = std.process.EnvMap; -const File = fs.File; +const File = std.Io.File; const Sha256 = std.crypto.hash.sha2.Sha256; -const Build = @This(); const ArrayList = std.ArrayList; pub const Cache = @import("Build/Cache.zig"); diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index acde47071d..2b1e6d8ace 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -667,7 +667,7 @@ fn clearZigProcess(s: *Step, gpa: Allocator) void { } } -fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { +fn sendMessage(file: Io.File, tag: std.zig.Client.Message.Tag) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 0, diff --git a/lib/std/Build/Step/ObjCopy.zig b/lib/std/Build/Step/ObjCopy.zig index b5f058ddfc..4aa1c0a9dc 100644 --- a/lib/std/Build/Step/ObjCopy.zig +++ b/lib/std/Build/Step/ObjCopy.zig @@ -3,7 +3,7 @@ const ObjCopy = @This(); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; -const File = std.fs.File; +const File = std.Io.File; const InstallDir = std.Build.InstallDir; const Step = std.Build.Step; const elf = std.elf; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index e66e30cc79..1df6f42a35 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1,15 +1,16 @@ -const std = @import("std"); +const Run = @This(); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const Build = std.Build; -const Step = Build.Step; +const Step = std.Build.Step; const fs = std.fs; const mem = std.mem; const process = std.process; -const EnvMap = process.EnvMap; +const EnvMap = std.process.EnvMap; const assert = std.debug.assert; -const Path = Build.Cache.Path; - -const Run = @This(); +const Path = std.Build.Cache.Path; pub const base_id: Step.Id = .run; @@ -2095,7 +2096,7 @@ pub const CachedTestMetadata = struct { } }; -fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { +fn requestNextTest(in: Io.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { while (metadata.next_index < metadata.names.len) { const i = metadata.next_index; metadata.next_index += 1; @@ -2114,7 +2115,7 @@ fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Pr } } -fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { +fn sendMessage(file: Io.File, tag: std.zig.Client.Message.Tag) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 0, @@ -2125,7 +2126,7 @@ fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { }; } -fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: u32) !void { +fn sendRunTestMessage(file: Io.File, tag: std.zig.Client.Message.Tag, index: u32) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 4, @@ -2140,7 +2141,7 @@ fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: } fn sendRunFuzzTestMessage( - file: std.fs.File, + file: Io.File, index: u32, kind: std.Build.abi.fuzz.LimitKind, amount_or_instance: u64, diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 0727e3efd3..f783718cef 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -528,7 +528,7 @@ pub fn Poller(comptime StreamEnum: type) type { /// Given an enum, returns a struct with fields of that enum, each field /// representing an I/O stream for polling. pub fn PollFiles(comptime StreamEnum: type) type { - return @Struct(.auto, null, std.meta.fieldNames(StreamEnum), &@splat(std.fs.File), &@splat(.{})); + return @Struct(.auto, null, std.meta.fieldNames(StreamEnum), &@splat(Io.File), &@splat(.{})); } test { diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 5601293cfb..3f25bc6a26 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -1,7 +1,8 @@ +const Writer = @This(); + const builtin = @import("builtin"); const native_endian = builtin.target.cpu.arch.endian(); -const Writer = @This(); const std = @import("../std.zig"); const assert = std.debug.assert; const Limit = std.Io.Limit; @@ -2837,7 +2838,7 @@ test "discarding sendFile" { const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [256]u8 = undefined; - var file_writer: std.fs.File.Writer = .init(file, &r_buffer); + var file_writer: File.Writer = .init(file, &r_buffer); try file_writer.interface.writeByte('h'); try file_writer.interface.flush(); @@ -2859,7 +2860,7 @@ test "allocating sendFile" { const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [2]u8 = undefined; - var file_writer: std.fs.File.Writer = .init(file, &r_buffer); + var file_writer: File.Writer = .init(file, &r_buffer); try file_writer.interface.writeAll("abcd"); try file_writer.interface.flush(); @@ -2883,7 +2884,7 @@ test sendFileReading { const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [2]u8 = undefined; - var file_writer: std.fs.File.Writer = .init(file, &r_buffer); + var file_writer: File.Writer = .init(file, &r_buffer); try file_writer.interface.writeAll("abcd"); try file_writer.interface.flush(); diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 9ea2d48ee5..b922acc333 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -10,7 +10,7 @@ const expectError = std.testing.expectError; const DefaultPrng = std.Random.DefaultPrng; const mem = std.mem; const fs = std.fs; -const File = std.fs.File; +const File = std.Io.File; const assert = std.debug.assert; const tmpDir = std.testing.tmpDir; diff --git a/lib/std/Io/tty.zig b/lib/std/Io/tty.zig index 08e0bd71f0..d1f8b576c2 100644 --- a/lib/std/Io/tty.zig +++ b/lib/std/Io/tty.zig @@ -1,9 +1,10 @@ -const std = @import("std"); const builtin = @import("builtin"); -const File = std.fs.File; +const native_os = builtin.os.tag; + +const std = @import("std"); +const File = std.Io.File; const process = std.process; const windows = std.os.windows; -const native_os = builtin.os.tag; pub const Color = enum { black, diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 467d4754ff..d8b22c2db0 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -1,19 +1,21 @@ //! This API is non-allocating, non-fallible, thread-safe, and lock-free. +const Progress = @This(); -const std = @import("std"); const builtin = @import("builtin"); +const is_big_endian = builtin.cpu.arch.endian() == .big; +const is_windows = builtin.os.tag == .windows; + +const std = @import("std"); +const Io = std.Io; const windows = std.os.windows; const testing = std.testing; const assert = std.debug.assert; -const Progress = @This(); const posix = std.posix; -const is_big_endian = builtin.cpu.arch.endian() == .big; -const is_windows = builtin.os.tag == .windows; const Writer = std.Io.Writer; /// `null` if the current node (and its children) should /// not print on update() -terminal: std.fs.File, +terminal: Io.File, terminal_mode: TerminalMode, @@ -472,7 +474,7 @@ pub fn start(options: Options) Node { if (options.disable_printing) { return Node.none; } - const stderr: std.fs.File = .stderr(); + const stderr: Io.File = .stderr(); global_progress.terminal = stderr; if (stderr.enableAnsiEscapeCodes()) |_| { global_progress.terminal_mode = .ansi_escape_codes; @@ -633,8 +635,8 @@ pub fn unlockStdErr() void { /// Protected by `stderr_mutex`. const stderr_writer: *Writer = &stderr_file_writer.interface; /// Protected by `stderr_mutex`. -var stderr_file_writer: std.fs.File.Writer = .{ - .interface = std.fs.File.Writer.initInterface(&.{}), +var stderr_file_writer: Io.File.Writer = .{ + .interface = Io.File.Writer.initInterface(&.{}), .file = if (is_windows) undefined else .stderr(), .mode = .streaming, }; diff --git a/lib/std/Random/benchmark.zig b/lib/std/Random/benchmark.zig index 57dc69051e..97afe23b95 100644 --- a/lib/std/Random/benchmark.zig +++ b/lib/std/Random/benchmark.zig @@ -1,7 +1,9 @@ // zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig -const std = @import("std"); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const time = std.time; const Timer = time.Timer; const Random = std.Random; @@ -123,7 +125,7 @@ fn mode(comptime x: comptime_int) comptime_int { pub fn main() !void { var stdout_buffer: [0x100]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; var buffer: [1024]u8 = undefined; diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 9f532c3bec..8d8e5979df 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -175,7 +175,7 @@ pub const SetNameError = error{ Unsupported, Unexpected, InvalidWtf8, -} || posix.PrctlError || posix.WriteError || std.fs.File.OpenError || std.fmt.BufPrintError; +} || posix.PrctlError || posix.WriteError || Io.File.OpenError || std.fmt.BufPrintError; pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { if (name.len > max_name_len) return error.NameTooLong; @@ -293,7 +293,7 @@ pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { pub const GetNameError = error{ Unsupported, Unexpected, -} || posix.PrctlError || posix.ReadError || std.fs.File.OpenError || std.fmt.BufPrintError; +} || posix.PrctlError || posix.ReadError || Io.File.OpenError || std.fmt.BufPrintError; /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. diff --git a/lib/std/crypto/Certificate/Bundle.zig b/lib/std/crypto/Certificate/Bundle.zig index 9541e01db5..1d21918b5c 100644 --- a/lib/std/crypto/Certificate/Bundle.zig +++ b/lib/std/crypto/Certificate/Bundle.zig @@ -171,7 +171,7 @@ fn rescanWindows(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp) RescanW cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len); } -pub const AddCertsFromDirPathError = fs.File.OpenError || AddCertsFromDirError; +pub const AddCertsFromDirPathError = Io.File.OpenError || AddCertsFromDirError; pub fn addCertsFromDirPath( cb: *Bundle, @@ -212,7 +212,7 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp, i } } -pub const AddCertsFromFilePathError = fs.File.OpenError || AddCertsFromFileError || Io.Clock.Error; +pub const AddCertsFromFilePathError = Io.File.OpenError || AddCertsFromFileError || Io.Clock.Error; pub fn addCertsFromFilePathAbsolute( cb: *Bundle, @@ -242,8 +242,8 @@ pub fn addCertsFromFilePath( } pub const AddCertsFromFileError = Allocator.Error || - fs.File.GetSeekPosError || - fs.File.ReadError || + Io.File.GetSeekPosError || + Io.File.ReadError || ParseCertError || std.base64.Error || error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker, Streaming }; diff --git a/lib/std/crypto/Certificate/Bundle/macos.zig b/lib/std/crypto/Certificate/Bundle/macos.zig index d32f1be8e0..473505ac51 100644 --- a/lib/std/crypto/Certificate/Bundle/macos.zig +++ b/lib/std/crypto/Certificate/Bundle/macos.zig @@ -6,7 +6,7 @@ const mem = std.mem; const Allocator = std.mem.Allocator; const Bundle = @import("../Bundle.zig"); -pub const RescanMacError = Allocator.Error || fs.File.OpenError || fs.File.ReadError || fs.File.SeekError || Bundle.ParseCertError || error{EndOfStream}; +pub const RescanMacError = Allocator.Error || Io.File.OpenError || Io.File.ReadError || Io.File.SeekError || Bundle.ParseCertError || error{EndOfStream}; pub fn rescanMac(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp) RescanMacError!void { cb.bytes.clearRetainingCapacity(); diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 54024f070e..1b71110be5 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -1,10 +1,12 @@ // zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig -const std = @import("std"); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const mem = std.mem; const time = std.time; -const Timer = time.Timer; +const Timer = std.time.Timer; const crypto = std.crypto; const KiB = 1024; @@ -504,7 +506,7 @@ fn mode(comptime x: comptime_int) comptime_int { pub fn main() !void { // Size of buffer is about size of printed message. var stdout_buffer: [0x100]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 0cb96ed593..97741ecb40 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -8,7 +8,7 @@ const posix = std.posix; const fs = std.fs; const testing = std.testing; const Allocator = mem.Allocator; -const File = std.fs.File; +const File = std.Io.File; const windows = std.os.windows; const builtin = @import("builtin"); @@ -575,7 +575,7 @@ pub fn defaultPanic( // A panic happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - fs.File.stderr().writeAll("aborting due to recursive panic\n") catch {}; + File.stderr().writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } @@ -1596,7 +1596,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex // A segfault happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - fs.File.stderr().writeAll("aborting due to recursive panic\n") catch {}; + File.stderr().writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } diff --git a/lib/std/debug/ElfFile.zig b/lib/std/debug/ElfFile.zig index 92bcca1bcf..5dbae18130 100644 --- a/lib/std/debug/ElfFile.zig +++ b/lib/std/debug/ElfFile.zig @@ -123,7 +123,7 @@ pub const LoadError = error{ pub fn load( gpa: Allocator, - elf_file: std.fs.File, + elf_file: Io.File, opt_build_id: ?[]const u8, di_search_paths: *const DebugInfoSearchPaths, ) LoadError!ElfFile { @@ -423,7 +423,7 @@ const LoadInnerResult = struct { }; fn loadInner( arena: Allocator, - elf_file: std.fs.File, + elf_file: Io.File, opt_crc: ?u32, ) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult { const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: { diff --git a/lib/std/debug/Info.zig b/lib/std/debug/Info.zig index 9268ca0247..da7656e626 100644 --- a/lib/std/debug/Info.zig +++ b/lib/std/debug/Info.zig @@ -27,7 +27,7 @@ coverage: *Coverage, pub const LoadError = error{ MissingDebugInfo, UnsupportedDebugInfo, -} || std.fs.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError; +} || Io.File.OpenError || ElfFile.LoadError || MachOFile.Error || std.debug.Dwarf.ScanError; pub fn load( gpa: Allocator, diff --git a/lib/std/debug/Pdb.zig b/lib/std/debug/Pdb.zig index c10b361f72..3ecfd1b363 100644 --- a/lib/std/debug/Pdb.zig +++ b/lib/std/debug/Pdb.zig @@ -1,5 +1,5 @@ const std = @import("../std.zig"); -const File = std.fs.File; +const File = std.Io.File; const Allocator = std.mem.Allocator; const pdb = std.pdb; const assert = std.debug.assert; diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index 557f3901eb..3af7223293 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -204,7 +204,7 @@ const Module = struct { coff_section_headers: []coff.SectionHeader, const MappedFile = struct { - file: fs.File, + file: Io.File, section_handle: windows.HANDLE, section_view: []const u8, fn deinit(mf: *const MappedFile, io: Io) void { diff --git a/lib/std/debug/simple_panic.zig b/lib/std/debug/simple_panic.zig index 45e97777c4..f6ff77e04f 100644 --- a/lib/std/debug/simple_panic.zig +++ b/lib/std/debug/simple_panic.zig @@ -15,7 +15,7 @@ pub fn call(msg: []const u8, ra: ?usize) noreturn { @branchHint(.cold); _ = ra; std.debug.lockStdErr(); - const stderr: std.fs.File = .stderr(); + const stderr: std.Io.File = .stderr(); stderr.writeAll(msg) catch {}; @trap(); } diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index c91056b0ab..ca36d5cbb9 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -225,7 +225,7 @@ pub const ElfDynLib = struct { const fd = try resolveFromName(io, path); defer posix.close(fd); - const file: std.fs.File = .{ .handle = fd }; + const file: Io.File = .{ .handle = fd }; const stat = try file.stat(); const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 15b8e9b558..f770ddd30e 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -12,7 +12,7 @@ const posix = std.posix; const ArenaAllocator = std.heap.ArenaAllocator; const Dir = std.fs.Dir; -const File = std.fs.File; +const File = std.Io.File; const tmpDir = testing.tmpDir; const SymLinkFlags = std.fs.Dir.SymLinkFlags; @@ -2231,7 +2231,7 @@ test "read file non vectored" { const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); defer file.close(io); { - var file_writer: std.fs.File.Writer = .init(file, &.{}); + var file_writer: File.Writer = .init(file, &.{}); try file_writer.interface.writeAll(contents); try file_writer.interface.flush(); } @@ -2263,7 +2263,7 @@ test "seek keeping partial buffer" { const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); defer file.close(io); { - var file_writer: std.fs.File.Writer = .init(file, &.{}); + var file_writer: File.Writer = .init(file, &.{}); try file_writer.interface.writeAll(contents); try file_writer.interface.flush(); } @@ -2325,7 +2325,7 @@ test "seekTo flushes buffered data" { defer file.close(io); { var buf: [16]u8 = undefined; - var file_writer = std.fs.File.writer(file, &buf); + var file_writer = File.writer(file, &buf); try file_writer.interface.writeAll(contents); try file_writer.seekTo(8); diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig index a21d6e9ada..6744b87fac 100644 --- a/lib/std/hash/benchmark.zig +++ b/lib/std/hash/benchmark.zig @@ -1,7 +1,8 @@ // zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig +const builtin = @import("builtin"); const std = @import("std"); -const builtin = @import("builtin"); +const Io = std.Io; const time = std.time; const Timer = time.Timer; const hash = std.hash; @@ -354,7 +355,7 @@ fn mode(comptime x: comptime_int) comptime_int { pub fn main() !void { var stdout_buffer: [0x100]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; var buffer: [1024]u8 = undefined; diff --git a/lib/std/http.zig b/lib/std/http.zig index a768372ecc..291e22539b 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -2,7 +2,7 @@ const builtin = @import("builtin"); const std = @import("std.zig"); const assert = std.debug.assert; const Writer = std.Io.Writer; -const File = std.fs.File; +const File = std.Io.File; pub const Client = @import("http/Client.zig"); pub const Server = @import("http/Server.zig"); diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index 36b0e04e5c..7e479de8d4 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -12,7 +12,7 @@ const math = std.math; const mem = std.mem; const coff = std.coff; const fs = std.fs; -const File = std.fs.File; +const File = std.Io.File; const debug = std.debug; const ArrayList = std.ArrayList; diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 8889e50ea3..82fa2c41d1 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -1,20 +1,20 @@ +const builtin = @import("builtin"); +const native_os = builtin.target.os.tag; + const std = @import("../std.zig"); +const Io = std.Io; const posix = std.posix; const testing = std.testing; -const expect = testing.expect; -const expectEqual = testing.expectEqual; -const expectError = testing.expectError; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const expectError = std.testing.expectError; const fs = std.fs; const mem = std.mem; const elf = std.elf; const linux = std.os.linux; - const a = std.testing.allocator; - -const builtin = @import("builtin"); const AtomicRmwOp = std.builtin.AtomicRmwOp; const AtomicOrder = std.builtin.AtomicOrder; -const native_os = builtin.target.os.tag; const tmpDir = std.testing.tmpDir; const AT = posix.AT; @@ -663,14 +663,14 @@ test "dup & dup2" { var file = try tmp.dir.createFile("os_dup_test", .{}); defer file.close(io); - var duped = std.fs.File{ .handle = try posix.dup(file.handle) }; + var duped = Io.File{ .handle = try posix.dup(file.handle) }; defer duped.close(io); try duped.writeAll("dup"); // Tests aren't run in parallel so using the next fd shouldn't be an issue. const new_fd = duped.handle + 1; try posix.dup2(file.handle, new_fd); - var dup2ed = std.fs.File{ .handle = new_fd }; + var dup2ed = Io.File{ .handle = new_fd }; defer dup2ed.close(io); try dup2ed.writeAll("dup2"); } diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index be3026ff10..87d2fe3ba9 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -8,7 +8,7 @@ const Io = std.Io; const unicode = std.unicode; const fs = std.fs; const process = std.process; -const File = std.fs.File; +const File = std.Io.File; const windows = std.os.windows; const linux = std.os.linux; const posix = std.posix; @@ -1055,7 +1055,7 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { fn writeIntFd(fd: i32, value: ErrInt) !void { var buffer: [8]u8 = undefined; - var fw: std.fs.File.Writer = .initStreaming(.{ .handle = fd }, &buffer); + var fw: File.Writer = .initStreaming(.{ .handle = fd }, &buffer); fw.interface.writeInt(u64, value, .little) catch unreachable; fw.interface.flush() catch return error.SystemResources; } diff --git a/lib/std/unicode/throughput_test.zig b/lib/std/unicode/throughput_test.zig index fd3f46ec58..c02f550a4a 100644 --- a/lib/std/unicode/throughput_test.zig +++ b/lib/std/unicode/throughput_test.zig @@ -1,8 +1,8 @@ const std = @import("std"); +const Io = std.Io; const time = std.time; const unicode = std.unicode; - -const Timer = time.Timer; +const Timer = std.time.Timer; const N = 1_000_000; @@ -41,7 +41,7 @@ fn benchmarkCodepointCount(buf: []const u8) !ResultCount { pub fn main() !void { // Size of buffer is about size of printed message. var stdout_buffer: [0x100]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; try stdout.print("short ASCII strings\n", .{}); diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 09c785613f..37ce7b4cfa 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -11,9 +11,11 @@ //! * In the future, possibly inline assembly, which needs to get parsed and //! handled by the codegen backend, and errors reported there. However for now, //! inline assembly is not an exception. +const Zir = @This(); +const builtin = @import("builtin"); const std = @import("std"); -const builtin = @import("builtin"); +const Io = std.Io; const mem = std.mem; const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -21,8 +23,6 @@ const BigIntConst = std.math.big.int.Const; const BigIntMutable = std.math.big.int.Mutable; const Ast = std.zig.Ast; -const Zir = @This(); - instructions: std.MultiArrayList(Inst).Slice, /// In order to store references to strings in fewer bytes, we copy all /// string bytes into here. String bytes can be null. It is up to whomever @@ -45,7 +45,7 @@ pub const Header = extern struct { /// it's essentially free to have a zero field here and makes the warning go away, /// making it more likely that following Valgrind warnings will be taken seriously. unused: u32 = 0, - stat_inode: std.fs.File.INode, + stat_inode: Io.File.INode, stat_size: u64, stat_mtime: i128, }; diff --git a/lib/std/zig/Zoir.zig b/lib/std/zig/Zoir.zig index 08a7fc9639..d82b8f1861 100644 --- a/lib/std/zig/Zoir.zig +++ b/lib/std/zig/Zoir.zig @@ -1,6 +1,13 @@ //! Zig Object Intermediate Representation. //! Simplified AST for the ZON (Zig Object Notation) format. //! `ZonGen` converts `Ast` to `Zoir`. +const Zoir = @This(); + +const std = @import("std"); +const Io = std.Io; +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Ast = std.zig.Ast; nodes: std.MultiArrayList(Node.Repr).Slice, extra: []u32, @@ -25,7 +32,7 @@ pub const Header = extern struct { /// making it more likely that following Valgrind warnings will be taken seriously. unused: u64 = 0, - stat_inode: std.fs.File.INode, + stat_inode: Io.File.INode, stat_size: u64, stat_mtime: i128, @@ -254,9 +261,3 @@ pub const CompileError = extern struct { assert(std.meta.hasUniqueRepresentation(Note)); } }; - -const std = @import("std"); -const assert = std.debug.assert; -const Allocator = std.mem.Allocator; -const Ast = std.zig.Ast; -const Zoir = @This(); diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 9a52ae2c81..9ca0124f4f 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -9578,7 +9578,7 @@ pub fn asmValue( pub fn dump(b: *Builder) void { var buffer: [4000]u8 = undefined; - const stderr: std.fs.File = .stderr(); + const stderr: Io.File = .stderr(); b.printToFile(stderr, &buffer) catch {}; } @@ -9589,7 +9589,7 @@ pub fn printToFilePath(b: *Builder, io: Io, dir: std.fs.Dir, path: []const u8) ! try b.printToFile(io, file, &buffer); } -pub fn printToFile(b: *Builder, file: std.fs.File, buffer: []u8) !void { +pub fn printToFile(b: *Builder, file: Io.File, buffer: []u8) !void { var fw = file.writer(buffer); try print(b, &fw.interface); try fw.interface.flush(); diff --git a/lib/std/zig/perf_test.zig b/lib/std/zig/perf_test.zig index 1566a15d2d..da3dd42f15 100644 --- a/lib/std/zig/perf_test.zig +++ b/lib/std/zig/perf_test.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const mem = std.mem; const Tokenizer = std.zig.Tokenizer; const fmtIntSizeBin = std.fmt.fmtIntSizeBin; @@ -22,7 +23,7 @@ pub fn main() !void { const bytes_per_sec = @as(u64, @intFromFloat(@floor(bytes_per_sec_float))); var stdout_buffer: [1024]u8 = undefined; - var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); const stdout = &stdout_writer.interface; try stdout.print("parsing speed: {Bi:.2}/s, {Bi:.2} used \n", .{ bytes_per_sec, memory_used }); try stdout.flush(); diff --git a/lib/std/zip.zig b/lib/std/zip.zig index c2dbaf5b81..9588651e7f 100644 --- a/lib/std/zip.zig +++ b/lib/std/zip.zig @@ -4,9 +4,10 @@ //! Note that this file uses the abbreviation "cd" for "central directory" const builtin = @import("builtin"); -const std = @import("std"); -const File = std.fs.File; const is_le = builtin.target.cpu.arch.endian() == .little; + +const std = @import("std"); +const File = std.Io.File; const Writer = std.Io.Writer; const Reader = std.Io.Reader; const flate = std.compress.flate; diff --git a/src/Compilation.zig b/src/Compilation.zig index df64dee19f..582c9dff3c 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5325,7 +5325,7 @@ fn docsCopyModule( comp: *Compilation, module: *Package.Module, name: []const u8, - tar_file_writer: *fs.File.Writer, + tar_file_writer: *Io.File.Writer, ) !void { const io = comp.io; const root = module.root; @@ -5361,7 +5361,7 @@ fn docsCopyModule( }; defer file.close(io); const stat = try file.stat(); - var file_reader: fs.File.Reader = .initSize(file.adaptToNewApi(), io, &buffer, stat.size); + var file_reader: Io.File.Reader = .initSize(file.adaptToNewApi(), io, &buffer, stat.size); archiver.writeFileTimestamp(entry.path, &file_reader, stat.mtime) catch |err| { return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive {f}{s}: {t}", .{ diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 58f970abe5..988282097b 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -882,7 +882,7 @@ fn fail(f: *Fetch, msg_tok: std.zig.Ast.TokenIndex, msg_str: u32) RunError { } const Resource = union(enum) { - file: fs.File.Reader, + file: Io.File.Reader, http_request: HttpRequest, git: Git, dir: Io.Dir, @@ -1653,7 +1653,7 @@ fn computeHash(f: *Fetch, pkg_path: Cache.Path, filter: Filter) RunError!Compute fn dumpHashInfo(all_files: []const *const HashedFile) !void { var stdout_buffer: [1024]u8 = undefined; - var stdout_writer: fs.File.Writer = .initStreaming(.stdout(), &stdout_buffer); + var stdout_writer: Io.File.Writer = .initStreaming(.stdout(), &stdout_buffer); const w = &stdout_writer.interface; for (all_files) |hashed_file| { try w.print("{t}: {x}: {s}\n", .{ hashed_file.kind, &hashed_file.hash, hashed_file.normalized_path }); @@ -1712,11 +1712,11 @@ fn deleteFileFallible(dir: Io.Dir, deleted_file: *DeletedFile) DeletedFile.Error try dir.deleteFile(deleted_file.fs_path); } -fn setExecutable(file: fs.File) !void { +fn setExecutable(file: Io.File) !void { if (!std.fs.has_executable_bit) return; const S = std.posix.S; - const mode = fs.File.default_mode | S.IXUSR | S.IXGRP | S.IXOTH; + const mode = Io.File.default_mode | S.IXUSR | S.IXGRP | S.IXOTH; try file.chmod(mode); } @@ -1738,10 +1738,10 @@ const HashedFile = struct { size: u64, const Error = - fs.File.OpenError || - fs.File.ReadError || - fs.File.StatError || - fs.File.ChmodError || + Io.File.OpenError || + Io.File.ReadError || + Io.File.StatError || + Io.File.ChmodError || Io.Dir.ReadLinkError; const Kind = enum { file, link }; diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index 864865bd19..0fca3a0ee3 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -198,8 +198,8 @@ pub const Repository = struct { repo: *Repository, allocator: Allocator, format: Oid.Format, - pack_file: *std.fs.File.Reader, - index_file: *std.fs.File.Reader, + pack_file: *Io.File.Reader, + index_file: *Io.File.Reader, ) !void { repo.* = .{ .odb = undefined }; try repo.odb.init(allocator, format, pack_file, index_file); @@ -372,9 +372,9 @@ pub const Repository = struct { /// [pack-format](https://git-scm.com/docs/pack-format). const Odb = struct { format: Oid.Format, - pack_file: *std.fs.File.Reader, + pack_file: *Io.File.Reader, index_header: IndexHeader, - index_file: *std.fs.File.Reader, + index_file: *Io.File.Reader, cache: ObjectCache = .{}, allocator: Allocator, @@ -383,8 +383,8 @@ const Odb = struct { odb: *Odb, allocator: Allocator, format: Oid.Format, - pack_file: *std.fs.File.Reader, - index_file: *std.fs.File.Reader, + pack_file: *Io.File.Reader, + index_file: *Io.File.Reader, ) !void { try pack_file.seekTo(0); try index_file.seekTo(0); @@ -1272,8 +1272,8 @@ const IndexEntry = struct { pub fn indexPack( allocator: Allocator, format: Oid.Format, - pack: *std.fs.File.Reader, - index_writer: *std.fs.File.Writer, + pack: *Io.File.Reader, + index_writer: *Io.File.Writer, ) !void { try pack.seekTo(0); @@ -1372,7 +1372,7 @@ pub fn indexPack( fn indexPackFirstPass( allocator: Allocator, format: Oid.Format, - pack: *std.fs.File.Reader, + pack: *Io.File.Reader, index_entries: *std.AutoHashMapUnmanaged(Oid, IndexEntry), pending_deltas: *std.ArrayList(IndexEntry), ) !Oid { @@ -1425,7 +1425,7 @@ fn indexPackFirstPass( fn indexPackHashDelta( allocator: Allocator, format: Oid.Format, - pack: *std.fs.File.Reader, + pack: *Io.File.Reader, delta: IndexEntry, index_entries: std.AutoHashMapUnmanaged(Oid, IndexEntry), cache: *ObjectCache, @@ -1477,7 +1477,7 @@ fn indexPackHashDelta( fn resolveDeltaChain( allocator: Allocator, format: Oid.Format, - pack: *std.fs.File.Reader, + pack: *Io.File.Reader, base_object: Object, delta_offsets: []const u64, cache: *ObjectCache, diff --git a/src/Zcu.zig b/src/Zcu.zig index 58d884afe3..cd4a8c7783 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -1200,7 +1200,7 @@ pub const EmbedFile = struct { /// `.none` means the file was not loaded, so `stat` is undefined. val: InternPool.Index, /// If this is `null` and `val` is `.none`, the file has never been loaded. - err: ?(std.fs.File.OpenError || std.fs.File.StatError || std.fs.File.ReadError || error{UnexpectedEof}), + err: ?(Io.File.OpenError || Io.File.StatError || Io.File.ReadError || error{UnexpectedEof}), stat: Cache.File.Stat, pub const Index = enum(u32) { @@ -2927,7 +2927,7 @@ comptime { } } -pub fn loadZirCache(gpa: Allocator, io: Io, cache_file: std.fs.File) !Zir { +pub fn loadZirCache(gpa: Allocator, io: Io, cache_file: Io.File) !Zir { var buffer: [2000]u8 = undefined; var file_reader = cache_file.reader(io, &buffer); return result: { @@ -2986,7 +2986,7 @@ pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_br: *Io.Reader return zir; } -pub fn saveZirCache(gpa: Allocator, cache_file: std.fs.File, stat: std.fs.File.Stat, zir: Zir) (std.fs.File.WriteError || Allocator.Error)!void { +pub fn saveZirCache(gpa: Allocator, cache_file: Io.File, stat: Io.File.Stat, zir: Zir) (Io.File.WriteError || Allocator.Error)!void { const safety_buffer = if (data_has_safety_tag) try gpa.alloc([8]u8, zir.instructions.len) else @@ -3026,7 +3026,7 @@ pub fn saveZirCache(gpa: Allocator, cache_file: std.fs.File, stat: std.fs.File.S }; } -pub fn saveZoirCache(cache_file: std.fs.File, stat: std.fs.File.Stat, zoir: Zoir) std.fs.File.WriteError!void { +pub fn saveZoirCache(cache_file: Io.File, stat: Io.File.Stat, zoir: Zoir) Io.File.WriteError!void { const header: Zoir.Header = .{ .nodes_len = @intCast(zoir.nodes.len), .extra_len = @intCast(zoir.extra.len), diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index d2ca004058..55d6a3861f 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -118,7 +118,7 @@ pub fn updateFile( const zir_dir = cache_directory.handle; // Determine whether we need to reload the file from disk and redo parsing and AstGen. - var lock: std.fs.File.Lock = switch (file.status) { + var lock: Io.File.Lock = switch (file.status) { .never_loaded, .retryable_failure => lock: { // First, load the cached ZIR code, if any. log.debug("AstGen checking cache: {f} (local={}, digest={s})", .{ @@ -346,8 +346,8 @@ pub fn updateFile( fn loadZirZoirCache( zcu: *Zcu, - cache_file: std.fs.File, - stat: std.fs.File.Stat, + cache_file: Io.File, + stat: Io.File.Stat, file: *Zcu.File, comptime mode: Ast.Mode, ) !enum { success, invalid, truncated, stale } { diff --git a/src/fmt.zig b/src/fmt.zig index 907c7885ad..7bdc24054e 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -37,9 +37,9 @@ const Fmt = struct { arena: Allocator, io: Io, out_buffer: std.Io.Writer.Allocating, - stdout_writer: *fs.File.Writer, + stdout_writer: *Io.File.Writer, - const SeenMap = std.AutoHashMap(fs.File.INode, void); + const SeenMap = std.AutoHashMap(Io.File.INode, void); }; pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { @@ -59,7 +59,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { - try fs.File.stdout().writeAll(usage_fmt); + try Io.File.stdout().writeAll(usage_fmt); return process.cleanExit(); } else if (mem.eql(u8, arg, "--color")) { if (i + 1 >= args.len) { @@ -99,9 +99,9 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! fatal("cannot use --stdin with positional arguments", .{}); } - const stdin: fs.File = .stdin(); + const stdin: Io.File = .stdin(); var stdio_buffer: [1024]u8 = undefined; - var file_reader: fs.File.Reader = stdin.reader(io, &stdio_buffer); + var file_reader: Io.File.Reader = stdin.reader(io, &stdio_buffer); const source_code = std.zig.readSourceFileToEndAlloc(gpa, &file_reader) catch |err| { fatal("unable to read stdin: {}", .{err}); }; @@ -154,7 +154,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! process.exit(code); } - return fs.File.stdout().writeAll(formatted); + return Io.File.stdout().writeAll(formatted); } if (input_files.items.len == 0) { @@ -162,7 +162,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! } var stdout_buffer: [4096]u8 = undefined; - var stdout_writer = fs.File.stdout().writer(&stdout_buffer); + var stdout_writer = Io.File.stdout().writer(&stdout_buffer); var fmt: Fmt = .{ .gpa = gpa, @@ -272,7 +272,7 @@ fn fmtPathFile( return error.IsDir; var read_buffer: [1024]u8 = undefined; - var file_reader: fs.File.Reader = source_file.reader(io, &read_buffer); + var file_reader: Io.File.Reader = source_file.reader(io, &read_buffer); file_reader.size = stat.size; const gpa = fmt.gpa; diff --git a/src/link.zig b/src/link.zig index ef095987c9..d5daf6fca7 100644 --- a/src/link.zig +++ b/src/link.zig @@ -393,7 +393,7 @@ pub const File = struct { comp: *Compilation, emit: Path, - file: ?fs.File, + file: ?Io.File, /// When using the LLVM backend, the emitted object is written to a file with this name. This /// object file then becomes a normal link input to LLD or a self-hosted linker. /// @@ -1110,7 +1110,7 @@ pub const File = struct { }; } - fn loadGnuLdScript(base: *File, path: Path, parent_query: UnresolvedInput.Query, file: fs.File) anyerror!void { + fn loadGnuLdScript(base: *File, path: Path, parent_query: UnresolvedInput.Query, file: Io.File) anyerror!void { const comp = base.comp; const diags = &comp.link_diags; const gpa = comp.gpa; @@ -1238,7 +1238,7 @@ pub const File = struct { pub fn determineMode( output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, - ) fs.File.Mode { + ) Io.File.Mode { // On common systems with a 0o022 umask, 0o777 will still result in a file created // with 0o755 permissions, but it works appropriately if the system is configured // more leniently. As another data point, C's fopen seems to open files with the @@ -1247,10 +1247,10 @@ pub const File = struct { switch (output_mode) { .Lib => return switch (link_mode) { .dynamic => executable_mode, - .static => fs.File.default_mode, + .static => Io.File.default_mode, }, .Exe => return executable_mode, - .Obj => return fs.File.default_mode, + .Obj => return Io.File.default_mode, } } @@ -1660,19 +1660,19 @@ pub const Input = union(enum) { pub const Object = struct { path: Path, - file: fs.File, + file: Io.File, must_link: bool, hidden: bool, }; pub const Res = struct { path: Path, - file: fs.File, + file: Io.File, }; pub const Dso = struct { path: Path, - file: fs.File, + file: Io.File, needed: bool, weak: bool, reexport: bool, @@ -1694,7 +1694,7 @@ pub const Input = union(enum) { } /// Returns `null` in the case of `dso_exact`. - pub fn pathAndFile(input: Input) ?struct { Path, fs.File } { + pub fn pathAndFile(input: Input) ?struct { Path, Io.File } { return switch (input) { .object, .archive => |obj| .{ obj.path, obj.file }, inline .res, .dso => |x| .{ x.path, x.file }, @@ -2075,7 +2075,7 @@ fn resolveLibInput( fn finishResolveLibInput( resolved_inputs: *std.ArrayList(Input), path: Path, - file: std.fs.File, + file: Io.File, link_mode: std.builtin.LinkMode, query: UnresolvedInput.Query, ) ResolveLibInputResult { diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 95f4ca8bbd..cfb02fba38 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1,3 +1,24 @@ +const Dwarf = @This(); + +const std = @import("std"); +const Io = std.Io; +const Allocator = std.mem.Allocator; +const DW = std.dwarf; +const Zir = std.zig.Zir; +const assert = std.debug.assert; +const log = std.log.scoped(.dwarf); +const Writer = std.Io.Writer; + +const InternPool = @import("../InternPool.zig"); +const Module = @import("../Package.zig").Module; +const Type = @import("../Type.zig"); +const Value = @import("../Value.zig"); +const Zcu = @import("../Zcu.zig"); +const codegen = @import("../codegen.zig"); +const dev = @import("../dev.zig"); +const link = @import("../link.zig"); +const target_info = @import("../target.zig"); + gpa: Allocator, bin_file: *link.File, format: DW.Format, @@ -29,16 +50,16 @@ pub const UpdateError = error{ UnexpectedEndOfFile, } || codegen.GenerateSymbolError || - std.fs.File.OpenError || - std.fs.File.SetEndPosError || - std.fs.File.CopyRangeError || - std.fs.File.PReadError || - std.fs.File.PWriteError; + Io.File.OpenError || + Io.File.SetEndPosError || + Io.File.CopyRangeError || + Io.File.PReadError || + Io.File.PWriteError; pub const FlushError = UpdateError; pub const RelocError = - std.fs.File.PWriteError; + Io.File.PWriteError; pub const AddressSize = enum(u8) { @"32" = 4, @@ -6350,7 +6371,7 @@ const AbbrevCode = enum { }); }; -fn getFile(dwarf: *Dwarf) ?std.fs.File { +fn getFile(dwarf: *Dwarf) ?Io.File { if (dwarf.bin_file.cast(.macho)) |macho_file| if (macho_file.d_sym) |*d_sym| return d_sym.file; return dwarf.bin_file.file; } @@ -6429,21 +6450,3 @@ const force_incremental = false; inline fn incremental(dwarf: Dwarf) bool { return force_incremental or dwarf.bin_file.comp.config.incremental; } - -const Allocator = std.mem.Allocator; -const DW = std.dwarf; -const Dwarf = @This(); -const InternPool = @import("../InternPool.zig"); -const Module = @import("../Package.zig").Module; -const Type = @import("../Type.zig"); -const Value = @import("../Value.zig"); -const Zcu = @import("../Zcu.zig"); -const Zir = std.zig.Zir; -const assert = std.debug.assert; -const codegen = @import("../codegen.zig"); -const dev = @import("../dev.zig"); -const link = @import("../link.zig"); -const log = std.log.scoped(.dwarf); -const std = @import("std"); -const target_info = @import("../target.zig"); -const Writer = std.Io.Writer; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ae7d631f09..584a50c7f2 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3651,7 +3651,7 @@ fn fileLookup(files: std.MultiArrayList(File.Entry), index: File.Index, zig_obje pub fn addFileHandle( gpa: Allocator, file_handles: *std.ArrayList(File.Handle), - handle: fs.File, + handle: Io.File, ) Allocator.Error!File.HandleIndex { try file_handles.append(gpa, handle); return @intCast(file_handles.items.len - 1); @@ -4068,7 +4068,7 @@ fn fmtDumpState(self: *Elf, writer: *std.Io.Writer) std.Io.Writer.Error!void { } /// Caller owns the memory. -pub fn preadAllAlloc(allocator: Allocator, handle: fs.File, offset: u64, size: u64) ![]u8 { +pub fn preadAllAlloc(allocator: Allocator, handle: Io.File, offset: u64, size: u64) ![]u8 { const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow); errdefer allocator.free(buffer); const amt = try handle.preadAll(buffer, offset); @@ -4460,6 +4460,7 @@ pub fn cast(elf_file: *Elf, comptime T: type, x: anytype) error{LinkFailure}!T { } const std = @import("std"); +const Io = std.Io; const build_options = @import("build_options"); const builtin = @import("builtin"); const assert = std.debug.assert; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index d51a82b266..c0dde4176a 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -1,3 +1,30 @@ +const Object = @This(); + +const std = @import("std"); +const Io = std.Io; +const assert = std.debug.assert; +const eh_frame = @import("eh_frame.zig"); +const elf = std.elf; +const fs = std.fs; +const log = std.log.scoped(.link); +const math = std.math; +const mem = std.mem; +const Path = std.Build.Cache.Path; +const Allocator = std.mem.Allocator; + +const Diags = @import("../../link.zig").Diags; +const Archive = @import("Archive.zig"); +const Atom = @import("Atom.zig"); +const AtomList = @import("AtomList.zig"); +const Cie = eh_frame.Cie; +const Elf = @import("../Elf.zig"); +const Fde = eh_frame.Fde; +const File = @import("file.zig").File; +const Merge = @import("Merge.zig"); +const Symbol = @import("Symbol.zig"); +const Alignment = Atom.Alignment; +const riscv = @import("../riscv.zig"); + archive: ?InArchive = null, /// Archive files cannot contain subdirectories, so only the basename is needed /// for output. However, the full path is kept for error reporting. @@ -68,7 +95,7 @@ pub fn parse( diags: *Diags, /// For error reporting purposes only. path: Path, - handle: fs.File, + handle: Io.File, target: *const std.Target, debug_fmt_strip: bool, default_sym_version: elf.Versym, @@ -97,7 +124,7 @@ pub fn parseCommon( gpa: Allocator, diags: *Diags, path: Path, - handle: fs.File, + handle: Io.File, target: *const std.Target, ) !void { const offset = if (self.archive) |ar| ar.offset else 0; @@ -264,7 +291,7 @@ fn initAtoms( gpa: Allocator, diags: *Diags, path: Path, - handle: fs.File, + handle: Io.File, debug_fmt_strip: bool, target: *const std.Target, ) !void { @@ -421,7 +448,7 @@ fn initSymbols( fn parseEhFrame( self: *Object, gpa: Allocator, - handle: fs.File, + handle: Io.File, shndx: u32, target: *const std.Target, ) !void { @@ -1310,7 +1337,7 @@ fn addString(self: *Object, gpa: Allocator, str: []const u8) !u32 { } /// Caller owns the memory. -fn preadShdrContentsAlloc(self: Object, gpa: Allocator, handle: fs.File, index: u32) ![]u8 { +fn preadShdrContentsAlloc(self: Object, gpa: Allocator, handle: Io.File, index: u32) ![]u8 { assert(index < self.shdrs.items.len); const offset = if (self.archive) |ar| ar.offset else 0; const shdr = self.shdrs.items[index]; @@ -1320,7 +1347,7 @@ fn preadShdrContentsAlloc(self: Object, gpa: Allocator, handle: fs.File, index: } /// Caller owns the memory. -fn preadRelocsAlloc(self: Object, gpa: Allocator, handle: fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela { +fn preadRelocsAlloc(self: Object, gpa: Allocator, handle: Io.File, shndx: u32) ![]align(1) const elf.Elf64_Rela { const raw = try self.preadShdrContentsAlloc(gpa, handle, shndx); const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela)); return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; @@ -1552,29 +1579,3 @@ const InArchive = struct { offset: u64, size: u32, }; - -const Object = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const eh_frame = @import("eh_frame.zig"); -const elf = std.elf; -const fs = std.fs; -const log = std.log.scoped(.link); -const math = std.math; -const mem = std.mem; -const Path = std.Build.Cache.Path; -const Allocator = std.mem.Allocator; - -const Diags = @import("../../link.zig").Diags; -const Archive = @import("Archive.zig"); -const Atom = @import("Atom.zig"); -const AtomList = @import("AtomList.zig"); -const Cie = eh_frame.Cie; -const Elf = @import("../Elf.zig"); -const Fde = eh_frame.Fde; -const File = @import("file.zig").File; -const Merge = @import("Merge.zig"); -const Symbol = @import("Symbol.zig"); -const Alignment = Atom.Alignment; -const riscv = @import("../riscv.zig"); diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 1e17aa34a8..3720fe53d6 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -1,3 +1,20 @@ +const SharedObject = @This(); + +const std = @import("std"); +const Io = std.Io; +const assert = std.debug.assert; +const elf = std.elf; +const log = std.log.scoped(.elf); +const mem = std.mem; +const Path = std.Build.Cache.Path; +const Stat = std.Build.Cache.File.Stat; +const Allocator = mem.Allocator; + +const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; +const Symbol = @import("Symbol.zig"); +const Diags = @import("../../link.zig").Diags; + path: Path, index: File.Index, @@ -94,7 +111,7 @@ pub fn parseHeader( gpa: Allocator, diags: *Diags, file_path: Path, - fs_file: std.fs.File, + fs_file: Io.File, stat: Stat, target: *const std.Target, ) !Header { @@ -192,7 +209,7 @@ pub fn parse( gpa: Allocator, /// Moves resources from header. Caller may unconditionally deinit. header: *Header, - fs_file: std.fs.File, + fs_file: Io.File, ) !Parsed { const symtab = if (header.dynsym_sect_index) |index| st: { const shdr = header.sections[index]; @@ -534,19 +551,3 @@ const Format = struct { } } }; - -const SharedObject = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const elf = std.elf; -const log = std.log.scoped(.elf); -const mem = std.mem; -const Path = std.Build.Cache.Path; -const Stat = std.Build.Cache.File.Stat; -const Allocator = mem.Allocator; - -const Elf = @import("../Elf.zig"); -const File = @import("file.zig").File; -const Symbol = @import("Symbol.zig"); -const Diags = @import("../../link.zig").Diags; diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 50f5159d18..52d3c6e6f0 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -1,3 +1,20 @@ +const std = @import("std"); +const Io = std.Io; +const elf = std.elf; +const log = std.log.scoped(.link); +const Path = std.Build.Cache.Path; +const Allocator = std.mem.Allocator; + +const Archive = @import("Archive.zig"); +const Atom = @import("Atom.zig"); +const Cie = @import("eh_frame.zig").Cie; +const Elf = @import("../Elf.zig"); +const LinkerDefined = @import("LinkerDefined.zig"); +const Object = @import("Object.zig"); +const SharedObject = @import("SharedObject.zig"); +const Symbol = @import("Symbol.zig"); +const ZigObject = @import("ZigObject.zig"); + pub const File = union(enum) { zig_object: *ZigObject, linker_defined: *LinkerDefined, @@ -279,22 +296,6 @@ pub const File = union(enum) { shared_object: SharedObject, }; - pub const Handle = std.fs.File; + pub const Handle = Io.File; pub const HandleIndex = Index; }; - -const std = @import("std"); -const elf = std.elf; -const log = std.log.scoped(.link); -const Path = std.Build.Cache.Path; -const Allocator = std.mem.Allocator; - -const Archive = @import("Archive.zig"); -const Atom = @import("Atom.zig"); -const Cie = @import("eh_frame.zig").Cie; -const Elf = @import("../Elf.zig"); -const LinkerDefined = @import("LinkerDefined.zig"); -const Object = @import("Object.zig"); -const SharedObject = @import("SharedObject.zig"); -const Symbol = @import("Symbol.zig"); -const ZigObject = @import("ZigObject.zig"); diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 7d12ccedb2..e35444bc02 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -1,3 +1,23 @@ +const Elf = @This(); + +const builtin = @import("builtin"); +const native_endian = builtin.cpu.arch.endian(); + +const std = @import("std"); +const Io = std.Io; +const assert = std.debug.assert; +const log = std.log.scoped(.link); + +const codegen = @import("../codegen.zig"); +const Compilation = @import("../Compilation.zig"); +const InternPool = @import("../InternPool.zig"); +const link = @import("../link.zig"); +const MappedFile = @import("MappedFile.zig"); +const target_util = @import("../target.zig"); +const Type = @import("../Type.zig"); +const Value = @import("../Value.zig"); +const Zcu = @import("../Zcu.zig"); + base: link.File, options: link.File.OpenOptions, mf: MappedFile, @@ -1973,8 +1993,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index { return lazy_gop.value_ptr.*; } -pub fn loadInput(elf: *Elf, input: link.Input) (std.fs.File.Reader.SizeError || - std.Io.File.Reader.Error || MappedFile.Error || error{ EndOfStream, BadMagic, LinkFailure })!void { +pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError || + Io.File.Reader.Error || MappedFile.Error || error{ EndOfStream, BadMagic, LinkFailure })!void { const io = elf.base.comp.io; var buf: [4096]u8 = undefined; switch (input) { @@ -2007,7 +2027,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) (std.fs.File.Reader.SizeError || .dso_exact => |dso_exact| try elf.loadDsoExact(dso_exact.name), } } -fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *std.Io.File.Reader) !void { +fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { const comp = elf.base.comp; const gpa = comp.gpa; const diags = &comp.link_diags; @@ -2067,7 +2087,7 @@ fn loadObject( elf: *Elf, path: std.Build.Cache.Path, member: ?[]const u8, - fr: *std.Io.File.Reader, + fr: *Io.File.Reader, fl: MappedFile.Node.FileLocation, ) !void { const comp = elf.base.comp; @@ -2310,7 +2330,7 @@ fn loadObject( }, } } -fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *std.Io.File.Reader) !void { +fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { const comp = elf.base.comp; const diags = &comp.link_diags; const r = &fr.interface; @@ -3822,19 +3842,3 @@ pub fn printNode( try w.writeByte('\n'); } } - -const assert = std.debug.assert; -const builtin = @import("builtin"); -const codegen = @import("../codegen.zig"); -const Compilation = @import("../Compilation.zig"); -const Elf = @This(); -const InternPool = @import("../InternPool.zig"); -const link = @import("../link.zig"); -const log = std.log.scoped(.link); -const MappedFile = @import("MappedFile.zig"); -const native_endian = builtin.cpu.arch.endian(); -const std = @import("std"); -const target_util = @import("../target.zig"); -const Type = @import("../Type.zig"); -const Value = @import("../Value.zig"); -const Zcu = @import("../Zcu.zig"); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 471465cea1..72a49c0c9e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -890,7 +890,7 @@ pub fn classifyInputFile(self: *MachO, input: link.Input) !void { _ = try self.addTbd(.fromLinkInput(input), true, fh); } -fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch { +fn parseFatFile(self: *MachO, file: Io.File, path: Path) !?fat.Arch { const diags = &self.base.comp.link_diags; const fat_h = fat.readFatHeader(file) catch return null; if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null; @@ -903,7 +903,7 @@ fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch { return diags.failParse(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)}); } -pub fn readMachHeader(file: std.fs.File, offset: usize) !macho.mach_header_64 { +pub fn readMachHeader(file: Io.File, offset: usize) !macho.mach_header_64 { var buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; const nread = try file.preadAll(&buffer, offset); if (nread != buffer.len) return error.InputOutput; @@ -911,7 +911,7 @@ pub fn readMachHeader(file: std.fs.File, offset: usize) !macho.mach_header_64 { return hdr; } -pub fn readArMagic(file: std.fs.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 { +pub fn readArMagic(file: Io.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 { const nread = try file.preadAll(buffer, offset); if (nread != buffer.len) return error.InputOutput; return buffer[0..Archive.SARMAG]; @@ -3768,7 +3768,7 @@ pub fn getInternalObject(self: *MachO) ?*InternalObject { return self.getFile(index).?.internal; } -pub fn addFileHandle(self: *MachO, file: fs.File) !File.HandleIndex { +pub fn addFileHandle(self: *MachO, file: Io.File) !File.HandleIndex { const gpa = self.base.comp.gpa; const index: File.HandleIndex = @intCast(self.file_handles.items.len); const fh = try self.file_handles.addOne(gpa); @@ -5373,10 +5373,11 @@ const max_distance = (1 << (jump_bits - 1)); const max_allowed_distance = max_distance - 0x500_000; const MachO = @This(); - -const std = @import("std"); const build_options = @import("build_options"); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const fs = std.fs; const log = std.log.scoped(.link); @@ -5386,6 +5387,11 @@ const math = std.math; const mem = std.mem; const meta = std.meta; const Writer = std.Io.Writer; +const AtomicBool = std.atomic.Value(bool); +const Cache = std.Build.Cache; +const Hash = std.hash.Wyhash; +const Md5 = std.crypto.hash.Md5; +const Allocator = std.mem.Allocator; const aarch64 = codegen.aarch64.encoding; const bind = @import("MachO/dyld_info/bind.zig"); @@ -5403,11 +5409,8 @@ const trace = @import("../tracy.zig").trace; const synthetic = @import("MachO/synthetic.zig"); const Alignment = Atom.Alignment; -const Allocator = mem.Allocator; const Archive = @import("MachO/Archive.zig"); -const AtomicBool = std.atomic.Value(bool); const Bind = bind.Bind; -const Cache = std.Build.Cache; const CodeSignature = @import("MachO/CodeSignature.zig"); const Compilation = @import("../Compilation.zig"); const DataInCode = synthetic.DataInCode; @@ -5417,14 +5420,12 @@ const ExportTrie = @import("MachO/dyld_info/Trie.zig"); const Path = Cache.Path; const File = @import("MachO/file.zig").File; const GotSection = synthetic.GotSection; -const Hash = std.hash.Wyhash; const Indsymtab = synthetic.Indsymtab; const InternalObject = @import("MachO/InternalObject.zig"); const ObjcStubsSection = synthetic.ObjcStubsSection; const Object = @import("MachO/Object.zig"); const LazyBind = bind.LazyBind; const LaSymbolPtrSection = synthetic.LaSymbolPtrSection; -const Md5 = std.crypto.hash.Md5; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); const Rebase = @import("MachO/dyld_info/Rebase.zig"); diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index 5bded3b9e3..5f9a9ecac9 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -1,17 +1,19 @@ const CodeSignature = @This(); const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const fs = std.fs; const log = std.log.scoped(.link); const macho = std.macho; const mem = std.mem; const testing = std.testing; +const Sha256 = std.crypto.hash.sha2.Sha256; +const Allocator = std.mem.Allocator; + const trace = @import("../../tracy.zig").trace; -const Allocator = mem.Allocator; const Hasher = @import("hasher.zig").ParallelHasher; const MachO = @import("../MachO.zig"); -const Sha256 = std.crypto.hash.sha2.Sha256; const hash_size = Sha256.digest_length; @@ -250,7 +252,7 @@ pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const } pub const WriteOpts = struct { - file: fs.File, + file: Io.File, exec_seg_base: u64, exec_seg_limit: u64, file_size: u32, diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig index 7772f7a4de..fd9a302531 100644 --- a/src/link/MachO/fat.zig +++ b/src/link/MachO/fat.zig @@ -1,18 +1,20 @@ +const builtin = @import("builtin"); +const native_endian = builtin.target.cpu.arch.endian(); + const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; -const builtin = @import("builtin"); const log = std.log.scoped(.macho); const macho = std.macho; const mem = std.mem; -const native_endian = builtin.target.cpu.arch.endian(); const MachO = @import("../MachO.zig"); -pub fn readFatHeader(file: std.fs.File) !macho.fat_header { +pub fn readFatHeader(file: Io.File) !macho.fat_header { return readFatHeaderGeneric(macho.fat_header, file, 0); } -fn readFatHeaderGeneric(comptime Hdr: type, file: std.fs.File, offset: usize) !Hdr { +fn readFatHeaderGeneric(comptime Hdr: type, file: Io.File, offset: usize) !Hdr { var buffer: [@sizeOf(Hdr)]u8 = undefined; const nread = try file.preadAll(&buffer, offset); if (nread != buffer.len) return error.InputOutput; @@ -27,7 +29,7 @@ pub const Arch = struct { size: u32, }; -pub fn parseArchs(file: std.fs.File, fat_header: macho.fat_header, out: *[2]Arch) ![]const Arch { +pub fn parseArchs(file: Io.File, fat_header: macho.fat_header, out: *[2]Arch) ![]const Arch { var count: usize = 0; var fat_arch_index: u32 = 0; while (fat_arch_index < fat_header.nfat_arch and count < out.len) : (fat_arch_index += 1) { diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index 05b43de181..cd687a4941 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -355,11 +355,12 @@ pub const File = union(enum) { dylib: Dylib, }; - pub const Handle = std.fs.File; + pub const Handle = Io.File; pub const HandleIndex = Index; }; const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const log = std.log.scoped(.link); const macho = std.macho; diff --git a/src/link/MachO/hasher.zig b/src/link/MachO/hasher.zig index 78cd847c40..8cf53071c8 100644 --- a/src/link/MachO/hasher.zig +++ b/src/link/MachO/hasher.zig @@ -1,3 +1,9 @@ +const std = @import("std"); +const Io = std.Io; +const Allocator = std.mem.Allocator; + +const trace = @import("../../tracy.zig").trace; + pub fn ParallelHasher(comptime Hasher: type) type { const hash_size = Hasher.digest_length; @@ -5,7 +11,7 @@ pub fn ParallelHasher(comptime Hasher: type) type { allocator: Allocator, io: std.Io, - pub fn hash(self: Self, file: fs.File, out: [][hash_size]u8, opts: struct { + pub fn hash(self: Self, file: Io.File, out: [][hash_size]u8, opts: struct { chunk_size: u64 = 0x4000, max_file_size: ?u64 = null, }) !void { @@ -23,7 +29,7 @@ pub fn ParallelHasher(comptime Hasher: type) type { const buffer = try self.allocator.alloc(u8, chunk_size * out.len); defer self.allocator.free(buffer); - const results = try self.allocator.alloc(fs.File.PReadError!usize, out.len); + const results = try self.allocator.alloc(Io.File.PReadError!usize, out.len); defer self.allocator.free(results); { @@ -51,11 +57,11 @@ pub fn ParallelHasher(comptime Hasher: type) type { } fn worker( - file: fs.File, + file: Io.File, fstart: usize, buffer: []u8, out: *[hash_size]u8, - err: *fs.File.PReadError!usize, + err: *Io.File.PReadError!usize, ) void { const tracy = trace(@src()); defer tracy.end(); @@ -66,11 +72,3 @@ pub fn ParallelHasher(comptime Hasher: type) type { const Self = @This(); }; } - -const assert = std.debug.assert; -const fs = std.fs; -const mem = std.mem; -const std = @import("std"); -const trace = @import("../../tracy.zig").trace; - -const Allocator = mem.Allocator; diff --git a/src/link/MachO/uuid.zig b/src/link/MachO/uuid.zig index d08ac0c5b8..4d8eac7523 100644 --- a/src/link/MachO/uuid.zig +++ b/src/link/MachO/uuid.zig @@ -1,10 +1,18 @@ +const std = @import("std"); +const Io = std.Io; +const Md5 = std.crypto.hash.Md5; + +const trace = @import("../../tracy.zig").trace; +const Compilation = @import("../../Compilation.zig"); +const Hasher = @import("hasher.zig").ParallelHasher; + /// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce /// the final digest. /// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD /// and we will use it too as it seems accepted by Apple OSes. /// TODO LLD also hashes the output filename to disambiguate between same builds with different /// output files. Should we also do that? -pub fn calcUuid(comp: *const Compilation, file: fs.File, file_size: u64, out: *[Md5.digest_length]u8) !void { +pub fn calcUuid(comp: *const Compilation, file: Io.File, file_size: u64, out: *[Md5.digest_length]u8) !void { const tracy = trace(@src()); defer tracy.end(); @@ -37,12 +45,3 @@ inline fn conform(out: *[Md5.digest_length]u8) void { out[6] = (out[6] & 0x0F) | (3 << 4); out[8] = (out[8] & 0x3F) | 0x80; } - -const fs = std.fs; -const mem = std.mem; -const std = @import("std"); -const trace = @import("../../tracy.zig").trace; - -const Compilation = @import("../../Compilation.zig"); -const Md5 = std.crypto.hash.Md5; -const Hasher = @import("hasher.zig").ParallelHasher; diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index 975b94578b..7d4134ccaf 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -1,3 +1,15 @@ +const MappedFile = @This(); + +const builtin = @import("builtin"); +const is_linux = builtin.os.tag == .linux; +const is_windows = builtin.os.tag == .windows; + +const std = @import("std"); +const Io = std.Io; +const assert = std.debug.assert; +const linux = std.os.linux; +const windows = std.os.windows; + file: std.Io.File, flags: packed struct { block_size: std.mem.Alignment, @@ -16,7 +28,7 @@ writers: std.SinglyLinkedList, pub const growth_factor = 4; -pub const Error = std.posix.MMapError || std.posix.MRemapError || std.fs.File.SetEndPosError || error{ +pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.SetEndPosError || error{ NotFile, SystemResources, IsDir, @@ -618,7 +630,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested // Resize the entire file if (ni == Node.Index.root) { try mf.ensureCapacityForSetLocation(gpa); - try std.fs.File.adaptFromNewApi(mf.file).setEndPos(new_size); + try Io.File.adaptFromNewApi(mf.file).setEndPos(new_size); try mf.ensureTotalCapacity(@intCast(new_size)); ni.setLocationAssumeCapacity(mf, old_offset, new_size); return; @@ -1059,12 +1071,3 @@ fn verifyNode(mf: *MappedFile, parent_ni: Node.Index) void { ni = node.next; } } - -const assert = std.debug.assert; -const builtin = @import("builtin"); -const is_linux = builtin.os.tag == .linux; -const is_windows = builtin.os.tag == .windows; -const linux = std.os.linux; -const MappedFile = @This(); -const std = @import("std"); -const windows = std.os.windows; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 160e6cdcc6..7ab1e0eb4b 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -20,6 +20,7 @@ const native_endian = builtin.cpu.arch.endian(); const build_options = @import("build_options"); const std = @import("std"); +const Io = std.Io; const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; const Path = Cache.Path; @@ -3001,9 +3002,9 @@ pub fn createEmpty( .read = true, .mode = if (fs.has_executable_bit) if (target.os.tag == .wasi and output_mode == .Exe) - fs.File.default_mode | 0b001_000_000 + Io.File.default_mode | 0b001_000_000 else - fs.File.default_mode + Io.File.default_mode else 0, }); diff --git a/src/link/tapi.zig b/src/link/tapi.zig index 4c1471a6b4..fff25b7544 100644 --- a/src/link/tapi.zig +++ b/src/link/tapi.zig @@ -1,10 +1,10 @@ const std = @import("std"); -const fs = std.fs; +const Io = std.Io; const mem = std.mem; const log = std.log.scoped(.tapi); -const yaml = @import("tapi/yaml.zig"); +const Allocator = std.mem.Allocator; -const Allocator = mem.Allocator; +const yaml = @import("tapi/yaml.zig"); const Yaml = yaml.Yaml; const VersionField = union(enum) { @@ -130,7 +130,7 @@ pub const Tbd = union(enum) { pub const TapiError = error{ NotLibStub, InputOutput, -} || yaml.YamlError || std.fs.File.PReadError; +} || yaml.YamlError || Io.File.PReadError; pub const LibStub = struct { /// Underlying memory for stub's contents. @@ -139,7 +139,7 @@ pub const LibStub = struct { /// Typed contents of the tbd file. inner: []Tbd, - pub fn loadFromFile(allocator: Allocator, file: fs.File) TapiError!LibStub { + pub fn loadFromFile(allocator: Allocator, file: Io.File) TapiError!LibStub { const filesize = blk: { const stat = file.stat() catch break :blk std.math.maxInt(u32); break :blk @min(stat.size, std.math.maxInt(u32)); -- cgit v1.2.3 From f53248a40936ebc9aaf75ddbd16e67ebec05ab84 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 13:39:09 -0800 Subject: update all std.fs.cwd() to std.Io.Dir.cwd() --- lib/compiler/aro/aro/Compilation.zig | 8 +- lib/compiler/aro/aro/Driver.zig | 10 +-- lib/compiler/aro/aro/Parser.zig | 27 +++---- lib/compiler/aro/aro/Preprocessor.zig | 6 +- lib/compiler/aro/aro/Tokenizer.zig | 5 +- lib/compiler/aro/aro/Value.zig | 11 +-- lib/compiler/aro/main.zig | 2 +- lib/compiler/objcopy.zig | 4 +- lib/compiler/reduce.zig | 6 +- lib/compiler/resinator/cli.zig | 2 +- lib/compiler/resinator/compile.zig | 4 +- lib/compiler/resinator/main.zig | 22 +++--- lib/compiler/std-docs.zig | 2 +- lib/compiler/translate-c/main.zig | 8 +- lib/std/Build.zig | 10 +-- lib/std/Build/Cache.zig | 8 +- lib/std/Build/Step.zig | 17 ++-- lib/std/Build/Step/CheckFile.zig | 4 +- lib/std/Build/Step/ConfigHeader.zig | 8 +- lib/std/Build/Step/Options.zig | 15 ++-- lib/std/Build/Step/Run.zig | 2 +- lib/std/Build/Watch.zig | 16 ++-- lib/std/Io/File.zig | 8 ++ lib/std/Io/Threaded.zig | 9 +-- lib/std/Io/Writer.zig | 6 +- lib/std/Io/net/test.zig | 2 +- lib/std/Io/test.zig | 10 +-- lib/std/Thread.zig | 4 +- lib/std/crypto/Certificate/Bundle/macos.zig | 2 +- lib/std/crypto/codecs/asn1/test.zig | 4 +- lib/std/debug.zig | 20 ++--- lib/std/debug/ElfFile.zig | 2 +- lib/std/debug/MachOFile.zig | 2 +- lib/std/debug/SelfInfo/Elf.zig | 19 +++-- lib/std/debug/SelfInfo/MachO.zig | 2 +- lib/std/debug/SelfInfo/Windows.zig | 4 +- lib/std/dynamic_library.zig | 8 +- lib/std/fs/test.zig | 116 +++++++++++++--------------- lib/std/os/linux/IoUring.zig | 24 +++--- lib/std/os/linux/test.zig | 6 +- lib/std/os/windows.zig | 4 +- lib/std/posix.zig | 14 ++-- lib/std/posix/test.zig | 52 ++++++------- lib/std/process/Child.zig | 2 +- lib/std/std.zig | 2 +- lib/std/tar.zig | 14 ++-- lib/std/testing.zig | 2 +- lib/std/zig/LibCInstallation.zig | 12 +-- lib/std/zig/WindowsSdk.zig | 2 +- lib/std/zig/system.zig | 6 +- lib/std/zig/system/darwin/macos.zig | 7 +- lib/std/zip.zig | 4 +- src/Compilation.zig | 20 ++--- src/Package/Fetch.zig | 12 +-- src/Package/Fetch/git.zig | 12 +-- src/Zcu/PerThread.zig | 4 +- src/codegen/llvm.zig | 17 ++-- src/fmt.zig | 6 +- src/introspect.zig | 4 +- src/libs/freebsd.zig | 12 +-- src/libs/glibc.zig | 16 ++-- src/libs/mingw.zig | 17 ++-- src/libs/netbsd.zig | 12 +-- src/link/C.zig | 4 +- src/link/Coff.zig | 6 +- src/link/Elf.zig | 4 +- src/link/Lld.zig | 4 +- src/link/MachO.zig | 14 ++-- src/link/MachO/CodeSignature.zig | 2 +- src/link/SpirV.zig | 3 +- src/link/Wasm.zig | 4 +- src/main.zig | 36 ++++----- 72 files changed, 398 insertions(+), 377 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/aro/aro/Compilation.zig b/lib/compiler/aro/aro/Compilation.zig index 09e4861d13..b3e4d5544d 100644 --- a/lib/compiler/aro/aro/Compilation.zig +++ b/lib/compiler/aro/aro/Compilation.zig @@ -2253,7 +2253,7 @@ test "addSourceFromBuffer" { var arena: std.heap.ArenaAllocator = .init(std.testing.allocator); defer arena.deinit(); var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); const source = try comp.addSourceFromBuffer("path", str); @@ -2267,7 +2267,7 @@ test "addSourceFromBuffer" { var arena: std.heap.ArenaAllocator = .init(allocator); defer arena.deinit(); var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(allocator, arena.allocator(), std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(allocator, arena.allocator(), std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); _ = try comp.addSourceFromBuffer("path", "spliced\\\nbuffer\n"); @@ -2313,7 +2313,7 @@ test "addSourceFromBuffer - exhaustive check for carriage return elimination" { var buf: [alphabet.len]u8 = @splat(alphabet[0]); var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); var source_count: u32 = 0; @@ -2341,7 +2341,7 @@ test "ignore BOM at beginning of file" { const Test = struct { fn run(arena: Allocator, buf: []const u8) !void { var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); const source = try comp.addSourceFromBuffer("file.c", buf); diff --git a/lib/compiler/aro/aro/Driver.zig b/lib/compiler/aro/aro/Driver.zig index f933e3ce52..fec3cea0f8 100644 --- a/lib/compiler/aro/aro/Driver.zig +++ b/lib/compiler/aro/aro/Driver.zig @@ -1327,7 +1327,7 @@ fn processSource( const dep_file_name = try d.getDepFileName(source, writer_buf[0..std.fs.max_name_bytes]); const file = if (dep_file_name) |path| - d.comp.cwd.createFile(path, .{}) catch |er| + d.comp.cwd.createFile(io, path, .{}) catch |er| return d.fatal("unable to create dependency file '{s}': {s}", .{ path, errorDescription(er) }) else Io.File.stdout(); @@ -1352,7 +1352,7 @@ fn processSource( } const file = if (d.output_name) |some| - d.comp.cwd.createFile(some, .{}) catch |er| + d.comp.cwd.createFile(io, some, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ some, errorDescription(er) }) else Io.File.stdout(); @@ -1405,7 +1405,7 @@ fn processSource( defer assembly.deinit(gpa); if (d.only_preprocess_and_compile) { - const out_file = d.comp.cwd.createFile(out_file_name, .{}) catch |er| + const out_file = d.comp.cwd.createFile(io, out_file_name, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ out_file_name, errorDescription(er) }); defer out_file.close(io); @@ -1419,7 +1419,7 @@ fn processSource( // then assemble to out_file_name var assembly_name_buf: [std.fs.max_name_bytes]u8 = undefined; const assembly_out_file_name = try d.getRandomFilename(&assembly_name_buf, ".s"); - const out_file = d.comp.cwd.createFile(assembly_out_file_name, .{}) catch |er| + const out_file = d.comp.cwd.createFile(io, assembly_out_file_name, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ assembly_out_file_name, errorDescription(er) }); defer out_file.close(io); assembly.writeToFile(out_file) catch |er| @@ -1455,7 +1455,7 @@ fn processSource( }; defer obj.deinit(); - const out_file = d.comp.cwd.createFile(out_file_name, .{}) catch |er| + const out_file = d.comp.cwd.createFile(io, out_file_name, .{}) catch |er| return d.fatal("unable to create output file '{s}': {s}", .{ out_file_name, errorDescription(er) }); defer out_file.close(io); diff --git a/lib/compiler/aro/aro/Parser.zig b/lib/compiler/aro/aro/Parser.zig index 4a89e0d460..fc21ee4d0b 100644 --- a/lib/compiler/aro/aro/Parser.zig +++ b/lib/compiler/aro/aro/Parser.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const mem = std.mem; const Allocator = mem.Allocator; const assert = std.debug.assert; @@ -211,7 +212,7 @@ fn checkIdentifierCodepointWarnings(p: *Parser, codepoint: u21, loc: Source.Loca const prev_total = p.diagnostics.total; var sf = std.heap.stackFallback(1024, p.comp.gpa); - var allocating: std.Io.Writer.Allocating = .init(sf.get()); + var allocating: Io.Writer.Allocating = .init(sf.get()); defer allocating.deinit(); if (!char_info.isC99IdChar(codepoint)) { @@ -425,7 +426,7 @@ pub fn err(p: *Parser, tok_i: TokenIndex, diagnostic: Diagnostic, args: anytype) if (p.diagnostics.effectiveKind(diagnostic) == .off) return; var sf = std.heap.stackFallback(1024, p.comp.gpa); - var allocating: std.Io.Writer.Allocating = .init(sf.get()); + var allocating: Io.Writer.Allocating = .init(sf.get()); defer allocating.deinit(); p.formatArgs(&allocating.writer, diagnostic.fmt, args) catch return error.OutOfMemory; @@ -447,7 +448,7 @@ pub fn err(p: *Parser, tok_i: TokenIndex, diagnostic: Diagnostic, args: anytype) }, p.pp.expansionSlice(tok_i), true); } -fn formatArgs(p: *Parser, w: *std.Io.Writer, fmt: []const u8, args: anytype) !void { +fn formatArgs(p: *Parser, w: *Io.Writer, fmt: []const u8, args: anytype) !void { var i: usize = 0; inline for (std.meta.fields(@TypeOf(args))) |arg_info| { const arg = @field(args, arg_info.name); @@ -476,13 +477,13 @@ fn formatArgs(p: *Parser, w: *std.Io.Writer, fmt: []const u8, args: anytype) !vo try w.writeAll(fmt[i..]); } -fn formatTokenId(w: *std.Io.Writer, fmt: []const u8, tok_id: Tree.Token.Id) !usize { +fn formatTokenId(w: *Io.Writer, fmt: []const u8, tok_id: Tree.Token.Id) !usize { const i = Diagnostics.templateIndex(w, fmt, "{tok_id}"); try w.writeAll(tok_id.symbol()); return i; } -fn formatQualType(p: *Parser, w: *std.Io.Writer, fmt: []const u8, qt: QualType) !usize { +fn formatQualType(p: *Parser, w: *Io.Writer, fmt: []const u8, qt: QualType) !usize { const i = Diagnostics.templateIndex(w, fmt, "{qt}"); try w.writeByte('\''); try qt.print(p.comp, w); @@ -501,7 +502,7 @@ fn formatQualType(p: *Parser, w: *std.Io.Writer, fmt: []const u8, qt: QualType) return i; } -fn formatResult(p: *Parser, w: *std.Io.Writer, fmt: []const u8, res: Result) !usize { +fn formatResult(p: *Parser, w: *Io.Writer, fmt: []const u8, res: Result) !usize { const i = Diagnostics.templateIndex(w, fmt, "{value}"); switch (res.val.opt_ref) { .none => try w.writeAll("(none)"), @@ -524,7 +525,7 @@ const Normalized = struct { return .{ .str = str }; } - pub fn format(ctx: Normalized, w: *std.Io.Writer, fmt: []const u8) !usize { + pub fn format(ctx: Normalized, w: *Io.Writer, fmt: []const u8) !usize { const i = Diagnostics.templateIndex(w, fmt, "{normalized}"); var it: std.unicode.Utf8Iterator = .{ .bytes = ctx.str, @@ -558,7 +559,7 @@ const Codepoint = struct { return .{ .codepoint = codepoint }; } - pub fn format(ctx: Codepoint, w: *std.Io.Writer, fmt: []const u8) !usize { + pub fn format(ctx: Codepoint, w: *Io.Writer, fmt: []const u8) !usize { const i = Diagnostics.templateIndex(w, fmt, "{codepoint}"); try w.print("{X:0>4}", .{ctx.codepoint}); return i; @@ -572,7 +573,7 @@ const Escaped = struct { return .{ .str = str }; } - pub fn format(ctx: Escaped, w: *std.Io.Writer, fmt: []const u8) !usize { + pub fn format(ctx: Escaped, w: *Io.Writer, fmt: []const u8) !usize { const i = Diagnostics.templateIndex(w, fmt, "{s}"); try std.zig.stringEscape(ctx.str, w); return i; @@ -1453,7 +1454,7 @@ fn decl(p: *Parser) Error!bool { return true; } -fn staticAssertMessage(p: *Parser, cond_node: Node.Index, maybe_message: ?Result, allocating: *std.Io.Writer.Allocating) !?[]const u8 { +fn staticAssertMessage(p: *Parser, cond_node: Node.Index, maybe_message: ?Result, allocating: *Io.Writer.Allocating) !?[]const u8 { const w = &allocating.writer; const cond = cond_node.get(&p.tree); @@ -1526,7 +1527,7 @@ fn staticAssert(p: *Parser) Error!bool { } else { if (!res.val.toBool(p.comp)) { var sf = std.heap.stackFallback(1024, gpa); - var allocating: std.Io.Writer.Allocating = .init(sf.get()); + var allocating: Io.Writer.Allocating = .init(sf.get()); defer allocating.deinit(); if (p.staticAssertMessage(res_node, str, &allocating) catch return error.OutOfMemory) |message| { @@ -9719,7 +9720,7 @@ fn primaryExpr(p: *Parser) Error!?Result { qt = some.qt; } else if (p.func.qt) |func_qt| { var sf = std.heap.stackFallback(1024, gpa); - var allocating: std.Io.Writer.Allocating = .init(sf.get()); + var allocating: Io.Writer.Allocating = .init(sf.get()); defer allocating.deinit(); func_qt.printNamed(p.tokSlice(p.func.name), p.comp, &allocating.writer) catch return error.OutOfMemory; @@ -10608,7 +10609,7 @@ test "Node locations" { const arena = arena_state.allocator(); var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); const file = try comp.addSourceFromBuffer("file.c", diff --git a/lib/compiler/aro/aro/Preprocessor.zig b/lib/compiler/aro/aro/Preprocessor.zig index d47727cbf0..e8343dc83a 100644 --- a/lib/compiler/aro/aro/Preprocessor.zig +++ b/lib/compiler/aro/aro/Preprocessor.zig @@ -3900,7 +3900,7 @@ test "Preserve pragma tokens sometimes" { defer arena.deinit(); var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(gpa, arena.allocator(), std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(gpa, arena.allocator(), std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); try comp.addDefaultPragmaHandlers(); @@ -3967,7 +3967,7 @@ test "destringify" { var arena: std.heap.ArenaAllocator = .init(gpa); defer arena.deinit(); var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(gpa, arena.allocator(), std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(gpa, arena.allocator(), std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); var pp = Preprocessor.init(&comp, .default); defer pp.deinit(); @@ -4030,7 +4030,7 @@ test "Include guards" { const arena = arena_state.allocator(); var diagnostics: Diagnostics = .{ .output = .ignore }; - var comp = Compilation.init(gpa, arena, std.testing.io, &diagnostics, std.fs.cwd()); + var comp = Compilation.init(gpa, arena, std.testing.io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); var pp = Preprocessor.init(&comp, .default); defer pp.deinit(); diff --git a/lib/compiler/aro/aro/Tokenizer.zig b/lib/compiler/aro/aro/Tokenizer.zig index c497c5ce82..198d49364a 100644 --- a/lib/compiler/aro/aro/Tokenizer.zig +++ b/lib/compiler/aro/aro/Tokenizer.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const Compilation = @import("Compilation.zig"); @@ -2326,7 +2327,7 @@ test "Tokenizer fuzz test" { fn testOne(_: @This(), input_bytes: []const u8) anyerror!void { var arena: std.heap.ArenaAllocator = .init(std.testing.allocator); defer arena.deinit(); - var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, undefined, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, undefined, Io.Dir.cwd()); defer comp.deinit(); const source = try comp.addSourceFromBuffer("fuzz.c", input_bytes); @@ -2351,7 +2352,7 @@ test "Tokenizer fuzz test" { fn expectTokensExtra(contents: []const u8, expected_tokens: []const Token.Id, langopts: ?LangOpts) !void { var arena: std.heap.ArenaAllocator = .init(std.testing.allocator); defer arena.deinit(); - var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, undefined, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, undefined, Io.Dir.cwd()); defer comp.deinit(); if (langopts) |provided| { comp.langopts = provided; diff --git a/lib/compiler/aro/aro/Value.zig b/lib/compiler/aro/aro/Value.zig index 25a2d1824f..14949ce03b 100644 --- a/lib/compiler/aro/aro/Value.zig +++ b/lib/compiler/aro/aro/Value.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const BigIntConst = std.math.big.int.Const; const BigIntMutable = std.math.big.int.Mutable; @@ -80,7 +81,7 @@ test "minUnsignedBits" { defer arena_state.deinit(); const arena = arena_state.allocator(); - var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, undefined, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, undefined, Io.Dir.cwd()); defer comp.deinit(); const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" }); comp.target = .fromZigTarget(try std.zig.system.resolveTargetQuery(std.testing.io, target_query)); @@ -119,7 +120,7 @@ test "minSignedBits" { defer arena_state.deinit(); const arena = arena_state.allocator(); - var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, undefined, std.fs.cwd()); + var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, undefined, Io.Dir.cwd()); defer comp.deinit(); const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" }); comp.target = .fromZigTarget(try std.zig.system.resolveTargetQuery(std.testing.io, target_query)); @@ -1080,7 +1081,7 @@ const NestedPrint = union(enum) { }, }; -pub fn printPointer(offset: Value, base: []const u8, comp: *const Compilation, w: *std.Io.Writer) std.Io.Writer.Error!void { +pub fn printPointer(offset: Value, base: []const u8, comp: *const Compilation, w: *Io.Writer) Io.Writer.Error!void { try w.writeByte('&'); try w.writeAll(base); if (!offset.isZero(comp)) { @@ -1089,7 +1090,7 @@ pub fn printPointer(offset: Value, base: []const u8, comp: *const Compilation, w } } -pub fn print(v: Value, qt: QualType, comp: *const Compilation, w: *std.Io.Writer) std.Io.Writer.Error!?NestedPrint { +pub fn print(v: Value, qt: QualType, comp: *const Compilation, w: *Io.Writer) Io.Writer.Error!?NestedPrint { if (qt.is(comp, .bool)) { try w.writeAll(if (v.isZero(comp)) "false" else "true"); return null; @@ -1116,7 +1117,7 @@ pub fn print(v: Value, qt: QualType, comp: *const Compilation, w: *std.Io.Writer return null; } -pub fn printString(bytes: []const u8, qt: QualType, comp: *const Compilation, w: *std.Io.Writer) std.Io.Writer.Error!void { +pub fn printString(bytes: []const u8, qt: QualType, comp: *const Compilation, w: *Io.Writer) Io.Writer.Error!void { const size: Compilation.CharUnitSize = @enumFromInt(qt.childType(comp).sizeof(comp)); const without_null = bytes[0 .. bytes.len - @intFromEnum(size)]; try w.writeByte('"'); diff --git a/lib/compiler/aro/main.zig b/lib/compiler/aro/main.zig index 66c8add4a3..d1be1dbb21 100644 --- a/lib/compiler/aro/main.zig +++ b/lib/compiler/aro/main.zig @@ -59,7 +59,7 @@ pub fn main() u8 { } }, }; - var comp = Compilation.initDefault(gpa, arena, io, &diagnostics, std.fs.cwd()) catch |er| switch (er) { + var comp = Compilation.initDefault(gpa, arena, io, &diagnostics, Io.Dir.cwd()) catch |er| switch (er) { error.OutOfMemory => { std.debug.print("out of memory\n", .{}); if (fast_exit) process.exit(1); diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index 485e644daa..e48f76a6a6 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -152,7 +152,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void defer threaded.deinit(); const io = threaded.io(); - const input_file = fs.cwd().openFile(input, .{}) catch |err| fatal("failed to open {s}: {t}", .{ input, err }); + const input_file = Io.Dir.cwd().openFile(input, .{}) catch |err| fatal("failed to open {s}: {t}", .{ input, err }); defer input_file.close(io); const stat = input_file.stat() catch |err| fatal("failed to stat {s}: {t}", .{ input, err }); @@ -180,7 +180,7 @@ fn cmdObjCopy(gpa: Allocator, arena: Allocator, args: []const []const u8) !void const mode = if (out_fmt != .elf or only_keep_debug) Io.File.default_mode else stat.mode; - var output_file = try fs.cwd().createFile(output, .{ .mode = mode }); + var output_file = try Io.Dir.cwd().createFile(io, output, .{ .mode = mode }); defer output_file.close(io); var out = output_file.writer(&output_buffer); diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig index bbd3d172b4..d3f33ad81a 100644 --- a/lib/compiler/reduce.zig +++ b/lib/compiler/reduce.zig @@ -233,7 +233,7 @@ pub fn main() !void { } } - try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); + try Io.Dir.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); // std.debug.print("trying this code:\n{s}\n", .{rendered.items}); const interestingness = try runCheck(arena, interestingness_argv.items); @@ -274,7 +274,7 @@ pub fn main() !void { fixups.clearRetainingCapacity(); rendered.clearRetainingCapacity(); try tree.render(gpa, &rendered.writer, fixups); - try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); + try Io.Dir.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); return std.process.cleanExit(); } @@ -398,7 +398,7 @@ fn transformationsToFixups( } fn parse(gpa: Allocator, file_path: []const u8) !Ast { - const source_code = std.fs.cwd().readFileAllocOptions( + const source_code = Io.Dir.cwd().readFileAllocOptions( file_path, gpa, .limited(std.math.maxInt(u32)), diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index bb54289e3e..ae4ece2968 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -2003,7 +2003,7 @@ test "maybeAppendRC" { // Create the file so that it's found. In this scenario, .rc should not get // appended. - var file = try tmp.dir.createFile("foo", .{}); + var file = try tmp.dir.createFile(io, "foo", .{}); file.close(io); try options.maybeAppendRC(tmp.dir); try std.testing.expectEqualStrings("foo", options.input_source.filename); diff --git a/lib/compiler/resinator/compile.zig b/lib/compiler/resinator/compile.zig index 7dc77e5ee1..3e046a10c1 100644 --- a/lib/compiler/resinator/compile.zig +++ b/lib/compiler/resinator/compile.zig @@ -111,7 +111,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io try search_dirs.append(allocator, .{ .dir = root_dir, .path = try allocator.dupe(u8, root_dir_path) }); } } - // Re-open the passed in cwd since we want to be able to close it (std.fs.cwd() shouldn't be closed) + // Re-open the passed in cwd since we want to be able to close it (Io.Dir.cwd() shouldn't be closed) const cwd_dir = options.cwd.openDir(".", .{}) catch |err| { try options.diagnostics.append(.{ .err = .failed_to_open_cwd, @@ -406,7 +406,7 @@ pub const Compiler = struct { // `/test.bin` relative to include paths and instead only treats it as // an absolute path. if (std.fs.path.isAbsolute(path)) { - const file = try utils.openFileNotDir(std.fs.cwd(), path, .{}); + const file = try utils.openFileNotDir(Io.Dir.cwd(), path, .{}); errdefer file.close(io); if (self.dependencies) |dependencies| { diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index c726a06cf4..416abc2ab7 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -67,7 +67,7 @@ pub fn main() !void { }, else => |e| return e, }; - try options.maybeAppendRC(std.fs.cwd()); + try options.maybeAppendRC(Io.Dir.cwd()); if (!zig_integration) { // print any warnings/notes @@ -141,7 +141,7 @@ pub fn main() !void { if (!zig_integration) std.debug.unlockStderrWriter(); } - var comp = aro.Compilation.init(aro_arena, aro_arena, io, &diagnostics, std.fs.cwd()); + var comp = aro.Compilation.init(aro_arena, aro_arena, io, &diagnostics, Io.Dir.cwd()); defer comp.deinit(); var argv: std.ArrayList([]const u8) = .empty; @@ -196,7 +196,7 @@ pub fn main() !void { }; }, .filename => |input_filename| { - break :full_input std.fs.cwd().readFileAlloc(input_filename, gpa, .unlimited) catch |err| { + break :full_input Io.Dir.cwd().readFileAlloc(input_filename, gpa, .unlimited) catch |err| { try error_handler.emitMessage(gpa, .err, "unable to read input file path '{s}': {s}", .{ input_filename, @errorName(err) }); std.process.exit(1); }; @@ -212,7 +212,7 @@ pub fn main() !void { try output_file.writeAll(full_input); }, .filename => |output_filename| { - try std.fs.cwd().writeFile(.{ .sub_path = output_filename, .data = full_input }); + try Io.Dir.cwd().writeFile(.{ .sub_path = output_filename, .data = full_input }); }, } return; @@ -277,7 +277,7 @@ pub fn main() !void { const output_buffered_stream = res_stream_writer.interface(); compile(gpa, io, final_input, output_buffered_stream, .{ - .cwd = std.fs.cwd(), + .cwd = Io.Dir.cwd(), .diagnostics = &diagnostics, .source_mappings = &mapping_results.mappings, .dependencies = maybe_dependencies, @@ -294,7 +294,7 @@ pub fn main() !void { .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page, }) catch |err| switch (err) { error.ParseError, error.CompileError => { - try error_handler.emitDiagnostics(gpa, std.fs.cwd(), final_input, &diagnostics, mapping_results.mappings); + try error_handler.emitDiagnostics(gpa, Io.Dir.cwd(), final_input, &diagnostics, mapping_results.mappings); // Delete the output file on error res_stream.cleanupAfterError(io); std.process.exit(1); @@ -306,12 +306,12 @@ pub fn main() !void { // print any warnings/notes if (!zig_integration) { - diagnostics.renderToStdErr(std.fs.cwd(), final_input, mapping_results.mappings); + diagnostics.renderToStdErr(Io.Dir.cwd(), final_input, mapping_results.mappings); } // write the depfile if (options.depfile_path) |depfile_path| { - var depfile = std.fs.cwd().createFile(depfile_path, .{}) catch |err| { + var depfile = Io.Dir.cwd().createFile(io, depfile_path, .{}) catch |err| { try error_handler.emitMessage(gpa, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) }); std.process.exit(1); }; @@ -440,7 +440,7 @@ const IoStream = struct { // Delete the output file on error file.close(io); // Failing to delete is not really a big deal, so swallow any errors - std.fs.cwd().deleteFile(self.name) catch {}; + Io.Dir.cwd().deleteFile(self.name) catch {}; }, .stdio, .memory, .closed => return, } @@ -457,8 +457,8 @@ const IoStream = struct { switch (source) { .filename => |filename| return .{ .file = switch (io) { - .input => try openFileNotDir(std.fs.cwd(), filename, .{}), - .output => try std.fs.cwd().createFile(filename, .{}), + .input => try openFileNotDir(Io.Dir.cwd(), filename, .{}), + .output => try Io.Dir.cwd().createFile(io, filename, .{}), }, }, .stdio => |file| return .{ .stdio = file }, diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index e4efac28cd..87c4da9faa 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -40,7 +40,7 @@ pub fn main() !void { const zig_exe_path = argv.next().?; const global_cache_path = argv.next().?; - var lib_dir = try std.fs.cwd().openDir(zig_lib_directory, .{}); + var lib_dir = try Io.Dir.cwd().openDir(zig_lib_directory, .{}); defer lib_dir.close(io); var listen_port: u16 = 0; diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig index 830c70e424..d0a873fd78 100644 --- a/lib/compiler/translate-c/main.zig +++ b/lib/compiler/translate-c/main.zig @@ -47,7 +47,7 @@ pub fn main() u8 { }; defer diagnostics.deinit(); - var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, std.fs.cwd()) catch |err| switch (err) { + var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, Io.Dir.cwd()) catch |err| switch (err) { error.OutOfMemory => { std.debug.print("ran out of memory initializing C compilation\n", .{}); if (fast_exit) process.exit(1); @@ -226,7 +226,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration const dep_file_name = try d.getDepFileName(source, out_buf[0..std.fs.max_name_bytes]); const file = if (dep_file_name) |path| - d.comp.cwd.createFile(path, .{}) catch |er| + d.comp.cwd.createFile(io, path, .{}) catch |er| return d.fatal("unable to create dependency file '{s}': {s}", .{ path, aro.Driver.errorDescription(er) }) else Io.File.stdout(); @@ -253,10 +253,10 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration if (d.output_name) |path| blk: { if (std.mem.eql(u8, path, "-")) break :blk; if (std.fs.path.dirname(path)) |dirname| { - std.fs.cwd().makePath(dirname) catch |err| + Io.Dir.cwd().makePath(dirname) catch |err| return d.fatal("failed to create path to '{s}': {s}", .{ path, aro.Driver.errorDescription(err) }); } - out_file = std.fs.cwd().createFile(path, .{}) catch |err| { + out_file = Io.Dir.cwd().createFile(io, path, .{}) catch |err| { return d.fatal("failed to create output file '{s}': {s}", .{ path, aro.Driver.errorDescription(err) }); }; close_out_file = true; diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 7bfdbb6449..cc2f70fd2f 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1702,13 +1702,13 @@ pub fn addCheckFile( pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.MakeError || Io.Dir.StatFileError)!void { const io = b.graph.io; if (b.verbose) log.info("truncate {s}", .{dest_path}); - const cwd = fs.cwd(); - var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) { + const cwd = Io.Dir.cwd(); + var src_file = cwd.createFile(io, dest_path, .{}) catch |err| switch (err) { error.FileNotFound => blk: { if (fs.path.dirname(dest_path)) |dirname| { try cwd.makePath(dirname); } - break :blk try cwd.createFile(dest_path, .{}); + break :blk try cwd.createFile(io, dest_path, .{}); }, else => |e| return e, }; @@ -1846,7 +1846,7 @@ pub fn runAllowFail( }; errdefer b.allocator.free(stdout); - const term = try child.wait(); + const term = try child.wait(io); switch (term) { .Exited => |code| { if (code != 0) { @@ -2193,7 +2193,7 @@ fn dependencyInner( const build_root: std.Build.Cache.Directory = .{ .path = build_root_string, - .handle = fs.cwd().openDir(build_root_string, .{}) catch |err| { + .handle = Io.Dir.cwd().openDir(build_root_string, .{}) catch |err| { std.debug.print("unable to open '{s}': {s}\n", .{ build_root_string, @errorName(err), }); diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 42459c033d..fdcb2ab714 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -508,7 +508,7 @@ pub const Manifest = struct { // and `want_shared_lock` is set, a shared lock might be sufficient, so we'll // open with a shared lock instead. while (true) { - if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ + if (self.cache.manifest_dir.createFile(io, &manifest_file_path, .{ .read = true, .truncate = false, .lock = .exclusive, @@ -543,7 +543,7 @@ pub const Manifest = struct { return error.CacheCheckFailed; } - if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ + if (self.cache.manifest_dir.createFile(io, &manifest_file_path, .{ .read = true, .truncate = false, .lock = .exclusive, @@ -873,7 +873,7 @@ pub const Manifest = struct { if (man.want_refresh_timestamp) { man.want_refresh_timestamp = false; - var file = man.cache.manifest_dir.createFile("timestamp", .{ + var file = man.cache.manifest_dir.createFile(io, "timestamp", .{ .read = true, .truncate = true, }) catch |err| switch (err) { @@ -1324,7 +1324,7 @@ fn hashFile(file: Io.File, bin_digest: *[Hasher.mac_length]u8) Io.File.PReadErro fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp { const test_out_file = "test-filetimestamp.tmp"; - var file = try dir.createFile(test_out_file, .{ + var file = try dir.createFile(io, test_out_file, .{ .read = true, .truncate = true, }); diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 56ef32e8d8..2ec1c0ef31 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -401,6 +401,9 @@ pub fn evalZigProcess( web_server: ?*Build.WebServer, gpa: Allocator, ) !?Path { + const b = s.owner; + const io = b.graph.io; + // If an error occurs, it's happened in this command: assert(s.result_failed_command == null); s.result_failed_command = try allocPrintCmd(gpa, null, argv); @@ -411,7 +414,7 @@ pub fn evalZigProcess( const result = zigProcessUpdate(s, zp, watch, web_server, gpa) catch |err| switch (err) { error.BrokenPipe => { // Process restart required. - const term = zp.child.wait() catch |e| { + const term = zp.child.wait(io) catch |e| { return s.fail("unable to wait for {s}: {t}", .{ argv[0], e }); }; _ = term; @@ -427,7 +430,7 @@ pub fn evalZigProcess( if (s.result_error_msgs.items.len > 0 and result == null) { // Crash detected. - const term = zp.child.wait() catch |e| { + const term = zp.child.wait(io) catch |e| { return s.fail("unable to wait for {s}: {t}", .{ argv[0], e }); }; s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0; @@ -439,9 +442,7 @@ pub fn evalZigProcess( return result; } assert(argv.len != 0); - const b = s.owner; const arena = b.allocator; - const io = b.graph.io; try handleChildProcUnsupported(s); try handleVerbose(s.owner, null, argv); @@ -478,7 +479,7 @@ pub fn evalZigProcess( zp.child.stdin.?.close(io); zp.child.stdin = null; - const term = zp.child.wait() catch |err| { + const term = zp.child.wait(io) catch |err| { return s.fail("unable to wait for {s}: {t}", .{ argv[0], err }); }; s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0; @@ -519,7 +520,7 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.MakePathStatus { const b = s.owner; try handleVerbose(b, null, &.{ "install", "-d", dest_path }); - return std.fs.cwd().makePathStatus(dest_path) catch |err| + return Io.Dir.cwd().makePathStatus(dest_path) catch |err| return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err }); } @@ -895,7 +896,7 @@ pub fn addWatchInput(step: *Step, lazy_file: Build.LazyPath) Allocator.Error!voi try addWatchInputFromPath(step, .{ .root_dir = .{ .path = null, - .handle = std.fs.cwd(), + .handle = Io.Dir.cwd(), }, .sub_path = std.fs.path.dirname(path_string) orelse "", }, std.fs.path.basename(path_string)); @@ -920,7 +921,7 @@ pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Alloc try addDirectoryWatchInputFromPath(step, .{ .root_dir = .{ .path = null, - .handle = std.fs.cwd(), + .handle = Io.Dir.cwd(), }, .sub_path = path_string, }); diff --git a/lib/std/Build/Step/CheckFile.zig b/lib/std/Build/Step/CheckFile.zig index efeedc8b80..560b6ad050 100644 --- a/lib/std/Build/Step/CheckFile.zig +++ b/lib/std/Build/Step/CheckFile.zig @@ -3,7 +3,9 @@ //! TODO: generalize the code in std.testing.expectEqualStrings and make this //! CheckFile step produce those helpful diagnostics when there is not a match. const CheckFile = @This(); + const std = @import("std"); +const Io = std.Io; const Step = std.Build.Step; const fs = std.fs; const mem = std.mem; @@ -53,7 +55,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try step.singleUnchangingWatchInput(check_file.source); const src_path = check_file.source.getPath2(b, step); - const contents = fs.cwd().readFileAlloc(src_path, b.allocator, .limited(check_file.max_bytes)) catch |err| { + const contents = Io.Dir.cwd().readFileAlloc(src_path, b.allocator, .limited(check_file.max_bytes)) catch |err| { return step.fail("unable to read '{s}': {s}", .{ src_path, @errorName(err), }); diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index df2419764d..ea7d9d99ff 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -1,5 +1,7 @@ -const std = @import("std"); const ConfigHeader = @This(); + +const std = @import("std"); +const Io = std.Io; const Step = std.Build.Step; const Allocator = std.mem.Allocator; const Writer = std.Io.Writer; @@ -205,7 +207,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { .autoconf_undef, .autoconf_at => |file_source| { try bw.writeAll(c_generated_line); const src_path = file_source.getPath2(b, step); - const contents = std.fs.cwd().readFileAlloc(src_path, arena, .limited(config_header.max_bytes)) catch |err| { + const contents = Io.Dir.cwd().readFileAlloc(src_path, arena, .limited(config_header.max_bytes)) catch |err| { return step.fail("unable to read autoconf input file '{s}': {s}", .{ src_path, @errorName(err), }); @@ -219,7 +221,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { .cmake => |file_source| { try bw.writeAll(c_generated_line); const src_path = file_source.getPath2(b, step); - const contents = std.fs.cwd().readFileAlloc(src_path, arena, .limited(config_header.max_bytes)) catch |err| { + const contents = Io.Dir.cwd().readFileAlloc(src_path, arena, .limited(config_header.max_bytes)) catch |err| { return step.fail("unable to read cmake input file '{s}': {s}", .{ src_path, @errorName(err), }); diff --git a/lib/std/Build/Step/Options.zig b/lib/std/Build/Step/Options.zig index 9f5665e93a..1416e0e916 100644 --- a/lib/std/Build/Step/Options.zig +++ b/lib/std/Build/Step/Options.zig @@ -1,12 +1,13 @@ -const std = @import("std"); +const Options = @This(); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const fs = std.fs; const Step = std.Build.Step; const GeneratedFile = std.Build.GeneratedFile; const LazyPath = std.Build.LazyPath; -const Options = @This(); - pub const base_id: Step.Id = .options; step: Step, @@ -542,11 +543,11 @@ test Options { .cache = .{ .io = io, .gpa = arena.allocator(), - .manifest_dir = std.fs.cwd(), + .manifest_dir = Io.Dir.cwd(), }, .zig_exe = "test", .env_map = std.process.EnvMap.init(arena.allocator()), - .global_cache_root = .{ .path = "test", .handle = std.fs.cwd() }, + .global_cache_root = .{ .path = "test", .handle = Io.Dir.cwd() }, .host = .{ .query = .{}, .result = try std.zig.system.resolveTargetQuery(io, .{}), @@ -557,8 +558,8 @@ test Options { var builder = try std.Build.create( &graph, - .{ .path = "test", .handle = std.fs.cwd() }, - .{ .path = "test", .handle = std.fs.cwd() }, + .{ .path = "test", .handle = Io.Dir.cwd() }, + .{ .path = "test", .handle = Io.Dir.cwd() }, &.{}, ); diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 7c54c8048e..af6bc20438 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1023,7 +1023,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, options, null); - const dep_file_dir = std.fs.cwd(); + const dep_file_dir = Io.Dir.cwd(); const dep_file_basename = dep_output_file.generated_file.getPath2(b, step); if (has_side_effects) try man.addDepFile(dep_file_dir, dep_file_basename) diff --git a/lib/std/Build/Watch.zig b/lib/std/Build/Watch.zig index ff06ad3ff3..f7ac47961e 100644 --- a/lib/std/Build/Watch.zig +++ b/lib/std/Build/Watch.zig @@ -122,7 +122,7 @@ const Os = switch (builtin.os.tag) { }) catch return error.NameTooLong; const stack_ptr: *std.os.linux.file_handle = @ptrCast(&file_handle_buffer); stack_ptr.handle_bytes = file_handle_buffer.len - @sizeOf(std.os.linux.file_handle); - try posix.name_to_handle_at(path.root_dir.handle.fd, adjusted_path, stack_ptr, mount_id, std.os.linux.AT.HANDLE_FID); + try posix.name_to_handle_at(path.root_dir.handle.handle, adjusted_path, stack_ptr, mount_id, std.os.linux.AT.HANDLE_FID); const stack_lfh: FileHandle = .{ .handle = stack_ptr }; return stack_lfh.clone(gpa); } @@ -222,7 +222,7 @@ const Os = switch (builtin.os.tag) { posix.fanotify_mark(fan_fd, .{ .ADD = true, .ONLYDIR = true, - }, fan_mask, path.root_dir.handle.fd, path.subPathOrDot()) catch |err| { + }, fan_mask, path.root_dir.handle.handle, path.subPathOrDot()) catch |err| { fatal("unable to watch {f}: {s}", .{ path, @errorName(err) }); }; } @@ -275,7 +275,7 @@ const Os = switch (builtin.os.tag) { posix.fanotify_mark(fan_fd, .{ .REMOVE = true, .ONLYDIR = true, - }, fan_mask, path.root_dir.handle.fd, path.subPathOrDot()) catch |err| switch (err) { + }, fan_mask, path.root_dir.handle.handle, path.subPathOrDot()) catch |err| switch (err) { error.FileNotFound => {}, // Expected, harmless. else => |e| std.log.warn("unable to unwatch '{f}': {s}", .{ path, @errorName(e) }), }; @@ -353,7 +353,7 @@ const Os = switch (builtin.os.tag) { // The following code is a drawn out NtCreateFile call. (mostly adapted from Io.Dir.makeOpenDirAccessMaskW) // It's necessary in order to get the specific flags that are required when calling ReadDirectoryChangesW. var dir_handle: windows.HANDLE = undefined; - const root_fd = path.root_dir.handle.fd; + const root_fd = path.root_dir.handle.handle; const sub_path = path.subPathOrDot(); const sub_path_w = try windows.sliceToPrefixedFileW(root_fd, sub_path); const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; @@ -681,9 +681,9 @@ const Os = switch (builtin.os.tag) { if (!gop.found_existing) { const skip_open_dir = path.sub_path.len == 0; const dir_fd = if (skip_open_dir) - path.root_dir.handle.fd + path.root_dir.handle.handle else - posix.openat(path.root_dir.handle.fd, path.sub_path, dir_open_flags, 0) catch |err| { + posix.openat(path.root_dir.handle.handle, path.sub_path, dir_open_flags, 0) catch |err| { fatal("failed to open directory {f}: {s}", .{ path, @errorName(err) }); }; // Empirically the dir has to stay open or else no events are triggered. @@ -750,7 +750,7 @@ const Os = switch (builtin.os.tag) { // to access that data via the dir_fd field. const path = w.dir_table.keys()[i]; const dir_fd = if (path.sub_path.len == 0) - path.root_dir.handle.fd + path.root_dir.handle.handle else handles.items(.dir_fd)[i]; assert(dir_fd != -1); @@ -761,7 +761,7 @@ const Os = switch (builtin.os.tag) { const last_dir_fd = fd: { const last_path = w.dir_table.keys()[handles.len - 1]; const last_dir_fd = if (last_path.sub_path.len == 0) - last_path.root_dir.handle.fd + last_path.root_dir.handle.handle else handles.items(.dir_fd)[handles.len - 1]; assert(last_dir_fd != -1); diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index 8e71f648e2..a9b4775772 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -527,6 +527,14 @@ pub fn writerStreaming(file: File, io: Io, buffer: []u8) Writer { return .initStreaming(file, io, buffer); } +/// Equivalent to creating a streaming writer, writing `bytes`, and then flushing. +pub fn writeStreamingAll(file: File, io: Io, bytes: []const u8) Writer.Error!void { + var index: usize = 0; + while (index < bytes.len) { + index += try io.vtable.fileWriteStreaming(io.userdata, file, &.{}, &.{bytes[index..]}, 1); + } +} + pub const LockError = error{ SystemResources, FileLocksUnsupported, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index fb76002201..124f886515 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -2361,7 +2361,7 @@ fn dirCreateFilePosix( .NFILE => return error.SystemFdQuotaExceeded, .NODEV => return error.NoDevice, .NOENT => return error.FileNotFound, - .SRCH => return error.ProcessNotFound, + .SRCH => return error.FileNotFound, // Linux when accessing procfs. .NOMEM => return error.SystemResources, .NOSPC => return error.NoSpaceLeft, .NOTDIR => return error.NotDir, @@ -2670,7 +2670,7 @@ fn dirOpenFilePosix( .NFILE => return error.SystemFdQuotaExceeded, .NODEV => return error.NoDevice, .NOENT => return error.FileNotFound, - .SRCH => return error.ProcessNotFound, + .SRCH => return error.FileNotFound, // Linux when opening procfs files. .NOMEM => return error.SystemResources, .NOSPC => return error.NoSpaceLeft, .NOTDIR => return error.NotDir, @@ -3287,7 +3287,7 @@ fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_b .NFILE => return error.SystemFdQuotaExceeded, .NODEV => return error.NoDevice, .NOENT => return error.FileNotFound, - .SRCH => return error.ProcessNotFound, + .SRCH => return error.FileNotFound, // Linux when accessing procfs. .NOMEM => return error.SystemResources, .NOSPC => return error.NoSpaceLeft, .NOTDIR => return error.NotDir, @@ -5548,7 +5548,6 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: [][]u8) File. switch (e) { .INVAL => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), - .SRCH => return error.ProcessNotFound, .AGAIN => return error.WouldBlock, .BADF => |err| { if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. @@ -5672,7 +5671,6 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: [][]u8, offs switch (e) { .INVAL => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), - .SRCH => return error.ProcessNotFound, .AGAIN => return error.WouldBlock, .BADF => |err| { if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. @@ -6312,7 +6310,6 @@ fn fileWriteStreaming( switch (e) { .INVAL => return error.InvalidArgument, .FAULT => |err| return errnoBug(err), - .SRCH => return error.ProcessNotFound, .AGAIN => return error.WouldBlock, .BADF => return error.NotOpenForWriting, // Can be a race condition. .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called. diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 3f25bc6a26..63ae6d93a0 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -2835,7 +2835,7 @@ test "discarding sendFile" { var tmp_dir = testing.tmpDir(.{}); defer tmp_dir.cleanup(); - const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [256]u8 = undefined; var file_writer: File.Writer = .init(file, &r_buffer); @@ -2857,7 +2857,7 @@ test "allocating sendFile" { var tmp_dir = testing.tmpDir(.{}); defer tmp_dir.cleanup(); - const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [2]u8 = undefined; var file_writer: File.Writer = .init(file, &r_buffer); @@ -2881,7 +2881,7 @@ test sendFileReading { var tmp_dir = testing.tmpDir(.{}); defer tmp_dir.cleanup(); - const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [2]u8 = undefined; var file_writer: File.Writer = .init(file, &r_buffer); diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig index 5818f6c3f7..c9ed0d3284 100644 --- a/lib/std/Io/net/test.zig +++ b/lib/std/Io/net/test.zig @@ -278,7 +278,7 @@ test "listen on a unix socket, send bytes, receive bytes" { defer testing.allocator.free(socket_path); const socket_addr = try net.UnixAddress.init(socket_path); - defer std.fs.cwd().deleteFile(socket_path) catch {}; + defer Io.Dir.cwd().deleteFile(socket_path) catch {}; var server = try socket_addr.listen(io, .{}); defer server.socket.close(io); diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 9f21fe50e7..e731dc18d7 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -27,7 +27,7 @@ test "write a file, read it, then delete it" { random.bytes(data[0..]); const tmp_file_name = "temp_test_file.txt"; { - var file = try tmp.dir.createFile(tmp_file_name, .{}); + var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); var file_writer = file.writer(&.{}); @@ -40,7 +40,7 @@ test "write a file, read it, then delete it" { { // Make sure the exclusive flag is honored. - try expectError(File.OpenError.PathAlreadyExists, tmp.dir.createFile(tmp_file_name, .{ .exclusive = true })); + try expectError(File.OpenError.PathAlreadyExists, tmp.dir.createFile(io, tmp_file_name, .{ .exclusive = true })); } { @@ -70,7 +70,7 @@ test "File seek ops" { const io = testing.io; const tmp_file_name = "temp_test_file.txt"; - var file = try tmp.dir.createFile(tmp_file_name, .{}); + var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); try file.writeAll(&([_]u8{0x55} ** 8192)); @@ -96,7 +96,7 @@ test "setEndPos" { defer tmp.cleanup(); const tmp_file_name = "temp_test_file.txt"; - var file = try tmp.dir.createFile(tmp_file_name, .{}); + var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); // Verify that the file size changes and the file offset is not moved @@ -121,7 +121,7 @@ test "updateTimes" { defer tmp.cleanup(); const tmp_file_name = "just_a_temporary_file.txt"; - var file = try tmp.dir.createFile(tmp_file_name, .{ .read = true }); + var file = try tmp.dir.createFile(io, tmp_file_name, .{ .read = true }); defer file.close(io); const stat_old = try file.stat(); diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 102bb59415..8453bc4c81 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -208,7 +208,7 @@ pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { var buf: [32]u8 = undefined; const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); - const file = try std.fs.cwd().openFile(io, path, .{ .mode = .write_only }); + const file = try Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }); defer file.close(io); try file.writeAll(name); @@ -325,7 +325,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co var threaded: std.Io.Threaded = .init_single_threaded; const io = threaded.ioBasic(); - const file = try std.fs.cwd().openFile(io, path, .{}); + const file = try Io.Dir.cwd().openFile(io, path, .{}); defer file.close(io); var file_reader = file.readerStreaming(io, &.{}); diff --git a/lib/std/crypto/Certificate/Bundle/macos.zig b/lib/std/crypto/Certificate/Bundle/macos.zig index 473505ac51..444d8da675 100644 --- a/lib/std/crypto/Certificate/Bundle/macos.zig +++ b/lib/std/crypto/Certificate/Bundle/macos.zig @@ -19,7 +19,7 @@ pub fn rescanMac(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp) RescanM _ = io; // TODO migrate file system to use std.Io for (keychain_paths) |keychain_path| { - const bytes = std.fs.cwd().readFileAlloc(keychain_path, gpa, .limited(std.math.maxInt(u32))) catch |err| switch (err) { + const bytes = Io.Dir.cwd().readFileAlloc(keychain_path, gpa, .limited(std.math.maxInt(u32))) catch |err| switch (err) { error.StreamTooLong => return error.FileTooBig, else => |e| return e, }; diff --git a/lib/std/crypto/codecs/asn1/test.zig b/lib/std/crypto/codecs/asn1/test.zig index ff854fcbde..3dbedb9f80 100644 --- a/lib/std/crypto/codecs/asn1/test.zig +++ b/lib/std/crypto/codecs/asn1/test.zig @@ -73,8 +73,8 @@ test AllTypes { try std.testing.expectEqualSlices(u8, encoded, buf); // Use this to update test file. - // const dir = try std.fs.cwd().openDir("lib/std/crypto/asn1", .{}); - // var file = try dir.createFile(path, .{}); + // const dir = try Io.Dir.cwd().openDir("lib/std/crypto/asn1", .{}); + // var file = try dir.createFile(io, path, .{}); // defer file.close(io); // try file.writeAll(buf); } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 97741ecb40..5df0eef2d5 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -60,7 +60,7 @@ pub const cpu_context = @import("debug/cpu_context.zig"); /// }; /// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's /// /// return address, or 0 if the end of the stack has been reached. -/// pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, context: *UnwindContext) SelfInfoError!usize; +/// pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) SelfInfoError!usize; /// ``` pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo")) root.debug.SelfInfo @@ -558,9 +558,9 @@ pub fn defaultPanic( stderr.print("{s}\n", .{msg}) catch break :trace; if (@errorReturnTrace()) |t| if (t.index > 0) { - stderr.writeAll("error return context:\n") catch break :trace; + stderr.writeStreamingAll("error return context:\n") catch break :trace; writeStackTrace(t, stderr, tty_config) catch break :trace; - stderr.writeAll("\nstack trace:\n") catch break :trace; + stderr.writeStreamingAll("\nstack trace:\n") catch break :trace; }; writeCurrentStackTrace(.{ .first_address = first_trace_addr orelse @returnAddress(), @@ -575,7 +575,7 @@ pub fn defaultPanic( // A panic happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - File.stderr().writeAll("aborting due to recursive panic\n") catch {}; + File.stderr().writeStreamingAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } @@ -960,7 +960,7 @@ const StackIterator = union(enum) { }, }; - fn next(it: *StackIterator) Result { + fn next(it: *StackIterator, io: Io) Result { switch (it.*) { .ctx_first => |context_ptr| { // After the first frame, start actually unwinding. @@ -976,7 +976,7 @@ const StackIterator = union(enum) { .di => |*unwind_context| { const di = getSelfDebugInfo() catch unreachable; const di_gpa = getDebugInfoAllocator(); - const ret_addr = di.unwindFrame(di_gpa, unwind_context) catch |err| { + const ret_addr = di.unwindFrame(di_gpa, io, unwind_context) catch |err| { const pc = unwind_context.pc; const fp = unwind_context.getFp(); it.* = .{ .fp = fp }; @@ -1297,7 +1297,7 @@ test printLineFromFile { aw.clearRetainingCapacity(); } { - const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{}); + const file = try test_dir.dir.createFile(io, "line_overlaps_page_boundary.zig", .{}); defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "line_overlaps_page_boundary.zig" }); defer gpa.free(path); @@ -1316,7 +1316,7 @@ test printLineFromFile { aw.clearRetainingCapacity(); } { - const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{}); + const file = try test_dir.dir.createFile(io, "file_ends_on_page_boundary.zig", .{}); defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); defer gpa.free(path); @@ -1330,7 +1330,7 @@ test printLineFromFile { aw.clearRetainingCapacity(); } { - const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{}); + const file = try test_dir.dir.createFile(io, "very_long_first_line_spanning_multiple_pages.zig", .{}); defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); defer gpa.free(path); @@ -1356,7 +1356,7 @@ test printLineFromFile { aw.clearRetainingCapacity(); } { - const file = try test_dir.dir.createFile("file_of_newlines.zig", .{}); + const file = try test_dir.dir.createFile(io, "file_of_newlines.zig", .{}); defer file.close(io); const path = try fs.path.join(gpa, &.{ test_dir_path, "file_of_newlines.zig" }); defer gpa.free(path); diff --git a/lib/std/debug/ElfFile.zig b/lib/std/debug/ElfFile.zig index a0f1188ade..203ee8effb 100644 --- a/lib/std/debug/ElfFile.zig +++ b/lib/std/debug/ElfFile.zig @@ -375,7 +375,7 @@ fn loadSeparateDebugFile( args: anytype, ) Allocator.Error!?[]align(std.heap.page_size_min) const u8 { const path = try std.fmt.allocPrint(arena, fmt, args); - const elf_file = std.fs.cwd().openFile(io, path, .{}) catch return null; + const elf_file = Io.Dir.cwd().openFile(io, path, .{}) catch return null; defer elf_file.close(io); const result = loadInner(arena, elf_file, opt_crc) catch |err| switch (err) { diff --git a/lib/std/debug/MachOFile.zig b/lib/std/debug/MachOFile.zig index ae904c0aec..18126a1c29 100644 --- a/lib/std/debug/MachOFile.zig +++ b/lib/std/debug/MachOFile.zig @@ -512,7 +512,7 @@ fn loadOFile(gpa: Allocator, io: Io, o_file_name: []const u8) !OFile { /// Uses `mmap` to map the file at `path` into memory. fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) const u8 { - const file = std.fs.cwd().openFile(io, path, .{}) catch |err| switch (err) { + const file = Io.Dir.cwd().openFile(io, path, .{}) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, else => return error.ReadFailed, }; diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 213389bf04..6ed18bcb80 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -29,13 +29,12 @@ pub fn deinit(si: *SelfInfo, gpa: Allocator) void { } pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol { - _ = io; const module = try si.findModule(gpa, address, .exclusive); defer si.rwlock.unlock(); const vaddr = address - module.load_offset; - const loaded_elf = try module.getLoadedElf(gpa); + const loaded_elf = try module.getLoadedElf(gpa, io); if (loaded_elf.file.dwarf) |*dwarf| { if (!loaded_elf.scanned_dwarf) { dwarf.open(gpa, native_endian) catch |err| switch (err) { @@ -180,7 +179,7 @@ comptime { } } pub const UnwindContext = Dwarf.SelfUnwinder; -pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!usize { +pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize { comptime assert(can_unwind); { @@ -201,7 +200,7 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error @memset(si.unwind_cache.?, .empty); } - const unwind_sections = try module.getUnwindSections(gpa); + const unwind_sections = try module.getUnwindSections(gpa, io); for (unwind_sections) |*unwind| { if (context.computeRules(gpa, unwind, module.load_offset, null)) |entry| { entry.populate(si.unwind_cache.?); @@ -261,12 +260,12 @@ const Module = struct { }; /// Assumes we already hold an exclusive lock. - fn getUnwindSections(mod: *Module, gpa: Allocator) Error![]Dwarf.Unwind { - if (mod.unwind == null) mod.unwind = loadUnwindSections(mod, gpa); + fn getUnwindSections(mod: *Module, gpa: Allocator, io: Io) Error![]Dwarf.Unwind { + if (mod.unwind == null) mod.unwind = loadUnwindSections(mod, gpa, io); const us = &(mod.unwind.? catch |err| return err); return us.buf[0..us.len]; } - fn loadUnwindSections(mod: *Module, gpa: Allocator) Error!UnwindSections { + fn loadUnwindSections(mod: *Module, gpa: Allocator, io: Io) Error!UnwindSections { var us: UnwindSections = .{ .buf = undefined, .len = 0, @@ -284,7 +283,7 @@ const Module = struct { } else { // There is no `.eh_frame_hdr` section. There may still be an `.eh_frame` or `.debug_frame` // section, but we'll have to load the binary to get at it. - const loaded = try mod.getLoadedElf(gpa); + const loaded = try mod.getLoadedElf(gpa, io); // If both are present, we can't just pick one -- the info could be split between them. // `.debug_frame` is likely to be the more complete section, so we'll prioritize that one. if (loaded.file.debug_frame) |*debug_frame| { @@ -325,7 +324,7 @@ const Module = struct { } fn loadElf(mod: *Module, gpa: Allocator, io: Io) Error!LoadedElf { const load_result = if (mod.name.len > 0) res: { - var file = std.fs.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo; + var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo; defer file.close(io); break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(mod.name)); } else res: { @@ -334,7 +333,7 @@ const Module = struct { else => return error.ReadFailed, }; defer gpa.free(path); - var file = std.fs.cwd().openFile(io, path, .{}) catch return error.MissingDebugInfo; + var file = Io.Dir.cwd().openFile(io, path, .{}) catch return error.MissingDebugInfo; defer file.close(io); break :res std.debug.ElfFile.load(gpa, file, mod.build_id, &.native(path)); }; diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index 15da616f3b..db8e5334e6 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -616,7 +616,7 @@ test { /// Uses `mmap` to map the file at `path` into memory. fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) const u8 { - const file = std.fs.cwd().openFile(io, path, .{}) catch |err| switch (err) { + const file = Io.Dir.cwd().openFile(io, path, .{}) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, else => return error.ReadFailed, }; diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index f0ac30cca2..9874efd497 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -432,7 +432,7 @@ const Module = struct { break :pdb null; }; const pdb_file_open_result = if (fs.path.isAbsolute(path)) res: { - break :res std.fs.cwd().openFile(io, path, .{}); + break :res Io.Dir.cwd().openFile(io, path, .{}); } else res: { const self_dir = std.process.executableDirPathAlloc(io, gpa) catch |err| switch (err) { error.OutOfMemory, error.Unexpected => |e| return e, @@ -441,7 +441,7 @@ const Module = struct { defer gpa.free(self_dir); const abs_path = try fs.path.join(gpa, &.{ self_dir, path }); defer gpa.free(abs_path); - break :res std.fs.cwd().openFile(io, abs_path, .{}); + break :res Io.Dir.cwd().openFile(io, abs_path, .{}); }; const pdb_file = pdb_file_open_result catch |err| switch (err) { error.FileNotFound, error.IsDir => break :pdb null, diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 7db177ad70..a1801d00d0 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -160,7 +160,7 @@ pub const ElfDynLib = struct { fn openPath(path: []const u8, io: Io) !Io.Dir { if (path.len == 0) return error.NotDir; var parts = std.mem.tokenizeScalar(u8, path, '/'); - var parent = if (path[0] == '/') try std.fs.cwd().openDir("/", .{}) else std.fs.cwd(); + var parent = if (path[0] == '/') try Io.Dir.cwd().openDir("/", .{}) else Io.Dir.cwd(); while (parts.next()) |part| { const child = try parent.openDir(part, .{}); parent.close(io); @@ -174,7 +174,7 @@ pub const ElfDynLib = struct { while (paths.next()) |p| { var dir = openPath(p) catch continue; defer dir.close(io); - const fd = posix.openat(dir.fd, file_name, .{ + const fd = posix.openat(dir.handle, file_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true, }, 0) catch continue; @@ -184,9 +184,9 @@ pub const ElfDynLib = struct { } fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?posix.fd_t { - var dir = std.fs.cwd().openDir(dir_path, .{}) catch return null; + var dir = Io.Dir.cwd().openDir(dir_path, .{}) catch return null; defer dir.close(io); - return posix.openat(dir.fd, file_name, .{ + return posix.openat(dir.handle, file_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true, }, 0) catch null; diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index f4bdecf89d..aab86d40a6 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -46,7 +46,7 @@ const PathType = enum { // The final path may not actually exist which would cause realpath to fail. // So instead, we get the path of the dir and join it with the relative path. var fd_path_buf: [fs.max_path_bytes]u8 = undefined; - const dir_path = try std.os.getFdPath(dir.fd, &fd_path_buf); + const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf); return fs.path.joinZ(allocator, &.{ dir_path, relative_path }); } }.transform, @@ -55,7 +55,7 @@ const PathType = enum { // Any drive absolute path (C:\foo) can be converted into a UNC path by // using '127.0.0.1' as the server name and '$' as the share name. var fd_path_buf: [fs.max_path_bytes]u8 = undefined; - const dir_path = try std.os.getFdPath(dir.fd, &fd_path_buf); + const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf); const windows_path_type = windows.getWin32PathType(u8, dir_path); switch (windows_path_type) { .unc_absolute => return fs.path.joinZ(allocator, &.{ dir_path, relative_path }), @@ -256,7 +256,7 @@ fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, sy const target_path_w = try std.unicode.wtf8ToWtf16LeAlloc(allocator, target_path); defer allocator.free(target_path_w); // Calling the W functions directly requires the path to be NT-prefixed - const symlink_path_w = try std.os.windows.sliceToPrefixedFileW(dir.fd, symlink_path); + const symlink_path_w = try std.os.windows.sliceToPrefixedFileW(dir.handle, symlink_path); const wtf16_buffer = try allocator.alloc(u16, target_path_w.len); defer allocator.free(wtf16_buffer); const actual = try dir.readLinkW(symlink_path_w.span(), wtf16_buffer); @@ -288,9 +288,11 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { var symlink: Dir = switch (builtin.target.os.tag) { .windows => windows_symlink: { - const sub_path_w = try windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink"); + const sub_path_w = try windows.cStrToPrefixedFileW(ctx.dir.handle, "symlink"); - var handle: windows.HANDLE = undefined; + var result: Dir = .{ + .handle = undefined, + }; const path_len_bytes = @as(u16, @intCast(sub_path_w.span().len * 2)); var nt_name = windows.UNICODE_STRING{ @@ -300,26 +302,16 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { }; var attr: windows.OBJECT_ATTRIBUTES = .{ .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), - .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.fd, - .Attributes = .{}, + .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.handle, + .Attributes = 0, .ObjectName = &nt_name, .SecurityDescriptor = null, .SecurityQualityOfService = null, }; var io_status_block: windows.IO_STATUS_BLOCK = undefined; const rc = windows.ntdll.NtCreateFile( - &handle, - .{ - .SPECIFIC = .{ .FILE_DIRECTORY = .{ - .READ_EA = true, - .TRAVERSE = true, - .READ_ATTRIBUTES = true, - } }, - .STANDARD = .{ - .RIGHTS = .READ, - .SYNCHRONIZE = true, - }, - }, + &result.handle, + windows.STANDARD_RIGHTS_READ | windows.FILE_READ_ATTRIBUTES | windows.FILE_READ_EA | windows.SYNCHRONIZE | windows.FILE_TRAVERSE, &attr, &io_status_block, null, @@ -337,7 +329,7 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { ); switch (rc) { - .SUCCESS => break :windows_symlink .{ .fd = handle }, + .SUCCESS => break :windows_symlink .{ .fd = result.handle }, else => return windows.unexpectedStatus(rc), } }, @@ -351,8 +343,8 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { .ACCMODE = .RDONLY, .CLOEXEC = true, }; - const fd = try posix.openatZ(ctx.dir.fd, &sub_path_c, flags, 0); - break :linux_symlink Dir{ .fd = fd }; + const fd = try posix.openatZ(ctx.dir.handle, &sub_path_c, flags, 0); + break :linux_symlink .{ .handle = fd }; }, else => unreachable, }; @@ -456,7 +448,7 @@ test "openDirAbsolute" { test "openDir cwd parent '..'" { const io = testing.io; - var dir = fs.cwd().openDir("..", .{}) catch |err| { + var dir = Io.Dir.cwd().openDir("..", .{}) catch |err| { if (native_os == .wasi and err == error.PermissionDenied) { return; // This is okay. WASI disallows escaping from the fs sandbox } @@ -534,7 +526,7 @@ test "Dir.Iterator" { defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. - const file = try tmp_dir.dir.createFile("some_file", .{}); + const file = try tmp_dir.dir.createFile(io, "some_file", .{}); file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -570,7 +562,7 @@ test "Dir.Iterator many entries" { var buf: [4]u8 = undefined; // Enough to store "1024". while (i < num) : (i += 1) { const name = try std.fmt.bufPrint(&buf, "{}", .{i}); - const file = try tmp_dir.dir.createFile(name, .{}); + const file = try tmp_dir.dir.createFile(io, name, .{}); file.close(io); } @@ -603,7 +595,7 @@ test "Dir.Iterator twice" { defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. - const file = try tmp_dir.dir.createFile("some_file", .{}); + const file = try tmp_dir.dir.createFile(io, "some_file", .{}); file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -638,7 +630,7 @@ test "Dir.Iterator reset" { defer tmp_dir.cleanup(); // First, create a couple of entries to iterate over. - const file = try tmp_dir.dir.createFile("some_file", .{}); + const file = try tmp_dir.dir.createFile(io, "some_file", .{}); file.close(io); try tmp_dir.dir.makeDir("some_dir"); @@ -769,7 +761,7 @@ test "readFileAlloc" { var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); - var file = try tmp_dir.dir.createFile("test_file", .{ .read = true }); + var file = try tmp_dir.dir.createFile(io, "test_file", .{ .read = true }); defer file.close(io); const buf1 = try tmp_dir.dir.readFileAlloc("test_file", testing.allocator, .limited(1024)); @@ -843,7 +835,7 @@ test "directory operations on files" { const test_file_name = try ctx.transformPath("test_file"); - var file = try ctx.dir.createFile(test_file_name, .{ .read = true }); + var file = try ctx.dir.createFile(io, test_file_name, .{ .read = true }); file.close(io); try testing.expectError(error.PathAlreadyExists, ctx.dir.makeDir(test_file_name)); @@ -876,7 +868,7 @@ test "file operations on directories" { try ctx.dir.makeDir(test_dir_name); - try testing.expectError(error.IsDir, ctx.dir.createFile(test_dir_name, .{})); + try testing.expectError(error.IsDir, ctx.dir.createFile(io, test_dir_name, .{})); try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name)); switch (native_os) { .dragonfly, .netbsd => { @@ -969,7 +961,7 @@ test "Dir.rename files" { // Renaming files const test_file_name = try ctx.transformPath("test_file"); const renamed_test_file_name = try ctx.transformPath("test_file_renamed"); - var file = try ctx.dir.createFile(test_file_name, .{ .read = true }); + var file = try ctx.dir.createFile(io, test_file_name, .{ .read = true }); file.close(io); try ctx.dir.rename(test_file_name, renamed_test_file_name); @@ -983,7 +975,7 @@ test "Dir.rename files" { // Rename to existing file succeeds const existing_file_path = try ctx.transformPath("existing_file"); - var existing_file = try ctx.dir.createFile(existing_file_path, .{ .read = true }); + var existing_file = try ctx.dir.createFile(io, existing_file_path, .{ .read = true }); existing_file.close(io); try ctx.dir.rename(renamed_test_file_name, existing_file_path); @@ -1017,7 +1009,7 @@ test "Dir.rename directories" { var dir = try ctx.dir.openDir(test_dir_renamed_path, .{}); // Put a file in the directory - var file = try dir.createFile("test_file", .{ .read = true }); + var file = try dir.createFile(io, "test_file", .{ .read = true }); file.close(io); dir.close(io); @@ -1070,7 +1062,7 @@ test "Dir.rename directory onto non-empty dir" { try ctx.dir.makeDir(test_dir_path); var target_dir = try ctx.dir.makeOpenPath(target_dir_path, .{}); - var file = try target_dir.createFile("test_file", .{ .read = true }); + var file = try target_dir.createFile(io, "test_file", .{ .read = true }); file.close(io); target_dir.close(io); @@ -1094,7 +1086,7 @@ test "Dir.rename file <-> dir" { const test_file_path = try ctx.transformPath("test_file"); const test_dir_path = try ctx.transformPath("test_dir"); - var file = try ctx.dir.createFile(test_file_path, .{ .read = true }); + var file = try ctx.dir.createFile(io, test_file_path, .{ .read = true }); file.close(io); try ctx.dir.makeDir(test_dir_path); try testing.expectError(error.IsDir, ctx.dir.rename(test_file_path, test_dir_path)); @@ -1115,7 +1107,7 @@ test "rename" { // Renaming files const test_file_name = "test_file"; const renamed_test_file_name = "test_file_renamed"; - var file = try tmp_dir1.dir.createFile(test_file_name, .{ .read = true }); + var file = try tmp_dir1.dir.createFile(io, test_file_name, .{ .read = true }); file.close(io); try fs.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name); @@ -1149,7 +1141,7 @@ test "renameAbsolute" { // Renaming files const test_file_name = "test_file"; const renamed_test_file_name = "test_file_renamed"; - var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true }); + var file = try tmp_dir.dir.createFile(io, test_file_name, .{ .read = true }); file.close(io); try fs.renameAbsolute( try fs.path.join(allocator, &.{ base_path, test_file_name }), @@ -1454,7 +1446,7 @@ test "writev, readv" { var write_vecs: [2][]const u8 = .{ line1, line2 }; var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; - var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); + var src_file = try tmp.dir.createFile(io, "test.txt", .{ .read = true }); defer src_file.close(io); var writer = src_file.writerStreaming(&.{}); @@ -1484,7 +1476,7 @@ test "pwritev, preadv" { var buf2: [line2.len]u8 = undefined; var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; - var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); + var src_file = try tmp.dir.createFile(io, "test.txt", .{ .read = true }); defer src_file.close(io); var writer = src_file.writer(&.{}); @@ -1584,14 +1576,14 @@ test "sendfile" { const line2 = "second line\n"; var vecs = [_][]const u8{ line1, line2 }; - var src_file = try dir.createFile("sendfile1.txt", .{ .read = true }); + var src_file = try dir.createFile(io, "sendfile1.txt", .{ .read = true }); defer src_file.close(io); { var fw = src_file.writer(&.{}); try fw.interface.writeVecAll(&vecs); } - var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true }); + var dest_file = try dir.createFile(io, "sendfile2.txt", .{ .read = true }); defer dest_file.close(io); const header1 = "header1\n"; @@ -1627,12 +1619,12 @@ test "sendfile with buffered data" { var dir = try tmp.dir.openDir("os_test_tmp", .{}); defer dir.close(io); - var src_file = try dir.createFile("sendfile1.txt", .{ .read = true }); + var src_file = try dir.createFile(io, "sendfile1.txt", .{ .read = true }); defer src_file.close(io); try src_file.writeAll("AAAABBBB"); - var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true }); + var dest_file = try dir.createFile(io, "sendfile2.txt", .{ .read = true }); defer dest_file.close(io); var src_buffer: [32]u8 = undefined; @@ -1718,10 +1710,10 @@ test "open file with exclusive nonblocking lock twice" { const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); - const file1 = try ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); + const file1 = try ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true }); defer file1.close(io); - const file2 = ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); + const file2 = ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); } }.impl); @@ -1735,10 +1727,10 @@ test "open file with shared and exclusive nonblocking lock" { const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); - const file1 = try ctx.dir.createFile(filename, .{ .lock = .shared, .lock_nonblocking = true }); + const file1 = try ctx.dir.createFile(io, filename, .{ .lock = .shared, .lock_nonblocking = true }); defer file1.close(io); - const file2 = ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); + const file2 = ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); } }.impl); @@ -1752,10 +1744,10 @@ test "open file with exclusive and shared nonblocking lock" { const io = ctx.io; const filename = try ctx.transformPath("file_nonblocking_lock_test.txt"); - const file1 = try ctx.dir.createFile(filename, .{ .lock = .exclusive, .lock_nonblocking = true }); + const file1 = try ctx.dir.createFile(io, filename, .{ .lock = .exclusive, .lock_nonblocking = true }); defer file1.close(io); - const file2 = ctx.dir.createFile(filename, .{ .lock = .shared, .lock_nonblocking = true }); + const file2 = ctx.dir.createFile(io, filename, .{ .lock = .shared, .lock_nonblocking = true }); try testing.expectError(error.WouldBlock, file2); } }.impl); @@ -1769,13 +1761,13 @@ test "open file with exclusive lock twice, make sure second lock waits" { const io = ctx.io; const filename = try ctx.transformPath("file_lock_test.txt"); - const file = try ctx.dir.createFile(filename, .{ .lock = .exclusive }); + const file = try ctx.dir.createFile(io, filename, .{ .lock = .exclusive }); errdefer file.close(io); const S = struct { fn checkFn(dir: *Io.Dir, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void { started.set(); - const file1 = try dir.createFile(path, .{ .lock = .exclusive }); + const file1 = try dir.createFile(io, path, .{ .lock = .exclusive }); locked.set(); file1.close(io); @@ -1847,13 +1839,13 @@ test "read from locked file" { const filename = try ctx.transformPath("read_lock_file_test.txt"); { - const f = try ctx.dir.createFile(filename, .{ .read = true }); + const f = try ctx.dir.createFile(io, filename, .{ .read = true }); defer f.close(io); var buffer: [1]u8 = undefined; _ = try f.read(&buffer); } { - const f = try ctx.dir.createFile(filename, .{ + const f = try ctx.dir.createFile(io, filename, .{ .read = true, .lock = .exclusive, }); @@ -2037,7 +2029,7 @@ test "'.' and '..' in Io.Dir functions" { var created_subdir = try ctx.dir.openDir(subdir_path, .{}); created_subdir.close(io); - const created_file = try ctx.dir.createFile(file_path, .{}); + const created_file = try ctx.dir.createFile(io, file_path, .{}); created_file.close(io); try ctx.dir.access(file_path, .{}); @@ -2103,7 +2095,7 @@ test "chmod" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - const file = try tmp.dir.createFile("test_file", .{ .mode = 0o600 }); + const file = try tmp.dir.createFile(io, "test_file", .{ .mode = 0o600 }); defer file.close(io); try testing.expectEqual(@as(File.Mode, 0o600), (try file.stat()).mode & 0o7777); @@ -2127,7 +2119,7 @@ test "chown" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - const file = try tmp.dir.createFile("test_file", .{}); + const file = try tmp.dir.createFile(io, "test_file", .{}); defer file.close(io); try file.chown(null, null); @@ -2228,7 +2220,7 @@ test "read file non vectored" { const contents = "hello, world!\n"; - const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); { var file_writer: File.Writer = .init(file, &.{}); @@ -2260,7 +2252,7 @@ test "seek keeping partial buffer" { const contents = "0123456789"; - const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); { var file_writer: File.Writer = .init(file, &.{}); @@ -2321,7 +2313,7 @@ test "seekTo flushes buffered data" { const contents = "data"; - const file = try tmp.dir.createFile("seek.bin", .{ .read = true }); + const file = try tmp.dir.createFile(io, "seek.bin", .{ .read = true }); defer file.close(io); { var buf: [16]u8 = undefined; @@ -2350,7 +2342,7 @@ test "File.Writer sendfile with buffered contents" { try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" }); const in = try tmp_dir.dir.openFile(io, "a", .{}); defer in.close(io); - const out = try tmp_dir.dir.createFile("b", .{}); + const out = try tmp_dir.dir.createFile(io, "b", .{}); defer out.close(io); var in_buf: [2]u8 = undefined; @@ -2397,7 +2389,7 @@ test "readlinkat" { // create a symbolic link if (native_os == .windows) { std.os.windows.CreateSymbolicLink( - tmp.dir.fd, + tmp.dir.handle, &[_]u16{ 'l', 'i', 'n', 'k' }, &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false, @@ -2407,7 +2399,7 @@ test "readlinkat" { else => return err, }; } else { - try posix.symlinkat("file.txt", tmp.dir.fd, "link"); + try posix.symlinkat("file.txt", tmp.dir.handle, "link"); } // read the link diff --git a/lib/std/os/linux/IoUring.zig b/lib/std/os/linux/IoUring.zig index c7d3f35d40..0972a302da 100644 --- a/lib/std/os/linux/IoUring.zig +++ b/lib/std/os/linux/IoUring.zig @@ -1991,7 +1991,7 @@ test "writev/fsync/readv" { defer tmp.cleanup(); const path = "test_io_uring_writev_fsync_readv"; - const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); + const file = try tmp.dir.createFile(io, path, .{ .read = true, .truncate = true }); defer file.close(io); const fd = file.handle; @@ -2062,7 +2062,7 @@ test "write/read" { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const path = "test_io_uring_write_read"; - const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); + const file = try tmp.dir.createFile(io, path, .{ .read = true, .truncate = true }); defer file.close(io); const fd = file.handle; @@ -2110,12 +2110,12 @@ test "splice/read" { var tmp = std.testing.tmpDir(.{}); const path_src = "test_io_uring_splice_src"; - const file_src = try tmp.dir.createFile(path_src, .{ .read = true, .truncate = true }); + const file_src = try tmp.dir.createFile(io, path_src, .{ .read = true, .truncate = true }); defer file_src.close(io); const fd_src = file_src.handle; const path_dst = "test_io_uring_splice_dst"; - const file_dst = try tmp.dir.createFile(path_dst, .{ .read = true, .truncate = true }); + const file_dst = try tmp.dir.createFile(io, path_dst, .{ .read = true, .truncate = true }); defer file_dst.close(io); const fd_dst = file_dst.handle; @@ -2185,7 +2185,7 @@ test "write_fixed/read_fixed" { defer tmp.cleanup(); const path = "test_io_uring_write_read_fixed"; - const file = try tmp.dir.createFile(path, .{ .read = true, .truncate = true }); + const file = try tmp.dir.createFile(io, path, .{ .read = true, .truncate = true }); defer file.close(io); const fd = file.handle; @@ -2306,7 +2306,7 @@ test "close" { defer tmp.cleanup(); const path = "test_io_uring_close"; - const file = try tmp.dir.createFile(path, .{}); + const file = try tmp.dir.createFile(io, path, .{}); errdefer file.close(io); const sqe_close = try ring.close(0x44444444, file.handle); @@ -2652,7 +2652,7 @@ test "fallocate" { defer tmp.cleanup(); const path = "test_io_uring_fallocate"; - const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(io, path, .{ .truncate = true, .mode = 0o666 }); defer file.close(io); try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2699,7 +2699,7 @@ test "statx" { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const path = "test_io_uring_statx"; - const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(io, path, .{ .truncate = true, .mode = 0o666 }); defer file.close(io); try testing.expectEqual(@as(u64, 0), (try file.stat()).size); @@ -2969,7 +2969,7 @@ test "renameat" { // Write old file with data - const old_file = try tmp.dir.createFile(old_path, .{ .truncate = true, .mode = 0o666 }); + const old_file = try tmp.dir.createFile(io, old_path, .{ .truncate = true, .mode = 0o666 }); defer old_file.close(io); try old_file.writeAll("hello"); @@ -3028,7 +3028,7 @@ test "unlinkat" { // Write old file with data - const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(io, path, .{ .truncate = true, .mode = 0o666 }); defer file.close(io); // Submit unlinkat @@ -3125,7 +3125,7 @@ test "symlinkat" { const path = "test_io_uring_symlinkat"; const link_path = "test_io_uring_symlinkat_link"; - const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(io, path, .{ .truncate = true, .mode = 0o666 }); defer file.close(io); // Submit symlinkat @@ -3177,7 +3177,7 @@ test "linkat" { // Write file with data - const first_file = try tmp.dir.createFile(first_path, .{ .truncate = true, .mode = 0o666 }); + const first_file = try tmp.dir.createFile(io, first_path, .{ .truncate = true, .mode = 0o666 }); defer first_file.close(io); try first_file.writeAll("hello"); diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 39606ddfac..d7cfb4e138 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -18,7 +18,7 @@ test "fallocate" { defer tmp.cleanup(); const path = "test_fallocate"; - const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 }); + const file = try tmp.dir.createFile(io, path, .{ .truncate = true, .mode = 0o666 }); defer file.close(io); try expect((try file.stat()).size == 0); @@ -85,7 +85,7 @@ test "statx" { defer tmp.cleanup(); const tmp_file_name = "just_a_temporary_file.txt"; - var file = try tmp.dir.createFile(tmp_file_name, .{}); + var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); var buf: linux.Statx = undefined; @@ -121,7 +121,7 @@ test "fadvise" { defer tmp.cleanup(); const tmp_file_name = "temp_posix_fadvise.txt"; - var file = try tmp.dir.createFile(tmp_file_name, .{}); + var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); var buf: [2048]u8 = undefined; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 6b5b678b20..ba5282256f 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -4639,8 +4639,8 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE break :path_to_get path; } // We can also skip GetFinalPathNameByHandle if the handle matches - // the handle returned by fs.cwd() - if (dir.? == std.fs.cwd().fd) { + // the handle returned by Io.Dir.cwd() + if (dir.? == Io.Dir.cwd().fd) { break :path_to_get path; } // At this point, we know we have a relative path that had too many diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 392987ec50..f4aa970413 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -15,15 +15,16 @@ //! deal with the exception. const builtin = @import("builtin"); -const root = @import("root"); +const native_os = builtin.os.tag; + const std = @import("std.zig"); +const Io = std.Io; const mem = std.mem; const fs = std.fs; -const max_path_bytes = fs.max_path_bytes; +const max_path_bytes = std.fs.max_path_bytes; const maxInt = std.math.maxInt; const cast = std.math.cast; const assert = std.debug.assert; -const native_os = builtin.os.tag; const page_size_min = std.heap.page_size_min; test { @@ -797,7 +798,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { .INTR => continue, .INVAL => unreachable, .FAULT => unreachable, - .SRCH => return error.ProcessNotFound, .AGAIN => return error.WouldBlock, .CANCELED => return error.Canceled, .BADF => return error.NotOpenForReading, // Can be a race condition. @@ -917,7 +917,6 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { .INTR => continue, .INVAL => return error.InvalidArgument, .FAULT => unreachable, - .SRCH => return error.ProcessNotFound, .AGAIN => return error.WouldBlock, .BADF => return error.NotOpenForWriting, // can be a race condition. .DESTADDRREQ => unreachable, // `connect` was never called. @@ -985,7 +984,8 @@ pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t { .NFILE => return error.SystemFdQuotaExceeded, .NODEV => return error.NoDevice, .NOENT => return error.FileNotFound, - .SRCH => return error.ProcessNotFound, + // Can happen on Linux when opening procfs files. + .SRCH => return error.FileNotFound, .NOMEM => return error.SystemResources, .NOSPC => return error.NoSpaceLeft, .NOTDIR => return error.NotDir, @@ -1560,7 +1560,7 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void { pub fn mkdirW(dir_path_w: []const u16, mode: mode_t) MakeDirError!void { _ = mode; const sub_dir_handle = windows.OpenFile(dir_path_w, .{ - .dir = fs.cwd().fd, + .dir = Io.Dir.cwd().handle, .access_mask = .{ .STANDARD = .{ .SYNCHRONIZE = true }, .GENERIC = .{ .READ = true }, diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 0071a72a26..dc63be6e14 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -148,7 +148,7 @@ test "linkat with different directories" { try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); // Test 1: link from file in subdir back up to target in parent directory - try posix.linkat(tmp.dir.fd, target_name, subdir.fd, link_name, 0); + try posix.linkat(tmp.dir.handle, target_name, subdir.handle, link_name, 0); const efd = try tmp.dir.openFile(io, target_name, .{}); defer efd.close(io); @@ -164,7 +164,7 @@ test "linkat with different directories" { } // Test 2: remove link - try posix.unlinkat(subdir.fd, link_name, 0); + try posix.unlinkat(subdir.handle, link_name, 0); _, const elink = try getLinkInfo(efd.handle); try testing.expectEqual(@as(posix.nlink_t, 1), elink); } @@ -373,7 +373,7 @@ test "mmap" { // Create a file used for testing mmap() calls with a file descriptor { - const file = try tmp.dir.createFile(test_out_file, .{}); + const file = try tmp.dir.createFile(io, test_out_file, .{}); defer file.close(io); var stream = file.writer(&.{}); @@ -444,7 +444,7 @@ test "fcntl" { const test_out_file = "os_tmp_test"; - const file = try tmp.dir.createFile(test_out_file, .{}); + const file = try tmp.dir.createFile(io, test_out_file, .{}); defer file.close(io); // Note: The test assumes createFile opens the file with CLOEXEC @@ -495,7 +495,7 @@ test "fsync" { defer tmp.cleanup(); const test_out_file = "os_tmp_test"; - const file = try tmp.dir.createFile(test_out_file, .{}); + const file = try tmp.dir.createFile(io, test_out_file, .{}); defer file.close(io); try posix.fsync(file.handle); @@ -617,7 +617,7 @@ test "dup & dup2" { defer tmp.cleanup(); { - var file = try tmp.dir.createFile("os_dup_test", .{}); + var file = try tmp.dir.createFile(io, "os_dup_test", .{}); defer file.close(io); var duped = Io.File{ .handle = try posix.dup(file.handle) }; @@ -659,7 +659,7 @@ test "writev longer than IOV_MAX" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - var file = try tmp.dir.createFile("pwritev", .{}); + var file = try tmp.dir.createFile(io, "pwritev", .{}); defer file.close(io); const iovecs = [_]posix.iovec_const{.{ .base = "a", .len = 1 }} ** (posix.IOV_MAX + 1); @@ -684,7 +684,7 @@ test "POSIX file locking with fcntl" { defer tmp.cleanup(); // Create a temporary lock file - var file = try tmp.dir.createFile("lock", .{ .read = true }); + var file = try tmp.dir.createFile(io, "lock", .{ .read = true }); defer file.close(io); try file.setEndPos(2); const fd = file.handle; @@ -881,7 +881,7 @@ test "isatty" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - var file = try tmp.dir.createFile("foo", .{}); + var file = try tmp.dir.createFile(io, "foo", .{}); defer file.close(io); try expectEqual(posix.isatty(file.handle), false); @@ -893,7 +893,7 @@ test "pread with empty buffer" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - var file = try tmp.dir.createFile("pread_empty", .{ .read = true }); + var file = try tmp.dir.createFile(io, "pread_empty", .{ .read = true }); defer file.close(io); const bytes = try a.alloc(u8, 0); @@ -909,7 +909,7 @@ test "write with empty buffer" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - var file = try tmp.dir.createFile("write_empty", .{}); + var file = try tmp.dir.createFile(io, "write_empty", .{}); defer file.close(io); const bytes = try a.alloc(u8, 0); @@ -925,7 +925,7 @@ test "pwrite with empty buffer" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - var file = try tmp.dir.createFile("pwrite_empty", .{}); + var file = try tmp.dir.createFile(io, "pwrite_empty", .{}); defer file.close(io); const bytes = try a.alloc(u8, 0); @@ -965,35 +965,35 @@ test "fchmodat smoke test" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - try expectError(error.FileNotFound, posix.fchmodat(tmp.dir.fd, "regfile", 0o666, 0)); + try expectError(error.FileNotFound, posix.fchmodat(tmp.dir.handle, "regfile", 0o666, 0)); const fd = try posix.openat( - tmp.dir.fd, + tmp.dir.handle, "regfile", .{ .ACCMODE = .WRONLY, .CREAT = true, .EXCL = true, .TRUNC = true }, 0o644, ); posix.close(fd); - try posix.symlinkat("regfile", tmp.dir.fd, "symlink"); - const sym_mode = try getFileMode(tmp.dir.fd, "symlink"); + try posix.symlinkat("regfile", tmp.dir.handle, "symlink"); + const sym_mode = try getFileMode(tmp.dir.handle, "symlink"); - try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0); - try expectMode(tmp.dir.fd, "regfile", 0o640); - try posix.fchmodat(tmp.dir.fd, "regfile", 0o600, posix.AT.SYMLINK_NOFOLLOW); - try expectMode(tmp.dir.fd, "regfile", 0o600); + try posix.fchmodat(tmp.dir.handle, "regfile", 0o640, 0); + try expectMode(tmp.dir.handle, "regfile", 0o640); + try posix.fchmodat(tmp.dir.handle, "regfile", 0o600, posix.AT.SYMLINK_NOFOLLOW); + try expectMode(tmp.dir.handle, "regfile", 0o600); - try posix.fchmodat(tmp.dir.fd, "symlink", 0o640, 0); - try expectMode(tmp.dir.fd, "regfile", 0o640); - try expectMode(tmp.dir.fd, "symlink", sym_mode); + try posix.fchmodat(tmp.dir.handle, "symlink", 0o640, 0); + try expectMode(tmp.dir.handle, "regfile", 0o640); + try expectMode(tmp.dir.handle, "symlink", sym_mode); var test_link = true; - posix.fchmodat(tmp.dir.fd, "symlink", 0o600, posix.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) { + posix.fchmodat(tmp.dir.handle, "symlink", 0o600, posix.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) { error.OperationNotSupported => test_link = false, else => |e| return e, }; if (test_link) - try expectMode(tmp.dir.fd, "symlink", 0o600); - try expectMode(tmp.dir.fd, "regfile", 0o640); + try expectMode(tmp.dir.handle, "symlink", 0o600); + try expectMode(tmp.dir.handle, "regfile", 0o640); } const CommonOpenFlags = packed struct { diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index b774303901..33faeef061 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -677,7 +677,7 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); if (self.cwd_dir) |cwd| { - posix.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err); + posix.fchdir(cwd.handle) catch |err| forkChildErrReport(err_pipe[1], err); } else if (self.cwd) |cwd| { posix.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err); } diff --git a/lib/std/std.zig b/lib/std/std.zig index 106811859b..1690c0575c 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -114,7 +114,7 @@ pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options pub const Options = struct { enable_segfault_handler: bool = debug.default_enable_segfault_handler, - /// Function used to implement `std.fs.cwd` for WASI. + /// Function used to implement `std.Io.Dir.cwd` for WASI. wasiCwd: fn () os.wasi.fd_t = os.defaultWasiCwd, /// The current log level. diff --git a/lib/std/tar.zig b/lib/std/tar.zig index d861314fec..8a0bbb342f 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -610,7 +610,7 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp } }, .file => { - if (createDirAndFile(dir, file_name, fileMode(file.mode, options))) |fs_file| { + if (createDirAndFile(io, dir, file_name, fileMode(file.mode, options))) |fs_file| { defer fs_file.close(io); var file_writer = fs_file.writer(&file_contents_buffer); try it.streamRemaining(file, &file_writer.interface); @@ -638,12 +638,12 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp } } -fn createDirAndFile(dir: Io.Dir, file_name: []const u8, mode: Io.File.Mode) !Io.File { - const fs_file = dir.createFile(file_name, .{ .exclusive = true, .mode = mode }) catch |err| { +fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, mode: Io.File.Mode) !Io.File { + const fs_file = dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode }) catch |err| { if (err == error.FileNotFound) { if (std.fs.path.dirname(file_name)) |dir_name| { try dir.makePath(dir_name); - return try dir.createFile(file_name, .{ .exclusive = true, .mode = mode }); + return try dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode }); } } return err; @@ -880,9 +880,9 @@ test "create file and symlink" { var root = testing.tmpDir(.{}); defer root.cleanup(); - var file = try createDirAndFile(root.dir, "file1", default_mode); + var file = try createDirAndFile(io, root.dir, "file1", default_mode); file.close(io); - file = try createDirAndFile(root.dir, "a/b/c/file2", default_mode); + file = try createDirAndFile(io, root.dir, "a/b/c/file2", default_mode); file.close(io); createDirAndSymlink(root.dir, "a/b/c/file2", "symlink1") catch |err| { @@ -894,7 +894,7 @@ test "create file and symlink" { // Danglink symlnik, file created later try createDirAndSymlink(root.dir, "../../../g/h/i/file4", "j/k/l/symlink3"); - file = try createDirAndFile(root.dir, "g/h/i/file4", default_mode); + file = try createDirAndFile(io, root.dir, "g/h/i/file4", default_mode); file.close(io); } diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 19038543a6..99d67ec132 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -628,7 +628,7 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { var sub_path: [TmpDir.sub_path_len]u8 = undefined; _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); - const cwd = std.fs.cwd(); + const cwd = Io.Dir.cwd(); var cache_dir = cwd.makeOpenPath(".zig-cache", .{}) catch @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); defer cache_dir.close(io); diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index c8bde2ab02..80317850df 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -57,7 +57,7 @@ pub fn parse( } } - const contents = try std.fs.cwd().readFileAlloc(libc_file, allocator, .limited(std.math.maxInt(usize))); + const contents = try Io.Dir.cwd().readFileAlloc(libc_file, allocator, .limited(std.math.maxInt(usize))); defer allocator.free(contents); var it = std.mem.tokenizeScalar(u8, contents, '\n'); @@ -337,7 +337,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) F // search in reverse order const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1]; const search_path = std.mem.trimStart(u8, search_path_untrimmed, " "); - var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) { + var search_dir = Io.Dir.cwd().openDir(search_path, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -392,7 +392,7 @@ fn findNativeIncludeDirWindows( result_buf.shrinkAndFree(0); try result_buf.print("{s}\\Include\\{s}\\ucrt", .{ install.path, install.version }); - var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { + var dir = Io.Dir.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -440,7 +440,7 @@ fn findNativeCrtDirWindows( result_buf.shrinkAndFree(0); try result_buf.print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ install.path, install.version, arch_sub_dir }); - var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { + var dir = Io.Dir.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -508,7 +508,7 @@ fn findNativeKernel32LibDir( result_buf.shrinkAndFree(0); try result_buf.print("{s}\\Lib\\{s}\\um\\{s}", .{ install.path, install.version, arch_sub_dir }); - var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { + var dir = Io.Dir.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -544,7 +544,7 @@ fn findNativeMsvcIncludeDir( const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" }); errdefer allocator.free(dir_path); - var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) { + var dir = Io.Dir.cwd().openDir(dir_path, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig index 6b6e4fa9f7..dca474020a 100644 --- a/lib/std/zig/WindowsSdk.zig +++ b/lib/std/zig/WindowsSdk.zig @@ -828,7 +828,7 @@ const MsvcLibDir = struct { try lib_dir_buf.appendSlice("VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); var default_tools_version_buf: [512]u8 = undefined; - const default_tools_version_contents = std.fs.cwd().readFile(lib_dir_buf.items, &default_tools_version_buf) catch { + const default_tools_version_contents = Io.Dir.cwd().readFile(lib_dir_buf.items, &default_tools_version_buf) catch { return error.PathNotFound; }; var tokenizer = std.mem.tokenizeAny(u8, default_tools_version_contents, " \r\n"); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index d3bafc16f2..9fa0546c3b 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -1,11 +1,12 @@ const builtin = @import("builtin"); +const native_endian = builtin.cpu.arch.endian(); + const std = @import("../std.zig"); const mem = std.mem; const elf = std.elf; const fs = std.fs; const assert = std.debug.assert; const Target = std.Target; -const native_endian = builtin.cpu.arch.endian(); const posix = std.posix; const Io = std.Io; @@ -69,7 +70,7 @@ pub fn getExternalExecutor( if (os_match and cpu_ok) native: { if (options.link_libc) { if (candidate.dynamic_linker.get()) |candidate_dl| { - fs.cwd().access(candidate_dl, .{}) catch { + Io.Dir.cwd().access(candidate_dl, .{}) catch { bad_result = .{ .bad_dl = candidate_dl }; break :native; }; @@ -710,6 +711,7 @@ fn abiAndDynamicLinkerFromFile( error.SystemResources, error.FileSystem, error.SymLinkLoop, + error.Canceled, error.Unexpected, => |e| return e, }; diff --git a/lib/std/zig/system/darwin/macos.zig b/lib/std/zig/system/darwin/macos.zig index 9bb4e34e3b..4ff6846a09 100644 --- a/lib/std/zig/system/darwin/macos.zig +++ b/lib/std/zig/system/darwin/macos.zig @@ -1,9 +1,10 @@ -const std = @import("std"); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const mem = std.mem; const testing = std.testing; - const Target = std.Target; /// Detect macOS version. @@ -54,7 +55,7 @@ pub fn detect(target_os: *Target.Os) !void { // approx. 4 times historical file size var buf: [2048]u8 = undefined; - if (std.fs.cwd().readFile(path, &buf)) |bytes| { + if (Io.Dir.cwd().readFile(path, &buf)) |bytes| { if (parseSystemVersion(bytes)) |ver| { // never return non-canonical `10.(16+)` if (!(ver.major == 10 and ver.minor >= 16)) { diff --git a/lib/std/zip.zig b/lib/std/zip.zig index 0ca77c98a1..9d08847092 100644 --- a/lib/std/zip.zig +++ b/lib/std/zip.zig @@ -564,9 +564,9 @@ pub const Iterator = struct { defer parent_dir.close(io); const basename = std.fs.path.basename(filename); - break :blk try parent_dir.createFile(basename, .{ .exclusive = true }); + break :blk try parent_dir.createFile(io, basename, .{ .exclusive = true }); } - break :blk try dest.createFile(filename, .{ .exclusive = true }); + break :blk try dest.createFile(io, filename, .{ .exclusive = true }); }; defer out_file.close(io); var out_file_buffer: [1024]u8 = undefined; diff --git a/src/Compilation.zig b/src/Compilation.zig index 24b994f608..5f15ef5f74 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -450,7 +450,7 @@ pub const Path = struct { const dir = switch (p.root) { .none => { const cwd_sub_path = absToCwdRelative(p.sub_path, dirs.cwd); - return .{ fs.cwd(), cwd_sub_path }; + return .{ Io.Dir.cwd(), cwd_sub_path }; }, .zig_lib => dirs.zig_lib.handle, .global_cache => dirs.global_cache.handle, @@ -723,7 +723,7 @@ pub const Directories = struct { pub fn deinit(dirs: *Directories, io: Io) void { // The local and global caches could be the same. - const close_local = dirs.local_cache.handle.fd != dirs.global_cache.handle.fd; + const close_local = dirs.local_cache.handle.handle != dirs.global_cache.handle.handle; dirs.global_cache.handle.close(io); if (close_local) dirs.local_cache.handle.close(io); @@ -814,7 +814,7 @@ pub const Directories = struct { return .{ .path = if (std.mem.eql(u8, name, ".")) null else name, .handle = .{ - .fd = preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}), + .handle = preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}), }, }; } @@ -824,8 +824,8 @@ pub const Directories = struct { }; const nonempty_path = if (path.len == 0) "." else path; const handle_or_err = switch (thing) { - .@"zig lib" => fs.cwd().openDir(nonempty_path, .{}), - .@"global cache", .@"local cache" => fs.cwd().makeOpenPath(nonempty_path, .{}), + .@"zig lib" => Io.Dir.cwd().openDir(nonempty_path, .{}), + .@"global cache", .@"local cache" => Io.Dir.cwd().makeOpenPath(nonempty_path, .{}), }; return .{ .path = if (path.len == 0) null else path, @@ -1104,7 +1104,7 @@ pub const CObject = struct { const source_line = source_line: { if (diag.src_loc.offset == 0 or diag.src_loc.column == 0) break :source_line 0; - const file = fs.cwd().openFile(io, file_name, .{}) catch break :source_line 0; + const file = Io.Dir.cwd().openFile(io, file_name, .{}) catch break :source_line 0; defer file.close(io); var buffer: [1024]u8 = undefined; var file_reader = file.reader(io, &buffer); @@ -1179,7 +1179,7 @@ pub const CObject = struct { }; var buffer: [1024]u8 = undefined; - const file = try fs.cwd().openFile(io, path, .{}); + const file = try Io.Dir.cwd().openFile(io, path, .{}); defer file.close(io); var file_reader = file.reader(io, &buffer); var bc = std.zig.llvm.BitcodeReader.init(gpa, .{ .reader = &file_reader.interface }); @@ -2109,7 +2109,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, }, }; // These correspond to std.zig.Server.Message.PathPrefix. - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(options.dirs.zig_lib); cache.addPrefix(options.dirs.local_cache); cache.addPrefix(options.dirs.global_cache); @@ -5220,7 +5220,7 @@ fn createDepFile( binfile: Cache.Path, ) anyerror!void { var buf: [4096]u8 = undefined; - var af = try std.fs.cwd().atomicFile(depfile, .{ .write_buffer = &buf }); + var af = try Io.Dir.cwd().atomicFile(depfile, .{ .write_buffer = &buf }); defer af.deinit(); comp.writeDepFile(binfile, &af.file_writer.interface) catch return af.file_writer.err.?; @@ -5284,7 +5284,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { }; } - var tar_file = out_dir.createFile("sources.tar", .{}) catch |err| { + var tar_file = out_dir.createFile(io, "sources.tar", .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, "unable to create '{f}/sources.tar': {s}", diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 3bd05120ff..8a30529bc5 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -383,14 +383,14 @@ pub fn run(f: *Fetch) RunError!void { }, .remote => |remote| remote, .path_or_url => |path_or_url| { - if (fs.cwd().openDir(path_or_url, .{ .iterate = true })) |dir| { + if (Io.Dir.cwd().openDir(path_or_url, .{ .iterate = true })) |dir| { var resource: Resource = .{ .dir = dir }; return f.runResource(path_or_url, &resource, null); } else |dir_err| { var server_header_buffer: [init_resource_buffer_size]u8 = undefined; const file_err = if (dir_err == error.NotDir) e: { - if (fs.cwd().openFile(io, path_or_url, .{})) |file| { + if (Io.Dir.cwd().openFile(io, path_or_url, .{})) |file| { var resource: Resource = .{ .file = file.reader(io, &server_header_buffer) }; return f.runResource(path_or_url, &resource, null); } else |err| break :e err; @@ -1303,7 +1303,7 @@ fn unzip( const random_integer = std.crypto.random.int(u64); zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer); - break cache_root.handle.createFile(&zip_path, .{ + break cache_root.handle.createFile(io, &zip_path, .{ .exclusive = true, .read = true, }) catch |err| switch (err) { @@ -1365,7 +1365,7 @@ fn unpackGitPack(f: *Fetch, out_dir: Io.Dir, resource: *Resource.Git) anyerror!U { var pack_dir = try out_dir.makeOpenPath(".git", .{}); defer pack_dir.close(io); - var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true }); + var pack_file = try pack_dir.createFile(io, "pkg.pack", .{ .read = true }); defer pack_file.close(io); var pack_file_buffer: [4096]u8 = undefined; var pack_file_reader = b: { @@ -1376,7 +1376,7 @@ fn unpackGitPack(f: *Fetch, out_dir: Io.Dir, resource: *Resource.Git) anyerror!U break :b pack_file_writer.moveToReader(io); }; - var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true }); + var index_file = try pack_dir.createFile(io, "pkg.idx", .{ .read = true }); defer index_file.close(io); var index_file_buffer: [2000]u8 = undefined; var index_file_writer = index_file.writer(&index_file_buffer); @@ -2235,7 +2235,7 @@ test "set executable bit based on file content" { fn saveEmbedFile(io: Io, comptime tarball_name: []const u8, dir: Io.Dir) !void { //const tarball_name = "duplicate_paths_excluded.tar.gz"; const tarball_content = @embedFile("Fetch/testdata/" ++ tarball_name); - var tmp_file = try dir.createFile(tarball_name, .{}); + var tmp_file = try dir.createFile(io, tarball_name, .{}); defer tmp_file.close(io); try tmp_file.writeAll(tarball_content); } diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index ccae9440e2..7b08a89cae 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -264,7 +264,7 @@ pub const Repository = struct { try repository.odb.seekOid(entry.oid); const file_object = try repository.odb.readObject(); if (file_object.type != .blob) return error.InvalidFile; - var file = dir.createFile(entry.name, .{ .exclusive = true }) catch |e| { + var file = dir.createFile(io, entry.name, .{ .exclusive = true }) catch |e| { const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name }); errdefer diagnostics.allocator.free(file_name); try diagnostics.errors.append(diagnostics.allocator, .{ .unable_to_create_file = .{ @@ -1584,14 +1584,14 @@ fn runRepositoryTest(io: Io, comptime format: Oid.Format, head_commit: []const u var git_dir = testing.tmpDir(.{}); defer git_dir.cleanup(); - var pack_file = try git_dir.dir.createFile("testrepo.pack", .{ .read = true }); + var pack_file = try git_dir.dir.createFile(io, "testrepo.pack", .{ .read = true }); defer pack_file.close(io); try pack_file.writeAll(testrepo_pack); var pack_file_buffer: [2000]u8 = undefined; var pack_file_reader = pack_file.reader(io, &pack_file_buffer); - var index_file = try git_dir.dir.createFile("testrepo.idx", .{ .read = true }); + var index_file = try git_dir.dir.createFile(io, "testrepo.idx", .{ .read = true }); defer index_file.close(io); var index_file_buffer: [2000]u8 = undefined; var index_file_writer = index_file.writer(&index_file_buffer); @@ -1714,20 +1714,20 @@ pub fn main() !void { const format = std.meta.stringToEnum(Oid.Format, args[1]) orelse return error.InvalidFormat; - var pack_file = try std.fs.cwd().openFile(io, args[2], .{}); + var pack_file = try Io.Dir.cwd().openFile(io, args[2], .{}); defer pack_file.close(io); var pack_file_buffer: [4096]u8 = undefined; var pack_file_reader = pack_file.reader(io, &pack_file_buffer); const commit = try Oid.parse(format, args[3]); - var worktree = try std.fs.cwd().makeOpenPath(args[4], .{}); + var worktree = try Io.Dir.cwd().makeOpenPath(args[4], .{}); defer worktree.close(io); var git_dir = try worktree.makeOpenPath(".git", .{}); defer git_dir.close(io); std.debug.print("Starting index...\n", .{}); - var index_file = try git_dir.createFile("idx", .{ .read = true }); + var index_file = try git_dir.createFile(io, "idx", .{ .read = true }); defer index_file.close(io); var index_file_buffer: [4096]u8 = undefined; var index_file_writer = index_file.writer(&index_file_buffer); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 45b1302138..9a75b2096e 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -170,7 +170,7 @@ pub fn updateFile( // version. Likewise if we're working on AstGen and another process asks for // the cached file, they'll get it. const cache_file = while (true) { - break zir_dir.createFile(&hex_digest, .{ + break zir_dir.createFile(io, &hex_digest, .{ .read = true, .truncate = false, .lock = lock, @@ -196,7 +196,7 @@ pub fn updateFile( cache_directory, }); } - break zir_dir.createFile(&hex_digest, .{ + break zir_dir.createFile(io, &hex_digest, .{ .read = true, .truncate = false, .lock = lock, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cb4fe0459f..4fc58c2c4b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1,19 +1,22 @@ -const std = @import("std"); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const log = std.log.scoped(.codegen); const math = std.math; const DW = std.dwarf; - const Builder = std.zig.llvm.Builder; + +const build_options = @import("build_options"); const llvm = if (build_options.have_llvm) @import("llvm/bindings.zig") else @compileError("LLVM unavailable"); + const link = @import("../link.zig"); const Compilation = @import("../Compilation.zig"); -const build_options = @import("build_options"); const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); const Package = @import("../Package.zig"); @@ -964,7 +967,7 @@ pub const Object = struct { if (std.mem.eql(u8, path, "-")) { o.builder.dump(); } else { - o.builder.printToFilePath(std.fs.cwd(), path) catch |err| { + o.builder.printToFilePath(Io.Dir.cwd(), path) catch |err| { log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); }; } @@ -978,7 +981,7 @@ pub const Object = struct { o.builder.clearAndFree(); if (options.pre_bc_path) |path| { - var file = std.fs.cwd().createFile(path, .{}) catch |err| + var file = Io.Dir.cwd().createFile(io, path, .{}) catch |err| return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) }); defer file.close(io); @@ -991,7 +994,7 @@ pub const Object = struct { options.post_ir_path == null and options.post_bc_path == null) return; if (options.post_bc_path) |path| { - var file = std.fs.cwd().createFile(path, .{}) catch |err| + var file = Io.Dir.cwd().createFile(io, path, .{}) catch |err| return diags.fail("failed to create '{s}': {s}", .{ path, @errorName(err) }); defer file.close(io); @@ -2711,7 +2714,7 @@ pub const Object = struct { } fn allocTypeName(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error![:0]const u8 { - var aw: std.Io.Writer.Allocating = .init(o.gpa); + var aw: Io.Writer.Allocating = .init(o.gpa); defer aw.deinit(); ty.print(&aw.writer, pt, null) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, diff --git a/src/fmt.zig b/src/fmt.zig index ce8a31fa4c..36a3833986 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -182,11 +182,11 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! // Mark any excluded files/directories as already seen, // so that they are skipped later during actual processing for (excluded_files.items) |file_path| { - const stat = fs.cwd().statFile(file_path) catch |err| switch (err) { + const stat = Io.Dir.cwd().statFile(file_path) catch |err| switch (err) { error.FileNotFound => continue, // On Windows, statFile does not work for directories error.IsDir => dir: { - var dir = try fs.cwd().openDir(file_path, .{}); + var dir = try Io.Dir.cwd().openDir(file_path, .{}); defer dir.close(io); break :dir try dir.stat(); }, @@ -196,7 +196,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! } for (input_files.items) |file_path| { - try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path); + try fmtPath(&fmt, file_path, check_flag, Io.Dir.cwd(), file_path); } try fmt.stdout_writer.interface.flush(); if (fmt.any_error) { diff --git a/src/introspect.zig b/src/introspect.zig index d2faa9a55c..04ddf47e8a 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -82,7 +82,7 @@ pub fn findZigLibDirFromSelfExe( cwd_path: []const u8, self_exe_path: []const u8, ) error{ OutOfMemory, FileNotFound }!Cache.Directory { - const cwd = fs.cwd(); + const cwd = Io.Dir.cwd(); var cur_path: []const u8 = self_exe_path; while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) { var base_dir = cwd.openDir(dirname, .{}) catch continue; @@ -206,7 +206,7 @@ pub fn resolveSuitableLocalCacheDir(arena: Allocator, cwd: []const u8) Allocator var cur_dir = cwd; while (true) { const joined = try fs.path.join(arena, &.{ cur_dir, Package.build_zig_basename }); - if (fs.cwd().access(joined, .{})) |_| { + if (Io.Dir.cwd().access(joined, .{})) |_| { return try fs.path.join(arena, &.{ cur_dir, default_local_zig_cache_basename }); } else |err| switch (err) { error.FileNotFound => { diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 8c5e0afe4b..cfd8d5554c 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -1,9 +1,9 @@ const std = @import("std"); +const Io = std.Io; const Allocator = std.mem.Allocator; const mem = std.mem; const log = std.log; -const fs = std.fs; -const path = fs.path; +const path = std.Io.Dir.path; const assert = std.debug.assert; const Version = std.SemanticVersion; const Path = std.Build.Cache.Path; @@ -446,7 +446,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .io = io, .manifest_dir = try comp.dirs.global_cache.handle.makeOpenPath("h", .{}), }; - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); defer cache.manifest_dir.close(io); @@ -468,7 +468,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lock = man.toOwnedLock(), .dir_path = .{ .root_dir = comp.dirs.global_cache, - .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest), }, }); } @@ -986,7 +986,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lock = man.toOwnedLock(), .dir_path = .{ .root_dir = comp.dirs.global_cache, - .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest), }, }); } @@ -1014,7 +1014,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.C const so_path: Path = .{ .root_dir = so_files.dir_path.root_dir, .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{ - so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.getSoVersion(&target.os), + so_files.dir_path.sub_path, path.sep, lib.name, lib.getSoVersion(&target.os), }) catch return comp.setAllocFailure(), }; task_buffer[task_buffer_i] = .{ .load_dso = so_path }; diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index bec20ff3d4..e3d8ce1f7f 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -1,9 +1,9 @@ const std = @import("std"); +const Io = std.Io; const Allocator = std.mem.Allocator; const mem = std.mem; const log = std.log; -const fs = std.fs; -const path = fs.path; +const path = std.Io.Dir.path; const assert = std.debug.assert; const Version = std.SemanticVersion; const Path = std.Build.Cache.Path; @@ -681,7 +681,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .io = io, .manifest_dir = try comp.dirs.global_cache.handle.makeOpenPath("h", .{}), }; - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); defer cache.manifest_dir.close(io); @@ -703,7 +703,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lock = man.toOwnedLock(), .dir_path = .{ .root_dir = comp.dirs.global_cache, - .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest), }, }); } @@ -775,7 +775,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye try stubs_asm.appendSlice(".text\n"); var sym_i: usize = 0; - var sym_name_buf: std.Io.Writer.Allocating = .init(arena); + var sym_name_buf: Io.Writer.Allocating = .init(arena); var opt_symbol_name: ?[]const u8 = null; var versions_buffer: [32]u8 = undefined; var versions_len: usize = undefined; @@ -796,7 +796,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye // twice, which causes a "duplicate symbol" assembler error. var versions_written = std.AutoArrayHashMap(Version, void).init(arena); - var inc_reader: std.Io.Reader = .fixed(metadata.inclusions); + var inc_reader: Io.Reader = .fixed(metadata.inclusions); const fn_inclusions_len = try inc_reader.takeInt(u16, .little); @@ -1130,7 +1130,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lock = man.toOwnedLock(), .dir_path = .{ .root_dir = comp.dirs.global_cache, - .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest), }, }); } @@ -1156,7 +1156,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.C const so_path: Path = .{ .root_dir = so_files.dir_path.root_dir, .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{ - so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover, + so_files.dir_path.sub_path, path.sep, lib.name, lib.sover, }) catch return comp.setAllocFailure(), }; task_buffer[task_buffer_i] = .{ .load_dso = so_path }; diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index 005696e1fc..b3ca51e833 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -1,7 +1,8 @@ const std = @import("std"); +const Io = std.Io; const Allocator = std.mem.Allocator; const mem = std.mem; -const path = std.fs.path; +const path = std.Io.Dir.path; const assert = std.debug.assert; const log = std.log.scoped(.mingw); @@ -259,7 +260,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { .io = io, .manifest_dir = try comp.dirs.global_cache.handle.makeOpenPath("h", .{}), }; - cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); defer cache.manifest_dir.close(io); @@ -304,7 +305,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { .output = .{ .to_list = .{ .arena = .init(gpa) } }, }; defer diagnostics.deinit(); - var aro_comp = aro.Compilation.init(gpa, arena, io, &diagnostics, std.fs.cwd()); + var aro_comp = aro.Compilation.init(gpa, arena, io, &diagnostics, Io.Dir.cwd()); defer aro_comp.deinit(); aro_comp.target = .fromZigTarget(target.*); @@ -343,7 +344,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { } const members = members: { - var aw: std.Io.Writer.Allocating = .init(gpa); + var aw: Io.Writer.Allocating = .init(gpa); errdefer aw.deinit(); try pp.prettyPrintTokens(&aw.writer, .result_only); @@ -376,7 +377,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { errdefer gpa.free(lib_final_path); { - const lib_final_file = try o_dir.createFile(final_lib_basename, .{ .truncate = true }); + const lib_final_file = try o_dir.createFile(io, final_lib_basename, .{ .truncate = true }); defer lib_final_file.close(io); var buffer: [1024]u8 = undefined; var file_writer = lib_final_file.writer(&buffer); @@ -442,7 +443,7 @@ fn findDef( } else { try override_path.print(fmt_path, .{ lib_path, lib_name }); } - if (std.fs.cwd().access(override_path.items, .{})) |_| { + if (Io.Dir.cwd().access(override_path.items, .{})) |_| { return override_path.toOwnedSlice(); } else |err| switch (err) { error.FileNotFound => {}, @@ -459,7 +460,7 @@ fn findDef( } else { try override_path.print(fmt_path, .{lib_name}); } - if (std.fs.cwd().access(override_path.items, .{})) |_| { + if (Io.Dir.cwd().access(override_path.items, .{})) |_| { return override_path.toOwnedSlice(); } else |err| switch (err) { error.FileNotFound => {}, @@ -476,7 +477,7 @@ fn findDef( } else { try override_path.print(fmt_path, .{lib_name}); } - if (std.fs.cwd().access(override_path.items, .{})) |_| { + if (Io.Dir.cwd().access(override_path.items, .{})) |_| { return override_path.toOwnedSlice(); } else |err| switch (err) { error.FileNotFound => {}, diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 67e6a2f903..cb6a80d69d 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -1,9 +1,9 @@ const std = @import("std"); +const Io = std.Io; const Allocator = std.mem.Allocator; const mem = std.mem; const log = std.log; -const fs = std.fs; -const path = fs.path; +const path = std.Io.Dir.path; const assert = std.debug.assert; const Version = std.SemanticVersion; const Path = std.Build.Cache.Path; @@ -387,7 +387,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .io = io, .manifest_dir = try comp.dirs.global_cache.handle.makeOpenPath("h", .{}), }; - cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); + cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(comp.dirs.zig_lib); cache.addPrefix(comp.dirs.global_cache); defer cache.manifest_dir.close(io); @@ -409,7 +409,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lock = man.toOwnedLock(), .dir_path = .{ .root_dir = comp.dirs.global_cache, - .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest), }, }); } @@ -640,7 +640,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .lock = man.toOwnedLock(), .dir_path = .{ .root_dir = comp.dirs.global_cache, - .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest), }, }); } @@ -661,7 +661,7 @@ fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.C const so_path: Path = .{ .root_dir = so_files.dir_path.root_dir, .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{ - so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover, + so_files.dir_path.sub_path, path.sep, lib.name, lib.sover, }) catch return comp.setAllocFailure(), }; task_buffer[task_buffer_i] = .{ .load_dso = so_path }; diff --git a/src/link/C.zig b/src/link/C.zig index 04c92443e5..a001f8fdd9 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -136,7 +136,7 @@ pub fn createEmpty( assert(!use_lld); assert(!use_llvm); - const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ + const file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{ // Truncation is done on `flush`. .truncate = false, }); @@ -792,7 +792,7 @@ pub fn flushEmitH(zcu: *Zcu) !void { } const directory = emit_h.loc.directory orelse zcu.comp.local_cache_directory; - const file = try directory.handle.createFile(emit_h.loc.basename, .{ + const file = try directory.handle.createFile(io, emit_h.loc.basename, .{ // We set the end position explicitly below; by not truncating the file, we possibly // make it easier on the file system by doing 1 reallocation instead of two. .truncate = false, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index e1d52fb7c4..009e59ed0d 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -631,12 +631,14 @@ fn create( else => return error.UnsupportedCOFFArchitecture, }; + const io = comp.io; + const coff = try arena.create(Coff); - const file = try path.root_dir.handle.createFile(comp.io, path.sub_path, .{ + const file = try path.root_dir.handle.createFile(io, path.sub_path, .{ .read = true, .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode), }); - errdefer file.close(comp.io); + errdefer file.close(io); coff.* = .{ .base = .{ .tag = .coff2, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 584a50c7f2..53812a37ec 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -313,9 +313,11 @@ pub fn createEmpty( const is_obj = output_mode == .Obj; const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static); + const io = comp.io; + // What path should this ELF linker code output to? const sub_path = emit.sub_path; - self.base.file = try emit.root_dir.handle.createFile(sub_path, .{ + self.base.file = try emit.root_dir.handle.createFile(io, sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(output_mode, link_mode), diff --git a/src/link/Lld.zig b/src/link/Lld.zig index 49f6d3f7c7..b25b9da9d9 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -1572,7 +1572,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { // report a nice error here with the file path if it fails instead of // just returning the error code. // chmod does not interact with umask, so we use a conservative -rwxr--r-- here. - std.posix.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) { + std.posix.fchmodat(Io.Dir.cwd().handle, full_out_path, 0o744, 0) catch |err| switch (err) { error.OperationNotSupported => unreachable, // Not a symlink. else => |e| return e, }; @@ -1624,7 +1624,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi const rand_int = std.crypto.random.int(u64); const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp"; - const rsp_file = try comp.dirs.local_cache.handle.createFile(rsp_path, .{}); + const rsp_file = try comp.dirs.local_cache.handle.createFile(io, rsp_path, .{}); defer comp.dirs.local_cache.handle.deleteFileZ(rsp_path) catch |err| log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e837cc853a..0f6127e10e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -219,7 +219,9 @@ pub fn createEmpty( }; errdefer self.base.destroy(); - self.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ + const io = comp.io; + + self.base.file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(output_mode, link_mode), @@ -1082,7 +1084,7 @@ fn accessLibPath( test_path.clearRetainingCapacity(); try test_path.print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext }); try checked_paths.append(try arena.dupe(u8, test_path.items)); - fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| return e, }; @@ -1110,7 +1112,7 @@ fn accessFrameworkPath( ext, }); try checked_paths.append(try arena.dupe(u8, test_path.items)); - fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| return e, }; @@ -1191,7 +1193,7 @@ fn parseDependentDylibs(self: *MachO) !void { try test_path.print("{s}{s}", .{ path, ext }); } try checked_paths.append(try arena.dupe(u8, test_path.items)); - fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| return e, }; @@ -3289,7 +3291,7 @@ pub fn reopenDebugInfo(self: *MachO) !void { var d_sym_bundle = try self.base.emit.root_dir.handle.makeOpenPath(d_sym_path, .{}); defer d_sym_bundle.close(io); - self.d_sym.?.file = try d_sym_bundle.createFile(fs.path.basename(self.base.emit.sub_path), .{ + self.d_sym.?.file = try d_sym_bundle.createFile(io, fs.path.basename(self.base.emit.sub_path), .{ .truncate = false, .read = true, }); @@ -4370,7 +4372,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi // The file/property is also available with vendored libc. fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 { const sdk_path = try fs.path.join(arena, &.{ dir, "SDKSettings.json" }); - const contents = try fs.cwd().readFileAlloc(sdk_path, arena, .limited(std.math.maxInt(u16))); + const contents = try Io.Dir.cwd().readFileAlloc(sdk_path, arena, .limited(std.math.maxInt(u16))); const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{}); if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string; return error.SdkVersionFailure; diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index 5f9a9ecac9..814faf234a 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -247,7 +247,7 @@ pub fn deinit(self: *CodeSignature, allocator: Allocator) void { } pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void { - const inner = try fs.cwd().readFileAlloc(path, allocator, .limited(std.math.maxInt(u32))); + const inner = try Io.Dir.cwd().readFileAlloc(path, allocator, .limited(std.math.maxInt(u32))); self.entitlements = .{ .inner = inner }; } diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 7e28dc0a8b..d13caaa315 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -33,6 +33,7 @@ pub fn createEmpty( options: link.File.OpenOptions, ) !*Linker { const gpa = comp.gpa; + const io = comp.io; const target = &comp.root_mod.resolved_target.result; assert(!comp.config.use_lld); // Caught by Compilation.Config.resolve @@ -78,7 +79,7 @@ pub fn createEmpty( }; errdefer linker.deinit(); - linker.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ + linker.base.file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{ .truncate = true, .read = true, }); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 7ab1e0eb4b..5f89625d56 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2997,7 +2997,9 @@ pub fn createEmpty( .named => |name| (try wasm.internString(name)).toOptional(), }; - wasm.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ + const io = comp.io; + + wasm.base.file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{ .truncate = true, .read = true, .mode = if (fs.has_executable_bit) diff --git a/src/main.zig b/src/main.zig index b040b6c8ef..67b7384b57 100644 --- a/src/main.zig +++ b/src/main.zig @@ -713,7 +713,7 @@ const Emit = union(enum) { } else e: { // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would. if (fs.path.dirname(path)) |dir_path| { - var dir = fs.cwd().openDir(dir_path, .{}) catch |err| { + var dir = Io.Dir.cwd().openDir(dir_path, .{}) catch |err| { fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) }); }; dir.close(io); @@ -3304,7 +3304,7 @@ fn buildOutputType( } else emit: { // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would. if (fs.path.dirname(path)) |dir_path| { - var dir = fs.cwd().openDir(dir_path, .{}) catch |err| { + var dir = Io.Dir.cwd().openDir(dir_path, .{}) catch |err| { fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) }); }; dir.close(io); @@ -3389,7 +3389,7 @@ fn buildOutputType( // file will not run and this temp file will be leaked. The filename // will be a hash of its contents — so multiple invocations of // `zig cc -` will result in the same temp file name. - var f = try dirs.local_cache.handle.createFile(dump_path, .{}); + var f = try dirs.local_cache.handle.createFile(io, dump_path, .{}); defer f.close(io); // Re-using the hasher from Cache, since the functional requirements @@ -4773,7 +4773,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! var ok_count: usize = 0; for (template_paths) |template_path| { - if (templates.write(arena, fs.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { + if (templates.write(arena, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { std.log.info("created {s}", .{template_path}); ok_count += 1; } else |err| switch (err) { @@ -5227,7 +5227,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) if (system_pkg_dir_path) |p| { job_queue.global_cache = .{ .path = p, - .handle = fs.cwd().openDir(p, .{}) catch |err| { + .handle = Io.Dir.cwd().openDir(p, .{}) catch |err| { fatal("unable to open system package directory '{s}': {s}", .{ p, @errorName(err), }); @@ -5823,7 +5823,7 @@ const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, /// Initialize the arguments from a Response File. "*.rsp" fn initArgIteratorResponseFile(allocator: Allocator, resp_file_path: []const u8) !ArgIteratorResponseFile { const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit - const cmd_line = try fs.cwd().readFileAlloc(resp_file_path, allocator, .limited(max_bytes)); + const cmd_line = try Io.Dir.cwd().readFileAlloc(resp_file_path, allocator, .limited(max_bytes)); errdefer allocator.free(cmd_line); return ArgIteratorResponseFile.initTakeOwnership(allocator, cmd_line); @@ -6187,7 +6187,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void { const display_path = zig_source_path orelse ""; const source: [:0]const u8 = s: { var f = if (zig_source_path) |p| file: { - break :file fs.cwd().openFile(io, p, .{}) catch |err| { + break :file Io.Dir.cwd().openFile(io, p, .{}) catch |err| { fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) }); }; } else Io.File.stdin(); @@ -6494,7 +6494,7 @@ fn cmdDumpZir(arena: Allocator, io: Io, args: []const []const u8) !void { const cache_file = args[0]; - var f = fs.cwd().openFile(io, cache_file, .{}) catch |err| { + var f = Io.Dir.cwd().openFile(io, cache_file, .{}) catch |err| { fatal("unable to open zir cache file for dumping '{s}': {s}", .{ cache_file, @errorName(err) }); }; defer f.close(io); @@ -6541,7 +6541,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void { const new_source_path = args[1]; const old_source = source: { - var f = fs.cwd().openFile(io, old_source_path, .{}) catch |err| + var f = Io.Dir.cwd().openFile(io, old_source_path, .{}) catch |err| fatal("unable to open old source file '{s}': {s}", .{ old_source_path, @errorName(err) }); defer f.close(io); var file_reader: Io.File.Reader = f.reader(io, &stdin_buffer); @@ -6549,7 +6549,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void { fatal("unable to read old source file '{s}': {s}", .{ old_source_path, @errorName(err) }); }; const new_source = source: { - var f = fs.cwd().openFile(io, new_source_path, .{}) catch |err| + var f = Io.Dir.cwd().openFile(io, new_source_path, .{}) catch |err| fatal("unable to open new source file '{s}': {s}", .{ new_source_path, @errorName(err) }); defer f.close(io); var file_reader: Io.File.Reader = f.reader(io, &stdin_buffer); @@ -6845,7 +6845,7 @@ fn accessFrameworkPath( framework_dir_path, framework_name, framework_name, ext, }); try checked_paths.print("\n {s}", .{test_path.items}); - fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| fatal("unable to search for {s} framework '{s}': {s}", .{ ext, test_path.items, @errorName(e), @@ -6957,7 +6957,7 @@ fn cmdFetch( var global_cache_directory: Directory = l: { const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); break :l .{ - .handle = try fs.cwd().makeOpenPath(p, .{}), + .handle = try Io.Dir.cwd().makeOpenPath(p, .{}), .path = p, }; }; @@ -7260,7 +7260,7 @@ fn findBuildRoot(arena: Allocator, options: FindBuildRootOptions) !BuildRoot { if (options.build_file) |bf| { if (fs.path.dirname(bf)) |dirname| { - const dir = fs.cwd().openDir(dirname, .{}) catch |err| { + const dir = Io.Dir.cwd().openDir(dirname, .{}) catch |err| { fatal("unable to open directory to build file from argument 'build-file', '{s}': {s}", .{ dirname, @errorName(err) }); }; return .{ @@ -7272,7 +7272,7 @@ fn findBuildRoot(arena: Allocator, options: FindBuildRootOptions) !BuildRoot { return .{ .build_zig_basename = build_zig_basename, - .directory = .{ .path = null, .handle = fs.cwd() }, + .directory = .{ .path = null, .handle = Io.Dir.cwd() }, .cleanup_build_dir = null, }; } @@ -7280,8 +7280,8 @@ fn findBuildRoot(arena: Allocator, options: FindBuildRootOptions) !BuildRoot { var dirname: []const u8 = cwd_path; while (true) { const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename }); - if (fs.cwd().access(joined_path, .{})) |_| { - const dir = fs.cwd().openDir(dirname, .{}) catch |err| { + if (Io.Dir.cwd().access(joined_path, .{})) |_| { + const dir = Io.Dir.cwd().openDir(dirname, .{}) catch |err| { fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) }); }; return .{ @@ -7443,7 +7443,7 @@ const Templates = struct { } }; fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime fmt: []const u8, args: anytype) !void { - const f = try fs.cwd().createFile(file_name, .{ .exclusive = true }); + const f = try Io.Dir.cwd().createFile(io, file_name, .{ .exclusive = true }); defer f.close(io); var buf: [4096]u8 = undefined; var fw = f.writer(&buf); @@ -7591,7 +7591,7 @@ fn addLibDirectoryWarn2( ignore_not_found: bool, ) void { lib_directories.appendAssumeCapacity(.{ - .handle = fs.cwd().openDir(path, .{}) catch |err| { + .handle = Io.Dir.cwd().openDir(path, .{}) catch |err| { if (err == error.FileNotFound and ignore_not_found) return; warn("unable to open library directory '{s}': {s}", .{ path, @errorName(err) }); return; -- cgit v1.2.3 From 9ccd68de0b79c3723bd11071fd836bc24ff25b33 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 16:13:51 -0800 Subject: std: move abort and exit from posix into process and delete the unit tests that called fork() no forking allowed in the std lib, including unit tests, except to implement child process spawning. --- lib/compiler/translate-c/main.zig | 2 +- lib/std/Build.zig | 9 ++-- lib/std/Build/Cache/Path.zig | 4 +- lib/std/Build/Step/ConfigHeader.zig | 3 +- lib/std/Build/Step/ObjCopy.zig | 3 +- lib/std/Build/Step/Run.zig | 7 +-- lib/std/Build/Step/UpdateSourceFiles.zig | 2 +- lib/std/Build/Step/WriteFile.zig | 6 +-- lib/std/debug.zig | 12 +++-- lib/std/fs/test.zig | 45 ++++++++++------- lib/std/os/linux/IoUring.zig | 26 ---------- lib/std/posix.zig | 87 -------------------------------- lib/std/posix/test.zig | 67 ------------------------ lib/std/process.zig | 86 ++++++++++++++++++++++++++++++- lib/std/process/Child.zig | 2 +- lib/std/start.zig | 6 +-- lib/std/tar.zig | 16 +++--- lib/std/zip.zig | 6 +-- src/Compilation.zig | 10 ++-- src/Package/Fetch.zig | 5 +- src/main.zig | 11 ++-- 21 files changed, 167 insertions(+), 248 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig index d0a873fd78..d140145032 100644 --- a/lib/compiler/translate-c/main.zig +++ b/lib/compiler/translate-c/main.zig @@ -253,7 +253,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration if (d.output_name) |path| blk: { if (std.mem.eql(u8, path, "-")) break :blk; if (std.fs.path.dirname(path)) |dirname| { - Io.Dir.cwd().makePath(dirname) catch |err| + Io.Dir.cwd().makePath(io, dirname) catch |err| return d.fatal("failed to create path to '{s}': {s}", .{ path, aro.Driver.errorDescription(err) }); } out_file = Io.Dir.cwd().createFile(io, path, .{}) catch |err| { diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 3d0e8b8dbc..1eff8813e5 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1706,7 +1706,7 @@ pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.MakeError || Io.Di var src_file = cwd.createFile(io, dest_path, .{}) catch |err| switch (err) { error.FileNotFound => blk: { if (fs.path.dirname(dest_path)) |dirname| { - try cwd.makePath(dirname); + try cwd.makePath(io, dirname); } break :blk try cwd.createFile(io, dest_path, .{}); }, @@ -2634,13 +2634,12 @@ pub const InstallDir = union(enum) { /// source of API breakage in the future, so keep that in mind when using this /// function. pub fn makeTempPath(b: *Build) []const u8 { + const io = b.graph.io; const rand_int = std.crypto.random.int(u64); const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM"); - b.cache_root.handle.makePath(tmp_dir_sub_path) catch |err| { - std.debug.print("unable to make tmp path '{s}': {s}\n", .{ - result_path, @errorName(err), - }); + b.cache_root.handle.makePath(io, tmp_dir_sub_path) catch |err| { + std.debug.print("unable to make tmp path '{s}': {t}\n", .{ result_path, err }); }; return result_path; } diff --git a/lib/std/Build/Cache/Path.zig b/lib/std/Build/Cache/Path.zig index 93e0c0d792..941948a9cd 100644 --- a/lib/std/Build/Cache/Path.zig +++ b/lib/std/Build/Cache/Path.zig @@ -128,14 +128,14 @@ pub fn access(p: Path, sub_path: []const u8, flags: Io.Dir.AccessOptions) !void return p.root_dir.handle.access(joined_path, flags); } -pub fn makePath(p: Path, sub_path: []const u8) !void { +pub fn makePath(p: Path, io: Io, sub_path: []const u8) !void { var buf: [fs.max_path_bytes]u8 = undefined; const joined_path = if (p.sub_path.len == 0) sub_path else p: { break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{ p.sub_path, sub_path, }) catch return error.NameTooLong; }; - return p.root_dir.handle.makePath(joined_path); + return p.root_dir.handle.makePath(io, joined_path); } pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 { diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index ea7d9d99ff..f377959610 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -184,6 +184,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const gpa = b.allocator; const arena = b.allocator; + const io = b.graph.io; var man = b.graph.cache.obtain(); defer man.deinit(); @@ -257,7 +258,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const sub_path = b.pathJoin(&.{ "o", &digest, config_header.include_path }); const sub_path_dirname = std.fs.path.dirname(sub_path).?; - b.cache_root.handle.makePath(sub_path_dirname) catch |err| { + b.cache_root.handle.makePath(io, sub_path_dirname) catch |err| { return step.fail("unable to make path '{f}{s}': {s}", .{ b.cache_root, sub_path_dirname, @errorName(err), }); diff --git a/lib/std/Build/Step/ObjCopy.zig b/lib/std/Build/Step/ObjCopy.zig index 4aa1c0a9dc..b81f59b9a1 100644 --- a/lib/std/Build/Step/ObjCopy.zig +++ b/lib/std/Build/Step/ObjCopy.zig @@ -143,6 +143,7 @@ pub fn getOutputSeparatedDebug(objcopy: *const ObjCopy) ?std.Build.LazyPath { fn make(step: *Step, options: Step.MakeOptions) !void { const prog_node = options.progress_node; const b = step.owner; + const io = b.graph.io; const objcopy: *ObjCopy = @fieldParentPtr("step", step); try step.singleUnchangingWatchInput(objcopy.input_file); @@ -176,7 +177,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const cache_path = "o" ++ fs.path.sep_str ++ digest; const full_dest_path = try b.cache_root.join(b.allocator, &.{ cache_path, objcopy.basename }); const full_dest_path_debug = try b.cache_root.join(b.allocator, &.{ cache_path, b.fmt("{s}.debug", .{objcopy.basename}) }); - b.cache_root.handle.makePath(cache_path) catch |err| { + b.cache_root.handle.makePath(io, cache_path) catch |err| { return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) }); }; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index af6bc20438..54e77bd614 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -973,7 +973,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { .output_directory => output_sub_path, else => unreachable, }; - b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { + b.cache_root.handle.makePath(io, output_sub_dir_path) catch |err| { return step.fail("unable to make path '{f}{s}': {s}", .{ b.cache_root, output_sub_dir_path, @errorName(err), }); @@ -1005,7 +1005,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { .output_directory => output_sub_path, else => unreachable, }; - b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { + b.cache_root.handle.makePath(io, output_sub_dir_path) catch |err| { return step.fail("unable to make path '{f}{s}': {s}", .{ b.cache_root, output_sub_dir_path, @errorName(err), }); @@ -1241,6 +1241,7 @@ fn runCommand( const b = step.owner; const arena = b.allocator; const gpa = options.gpa; + const io = b.graph.io; const cwd: ?[]const u8 = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, step) else null; @@ -1470,7 +1471,7 @@ fn runCommand( const sub_path = b.pathJoin(&output_components); const sub_path_dirname = fs.path.dirname(sub_path).?; - b.cache_root.handle.makePath(sub_path_dirname) catch |err| { + b.cache_root.handle.makePath(io, sub_path_dirname) catch |err| { return step.fail("unable to make path '{f}{s}': {s}", .{ b.cache_root, sub_path_dirname, @errorName(err), }); diff --git a/lib/std/Build/Step/UpdateSourceFiles.zig b/lib/std/Build/Step/UpdateSourceFiles.zig index f5d95182e9..44c6ae1ed4 100644 --- a/lib/std/Build/Step/UpdateSourceFiles.zig +++ b/lib/std/Build/Step/UpdateSourceFiles.zig @@ -78,7 +78,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { var any_miss = false; for (usf.output_source_files.items) |output_source_file| { if (fs.path.dirname(output_source_file.sub_path)) |dirname| { - b.build_root.handle.makePath(dirname) catch |err| { + b.build_root.handle.makePath(io, dirname) catch |err| { return step.fail("unable to make path '{f}{s}': {t}", .{ b.build_root, dirname, err }); }; } diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 85dc9b3fa2..21346959e7 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -268,7 +268,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { for (write_file.files.items) |file| { if (fs.path.dirname(file.sub_path)) |dirname| { - cache_dir.makePath(dirname) catch |err| { + cache_dir.makePath(io, dirname) catch |err| { return step.fail("unable to make path '{f}{s}{c}{s}': {t}", .{ b.cache_root, cache_path, fs.path.sep, dirname, err, }); @@ -303,7 +303,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const dest_dirname = dir.sub_path; if (dest_dirname.len != 0) { - cache_dir.makePath(dest_dirname) catch |err| { + cache_dir.makePath(io, dest_dirname) catch |err| { return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{ b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err), }); @@ -318,7 +318,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const src_entry_path = try src_dir_path.join(arena, entry.path); const dest_path = b.pathJoin(&.{ dest_dirname, entry.path }); switch (entry.kind) { - .directory => try cache_dir.makePath(dest_path), + .directory => try cache_dir.makePath(io, dest_path), .file => { const prev_status = Io.Dir.updateFile( src_entry_path.root_dir.handle, diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 5df0eef2d5..7ede172d86 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -522,7 +522,7 @@ pub fn defaultPanic( } @trap(); }, - .cuda, .amdhsa => std.posix.abort(), + .cuda, .amdhsa => std.process.abort(), .plan9 => { var status: [std.os.plan9.ERRMAX]u8 = undefined; const len = @min(msg.len, status.len - 1); @@ -575,12 +575,13 @@ pub fn defaultPanic( // A panic happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - File.stderr().writeStreamingAll("aborting due to recursive panic\n") catch {}; + const stderr, _ = lockStderrWriter(&.{}); + stderr.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } - posix.abort(); + std.process.abort(); } /// Must be called only after adding 1 to `panicking`. There are three callsites. @@ -1596,7 +1597,8 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex // A segfault happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - File.stderr().writeAll("aborting due to recursive panic\n") catch {}; + const stderr, _ = lockStderrWriter(&.{}); + stderr().writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } @@ -1604,7 +1606,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex // We cannot allow the signal handler to return because when it runs the original instruction // again, the memory may be mapped and undefined behavior would occur rather than repeating // the segfault. So we simply abort here. - posix.abort(); + std.process.abort(); } pub fn dumpStackPointerAddr(prefix: []const u8) void { diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index e044a97620..bc84500419 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -674,7 +674,7 @@ test "Dir.Iterator but dir is deleted during iteration" { var iterator = subdir.iterate(); // Create something to iterate over within the subdir - try tmp.dir.makePath("subdir" ++ fs.path.sep_str ++ "b"); + try tmp.dir.makePath(io, "subdir" ++ fs.path.sep_str ++ "b"); // Then, before iterating, delete the directory that we're iterating. // This is a contrived reproduction, but this could happen outside of the program, in another thread, etc. @@ -1196,7 +1196,7 @@ test "deleteTree does not follow symlinks" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - try tmp.dir.makePath("b"); + try tmp.dir.makePath(io, "b"); { var a = try tmp.dir.makeOpenPath("a", .{}); defer a.close(io); @@ -1211,6 +1211,8 @@ test "deleteTree does not follow symlinks" { } test "deleteTree on a symlink" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1223,7 +1225,7 @@ test "deleteTree on a symlink" { try tmp.dir.access("file", .{}); // Symlink to a directory - try tmp.dir.makePath("dir"); + try tmp.dir.makePath(io, "dir"); try setupSymlink(tmp.dir, "dir", "dirlink", .{ .is_directory = true }); try tmp.dir.deleteTree("dirlink"); @@ -1238,7 +1240,7 @@ test "makePath, put some files in it, deleteTree" { const allocator = ctx.arena.allocator(); const dir_path = try ctx.transformPath("os_test_tmp"); - try ctx.dir.makePath(try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" })); + try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" })); try ctx.dir.writeFile(.{ .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }), .data = "nonsense", @@ -1261,7 +1263,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" { const allocator = ctx.arena.allocator(); const dir_path = try ctx.transformPath("os_test_tmp"); - try ctx.dir.makePath(try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" })); + try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" })); try ctx.dir.writeFile(.{ .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }), .data = "nonsense", @@ -1280,21 +1282,25 @@ test "makePath, put some files in it, deleteTreeMinStackSize" { test "makePath in a directory that no longer exists" { if (native_os == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); try tmp.parent_dir.deleteTree(&tmp.sub_path); - try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path")); + try testing.expectError(error.FileNotFound, tmp.dir.makePath(io, "sub-path")); } test "makePath but sub_path contains pre-existing file" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.makeDir("foo"); try tmp.dir.writeFile(.{ .sub_path = "foo/bar", .data = "" }); - try testing.expectError(error.NotDir, tmp.dir.makePath("foo/bar/baz")); + try testing.expectError(error.NotDir, tmp.dir.makePath(io, "foo/bar/baz")); } fn expectDir(io: Io, dir: Dir, path: []const u8) !void { @@ -1314,7 +1320,7 @@ test "makepath existing directories" { try tmpA.makeDir("B"); const testPath = "A" ++ fs.path.sep_str ++ "B" ++ fs.path.sep_str ++ "C"; - try tmp.dir.makePath(testPath); + try tmp.dir.makePath(io, testPath); try expectDir(io, tmp.dir, testPath); } @@ -1328,7 +1334,7 @@ test "makepath through existing valid symlink" { try tmp.dir.makeDir("realfolder"); try setupSymlink(tmp.dir, "." ++ fs.path.sep_str ++ "realfolder", "working-symlink", .{}); - try tmp.dir.makePath("working-symlink" ++ fs.path.sep_str ++ "in-realfolder"); + try tmp.dir.makePath(io, "working-symlink" ++ fs.path.sep_str ++ "in-realfolder"); try expectDir(io, tmp.dir, "realfolder" ++ fs.path.sep_str ++ "in-realfolder"); } @@ -1344,7 +1350,7 @@ test "makepath relative walks" { }); defer testing.allocator.free(relPath); - try tmp.dir.makePath(relPath); + try tmp.dir.makePath(io, relPath); // How .. is handled is different on Windows than non-Windows switch (native_os) { @@ -1383,7 +1389,7 @@ test "makepath ignores '.'" { }); defer testing.allocator.free(expectedPath); - try tmp.dir.makePath(dotPath); + try tmp.dir.makePath(io, dotPath); try expectDir(io, tmp.dir, expectedPath); } @@ -1550,10 +1556,11 @@ test "setEndPos" { test "access file" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const dir_path = try ctx.transformPath("os_test_tmp"); const file_path = try ctx.transformPath("os_test_tmp" ++ fs.path.sep_str ++ "file.txt"); - try ctx.dir.makePath(dir_path); + try ctx.dir.makePath(io, dir_path); try testing.expectError(error.FileNotFound, ctx.dir.access(file_path, .{})); try ctx.dir.writeFile(.{ .sub_path = file_path, .data = "" }); @@ -1569,7 +1576,7 @@ test "sendfile" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - try tmp.dir.makePath("os_test_tmp"); + try tmp.dir.makePath(io, "os_test_tmp"); var dir = try tmp.dir.openDir(io, "os_test_tmp", .{}); defer dir.close(io); @@ -1616,7 +1623,7 @@ test "sendfile with buffered data" { var tmp = tmpDir(.{}); defer tmp.cleanup(); - try tmp.dir.makePath("os_test_tmp"); + try tmp.dir.makePath(io, "os_test_tmp"); var dir = try tmp.dir.openDir(io, "os_test_tmp", .{}); defer dir.close(io); @@ -1894,7 +1901,7 @@ test "walker" { }); for (expected_paths.keys()) |key| { - try tmp.dir.makePath(key); + try tmp.dir.makePath(io, key); } var walker = try tmp.dir.walk(testing.allocator); @@ -1956,7 +1963,7 @@ test "selective walker, skip entries that start with ." { }); for (paths_to_create) |path| { - try tmp.dir.makePath(path); + try tmp.dir.makePath(io, path); } var walker = try tmp.dir.walkSelectively(testing.allocator); @@ -1991,6 +1998,8 @@ test "selective walker, skip entries that start with ." { } test "walker without fully iterating" { + const io = testing.io; + var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -2000,8 +2009,8 @@ test "walker without fully iterating" { // Create 2 directories inside the tmp directory, but then only iterate once before breaking. // This ensures that walker doesn't try to close the initial directory when not fully iterating. - try tmp.dir.makePath("a"); - try tmp.dir.makePath("b"); + try tmp.dir.makePath(io, "a"); + try tmp.dir.makePath(io, "b"); var num_walked: usize = 0; while (try walker.next()) |_| { diff --git a/lib/std/os/linux/IoUring.zig b/lib/std/os/linux/IoUring.zig index 3c9a313a4a..72b8369d68 100644 --- a/lib/std/os/linux/IoUring.zig +++ b/lib/std/os/linux/IoUring.zig @@ -4090,32 +4090,6 @@ test "openat_direct/close_direct" { try ring.unregister_files(); } -test "waitid" { - try skipKernelLessThan(.{ .major = 6, .minor = 7, .patch = 0 }); - - var ring = IoUring.init(16, 0) catch |err| switch (err) { - error.SystemOutdated => return error.SkipZigTest, - error.PermissionDenied => return error.SkipZigTest, - else => return err, - }; - defer ring.deinit(); - - const pid = try posix.fork(); - if (pid == 0) { - posix.exit(7); - } - - var siginfo: posix.siginfo_t = undefined; - _ = try ring.waitid(0, .PID, pid, &siginfo, posix.W.EXITED, 0); - - try testing.expectEqual(1, try ring.submit()); - - const cqe_waitid = try ring.copy_cqe(); - try testing.expectEqual(0, cqe_waitid.res); - try testing.expectEqual(pid, siginfo.fields.common.first.piduid.pid); - try testing.expectEqual(7, siginfo.fields.common.second.sigchld.status); -} - /// For use in tests. Returns SkipZigTest if kernel version is less than required. inline fn skipKernelLessThan(required: std.SemanticVersion) !void { if (!is_linux) return error.SkipZigTest; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index f4aa970413..42429a4993 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -615,66 +615,6 @@ fn getRandomBytesDevURandom(buf: []u8) GetRandomError!void { } } -/// Causes abnormal process termination. -/// If linking against libc, this calls the abort() libc function. Otherwise -/// it raises SIGABRT followed by SIGKILL and finally lo -/// Invokes the current signal handler for SIGABRT, if any. -pub fn abort() noreturn { - @branchHint(.cold); - // MSVCRT abort() sometimes opens a popup window which is undesirable, so - // even when linking libc on Windows we use our own abort implementation. - // See https://github.com/ziglang/zig/issues/2071 for more details. - if (native_os == .windows) { - if (builtin.mode == .Debug and windows.peb().BeingDebugged != 0) { - @breakpoint(); - } - windows.ntdll.RtlExitUserProcess(3); - } - if (!builtin.link_libc and native_os == .linux) { - // The Linux man page says that the libc abort() function - // "first unblocks the SIGABRT signal", but this is a footgun - // for user-defined signal handlers that want to restore some state in - // some program sections and crash in others. - // So, the user-installed SIGABRT handler is run, if present. - raise(.ABRT) catch {}; - - // Disable all signal handlers. - const filledset = linux.sigfillset(); - sigprocmask(SIG.BLOCK, &filledset, null); - - // Only one thread may proceed to the rest of abort(). - if (!builtin.single_threaded) { - const global = struct { - var abort_entered: bool = false; - }; - while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {} - } - - // Install default handler so that the tkill below will terminate. - const sigact = Sigaction{ - .handler = .{ .handler = SIG.DFL }, - .mask = sigemptyset(), - .flags = 0, - }; - sigaction(.ABRT, &sigact, null); - - _ = linux.tkill(linux.gettid(), .ABRT); - - var sigabrtmask = sigemptyset(); - sigaddset(&sigabrtmask, .ABRT); - sigprocmask(SIG.UNBLOCK, &sigabrtmask, null); - - // Beyond this point should be unreachable. - @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0; - raise(.KILL) catch {}; - exit(127); // Pid 1 might not be signalled in some containers. - } - switch (native_os) { - .uefi, .wasi, .emscripten, .cuda, .amdhsa => @trap(), - else => system.abort(), - } -} - pub const RaiseError = UnexpectedError; pub fn raise(sig: SIG) RaiseError!void { @@ -715,33 +655,6 @@ pub fn kill(pid: pid_t, sig: SIG) KillError!void { } } -/// Exits all threads of the program with the specified status code. -pub fn exit(status: u8) noreturn { - if (builtin.link_libc) { - std.c.exit(status); - } - if (native_os == .windows) { - windows.ntdll.RtlExitUserProcess(status); - } - if (native_os == .wasi) { - wasi.proc_exit(status); - } - if (native_os == .linux and !builtin.single_threaded) { - linux.exit_group(status); - } - if (native_os == .uefi) { - const uefi = std.os.uefi; - // exit() is only available if exitBootServices() has not been called yet. - // This call to exit should not fail, so we catch-ignore errors. - if (uefi.system_table.boot_services) |bs| { - bs.exit(uefi.handle, @enumFromInt(status), null) catch {}; - } - // If we can't exit, reboot the system instead. - uefi.system_table.runtime_services.resetSystem(.cold, @enumFromInt(status), null); - } - system.exit(status); -} - pub const ReadError = std.Io.File.Reader.Error; /// Returns the number of bytes that were read, which can be less than diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index dc63be6e14..169c5a70c2 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -667,73 +667,6 @@ test "writev longer than IOV_MAX" { try testing.expectEqual(@as(usize, posix.IOV_MAX), amt); } -test "POSIX file locking with fcntl" { - if (native_os == .windows or native_os == .wasi) { - // Not POSIX. - return error.SkipZigTest; - } - - if (true) { - // https://github.com/ziglang/zig/issues/11074 - return error.SkipZigTest; - } - - const io = testing.io; - - var tmp = tmpDir(.{}); - defer tmp.cleanup(); - - // Create a temporary lock file - var file = try tmp.dir.createFile(io, "lock", .{ .read = true }); - defer file.close(io); - try file.setEndPos(2); - const fd = file.handle; - - // Place an exclusive lock on the first byte, and a shared lock on the second byte: - var struct_flock = std.mem.zeroInit(posix.Flock, .{ .type = posix.F.WRLCK }); - _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock)); - struct_flock.start = 1; - struct_flock.type = posix.F.RDLCK; - _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock)); - - // Check the locks in a child process: - const pid = try posix.fork(); - if (pid == 0) { - // child expects be denied the exclusive lock: - struct_flock.start = 0; - struct_flock.type = posix.F.WRLCK; - try expectError(error.Locked, posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock))); - // child expects to get the shared lock: - struct_flock.start = 1; - struct_flock.type = posix.F.RDLCK; - _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock)); - // child waits for the exclusive lock in order to test deadlock: - struct_flock.start = 0; - struct_flock.type = posix.F.WRLCK; - _ = try posix.fcntl(fd, posix.F.SETLKW, @intFromPtr(&struct_flock)); - // child exits without continuing: - posix.exit(0); - } else { - // parent waits for child to get shared lock: - std.Thread.sleep(1 * std.time.ns_per_ms); - // parent expects deadlock when attempting to upgrade the shared lock to exclusive: - struct_flock.start = 1; - struct_flock.type = posix.F.WRLCK; - try expectError(error.DeadLock, posix.fcntl(fd, posix.F.SETLKW, @intFromPtr(&struct_flock))); - // parent releases exclusive lock: - struct_flock.start = 0; - struct_flock.type = posix.F.UNLCK; - _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock)); - // parent releases shared lock: - struct_flock.start = 1; - struct_flock.type = posix.F.UNLCK; - _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock)); - // parent waits for child: - const result = posix.waitpid(pid, 0); - try expect(result.status == 0 * 256); - } -} - test "rename smoke test" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .windows) return error.SkipZigTest; diff --git a/lib/std/process.zig b/lib/std/process.zig index 5d60190c2b..f7ecf5fdc2 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -16,8 +16,6 @@ const unicode = std.unicode; const max_path_bytes = std.fs.max_path_bytes; pub const Child = @import("process/Child.zig"); -pub const abort = posix.abort; -pub const exit = posix.exit; pub const changeCurDir = posix.chdir; pub const changeCurDirZ = posix.chdirZ; @@ -2208,3 +2206,87 @@ pub const OpenExecutableError = File.OpenError || ExecutablePathError || File.Lo pub fn openExecutable(io: Io, flags: File.OpenFlags) OpenExecutableError!File { return io.vtable.processExecutableOpen(io.userdata, flags); } + +/// Causes abnormal process termination. +/// +/// If linking against libc, this calls `std.c.abort`. Otherwise it raises +/// SIGABRT followed by SIGKILL. +/// +/// Invokes the current signal handler for SIGABRT, if any. +pub fn abort() noreturn { + @branchHint(.cold); + // MSVCRT abort() sometimes opens a popup window which is undesirable, so + // even when linking libc on Windows we use our own abort implementation. + // See https://github.com/ziglang/zig/issues/2071 for more details. + if (native_os == .windows) { + if (builtin.mode == .Debug and windows.peb().BeingDebugged != 0) { + @breakpoint(); + } + windows.ntdll.RtlExitUserProcess(3); + } + if (!builtin.link_libc and native_os == .linux) { + // The Linux man page says that the libc abort() function + // "first unblocks the SIGABRT signal", but this is a footgun + // for user-defined signal handlers that want to restore some state in + // some program sections and crash in others. + // So, the user-installed SIGABRT handler is run, if present. + posix.raise(.ABRT) catch {}; + + // Disable all signal handlers. + const filledset = std.os.linux.sigfillset(); + posix.sigprocmask(posix.SIG.BLOCK, &filledset, null); + + // Only one thread may proceed to the rest of abort(). + if (!builtin.single_threaded) { + const global = struct { + var abort_entered: bool = false; + }; + while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {} + } + + // Install default handler so that the tkill below will terminate. + const sigact: posix.Sigaction = .{ + .handler = .{ .handler = posix.SIG.DFL }, + .mask = posix.sigemptyset(), + .flags = 0, + }; + posix.sigaction(.ABRT, &sigact, null); + + _ = std.os.linux.tkill(std.os.linux.gettid(), .ABRT); + + var sigabrtmask = posix.sigemptyset(); + posix.sigaddset(&sigabrtmask, .ABRT); + posix.sigprocmask(posix.SIG.UNBLOCK, &sigabrtmask, null); + + // Beyond this point should be unreachable. + @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0; + posix.raise(.KILL) catch {}; + exit(127); // Pid 1 might not be signalled in some containers. + } + switch (native_os) { + .uefi, .wasi, .emscripten, .cuda, .amdhsa => @trap(), + else => posix.system.abort(), + } +} + +/// Exits all threads of the program with the specified status code. +pub fn exit(status: u8) noreturn { + if (builtin.link_libc) { + std.c.exit(status); + } else switch (native_os) { + .windows => windows.ntdll.RtlExitUserProcess(status), + .wasi => std.os.wasi.proc_exit(status), + .linux => if (!builtin.single_threaded) std.os.linux.exit_group(status), + .uefi => { + const uefi = std.os.uefi; + // exit() is only available if exitBootServices() has not been called yet. + // This call to exit should not fail, so we catch-ignore errors. + if (uefi.system_table.boot_services) |bs| { + bs.exit(uefi.handle, @enumFromInt(status), null) catch {}; + } + // If we can't exit, reboot the system instead. + uefi.system_table.runtime_services.resetSystem(.cold, @enumFromInt(status), null); + }, + else => posix.system.exit(status), + } +} diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 33faeef061..4521cd511f 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -1050,7 +1050,7 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { // The _exit(2) function does nothing but make the exit syscall, unlike exit(3) std.c._exit(1); } - posix.exit(1); + posix.system.exit(1); } fn writeIntFd(fd: i32, value: ErrInt) !void { diff --git a/lib/std/start.zig b/lib/std/start.zig index a5bec41231..64a1c17175 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -110,7 +110,7 @@ fn main2() callconv(.c) c_int { } fn _start2() callconv(.withStackAlign(.c, 1)) noreturn { - std.posix.exit(callMain()); + std.process.exit(callMain()); } fn spirvMain2() callconv(.kernel) void { @@ -118,7 +118,7 @@ fn spirvMain2() callconv(.kernel) void { } fn wWinMainCRTStartup2() callconv(.c) noreturn { - std.posix.exit(callMain()); + std.process.exit(callMain()); } //////////////////////////////////////////////////////////////////////////////// @@ -627,7 +627,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { for (slice) |func| func(); } - std.posix.exit(callMainWithArgs(argc, argv, envp)); + std.process.exit(callMainWithArgs(argc, argv, envp)); } fn expandStackSize(phdrs: []elf.Phdr) void { diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 8a0bbb342f..b2a5306458 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -606,7 +606,7 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp switch (file.kind) { .directory => { if (file_name.len > 0 and !options.exclude_empty_directories) { - try dir.makePath(file_name); + try dir.makePath(io, file_name); } }, .file => { @@ -625,7 +625,7 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp }, .sym_link => { const link_name = file.link_name; - createDirAndSymlink(dir, link_name, file_name) catch |err| { + createDirAndSymlink(io, dir, link_name, file_name) catch |err| { const d = options.diagnostics orelse return error.UnableToCreateSymLink; try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{ .code = err, @@ -642,7 +642,7 @@ fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, mode: Io.File.Mo const fs_file = dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode }) catch |err| { if (err == error.FileNotFound) { if (std.fs.path.dirname(file_name)) |dir_name| { - try dir.makePath(dir_name); + try dir.makePath(io, dir_name); return try dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode }); } } @@ -652,11 +652,11 @@ fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, mode: Io.File.Mo } // Creates a symbolic link at path `file_name` which points to `link_name`. -fn createDirAndSymlink(dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void { +fn createDirAndSymlink(io: Io, dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void { dir.symLink(link_name, file_name, .{}) catch |err| { if (err == error.FileNotFound) { if (std.fs.path.dirname(file_name)) |dir_name| { - try dir.makePath(dir_name); + try dir.makePath(io, dir_name); return try dir.symLink(link_name, file_name, .{}); } } @@ -885,15 +885,15 @@ test "create file and symlink" { file = try createDirAndFile(io, root.dir, "a/b/c/file2", default_mode); file.close(io); - createDirAndSymlink(root.dir, "a/b/c/file2", "symlink1") catch |err| { + createDirAndSymlink(io, root.dir, "a/b/c/file2", "symlink1") catch |err| { // On Windows when developer mode is not enabled if (err == error.AccessDenied) return error.SkipZigTest; return err; }; - try createDirAndSymlink(root.dir, "../../../file1", "d/e/f/symlink2"); + try createDirAndSymlink(io, root.dir, "../../../file1", "d/e/f/symlink2"); // Danglink symlnik, file created later - try createDirAndSymlink(root.dir, "../../../g/h/i/file4", "j/k/l/symlink3"); + try createDirAndSymlink(io, root.dir, "../../../g/h/i/file4", "j/k/l/symlink3"); file = try createDirAndFile(io, root.dir, "g/h/i/file4", default_mode); file.close(io); } diff --git a/lib/std/zip.zig b/lib/std/zip.zig index 9d08847092..acb3fc65ab 100644 --- a/lib/std/zip.zig +++ b/lib/std/zip.zig @@ -464,6 +464,8 @@ pub const Iterator = struct { filename_buf: []u8, dest: Io.Dir, ) !void { + const io = stream.io; + if (filename_buf.len < self.filename_len) return error.ZipInsufficientBuffer; switch (self.compression_method) { @@ -552,12 +554,10 @@ pub const Iterator = struct { if (filename[filename.len - 1] == '/') { if (self.uncompressed_size != 0) return error.ZipBadDirectorySize; - try dest.makePath(filename[0 .. filename.len - 1]); + try dest.makePath(io, filename[0 .. filename.len - 1]); return; } - const io = stream.io; - const out_file = blk: { if (std.fs.path.dirname(filename)) |dirname| { var parent_dir = try dest.makeOpenPath(dirname, .{}); diff --git a/src/Compilation.zig b/src/Compilation.zig index 3a48705880..280d34cdbf 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3180,7 +3180,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE const s = fs.path.sep_str; const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int); const o_sub_path = "o" ++ s ++ hex_digest; - renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| { + renameTmpIntoCache(io, comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| { return comp.setMiscFailure( .rename_results, "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}", @@ -3399,17 +3399,19 @@ fn flush( /// implementation at the bottom of this function. /// This function is only called when CacheMode is `whole`. fn renameTmpIntoCache( + io: Io, cache_directory: Cache.Directory, tmp_dir_sub_path: []const u8, o_sub_path: []const u8, ) !void { var seen_eaccess = false; while (true) { - fs.rename( + Io.Dir.rename( cache_directory.handle, tmp_dir_sub_path, cache_directory.handle, o_sub_path, + io, ) catch |err| switch (err) { // On Windows, rename fails with `AccessDenied` rather than `PathAlreadyExists`. // See https://github.com/ziglang/zig/issues/8362 @@ -3427,7 +3429,7 @@ fn renameTmpIntoCache( continue; }, error.FileNotFound => { - try cache_directory.handle.makePath("o"); + try cache_directory.handle.makePath(io, "o"); continue; }, else => |e| return e, @@ -5816,7 +5818,7 @@ pub fn translateC( const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest; if (comp.verbose_cimport) log.info("renaming {s} to {s}", .{ tmp_sub_path, o_sub_path }); - try renameTmpIntoCache(comp.dirs.local_cache, tmp_sub_path, o_sub_path); + try renameTmpIntoCache(io, comp.dirs.local_cache, tmp_sub_path, o_sub_path); return .{ .digest = bin_digest, diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index a651107bf5..e54dcb9914 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -1414,6 +1414,7 @@ fn unpackGitPack(f: *Fetch, out_dir: Io.Dir, resource: *Resource.Git) anyerror!U fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void { const gpa = f.arena.child_allocator; + const io = f.job_queue.io; // Recursive directory copy. var it = try dir.walk(gpa); defer it.deinit(); @@ -1428,7 +1429,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void .{}, ) catch |err| switch (err) { error.FileNotFound => { - if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(dirname); + if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(io, dirname); try dir.copyFile(entry.path, tmp_dir, entry.path, .{}); }, else => |e| return e, @@ -1441,7 +1442,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void // the destination directory, fail with an error instead. tmp_dir.symLink(link_name, entry.path, .{}) catch |err| switch (err) { error.FileNotFound => { - if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(dirname); + if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(io, dirname); try tmp_dir.symLink(link_name, entry.path, .{}); }, else => |e| return e, diff --git a/src/main.zig b/src/main.zig index fe48efa2fd..a063591b64 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3382,7 +3382,7 @@ fn buildOutputType( const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{ std.crypto.random.int(u64), ext.canonicalName(target), }); - try dirs.local_cache.handle.makePath("tmp"); + try dirs.local_cache.handle.makePath(io, "tmp"); // Note that in one of the happy paths, execve() is used to switch to // clang in which case any cleanup logic that exists for this temporary @@ -4773,7 +4773,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! var ok_count: usize = 0; for (template_paths) |template_path| { - if (templates.write(arena, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { + if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| { std.log.info("created {s}", .{template_path}); ok_count += 1; } else |err| switch (err) { @@ -7394,20 +7394,21 @@ const Templates = struct { fn write( templates: *Templates, arena: Allocator, + io: Io, out_dir: Io.Dir, root_name: []const u8, template_path: []const u8, fingerprint: Package.Fingerprint, ) !void { if (fs.path.dirname(template_path)) |dirname| { - out_dir.makePath(dirname) catch |err| { - fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) }); + out_dir.makePath(io, dirname) catch |err| { + fatal("unable to make path '{s}': {t}", .{ dirname, err }); }; } const max_bytes = 10 * 1024 * 1024; const contents = templates.dir.readFileAlloc(template_path, arena, .limited(max_bytes)) catch |err| { - fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) }); + fatal("unable to read template file '{s}': {t}", .{ template_path, err }); }; templates.buffer.clearRetainingCapacity(); try templates.buffer.ensureUnusedCapacity(contents.len); -- cgit v1.2.3 From 314c906dba32e72317947a15254519b22745b13f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 16:41:24 -0800 Subject: std.debug: simplify printLineFromFile --- lib/compiler/build_runner.zig | 2 +- lib/std/Build/Fuzz.zig | 2 +- lib/std/Io/test.zig | 12 +++---- lib/std/debug.zig | 74 +++++++++++++-------------------------- lib/std/debug/ElfFile.zig | 18 +++++----- lib/std/debug/MachOFile.zig | 2 +- lib/std/debug/SelfInfo/Elf.zig | 4 +-- lib/std/debug/SelfInfo/MachO.zig | 2 +- lib/std/os/uefi/protocol/file.zig | 9 ----- 9 files changed, 46 insertions(+), 79 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 6e0e2d8eca..36c73e96eb 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -824,7 +824,7 @@ fn runStepNames( } if (@bitSizeOf(usize) != 64) { // Current implementation depends on posix.mmap()'s second parameter, `length: usize`, - // being compatible with `std.fs.getEndPos() u64`'s return value. This is not the case + // being compatible with file system's u64 return value. This is not the case // on 32-bit platforms. // Affects or affected by issues #5185, #22523, and #22464. fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)}); diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index c95c9bd354..8d5bc27f3a 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -413,7 +413,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO }; defer coverage_file.close(io); - const file_size = coverage_file.getEndPos() catch |err| { + const file_size = coverage_file.length(io) catch |err| { log.err("unable to check len of coverage file '{f}': {t}", .{ coverage_file_path, err }); return error.AlreadyReported; }; diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 8b9d714ee8..e911031c7f 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -47,7 +47,7 @@ test "write a file, read it, then delete it" { var file = try tmp.dir.openFile(io, tmp_file_name, .{}); defer file.close(io); - const file_size = try file.getEndPos(); + const file_size = try file.length(io); const expected_file_size: u64 = "begin".len + data.len + "end".len; try expectEqual(expected_file_size, file_size); @@ -77,7 +77,7 @@ test "File seek ops" { // Seek to the end try file.seekFromEnd(0); - try expect((try file.getPos()) == try file.getEndPos()); + try expect((try file.getPos()) == try file.length(io)); // Negative delta try file.seekBy(-4096); try expect((try file.getPos()) == 4096); @@ -100,17 +100,17 @@ test "setEndPos" { defer file.close(io); // Verify that the file size changes and the file offset is not moved - try expect((try file.getEndPos()) == 0); + try expect((try file.length(io)) == 0); try expect((try file.getPos()) == 0); try file.setEndPos(8192); - try expect((try file.getEndPos()) == 8192); + try expect((try file.length(io)) == 8192); try expect((try file.getPos()) == 0); try file.seekTo(100); try file.setEndPos(4096); - try expect((try file.getEndPos()) == 4096); + try expect((try file.length(io)) == 4096); try expect((try file.getPos()) == 100); try file.setEndPos(0); - try expect((try file.getEndPos()) == 0); + try expect((try file.length(io)) == 0); try expect((try file.getPos()) == 100); } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 7ede172d86..34f740bfee 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -558,9 +558,9 @@ pub fn defaultPanic( stderr.print("{s}\n", .{msg}) catch break :trace; if (@errorReturnTrace()) |t| if (t.index > 0) { - stderr.writeStreamingAll("error return context:\n") catch break :trace; + stderr.writeAll("error return context:\n") catch break :trace; writeStackTrace(t, stderr, tty_config) catch break :trace; - stderr.writeStreamingAll("\nstack trace:\n") catch break :trace; + stderr.writeAll("\nstack trace:\n") catch break :trace; }; writeCurrentStackTrace(.{ .first_address = first_trace_addr orelse @returnAddress(), @@ -617,6 +617,8 @@ pub const StackUnwindOptions = struct { /// /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace { + var threaded: Io.Threaded = .init_single_threaded; + const io = threaded.ioBasic(); const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} }; if (!std.options.allow_stack_tracing) return empty_trace; var it: StackIterator = .init(options.context); @@ -628,7 +630,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: // Ideally, we would iterate the whole stack so that the `index` in the returned trace was // indicative of how many frames were skipped. However, this has a significant runtime cost // in some cases, so at least for now, we don't do that. - while (index < addr_buf.len) switch (it.next()) { + while (index < addr_buf.len) switch (it.next(io)) { .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break, .end => break, .frame => |ret_addr| { @@ -684,7 +686,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri var total_frames: usize = 0; var wait_for = options.first_address; var printed_any_frame = false; - while (true) switch (it.next()) { + while (true) switch (it.next(io)) { .switch_to_fp => |unwind_error| { switch (StackIterator.fp_usability) { .useless, .unsafe => {}, @@ -1196,54 +1198,26 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) ! // Need this to always block even in async I/O mode, because this could potentially // be called from e.g. the event loop code crashing. const cwd: Io.Dir = .cwd(); - var f = try cwd.openFile(io, source_location.file_name, .{}); - defer f.close(io); + var file = try cwd.openFile(io, source_location.file_name, .{}); + defer file.close(io); // TODO fstat and make sure that the file has the correct size - var buf: [4096]u8 = undefined; - var amt_read = try f.read(buf[0..]); - const line_start = seek: { - var current_line_start: usize = 0; - var next_line: usize = 1; - while (next_line != source_location.line) { - const slice = buf[current_line_start..amt_read]; - if (mem.findScalar(u8, slice, '\n')) |pos| { - next_line += 1; - if (pos == slice.len - 1) { - amt_read = try f.read(buf[0..]); - current_line_start = 0; - } else current_line_start += pos + 1; - } else if (amt_read < buf.len) { - return error.EndOfFile; - } else { - amt_read = try f.read(buf[0..]); - current_line_start = 0; - } - } - break :seek current_line_start; - }; - const slice = buf[line_start..amt_read]; - if (mem.findScalar(u8, slice, '\n')) |pos| { - const line = slice[0 .. pos + 1]; - mem.replaceScalar(u8, line, '\t', ' '); - return writer.writeAll(line); - } else { // Line is the last inside the buffer, and requires another read to find delimiter. Alternatively the file ends. - mem.replaceScalar(u8, slice, '\t', ' '); - try writer.writeAll(slice); - while (amt_read == buf.len) { - amt_read = try f.read(buf[0..]); - if (mem.findScalar(u8, buf[0..amt_read], '\n')) |pos| { - const line = buf[0 .. pos + 1]; - mem.replaceScalar(u8, line, '\t', ' '); - return writer.writeAll(line); - } else { - const line = buf[0..amt_read]; - mem.replaceScalar(u8, line, '\t', ' '); - try writer.writeAll(line); - } + var buffer: [4096]u8 = undefined; + var file_reader: File.Reader = .init(file, io, &buffer); + const r = &file_reader.interface; + var line_index: usize = 0; + while (r.takeDelimiterExclusive('\n')) |line| { + line_index += 1; + if (line_index == source_location.line) { + // TODO delete hard tabs from the language + mem.replaceScalar(u8, line, '\t', ' '); + try writer.writeAll(line); + // Make sure printing last line of file inserts extra newline. + try writer.writeByte('\n'); + return; } - // Make sure printing last line of file inserts extra newline - try writer.writeByte('\n'); + } else |err| { + return err; } } @@ -1598,7 +1572,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex // We're still holding the mutex but that's fine as we're going to // call abort(). const stderr, _ = lockStderrWriter(&.{}); - stderr().writeAll("aborting due to recursive panic\n") catch {}; + stderr.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } diff --git a/lib/std/debug/ElfFile.zig b/lib/std/debug/ElfFile.zig index 203ee8effb..a101309d22 100644 --- a/lib/std/debug/ElfFile.zig +++ b/lib/std/debug/ElfFile.zig @@ -123,6 +123,7 @@ pub const LoadError = error{ pub fn load( gpa: Allocator, + io: Io, elf_file: Io.File, opt_build_id: ?[]const u8, di_search_paths: *const DebugInfoSearchPaths, @@ -131,7 +132,7 @@ pub fn load( errdefer arena_instance.deinit(); const arena = arena_instance.allocator(); - var result = loadInner(arena, elf_file, null) catch |err| switch (err) { + var result = loadInner(arena, io, elf_file, null) catch |err| switch (err) { error.CrcMismatch => unreachable, // we passed crc as null else => |e| return e, }; @@ -156,7 +157,7 @@ pub fn load( if (build_id.len < 3) break :build_id; for (di_search_paths.global_debug) |global_debug| { - if (try loadSeparateDebugFile(arena, &result, null, "{s}/.build-id/{x}/{x}.debug", .{ + if (try loadSeparateDebugFile(arena, io, &result, null, "{s}/.build-id/{x}/{x}.debug", .{ global_debug, build_id[0..1], build_id[1..], @@ -164,7 +165,7 @@ pub fn load( } if (di_search_paths.debuginfod_client) |components| { - if (try loadSeparateDebugFile(arena, &result, null, "{s}{s}/{x}/debuginfo", .{ + if (try loadSeparateDebugFile(arena, io, &result, null, "{s}{s}/{x}/debuginfo", .{ components[0], components[1], build_id, @@ -181,18 +182,18 @@ pub fn load( const exe_dir = di_search_paths.exe_dir orelse break :debug_link; - if (try loadSeparateDebugFile(arena, &result, debug_crc, "{s}/{s}", .{ + if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/{s}", .{ exe_dir, debug_filename, })) |mapped| break :load_di mapped; - if (try loadSeparateDebugFile(arena, &result, debug_crc, "{s}/.debug/{s}", .{ + if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/.debug/{s}", .{ exe_dir, debug_filename, })) |mapped| break :load_di mapped; for (di_search_paths.global_debug) |global_debug| { // This looks like a bug; it isn't. They really do embed the absolute path to the // exe's dirname, *under* the global debug path. - if (try loadSeparateDebugFile(arena, &result, debug_crc, "{s}/{s}/{s}", .{ + if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/{s}/{s}", .{ global_debug, exe_dir, debug_filename, @@ -378,7 +379,7 @@ fn loadSeparateDebugFile( const elf_file = Io.Dir.cwd().openFile(io, path, .{}) catch return null; defer elf_file.close(io); - const result = loadInner(arena, elf_file, opt_crc) catch |err| switch (err) { + const result = loadInner(arena, io, elf_file, opt_crc) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.CrcMismatch => return null, else => return null, @@ -423,13 +424,14 @@ const LoadInnerResult = struct { }; fn loadInner( arena: Allocator, + io: Io, elf_file: Io.File, opt_crc: ?u32, ) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult { const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: { const file_len = std.math.cast( usize, - elf_file.getEndPos() catch |err| switch (err) { + elf_file.length(io) catch |err| switch (err) { error.PermissionDenied => unreachable, // not asking for PROT_EXEC else => |e| return e, }, diff --git a/lib/std/debug/MachOFile.zig b/lib/std/debug/MachOFile.zig index 18126a1c29..9e81fb8911 100644 --- a/lib/std/debug/MachOFile.zig +++ b/lib/std/debug/MachOFile.zig @@ -520,7 +520,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c const file_len = std.math.cast( usize, - file.getEndPos() catch return error.ReadFailed, + file.length(io) catch return error.ReadFailed, ) orelse return error.ReadFailed; return posix.mmap( diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 6ed18bcb80..be76f3a8c2 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -326,7 +326,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, file, mod.build_id, &.native(mod.name)); + 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, @@ -335,7 +335,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, file, mod.build_id, &.native(path)); + 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/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index db8e5334e6..714ff539f1 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -622,7 +622,7 @@ fn mapDebugInfoFile(io: Io, path: []const u8) ![]align(std.heap.page_size_min) c }; defer file.close(io); - const file_end_pos = file.getEndPos() catch |err| switch (err) { + const file_end_pos = file.length(io) catch |err| switch (err) { error.Unexpected => |e| return e, else => return error.ReadFailed, }; diff --git a/lib/std/os/uefi/protocol/file.zig b/lib/std/os/uefi/protocol/file.zig index 9e3e7cc081..9b371916a7 100644 --- a/lib/std/os/uefi/protocol/file.zig +++ b/lib/std/os/uefi/protocol/file.zig @@ -163,15 +163,6 @@ pub const File = extern struct { } } - fn getEndPos(self: *File) SeekError!u64 { - const start_pos = try self.getPosition(); - // ignore error - defer self.setPosition(start_pos) catch {}; - - try self.setPosition(end_of_file); - return self.getPosition(); - } - pub fn setPosition(self: *File, position: u64) SeekError!void { switch (self._set_position(self, position)) { .success => {}, -- cgit v1.2.3 From 950d18ef695bb7a28397e080dc3c201559ec4ee2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 17:14:31 -0800 Subject: update all access() to access(io) --- lib/compiler/aro/aro/Driver.zig | 6 +-- lib/compiler/aro/aro/Driver/Filesystem.zig | 8 ++-- lib/compiler/aro/aro/Toolchain.zig | 13 ++++--- lib/compiler/aro/backend/Assembly.zig | 4 +- lib/compiler/resinator/cli.zig | 10 ++--- lib/compiler/resinator/main.zig | 6 +-- lib/compiler/std-docs.zig | 8 ++-- lib/compiler/translate-c/main.zig | 4 +- lib/std/Build.zig | 2 +- lib/std/Build/Cache/Path.zig | 4 +- lib/std/Build/Step.zig | 12 +++--- lib/std/Build/Step/InstallDir.zig | 2 +- lib/std/Build/Step/Run.zig | 47 ++++++++++++----------- lib/std/Build/Step/WriteFile.zig | 9 ++--- lib/std/Io.zig | 4 +- lib/std/Io/Dir.zig | 16 ++++---- lib/std/Io/Threaded.zig | 13 +++++-- lib/std/Io/test.zig | 2 +- lib/std/debug.zig | 8 ++-- lib/std/fs/test.zig | 24 ++++++------ lib/std/posix.zig | 61 ------------------------------ lib/std/posix/test.zig | 12 +++--- lib/std/zig/LibCInstallation.zig | 10 ++--- lib/std/zig/llvm/Builder.zig | 4 +- lib/std/zig/system.zig | 3 +- src/Compilation.zig | 2 +- src/Package/Fetch.zig | 5 ++- src/introspect.zig | 4 +- src/libs/mingw.zig | 12 +++--- src/link/C.zig | 3 +- src/link/Lld.zig | 9 +++-- src/link/MachO.zig | 21 +++++----- src/main.zig | 8 ++-- 33 files changed, 159 insertions(+), 197 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/aro/aro/Driver.zig b/lib/compiler/aro/aro/Driver.zig index fec3cea0f8..b344cc8a9d 100644 --- a/lib/compiler/aro/aro/Driver.zig +++ b/lib/compiler/aro/aro/Driver.zig @@ -1333,7 +1333,7 @@ fn processSource( Io.File.stdout(); defer if (dep_file_name != null) file.close(io); - var file_writer = file.writer(&writer_buf); + var file_writer = file.writer(io, &writer_buf); dep_file.write(&file_writer.interface) catch return d.fatal("unable to write dependency file: {s}", .{errorDescription(file_writer.err.?)}); } @@ -1358,7 +1358,7 @@ fn processSource( Io.File.stdout(); defer if (d.output_name != null) file.close(io); - var file_writer = file.writer(&writer_buf); + var file_writer = file.writer(io, &writer_buf); pp.prettyPrintTokens(&file_writer.interface, dump_mode) catch return d.fatal("unable to write result: {s}", .{errorDescription(file_writer.err.?)}); @@ -1459,7 +1459,7 @@ fn processSource( return d.fatal("unable to create output file '{s}': {s}", .{ out_file_name, errorDescription(er) }); defer out_file.close(io); - var file_writer = out_file.writer(&writer_buf); + var file_writer = out_file.writer(io, &writer_buf); obj.finish(&file_writer.interface) catch return d.fatal("could not output to object file '{s}': {s}", .{ out_file_name, errorDescription(file_writer.err.?) }); } diff --git a/lib/compiler/aro/aro/Driver/Filesystem.zig b/lib/compiler/aro/aro/Driver/Filesystem.zig index 001f10f27c..c229dfd831 100644 --- a/lib/compiler/aro/aro/Driver/Filesystem.zig +++ b/lib/compiler/aro/aro/Driver/Filesystem.zig @@ -57,8 +57,8 @@ fn existsFake(entries: []const Filesystem.Entry, path: []const u8) bool { return false; } -fn canExecutePosix(path: []const u8) bool { - std.posix.access(path, std.posix.X_OK) catch return false; +fn canExecutePosix(io: Io, path: []const u8) bool { + Io.Dir.accessAbsolute(io, path, .{ .execute = true }) catch return false; // Todo: ensure path is not a directory return true; } @@ -172,10 +172,10 @@ pub const Filesystem = union(enum) { } }; - pub fn exists(fs: Filesystem, path: []const u8) bool { + pub fn exists(fs: Filesystem, io: Io, path: []const u8) bool { switch (fs) { .real => |cwd| { - cwd.access(path, .{}) catch return false; + cwd.access(io, path, .{}) catch return false; return true; }, .fake => |paths| return existsFake(paths, path), diff --git a/lib/compiler/aro/aro/Toolchain.zig b/lib/compiler/aro/aro/Toolchain.zig index 0328c264d0..0aa9d76fc8 100644 --- a/lib/compiler/aro/aro/Toolchain.zig +++ b/lib/compiler/aro/aro/Toolchain.zig @@ -501,7 +501,7 @@ pub fn addBuiltinIncludeDir(tc: *const Toolchain) !void { try d.includes.ensureUnusedCapacity(gpa, 1); if (d.resource_dir) |resource_dir| { const path = try std.fs.path.join(arena, &.{ resource_dir, "include" }); - comp.cwd.access(path, .{}) catch { + comp.cwd.access(io, path, .{}) catch { return d.fatal("Aro builtin headers not found in provided -resource-dir", .{}); }; d.includes.appendAssumeCapacity(.{ .kind = .system, .path = path }); @@ -512,7 +512,7 @@ pub fn addBuiltinIncludeDir(tc: *const Toolchain) !void { var base_dir = d.comp.cwd.openDir(io, dirname, .{}) catch continue; defer base_dir.close(io); - base_dir.access("include/stddef.h", .{}) catch continue; + base_dir.access(io, "include/stddef.h", .{}) catch continue; const path = try std.fs.path.join(arena, &.{ dirname, "include" }); d.includes.appendAssumeCapacity(.{ .kind = .system, .path = path }); break; @@ -524,12 +524,14 @@ pub fn addBuiltinIncludeDir(tc: *const Toolchain) !void { /// Otherwise returns a slice of `buf`. If the file is larger than `buf` partial contents are returned pub fn readFile(tc: *const Toolchain, path: []const u8, buf: []u8) ?[]const u8 { const comp = tc.driver.comp; - return comp.cwd.readFile(comp.io, path, buf) catch null; + const io = comp.io; + return comp.cwd.readFile(io, path, buf) catch null; } pub fn exists(tc: *const Toolchain, path: []const u8) bool { const comp = tc.driver.comp; - comp.cwd.access(comp.io, path, .{}) catch return false; + const io = comp.io; + comp.cwd.access(io, path, .{}) catch return false; return true; } @@ -547,7 +549,8 @@ pub fn canExecute(tc: *const Toolchain, path: []const u8) bool { } const comp = tc.driver.comp; - comp.cwd.access(comp.io, path, .{ .execute = true }) catch return false; + const io = comp.io; + comp.cwd.access(io, path, .{ .execute = true }) catch return false; // Todo: ensure path is not a directory return true; } diff --git a/lib/compiler/aro/backend/Assembly.zig b/lib/compiler/aro/backend/Assembly.zig index 80143bf97f..4ec4860b51 100644 --- a/lib/compiler/aro/backend/Assembly.zig +++ b/lib/compiler/aro/backend/Assembly.zig @@ -12,8 +12,8 @@ pub fn deinit(self: *const Assembly, gpa: Allocator) void { gpa.free(self.text); } -pub fn writeToFile(self: Assembly, file: Io.File) !void { - var file_writer = file.writer(&.{}); +pub fn writeToFile(self: Assembly, io: Io, file: Io.File) !void { + var file_writer = file.writer(io, &.{}); var buffers = [_][]const u8{ self.data, self.text }; try file_writer.interface.writeSplatAll(&buffers, 1); diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index ae4ece2968..5588390197 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -250,13 +250,13 @@ pub const Options = struct { /// worlds' situation where we'll be compatible with most use-cases /// of the .rc extension being omitted from the CLI args, but still /// work fine if the file itself does not have an extension. - pub fn maybeAppendRC(options: *Options, cwd: Io.Dir) !void { + pub fn maybeAppendRC(options: *Options, io: Io, cwd: Io.Dir) !void { switch (options.input_source) { .stdio => return, .filename => {}, } if (options.input_format == .rc and std.fs.path.extension(options.input_source.filename).len == 0) { - cwd.access(options.input_source.filename, .{}) catch |err| switch (err) { + cwd.access(io, options.input_source.filename, .{}) catch |err| switch (err) { error.FileNotFound => { var filename_bytes = try options.allocator.alloc(u8, options.input_source.filename.len + 3); @memcpy(filename_bytes[0..options.input_source.filename.len], options.input_source.filename); @@ -2005,19 +2005,19 @@ test "maybeAppendRC" { // appended. var file = try tmp.dir.createFile(io, "foo", .{}); file.close(io); - try options.maybeAppendRC(tmp.dir); + try options.maybeAppendRC(io, tmp.dir); try std.testing.expectEqualStrings("foo", options.input_source.filename); // Now delete the file and try again. But this time change the input format // to non-rc. try tmp.dir.deleteFile("foo"); options.input_format = .res; - try options.maybeAppendRC(tmp.dir); + try options.maybeAppendRC(io, tmp.dir); try std.testing.expectEqualStrings("foo", options.input_source.filename); // Finally, reset the input format to rc. Since the verbatim name is no longer found // and the input filename does not have an extension, .rc should get appended. options.input_format = .rc; - try options.maybeAppendRC(tmp.dir); + try options.maybeAppendRC(io, tmp.dir); try std.testing.expectEqualStrings("foo.rc", options.input_source.filename); } diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 416abc2ab7..1a6ef5eea3 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -318,7 +318,7 @@ pub fn main() !void { defer depfile.close(io); var depfile_buffer: [1024]u8 = undefined; - var depfile_writer = depfile.writer(&depfile_buffer); + var depfile_writer = depfile.writer(io, &depfile_buffer); switch (options.depfile_fmt) { .json => { var write_stream: std.json.Stringify = .{ @@ -521,9 +521,9 @@ const IoStream = struct { } }; - pub fn writer(source: *Source, allocator: Allocator, buffer: []u8) Writer { + pub fn writer(source: *Source, allocator: Allocator, io: Io, buffer: []u8) Writer { return switch (source.*) { - .file, .stdio => |file| .{ .file = file.writer(buffer) }, + .file, .stdio => |file| .{ .file = file.writer(io, buffer) }, .memory => |*list| .{ .allocating = .fromArrayList(allocator, list) }, .closed => unreachable, }; diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index d5beab5f17..f1382e6eae 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -334,8 +334,8 @@ fn buildWasmBinary( }); defer poller.deinit(); - try sendMessage(child.stdin.?, .update); - try sendMessage(child.stdin.?, .exit); + try sendMessage(io, child.stdin.?, .update); + try sendMessage(io, child.stdin.?, .exit); var result: ?Cache.Path = null; var result_error_bundle = std.zig.ErrorBundle.empty; @@ -421,12 +421,12 @@ fn buildWasmBinary( }; } -fn sendMessage(file: std.Io.File, tag: std.zig.Client.Message.Tag) !void { +fn sendMessage(io: Io, file: std.Io.File, tag: std.zig.Client.Message.Tag) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 0, }; - var w = file.writer(&.{}); + var w = file.writer(io, &.{}); w.interface.writeStruct(header, .little) catch |err| switch (err) { error.WriteFailed => return w.err.?, }; diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig index d140145032..d02f21a2a8 100644 --- a/lib/compiler/translate-c/main.zig +++ b/lib/compiler/translate-c/main.zig @@ -232,7 +232,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration Io.File.stdout(); defer if (dep_file_name != null) file.close(io); - var file_writer = file.writer(&out_buf); + var file_writer = file.writer(io, &out_buf); dep_file.write(&file_writer.interface) catch return d.fatal("unable to write dependency file: {s}", .{aro.Driver.errorDescription(file_writer.err.?)}); } @@ -263,7 +263,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration out_file_path = path; } - var out_writer = out_file.writer(&out_buf); + var out_writer = out_file.writer(io, &out_buf); out_writer.interface.writeAll(rendered_zig) catch {}; out_writer.interface.flush() catch {}; if (out_writer.err) |write_err| diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 1eff8813e5..746b41860b 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1699,7 +1699,7 @@ pub fn addCheckFile( return Step.CheckFile.create(b, file_source, options); } -pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.MakeError || Io.Dir.StatFileError)!void { +pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.MakeError || Io.Dir.StatPathError)!void { const io = b.graph.io; if (b.verbose) log.info("truncate {s}", .{dest_path}); const cwd = Io.Dir.cwd(); diff --git a/lib/std/Build/Cache/Path.zig b/lib/std/Build/Cache/Path.zig index 941948a9cd..759eb143b8 100644 --- a/lib/std/Build/Cache/Path.zig +++ b/lib/std/Build/Cache/Path.zig @@ -118,14 +118,14 @@ pub fn atomicFile( return p.root_dir.handle.atomicFile(joined_path, options); } -pub fn access(p: Path, sub_path: []const u8, flags: Io.Dir.AccessOptions) !void { +pub fn access(p: Path, io: Io, sub_path: []const u8, flags: Io.Dir.AccessOptions) !void { var buf: [fs.max_path_bytes]u8 = undefined; const joined_path = if (p.sub_path.len == 0) sub_path else p: { break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{ p.sub_path, sub_path, }) catch return error.NameTooLong; }; - return p.root_dir.handle.access(joined_path, flags); + return p.root_dir.handle.access(io, joined_path, flags); } pub fn makePath(p: Path, io: Io, sub_path: []const u8) !void { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 2ec1c0ef31..f66f8df4c8 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -519,19 +519,21 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u /// Wrapper around `Io.Dir.makePathStatus` that handles verbose and error output. pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.MakePathStatus { const b = s.owner; + const io = b.graph.io; try handleVerbose(b, null, &.{ "install", "-d", dest_path }); - return Io.Dir.cwd().makePathStatus(dest_path) catch |err| + return Io.Dir.cwd().makePathStatus(io, dest_path, .default_dir) catch |err| return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err }); } fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.WebServer, gpa: Allocator) !?Path { const b = s.owner; const arena = b.allocator; + const io = b.graph.io; var timer = try std.time.Timer.start(); - try sendMessage(zp.child.stdin.?, .update); - if (!watch) try sendMessage(zp.child.stdin.?, .exit); + try sendMessage(io, zp.child.stdin.?, .update); + if (!watch) try sendMessage(io, zp.child.stdin.?, .exit); var result: ?Path = null; @@ -668,12 +670,12 @@ fn clearZigProcess(s: *Step, gpa: Allocator) void { } } -fn sendMessage(file: Io.File, tag: std.zig.Client.Message.Tag) !void { +fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 0, }; - var w = file.writer(&.{}); + var w = file.writer(io, &.{}); w.interface.writeStruct(header, .little) catch |err| switch (err) { error.WriteFailed => return w.err.?, }; diff --git a/lib/std/Build/Step/InstallDir.zig b/lib/std/Build/Step/InstallDir.zig index 788d5565a7..d03e72ca75 100644 --- a/lib/std/Build/Step/InstallDir.zig +++ b/lib/std/Build/Step/InstallDir.zig @@ -71,7 +71,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { defer src_dir.close(io); var it = try src_dir.walk(arena); var all_cached = true; - next_entry: while (try it.next()) |entry| { + next_entry: while (try it.next(io)) |entry| { for (install_dir.options.exclude_extensions) |ext| { if (mem.endsWith(u8, entry.path, ext)) continue :next_entry; } diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 54e77bd614..f6b29635c1 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1310,7 +1310,7 @@ fn runCommand( const need_cross_libc = exe.is_linking_libc and (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic)); const other_target = exe.root_module.resolved_target.?.result; - switch (std.zig.system.getExternalExecutor(&b.graph.host.result, &other_target, .{ + switch (std.zig.system.getExternalExecutor(io, &b.graph.host.result, &other_target, .{ .qemu_fixes_dl = need_cross_libc and b.libc_runtimes_dir != null, .link_libc = exe.is_linking_libc, })) { @@ -1702,7 +1702,7 @@ fn evalZigTest( }); var child_killed = false; defer if (!child_killed) { - _ = child.kill() catch {}; + _ = child.kill(io) catch {}; poller.deinit(); run.step.result_peak_rss = @max( run.step.result_peak_rss, @@ -1732,7 +1732,7 @@ fn evalZigTest( child.stdin = null; poller.deinit(); child_killed = true; - const term = try child.wait(); + const term = try child.wait(io); run.step.result_peak_rss = @max( run.step.result_peak_rss, child.resource_usage_statistics.getMaxRss() orelse 0, @@ -1752,7 +1752,7 @@ fn evalZigTest( child.stdin = null; poller.deinit(); child_killed = true; - const term = try child.wait(); + const term = try child.wait(io); run.step.result_peak_rss = @max( run.step.result_peak_rss, child.resource_usage_statistics.getMaxRss() orelse 0, @@ -1840,6 +1840,7 @@ fn pollZigTest( switch (ctx.fuzz.mode) { .forever => { sendRunFuzzTestMessage( + io, child.stdin.?, ctx.unit_test_index, .forever, @@ -1848,6 +1849,7 @@ fn pollZigTest( }, .limit => |limit| { sendRunFuzzTestMessage( + io, child.stdin.?, ctx.unit_test_index, .iterations, @@ -1857,11 +1859,11 @@ fn pollZigTest( } } else if (opt_metadata.*) |*md| { // Previous unit test process died or was killed; we're continuing where it left off - requestNextTest(child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; + requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; } else { // Running unit tests normally run.fuzz_tests.clearRetainingCapacity(); - sendMessage(child.stdin.?, .query_test_metadata) catch |err| return .{ .write_failed = err }; + sendMessage(io, child.stdin.?, .query_test_metadata) catch |err| return .{ .write_failed = err }; } var active_test_index: ?u32 = null; @@ -1977,7 +1979,7 @@ fn pollZigTest( active_test_index = null; if (timer) |*t| t.reset(); - requestNextTest(child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; + requestNextTest(io, child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; }, .test_started => { active_test_index = opt_metadata.*.?.next_index - 1; @@ -2026,7 +2028,7 @@ fn pollZigTest( active_test_index = null; if (timer) |*t| md.ns_per_test[tr_hdr.index] = t.lap(); - requestNextTest(child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; + requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; }, .coverage_id => { coverage_id = body_r.takeInt(u64, .little) catch unreachable; @@ -2097,7 +2099,7 @@ pub const CachedTestMetadata = struct { } }; -fn requestNextTest(in: Io.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { +fn requestNextTest(io: Io, in: Io.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { while (metadata.next_index < metadata.names.len) { const i = metadata.next_index; metadata.next_index += 1; @@ -2108,31 +2110,31 @@ fn requestNextTest(in: Io.File, metadata: *TestMetadata, sub_prog_node: *?std.Pr if (sub_prog_node.*) |n| n.end(); sub_prog_node.* = metadata.prog_node.start(name, 0); - try sendRunTestMessage(in, .run_test, i); + try sendRunTestMessage(io, in, .run_test, i); return; } else { metadata.next_index = std.math.maxInt(u32); // indicate that all tests are done - try sendMessage(in, .exit); + try sendMessage(io, in, .exit); } } -fn sendMessage(file: Io.File, tag: std.zig.Client.Message.Tag) !void { +fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 0, }; - var w = file.writer(&.{}); + var w = file.writer(io, &.{}); w.interface.writeStruct(header, .little) catch |err| switch (err) { error.WriteFailed => return w.err.?, }; } -fn sendRunTestMessage(file: Io.File, tag: std.zig.Client.Message.Tag, index: u32) !void { +fn sendRunTestMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag, index: u32) !void { const header: std.zig.Client.Message.Header = .{ .tag = tag, .bytes_len = 4, }; - var w = file.writer(&.{}); + var w = file.writer(io, &.{}); w.interface.writeStruct(header, .little) catch |err| switch (err) { error.WriteFailed => return w.err.?, }; @@ -2142,6 +2144,7 @@ fn sendRunTestMessage(file: Io.File, tag: std.zig.Client.Message.Tag, index: u32 } fn sendRunFuzzTestMessage( + io: Io, file: Io.File, index: u32, kind: std.Build.abi.fuzz.LimitKind, @@ -2151,7 +2154,7 @@ fn sendRunFuzzTestMessage( .tag = .start_fuzzing, .bytes_len = 4 + 1 + 8, }; - var w = file.writer(&.{}); + var w = file.writer(io, &.{}); w.interface.writeStruct(header, .little) catch |err| switch (err) { error.WriteFailed => return w.err.?, }; @@ -2172,14 +2175,14 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { const arena = b.allocator; try child.spawn(); - errdefer _ = child.kill() catch {}; + errdefer _ = child.kill(io) catch {}; try child.waitForSpawn(); switch (run.stdin) { .bytes => |bytes| { - child.stdin.?.writeAll(bytes) catch |err| { - return run.step.fail("unable to write stdin: {s}", .{@errorName(err)}); + child.stdin.?.writeStreamingAll(io, bytes) catch |err| { + return run.step.fail("unable to write stdin: {t}", .{err}); }; child.stdin.?.close(io); child.stdin = null; @@ -2187,14 +2190,14 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { .lazy_path => |lazy_path| { const path = lazy_path.getPath3(b, &run.step); const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| { - return run.step.fail("unable to open stdin file: {s}", .{@errorName(err)}); + return run.step.fail("unable to open stdin file: {t}", .{err}); }; defer file.close(io); // TODO https://github.com/ziglang/zig/issues/23955 var read_buffer: [1024]u8 = undefined; var file_reader = file.reader(io, &read_buffer); var write_buffer: [1024]u8 = undefined; - var stdin_writer = child.stdin.?.writer(&write_buffer); + var stdin_writer = child.stdin.?.writer(io, &write_buffer); _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) { error.ReadFailed => return run.step.fail("failed to read from {f}: {t}", .{ path, file_reader.err.?, @@ -2267,7 +2270,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { run.step.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0; return .{ - .term = try child.wait(), + .term = try child.wait(io), .stdout = stdout_bytes, .stderr = stderr_bytes, }; diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 21346959e7..94b04b4212 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -228,7 +228,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { var it = try src_dir.walk(gpa); defer it.deinit(); - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (!dir.options.pathIncluded(entry.path)) continue; switch (entry.kind) { @@ -259,11 +259,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest }); - var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { - return step.fail("unable to make path '{f}{s}': {s}", .{ - b.cache_root, cache_path, @errorName(err), - }); - }; + var cache_dir = b.cache_root.handle.makeOpenPath(io, cache_path, .{}) catch |err| + return step.fail("unable to make path '{f}{s}': {t}", .{ b.cache_root, cache_path, err }); defer cache_dir.close(io); for (write_file.files.items) |file| { diff --git a/lib/std/Io.zig b/lib/std/Io.zig index ad0764ebe1..7f5f049d34 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -664,13 +664,13 @@ pub const VTable = struct { dirMake: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions) Dir.MakeError!void, dirMakePath: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions) Dir.MakePathError!Dir.MakePathStatus, - dirMakeOpenPath: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.MakeOpenPathError!Dir, + dirMakeOpenPath: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions, Dir.OpenOptions) Dir.MakeOpenPathError!Dir, + dirOpenDir: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.OpenError!Dir, dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat, dirStatPath: *const fn (?*anyopaque, Dir, []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat, dirAccess: *const fn (?*anyopaque, Dir, []const u8, Dir.AccessOptions) Dir.AccessError!void, dirCreateFile: *const fn (?*anyopaque, Dir, []const u8, File.CreateFlags) File.OpenError!File, dirOpenFile: *const fn (?*anyopaque, Dir, []const u8, File.OpenFlags) File.OpenError!File, - dirOpenDir: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenOptions) Dir.OpenError!Dir, dirClose: *const fn (?*anyopaque, []const Dir) void, dirRead: *const fn (?*anyopaque, *Dir.Reader, []Dir.Entry) Dir.Reader.Error!usize, dirRealPath: *const fn (?*anyopaque, Dir, path_name: []const u8, out_buffer: []u8) Dir.RealPathError!usize, diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 58f81cbb90..755ce924ad 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -191,7 +191,7 @@ pub const SelectiveWalker = struct { while (self.stack.items.len > 0) { const top = &self.stack.items[self.stack.items.len - 1]; var dirname_len = top.dirname_len; - if (top.iter.next() catch |err| { + if (top.iter.next(io) catch |err| { // If we get an error, then we want the user to be able to continue // walking if they want, which means that we need to pop the directory // that errored from the stack. Otherwise, all future `next` calls would @@ -302,7 +302,7 @@ pub const Walker = struct { dir: Dir, basename: [:0]const u8, path: [:0]const u8, - kind: Dir.Entry.Kind, + kind: File.Kind, /// Returns the depth of the entry relative to the initial directory. /// Returns 1 for a direct child of the initial directory, 2 for an entry @@ -320,10 +320,10 @@ pub const Walker = struct { /// After each call to this function, and on deinit(), the memory returned /// from this function becomes invalid. A copy must be made in order to keep /// a reference to the path. - pub fn next(self: *Walker) !?Walker.Entry { - const entry = try self.inner.next(); + pub fn next(self: *Walker, io: Io) !?Walker.Entry { + const entry = try self.inner.next(io); if (entry != null and entry.?.kind == .directory) { - try self.inner.enter(entry.?); + try self.inner.enter(io, entry.?); } return entry; } @@ -495,7 +495,7 @@ pub const WriteFileOptions = struct { flags: File.CreateFlags = .{}, }; -pub const WriteFileError = File.WriteError || File.OpenError; +pub const WriteFileError = File.Writer.Error || File.OpenError; /// Writes content to the file system, using the file creation flags provided. pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void { @@ -556,11 +556,11 @@ pub fn updateFile( } if (path.dirname(dest_path)) |dirname| { - try dest_dir.makePath(io, dirname, .default_dir); + try dest_dir.makePath(io, dirname); } var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available. - var atomic_file = try Dir.atomicFile(dest_dir, dest_path, .{ + var atomic_file = try dest_dir.atomicFile(io, dest_path, .{ .permissions = actual_permissions, .write_buffer = &buffer, }); diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 4ee1d5db53..16e8930267 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -12,7 +12,7 @@ const std = @import("../std.zig"); const Io = std.Io; const net = std.Io.net; const File = std.Io.File; -const Dir = std.Dir; +const Dir = std.Io.Dir; const HostName = std.Io.net.HostName; const IpAddress = std.Io.net.IpAddress; const Allocator = std.mem.Allocator; @@ -1614,13 +1614,14 @@ fn dirMakeOpenPathPosix( userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, + permissions: Dir.Permissions, options: Dir.OpenOptions, ) Dir.MakeOpenPathError!Dir { const t: *Threaded = @ptrCast(@alignCast(userdata)); const t_io = ioBasic(t); - return dirOpenDirPosix(t, dir, sub_path, options) catch |err| switch (err) { + return dirOpenDirPosix(t, dir, sub_path, permissions, options) catch |err| switch (err) { error.FileNotFound => { - try dir.makePath(t_io, sub_path); + _ = try dir.makePathStatus(t_io, sub_path, permissions); return dirOpenDirPosix(t, dir, sub_path, options); }, else => |e| return e, @@ -1631,12 +1632,15 @@ fn dirMakeOpenPathWindows( userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, + permissions: Dir.Permissions, options: Dir.OpenOptions, ) Dir.MakeOpenPathError!Dir { const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); const w = windows; + _ = permissions; // TODO apply these permissions + var it = std.fs.path.componentIterator(sub_path); // If there are no components in the path, then create a dummy component with the full path. var component: std.fs.path.NativeComponentIterator.Component = it.last() orelse .{ @@ -1746,13 +1750,14 @@ fn dirMakeOpenPathWasi( userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, + permissions: Dir.Permissions, options: Dir.OpenOptions, ) Dir.MakeOpenPathError!Dir { const t: *Threaded = @ptrCast(@alignCast(userdata)); const t_io = ioBasic(t); return dirOpenDirWasi(t, dir, sub_path, options) catch |err| switch (err) { error.FileNotFound => { - try dir.makePath(t_io, sub_path); + _ = try dir.makePathStatus(t_io, sub_path, permissions); return dirOpenDirWasi(t, dir, sub_path, options); }, else => |e| return e, diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index e911031c7f..9763fb2397 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -30,7 +30,7 @@ test "write a file, read it, then delete it" { var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); - var file_writer = file.writer(&.{}); + var file_writer = file.writer(io, &.{}); const st = &file_writer.interface; try st.print("begin", .{}); try st.writeAll(&data); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 34f740bfee..39207f938d 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1279,7 +1279,7 @@ test printLineFromFile { const overlap = 10; var buf: [16]u8 = undefined; - var file_writer = file.writer(&buf); + var file_writer = file.writer(io, &buf); const writer = &file_writer.interface; try writer.splatByteAll('a', std.heap.page_size_min - overlap); try writer.writeByte('\n'); @@ -1296,7 +1296,7 @@ test printLineFromFile { const path = try fs.path.join(gpa, &.{ test_dir_path, "file_ends_on_page_boundary.zig" }); defer gpa.free(path); - var file_writer = file.writer(&.{}); + var file_writer = file.writer(io, &.{}); const writer = &file_writer.interface; try writer.splatByteAll('a', std.heap.page_size_max); @@ -1310,7 +1310,7 @@ test printLineFromFile { const path = try fs.path.join(gpa, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" }); defer gpa.free(path); - var file_writer = file.writer(&.{}); + var file_writer = file.writer(io, &.{}); const writer = &file_writer.interface; try writer.splatByteAll('a', 3 * std.heap.page_size_max); @@ -1336,7 +1336,7 @@ test printLineFromFile { const path = try fs.path.join(gpa, &.{ test_dir_path, "file_of_newlines.zig" }); defer gpa.free(path); - var file_writer = file.writer(&.{}); + var file_writer = file.writer(io, &.{}); const writer = &file_writer.interface; const real_file_start = 3 * std.heap.page_size_min; try writer.splatByteAll('\n', real_file_start); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index bc84500419..59bacff2d0 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1206,8 +1206,8 @@ test "deleteTree does not follow symlinks" { try tmp.dir.deleteTree("a"); - try testing.expectError(error.FileNotFound, tmp.dir.access("a", .{})); - try tmp.dir.access("b", .{}); + try testing.expectError(error.FileNotFound, tmp.dir.access(io, "a", .{})); + try tmp.dir.access(io, "b", .{}); } test "deleteTree on a symlink" { @@ -1221,16 +1221,16 @@ test "deleteTree on a symlink" { try setupSymlink(tmp.dir, "file", "filelink", .{}); try tmp.dir.deleteTree("filelink"); - try testing.expectError(error.FileNotFound, tmp.dir.access("filelink", .{})); - try tmp.dir.access("file", .{}); + try testing.expectError(error.FileNotFound, tmp.dir.access(io, "filelink", .{})); + try tmp.dir.access(io, "file", .{}); // Symlink to a directory try tmp.dir.makePath(io, "dir"); try setupSymlink(tmp.dir, "dir", "dirlink", .{ .is_directory = true }); try tmp.dir.deleteTree("dirlink"); - try testing.expectError(error.FileNotFound, tmp.dir.access("dirlink", .{})); - try tmp.dir.access("dir", .{}); + try testing.expectError(error.FileNotFound, tmp.dir.access(io, "dirlink", .{})); + try tmp.dir.access(io, "dir", .{}); } test "makePath, put some files in it, deleteTree" { @@ -1358,8 +1358,8 @@ test "makepath relative walks" { // On Windows, .. is resolved before passing the path to NtCreateFile, // meaning everything except `first/C` drops out. try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "C"); - try testing.expectError(error.FileNotFound, tmp.dir.access("second", .{})); - try testing.expectError(error.FileNotFound, tmp.dir.access("third", .{})); + try testing.expectError(error.FileNotFound, tmp.dir.access(io, "second", .{})); + try testing.expectError(error.FileNotFound, tmp.dir.access(io, "third", .{})); }, else => { try expectDir(io, tmp.dir, "first" ++ fs.path.sep_str ++ "A"); @@ -1561,10 +1561,10 @@ test "access file" { const file_path = try ctx.transformPath("os_test_tmp" ++ fs.path.sep_str ++ "file.txt"); try ctx.dir.makePath(io, dir_path); - try testing.expectError(error.FileNotFound, ctx.dir.access(file_path, .{})); + try testing.expectError(error.FileNotFound, ctx.dir.access(io, file_path, .{})); try ctx.dir.writeFile(.{ .sub_path = file_path, .data = "" }); - try ctx.dir.access(file_path, .{}); + try ctx.dir.access(io, file_path, .{}); try ctx.dir.deleteTree(dir_path); } }.impl); @@ -2036,13 +2036,13 @@ test "'.' and '..' in Io.Dir functions" { const update_path = try ctx.transformPath("./subdir/../update"); try ctx.dir.makeDir(subdir_path); - try ctx.dir.access(subdir_path, .{}); + try ctx.dir.access(io, subdir_path, .{}); var created_subdir = try ctx.dir.openDir(io, subdir_path, .{}); created_subdir.close(io); const created_file = try ctx.dir.createFile(io, file_path, .{}); created_file.close(io); - try ctx.dir.access(file_path, .{}); + try ctx.dir.access(io, file_path, .{}); try ctx.dir.copyFile(file_path, ctx.dir, copy_path, .{}); try ctx.dir.rename(copy_path, rename_path); diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 42429a4993..609cf62e82 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -2842,67 +2842,6 @@ pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void { } } -pub const AccessError = error{ - AccessDenied, - PermissionDenied, - FileNotFound, - NameTooLong, - InputOutput, - SystemResources, - FileBusy, - SymLinkLoop, - ReadOnlyFileSystem, - /// WASI: file paths must be valid UTF-8. - /// Windows: file paths provided by the user must be valid WTF-8. - /// https://wtf-8.codeberg.page/ - BadPathName, - Canceled, -} || UnexpectedError; - -/// check user's permissions for a file -/// -/// * On Windows, asserts `path` is valid [WTF-8](https://wtf-8.codeberg.page/). -/// * On WASI, invalid UTF-8 passed to `path` causes `error.BadPathName`. -/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding. -/// -/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by -/// Windows. See `fs` for the cross-platform file system API. -pub fn access(path: []const u8, mode: u32) AccessError!void { - if (native_os == .windows) { - @compileError("use std.Io instead"); - } else if (native_os == .wasi and !builtin.link_libc) { - @compileError("wasi doesn't support absolute paths"); - } - const path_c = try toPosixPath(path); - return accessZ(&path_c, mode); -} - -/// Same as `access` except `path` is null-terminated. -pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { - if (native_os == .windows) { - @compileError("use std.Io instead"); - } else if (native_os == .wasi and !builtin.link_libc) { - return access(mem.sliceTo(path, 0), mode); - } - switch (errno(system.access(path, mode))) { - .SUCCESS => return, - .ACCES => return error.AccessDenied, - .PERM => return error.PermissionDenied, - .ROFS => return error.ReadOnlyFileSystem, - .LOOP => return error.SymLinkLoop, - .TXTBSY => return error.FileBusy, - .NOTDIR => return error.FileNotFound, - .NOENT => return error.FileNotFound, - .NAMETOOLONG => return error.NameTooLong, - .INVAL => unreachable, - .FAULT => unreachable, - .IO => return error.InputOutput, - .NOMEM => return error.SystemResources, - .ILSEQ => return error.BadPathName, - else => |err| return unexpectedErrno(err), - } -} - pub const PipeError = error{ SystemFdQuotaExceeded, ProcessFdQuotaExceeded, diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 169c5a70c2..af17f1f9ff 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -376,7 +376,7 @@ test "mmap" { const file = try tmp.dir.createFile(io, test_out_file, .{}); defer file.close(io); - var stream = file.writer(&.{}); + var stream = file.writer(io, &.{}); var i: usize = 0; while (i < alloc_size / @sizeOf(u32)) : (i += 1) { @@ -741,6 +741,8 @@ test "access smoke test" { if (native_os == .windows) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -761,9 +763,9 @@ test "access smoke test" { const file_path = try fs.path.join(a, &.{ base_path, "some_file" }); defer a.free(file_path); if (native_os == .windows) { - try posix.access(file_path, posix.F_OK); + try posix.access(io, file_path, posix.F_OK); } else { - try posix.access(file_path, posix.F_OK | posix.W_OK | posix.R_OK); + try posix.access(io, file_path, posix.F_OK | posix.W_OK | posix.R_OK); } } @@ -771,7 +773,7 @@ test "access smoke test" { // Try to access() a non-existent file - should fail with error.FileNotFound const file_path = try fs.path.join(a, &.{ base_path, "some_other_file" }); defer a.free(file_path); - try expectError(error.FileNotFound, posix.access(file_path, posix.F_OK)); + try expectError(error.FileNotFound, posix.access(io, file_path, posix.F_OK)); } { @@ -786,7 +788,7 @@ test "access smoke test" { const file_path = try fs.path.join(a, &.{ base_path, "some_dir" }); defer a.free(file_path); - try posix.access(file_path, posix.F_OK); + try posix.access(io, file_path, posix.F_OK); } } diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index a5c55d3522..f3dc73838f 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -357,7 +357,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) F } if (self.sys_include_dir == null) { - if (search_dir.access(sys_include_dir_example_file, .{})) |_| { + if (search_dir.access(io, sys_include_dir_example_file, .{})) |_| { self.sys_include_dir = try allocator.dupeZ(u8, search_path); } else |err| switch (err) { error.FileNotFound => {}, @@ -402,7 +402,7 @@ fn findNativeIncludeDirWindows( }; defer dir.close(io); - dir.access("stdlib.h", .{}) catch |err| switch (err) { + dir.access(io, "stdlib.h", .{}) catch |err| switch (err) { error.FileNotFound => continue, else => return error.FileSystem, }; @@ -450,7 +450,7 @@ fn findNativeCrtDirWindows( }; defer dir.close(io); - dir.access("ucrt.lib", .{}) catch |err| switch (err) { + dir.access(io, "ucrt.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, else => return error.FileSystem, }; @@ -518,7 +518,7 @@ fn findNativeKernel32LibDir( }; defer dir.close(io); - dir.access("kernel32.lib", .{}) catch |err| switch (err) { + dir.access(io, "kernel32.lib", .{}) catch |err| switch (err) { error.FileNotFound => continue, else => return error.FileSystem, }; @@ -554,7 +554,7 @@ fn findNativeMsvcIncludeDir( }; defer dir.close(io); - dir.access("vcruntime.h", .{}) catch |err| switch (err) { + dir.access(io, "vcruntime.h", .{}) catch |err| switch (err) { error.FileNotFound => return error.LibCStdLibHeaderNotFound, else => return error.FileSystem, }; diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 443bdcc391..251c87defc 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -9589,8 +9589,8 @@ pub fn printToFilePath(b: *Builder, io: Io, dir: Io.Dir, path: []const u8) !void try b.printToFile(io, file, &buffer); } -pub fn printToFile(b: *Builder, file: Io.File, buffer: []u8) !void { - var fw = file.writer(buffer); +pub fn printToFile(b: *Builder, io: Io, file: Io.File, buffer: []u8) !void { + var fw = file.writer(io, buffer); try print(b, &fw.interface); try fw.interface.flush(); } diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 9fa0546c3b..feed21efec 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -40,6 +40,7 @@ pub const GetExternalExecutorOptions = struct { /// Return whether or not the given host is capable of running executables of /// the other target. pub fn getExternalExecutor( + io: Io, host: *const std.Target, candidate: *const std.Target, options: GetExternalExecutorOptions, @@ -70,7 +71,7 @@ pub fn getExternalExecutor( if (os_match and cpu_ok) native: { if (options.link_libc) { if (candidate.dynamic_linker.get()) |candidate_dl| { - Io.Dir.cwd().access(candidate_dl, .{}) catch { + Io.Dir.cwd().access(io, candidate_dl, .{}) catch { bad_result = .{ .bad_dl = candidate_dl }; break :native; }; diff --git a/src/Compilation.zig b/src/Compilation.zig index 280d34cdbf..36429a42f8 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -788,7 +788,7 @@ pub const Directories = struct { const local_cache: Cache.Directory = switch (local_cache_strat) { .override => |path| openUnresolved(arena, io, cwd, path, .@"local cache"), .search => d: { - const maybe_path = introspect.resolveSuitableLocalCacheDir(arena, cwd) catch |err| { + const maybe_path = introspect.resolveSuitableLocalCacheDir(arena, io, cwd) catch |err| { fatal("unable to resolve zig cache directory: {s}", .{@errorName(err)}); }; const path = maybe_path orelse break :d global_cache; diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index e54dcb9914..860fb8974e 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -418,7 +418,7 @@ pub fn run(f: *Fetch) RunError!void { const prefixed_pkg_sub_path = prefixed_pkg_sub_path_buffer[0 .. 2 + hash_slice.len]; const prefix_len: usize = if (f.job_queue.read_only) "p/".len else 0; const pkg_sub_path = prefixed_pkg_sub_path[prefix_len..]; - if (cache_root.handle.access(pkg_sub_path, .{})) |_| { + if (cache_root.handle.access(io, pkg_sub_path, .{})) |_| { assert(f.lazy_status != .unavailable); f.package_root = .{ .root_dir = cache_root, @@ -637,8 +637,9 @@ pub fn computedPackageHash(f: *const Fetch) Package.Hash { /// `computeHash` gets a free check for the existence of `build.zig`, but when /// not computing a hash, we need to do a syscall to check for it. fn checkBuildFileExistence(f: *Fetch) RunError!void { + const io = f.job_queue.io; const eb = &f.error_bundle; - if (f.package_root.access(Package.build_zig_basename, .{})) |_| { + if (f.package_root.access(io, Package.build_zig_basename, .{})) |_| { f.has_build_zig = true; } else |err| switch (err) { error.FileNotFound => {}, diff --git a/src/introspect.zig b/src/introspect.zig index a56a214cbe..3f8308961f 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -202,11 +202,11 @@ pub const default_local_zig_cache_basename = ".zig-cache"; /// Searches upwards from `cwd` for a directory containing a `build.zig` file. /// If such a directory is found, returns the path to it joined to the `.zig_cache` name. /// Otherwise, returns `null`, indicating no suitable local cache location. -pub fn resolveSuitableLocalCacheDir(arena: Allocator, cwd: []const u8) Allocator.Error!?[]u8 { +pub fn resolveSuitableLocalCacheDir(arena: Allocator, io: Io, cwd: []const u8) Allocator.Error!?[]u8 { var cur_dir = cwd; while (true) { const joined = try fs.path.join(arena, &.{ cur_dir, Package.build_zig_basename }); - if (Io.Dir.cwd().access(joined, .{})) |_| { + if (Io.Dir.cwd().access(io, joined, .{})) |_| { return try fs.path.join(arena, &.{ cur_dir, default_local_zig_cache_basename }); } else |err| switch (err) { error.FileNotFound => { diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index b3ca51e833..93f88b9689 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -242,7 +242,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const def_file_path = findDef(arena, comp.getTarget(), comp.dirs.zig_lib, lib_name) catch |err| switch (err) { + const def_file_path = findDef(arena, io, comp.getTarget(), comp.dirs.zig_lib, lib_name) catch |err| switch (err) { error.FileNotFound => { log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name }); // In this case we will end up putting foo.lib onto the linker line and letting the linker @@ -402,11 +402,12 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { pub fn libExists( allocator: Allocator, + io: Io, target: *const std.Target, zig_lib_directory: Cache.Directory, lib_name: []const u8, ) !bool { - const s = findDef(allocator, target, zig_lib_directory, lib_name) catch |err| switch (err) { + const s = findDef(allocator, io, target, zig_lib_directory, lib_name) catch |err| switch (err) { error.FileNotFound => return false, else => |e| return e, }; @@ -418,6 +419,7 @@ pub fn libExists( /// see if a .def file exists. fn findDef( allocator: Allocator, + io: Io, target: *const std.Target, zig_lib_directory: Cache.Directory, lib_name: []const u8, @@ -443,7 +445,7 @@ fn findDef( } else { try override_path.print(fmt_path, .{ lib_path, lib_name }); } - if (Io.Dir.cwd().access(override_path.items, .{})) |_| { + if (Io.Dir.cwd().access(io, override_path.items, .{})) |_| { return override_path.toOwnedSlice(); } else |err| switch (err) { error.FileNotFound => {}, @@ -460,7 +462,7 @@ fn findDef( } else { try override_path.print(fmt_path, .{lib_name}); } - if (Io.Dir.cwd().access(override_path.items, .{})) |_| { + if (Io.Dir.cwd().access(io, override_path.items, .{})) |_| { return override_path.toOwnedSlice(); } else |err| switch (err) { error.FileNotFound => {}, @@ -477,7 +479,7 @@ fn findDef( } else { try override_path.print(fmt_path, .{lib_name}); } - if (Io.Dir.cwd().access(override_path.items, .{})) |_| { + if (Io.Dir.cwd().access(io, override_path.items, .{})) |_| { return override_path.toOwnedSlice(); } else |err| switch (err) { error.FileNotFound => {}, diff --git a/src/link/C.zig b/src/link/C.zig index a001f8fdd9..db5acebf07 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -371,6 +371,7 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P const comp = self.base.comp; const diags = &comp.link_diags; const gpa = comp.gpa; + const io = comp.io; const zcu = self.base.comp.zcu.?; const ip = &zcu.intern_pool; const pt: Zcu.PerThread = .activate(zcu, tid); @@ -509,7 +510,7 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P const file = self.base.file.?; file.setEndPos(f.file_size) catch |err| return diags.fail("failed to allocate file: {s}", .{@errorName(err)}); - var fw = file.writer(&.{}); + var fw = file.writer(io, &.{}); var w = &fw.interface; w.writeVecAll(f.all_buffers.items) catch |err| switch (err) { error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{ diff --git a/src/link/Lld.zig b/src/link/Lld.zig index b25b9da9d9..ca15a38bb0 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -359,6 +359,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { fn coffLink(lld: *Lld, arena: Allocator) !void { const comp = lld.base.comp; const gpa = comp.gpa; + const io = comp.io; const base = &lld.base; const coff = &lld.ofmt.coff; @@ -718,13 +719,13 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena)); continue; } - if (try findLib(arena, lib_basename, coff.lib_directories)) |full_path| { + if (try findLib(arena, io, lib_basename, coff.lib_directories)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } if (target.abi.isGnu()) { const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); - if (try findLib(arena, fallback_name, coff.lib_directories)) |full_path| { + if (try findLib(arena, io, fallback_name, coff.lib_directories)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } @@ -741,9 +742,9 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { try spawnLld(comp, arena, argv.items); } } -fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Cache.Directory) !?[]const u8 { +fn findLib(arena: Allocator, io: Io, name: []const u8, lib_directories: []const Cache.Directory) !?[]const u8 { for (lib_directories) |lib_directory| { - lib_directory.handle.access(name, .{}) catch |err| switch (err) { + lib_directory.handle.access(io, name, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| return e, }; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 78e035e2ad..6cd906340a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -829,7 +829,8 @@ pub fn resolveLibSystem( comp: *Compilation, out_libs: anytype, ) !void { - const diags = &self.base.comp.link_diags; + const io = comp.io; + const diags = &comp.link_diags; var test_path = std.array_list.Managed(u8).init(arena); var checked_paths = std.array_list.Managed([]const u8).init(arena); @@ -838,16 +839,16 @@ pub fn resolveLibSystem( if (self.sdk_layout) |sdk_layout| switch (sdk_layout) { .sdk => { const dir = try fs.path.join(arena, &.{ comp.sysroot.?, "usr", "lib" }); - if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success; + if (try accessLibPath(arena, io, &test_path, &checked_paths, dir, "System")) break :success; }, .vendored => { const dir = try comp.dirs.zig_lib.join(arena, &.{ "libc", "darwin" }); - if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success; + if (try accessLibPath(arena, io, &test_path, &checked_paths, dir, "System")) break :success; }, }; for (self.lib_directories) |directory| { - if (try accessLibPath(arena, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success; + if (try accessLibPath(arena, io, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success; } diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); @@ -1074,6 +1075,7 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool { /// TODO delete this, libraries must be instead resolved when instantiating the compilation pipeline fn accessLibPath( arena: Allocator, + io: Io, test_path: *std.array_list.Managed(u8), checked_paths: *std.array_list.Managed([]const u8), search_dir: []const u8, @@ -1085,7 +1087,7 @@ fn accessLibPath( test_path.clearRetainingCapacity(); try test_path.print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext }); try checked_paths.append(try arena.dupe(u8, test_path.items)); - Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(io, test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| return e, }; @@ -1097,6 +1099,7 @@ fn accessLibPath( fn accessFrameworkPath( arena: Allocator, + io: Io, test_path: *std.array_list.Managed(u8), checked_paths: *std.array_list.Managed([]const u8), search_dir: []const u8, @@ -1113,7 +1116,7 @@ fn accessFrameworkPath( ext, }); try checked_paths.append(try arena.dupe(u8, test_path.items)); - Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(io, test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| return e, }; @@ -1172,14 +1175,14 @@ fn parseDependentDylibs(self: *MachO) !void { // Framework for (framework_dirs) |dir| { test_path.clearRetainingCapacity(); - if (try accessFrameworkPath(arena, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items; + if (try accessFrameworkPath(arena, io, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items; } // Library const lib_name = eatPrefix(stem, "lib") orelse stem; for (lib_directories) |lib_directory| { test_path.clearRetainingCapacity(); - if (try accessLibPath(arena, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items; + if (try accessLibPath(arena, io, &test_path, &checked_paths, lib_directory.path orelse ".", lib_name)) break :full_path test_path.items; } } @@ -1194,7 +1197,7 @@ fn parseDependentDylibs(self: *MachO) !void { try test_path.print("{s}{s}", .{ path, ext }); } try checked_paths.append(try arena.dupe(u8, test_path.items)); - Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(io, test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| return e, }; diff --git a/src/main.zig b/src/main.zig index a063591b64..99850f5ffe 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3208,6 +3208,7 @@ fn buildOutputType( for (create_module.framework_dirs.items) |framework_dir_path| { if (try accessFrameworkPath( + io, &test_path, &checked_paths, framework_dir_path, @@ -6626,7 +6627,7 @@ fn warnAboutForeignBinaries( const host_query: std.Target.Query = .{}; const host_target = std.zig.resolveTargetQueryOrFatal(io, host_query); - switch (std.zig.system.getExternalExecutor(&host_target, target, .{ .link_libc = link_libc })) { + switch (std.zig.system.getExternalExecutor(io, &host_target, target, .{ .link_libc = link_libc })) { .native => return, .rosetta => { const host_name = try host_target.zigTriple(arena); @@ -6832,6 +6833,7 @@ const ClangSearchSanitizer = struct { }; fn accessFrameworkPath( + io: Io, test_path: *std.array_list.Managed(u8), checked_paths: *std.array_list.Managed(u8), framework_dir_path: []const u8, @@ -6845,7 +6847,7 @@ fn accessFrameworkPath( framework_dir_path, framework_name, framework_name, ext, }); try checked_paths.print("\n {s}", .{test_path.items}); - Io.Dir.cwd().access(test_path.items, .{}) catch |err| switch (err) { + Io.Dir.cwd().access(io, test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => continue, else => |e| fatal("unable to search for {s} framework '{s}': {s}", .{ ext, test_path.items, @errorName(e), @@ -7280,7 +7282,7 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build var dirname: []const u8 = cwd_path; while (true) { const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename }); - if (Io.Dir.cwd().access(joined_path, .{})) |_| { + if (Io.Dir.cwd().access(io, joined_path, .{})) |_| { const dir = Io.Dir.cwd().openDir(io, dirname, .{}) catch |err| { fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) }); }; -- cgit v1.2.3 From 4218344dd3178f2fd3d9d00e9ff6895ee344df6d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 17:21:52 -0800 Subject: std.Build.Cache: remove readSmallFile and writeSmallFile These were to support optimizations involving detecting when to avoid calling into LLD, which are no longer implemented. --- lib/compiler/build_runner.zig | 2 +- lib/compiler/reduce.zig | 12 +++++--- lib/compiler/resinator/main.zig | 2 +- lib/compiler/std-docs.zig | 2 +- lib/std/Build/Cache.zig | 36 ++++-------------------- lib/std/Build/Step/ConfigHeader.zig | 2 +- lib/std/Build/Step/Run.zig | 2 +- lib/std/Build/Step/UpdateSourceFiles.zig | 2 +- lib/std/Build/Step/WriteFile.zig | 2 +- lib/std/Build/WebServer.zig | 2 +- lib/std/debug.zig | 4 +-- lib/std/fs/test.zig | 48 +++++++++++++++++++------------- lib/std/posix/test.zig | 2 +- src/Compilation.zig | 4 +-- src/libs/freebsd.zig | 4 +-- src/libs/glibc.zig | 4 +-- src/libs/netbsd.zig | 2 +- src/main.zig | 6 ++-- 18 files changed, 63 insertions(+), 75 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 36c73e96eb..9150d84470 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -459,7 +459,7 @@ pub fn main() !void { } const s = std.fs.path.sep_str; const tmp_sub_path = "tmp" ++ s ++ (output_tmp_nonce orelse fatal("missing -Z arg", .{})); - local_cache_directory.handle.writeFile(.{ + local_cache_directory.handle.writeFile(io, .{ .sub_path = tmp_sub_path, .data = buffer.items, .flags = .{ .exclusive = true }, diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig index d3f33ad81a..0bfa1902ab 100644 --- a/lib/compiler/reduce.zig +++ b/lib/compiler/reduce.zig @@ -55,6 +55,10 @@ pub fn main() !void { var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; const gpa = general_purpose_allocator.allocator(); + var threaded: std.Io.Threaded = .init(gpa); + defer threaded.deinit(); + const io = threaded.io(); + const args = try std.process.argsAlloc(arena); var opt_checker_path: ?[]const u8 = null; @@ -233,12 +237,12 @@ pub fn main() !void { } } - try Io.Dir.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); + try Io.Dir.cwd().writeFile(io, .{ .sub_path = root_source_file_path, .data = rendered.written() }); // std.debug.print("trying this code:\n{s}\n", .{rendered.items}); const interestingness = try runCheck(arena, interestingness_argv.items); - std.debug.print("{d} random transformations: {s}. {d}/{d}\n", .{ - subset_size, @tagName(interestingness), start_index, transformations.items.len, + std.debug.print("{d} random transformations: {t}. {d}/{d}\n", .{ + subset_size, interestingness, start_index, transformations.items.len, }); switch (interestingness) { .interesting => { @@ -274,7 +278,7 @@ pub fn main() !void { fixups.clearRetainingCapacity(); rendered.clearRetainingCapacity(); try tree.render(gpa, &rendered.writer, fixups); - try Io.Dir.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() }); + try Io.Dir.cwd().writeFile(io, .{ .sub_path = root_source_file_path, .data = rendered.written() }); return std.process.cleanExit(); } diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index 1a6ef5eea3..afe1dcbe91 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -212,7 +212,7 @@ pub fn main() !void { try output_file.writeAll(full_input); }, .filename => |output_filename| { - try Io.Dir.cwd().writeFile(.{ .sub_path = output_filename, .data = full_input }); + try Io.Dir.cwd().writeFile(io, .{ .sub_path = output_filename, .data = full_input }); }, } return; diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index f1382e6eae..8f02f05958 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -233,7 +233,7 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void { .interface = std.Io.File.Reader.initInterface(&.{}), .size = stat.size, }; - try archiver.writeFile(entry.path, &file_reader, stat.mtime); + try archiver.writeFile(io, entry.path, &file_reader, stat.mtime); } { diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index dab1926f53..f7c4d729bc 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -1276,30 +1276,6 @@ pub const Manifest = struct { } }; -/// On operating systems that support symlinks, does a readlink. On other operating systems, -/// uses the file contents. Windows supports symlinks but only with elevated privileges, so -/// it is treated as not supporting symlinks. -pub fn readSmallFile(dir: Io.Dir, sub_path: []const u8, buffer: []u8) ![]u8 { - if (builtin.os.tag == .windows) { - return dir.readFile(sub_path, buffer); - } else { - return dir.readLink(sub_path, buffer); - } -} - -/// On operating systems that support symlinks, does a symlink. On other operating systems, -/// uses the file contents. Windows supports symlinks but only with elevated privileges, so -/// it is treated as not supporting symlinks. -/// `data` must be a valid UTF-8 encoded file path and 255 bytes or fewer. -pub fn writeSmallFile(dir: Io.Dir, sub_path: []const u8, data: []const u8) !void { - assert(data.len <= 255); - if (builtin.os.tag == .windows) { - return dir.writeFile(.{ .sub_path = sub_path, .data = data }); - } else { - return dir.symLink(data, sub_path, .{}); - } -} - fn hashFile(io: Io, file: Io.File, bin_digest: *[Hasher.mac_length]u8) Io.File.ReadPositionalError!void { var buffer: [2048]u8 = undefined; var hasher = hasher_init; @@ -1338,7 +1314,7 @@ test "cache file and then recall it" { const temp_file = "test.txt"; const temp_manifest_dir = "temp_manifest_dir"; - try tmp.dir.writeFile(.{ .sub_path = temp_file, .data = "Hello, world!\n" }); + try tmp.dir.writeFile(io, .{ .sub_path = temp_file, .data = "Hello, world!\n" }); // Wait for file timestamps to tick const initial_time = try testGetCurrentFileTimestamp(io, tmp.dir); @@ -1404,7 +1380,7 @@ test "check that changing a file makes cache fail" { const original_temp_file_contents = "Hello, world!\n"; const updated_temp_file_contents = "Hello, world; but updated!\n"; - try tmp.dir.writeFile(.{ .sub_path = temp_file, .data = original_temp_file_contents }); + try tmp.dir.writeFile(io, .{ .sub_path = temp_file, .data = original_temp_file_contents }); // Wait for file timestamps to tick const initial_time = try testGetCurrentFileTimestamp(tmp.dir); @@ -1441,7 +1417,7 @@ test "check that changing a file makes cache fail" { try ch.writeManifest(); } - try tmp.dir.writeFile(.{ .sub_path = temp_file, .data = updated_temp_file_contents }); + try tmp.dir.writeFile(io, .{ .sub_path = temp_file, .data = updated_temp_file_contents }); { var ch = cache.obtain(); @@ -1521,8 +1497,8 @@ test "Manifest with files added after initial hash work" { const temp_file2 = "cache_hash_post_file_test2.txt"; const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; - try tmp.dir.writeFile(.{ .sub_path = temp_file1, .data = "Hello, world!\n" }); - try tmp.dir.writeFile(.{ .sub_path = temp_file2, .data = "Hello world the second!\n" }); + try tmp.dir.writeFile(io, .{ .sub_path = temp_file1, .data = "Hello, world!\n" }); + try tmp.dir.writeFile(io, .{ .sub_path = temp_file2, .data = "Hello world the second!\n" }); // Wait for file timestamps to tick const initial_time = try testGetCurrentFileTimestamp(tmp.dir); @@ -1573,7 +1549,7 @@ test "Manifest with files added after initial hash work" { try testing.expect(mem.eql(u8, &digest1, &digest2)); // Modify the file added after initial hash - try tmp.dir.writeFile(.{ .sub_path = temp_file2, .data = "Hello world the second, updated\n" }); + try tmp.dir.writeFile(io, .{ .sub_path = temp_file2, .data = "Hello world the second, updated\n" }); // Wait for file timestamps to tick const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir); diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index f377959610..589110d4c4 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -264,7 +264,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { }); }; - b.cache_root.handle.writeFile(.{ .sub_path = sub_path, .data = output }) catch |err| { + b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = output }) catch |err| { return step.fail("unable to write file '{f}{s}': {s}", .{ b.cache_root, sub_path, @errorName(err), }); diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index f6b29635c1..a1618beb02 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1482,7 +1482,7 @@ fn runCommand( .leading => mem.trimStart(u8, stream.bytes.?, &std.ascii.whitespace), .trailing => mem.trimEnd(u8, stream.bytes.?, &std.ascii.whitespace), }; - b.cache_root.handle.writeFile(.{ .sub_path = sub_path, .data = data }) catch |err| { + b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = data }) catch |err| { return step.fail("unable to write file '{f}{s}': {s}", .{ b.cache_root, sub_path, @errorName(err), }); diff --git a/lib/std/Build/Step/UpdateSourceFiles.zig b/lib/std/Build/Step/UpdateSourceFiles.zig index 44c6ae1ed4..eb8a6a85dd 100644 --- a/lib/std/Build/Step/UpdateSourceFiles.zig +++ b/lib/std/Build/Step/UpdateSourceFiles.zig @@ -84,7 +84,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } switch (output_source_file.contents) { .bytes => |bytes| { - b.build_root.handle.writeFile(.{ .sub_path = output_source_file.sub_path, .data = bytes }) catch |err| { + b.build_root.handle.writeFile(io, .{ .sub_path = output_source_file.sub_path, .data = bytes }) catch |err| { return step.fail("unable to write file '{f}{s}': {t}", .{ b.build_root, output_source_file.sub_path, err, }); diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 94b04b4212..3d712fa1d4 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -273,7 +273,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } switch (file.contents) { .bytes => |bytes| { - cache_dir.writeFile(.{ .sub_path = file.sub_path, .data = bytes }) catch |err| { + cache_dir.writeFile(io, .{ .sub_path = file.sub_path, .data = bytes }) catch |err| { return step.fail("unable to write file '{f}{s}{c}{s}': {t}", .{ b.cache_root, cache_path, fs.path.sep, file.sub_path, err, }); diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 162d17f070..ed07d04d57 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -523,7 +523,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa); break :cwd cached_cwd_path.?; }; - try archiver.writeFile(path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds())); + try archiver.writeFile(io, path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds())); } // intentionally not calling `archiver.finishPedantically` diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 39207f938d..cb79bb7855 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1243,7 +1243,7 @@ test printLineFromFile { { const path = try join(gpa, &.{ test_dir_path, "one_line.zig" }); defer gpa.free(path); - try test_dir.dir.writeFile(.{ .sub_path = "one_line.zig", .data = "no new lines in this file, but one is printed anyway" }); + try test_dir.dir.writeFile(io, .{ .sub_path = "one_line.zig", .data = "no new lines in this file, but one is printed anyway" }); try expectError(error.EndOfFile, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 })); @@ -1254,7 +1254,7 @@ test printLineFromFile { { const path = try fs.path.join(gpa, &.{ test_dir_path, "three_lines.zig" }); defer gpa.free(path); - try test_dir.dir.writeFile(.{ + try test_dir.dir.writeFile(io, .{ .sub_path = "three_lines.zig", .data = \\1 diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 59bacff2d0..59e0990eb0 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -184,7 +184,7 @@ test "Dir.readLink" { fn impl(ctx: *TestContext) !void { // Create some targets const file_target_path = try ctx.transformPath("file.txt"); - try ctx.dir.writeFile(.{ .sub_path = file_target_path, .data = "nonsense" }); + try ctx.dir.writeFile(io, .{ .sub_path = file_target_path, .data = "nonsense" }); const dir_target_path = try ctx.transformPath("subdir"); try ctx.dir.makeDir(dir_target_path); @@ -487,11 +487,13 @@ test "readLinkAbsolute" { if (native_os == .wasi) return error.SkipZigTest; if (native_os == .openbsd) return error.SkipZigTest; + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); // Create some targets - try tmp.dir.writeFile(.{ .sub_path = "file.txt", .data = "nonsense" }); + try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = "nonsense" }); try tmp.dir.makeDir("subdir"); // Get base abs path @@ -708,6 +710,7 @@ test "Dir.realpath smoke test" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const allocator = ctx.arena.allocator(); const test_file_path = try ctx.transformPath("test_file"); const test_dir_path = try ctx.transformPath("test_dir"); @@ -720,7 +723,7 @@ test "Dir.realpath smoke test" { try testing.expectError(error.FileNotFound, ctx.dir.realpath(test_dir_path, &buf)); // Now create the file and dir - try ctx.dir.writeFile(.{ .sub_path = test_file_path, .data = "" }); + try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" }); try ctx.dir.makeDir(test_dir_path); const base_path = try ctx.transformPath("."); @@ -803,11 +806,12 @@ test "readFileAlloc" { test "Dir.statFile" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const test_file_name = try ctx.transformPath("test_file"); try testing.expectError(error.FileNotFound, ctx.dir.statFile(test_file_name)); - try ctx.dir.writeFile(.{ .sub_path = test_file_name, .data = "" }); + try ctx.dir.writeFile(io, .{ .sub_path = test_file_name, .data = "" }); const stat = try ctx.dir.statFile(test_file_name); try testing.expectEqual(File.Kind.file, stat.kind); @@ -925,6 +929,7 @@ test "makeOpenPath parent dirs do not exist" { test "deleteDir" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const test_dir_path = try ctx.transformPath("test_dir"); const test_file_path = try ctx.transformPath("test_dir" ++ fs.path.sep_str ++ "test_file"); @@ -933,7 +938,7 @@ test "deleteDir" { // deleting a non-empty directory try ctx.dir.makeDir(test_dir_path); - try ctx.dir.writeFile(.{ .sub_path = test_file_path, .data = "" }); + try ctx.dir.writeFile(io, .{ .sub_path = test_file_path, .data = "" }); try testing.expectError(error.DirNotEmpty, ctx.dir.deleteDir(test_dir_path)); // deleting an empty directory @@ -1217,7 +1222,7 @@ test "deleteTree on a symlink" { defer tmp.cleanup(); // Symlink to a file - try tmp.dir.writeFile(.{ .sub_path = "file", .data = "" }); + try tmp.dir.writeFile(io, .{ .sub_path = "file", .data = "" }); try setupSymlink(tmp.dir, "file", "filelink", .{}); try tmp.dir.deleteTree("filelink"); @@ -1241,11 +1246,11 @@ test "makePath, put some files in it, deleteTree" { const dir_path = try ctx.transformPath("os_test_tmp"); try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" })); - try ctx.dir.writeFile(.{ + try ctx.dir.writeFile(io, .{ .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }), .data = "nonsense", }); - try ctx.dir.writeFile(.{ + try ctx.dir.writeFile(io, .{ .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "file2.txt" }), .data = "blah", }); @@ -1264,11 +1269,11 @@ test "makePath, put some files in it, deleteTreeMinStackSize" { const dir_path = try ctx.transformPath("os_test_tmp"); try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" })); - try ctx.dir.writeFile(.{ + try ctx.dir.writeFile(io, .{ .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }), .data = "nonsense", }); - try ctx.dir.writeFile(.{ + try ctx.dir.writeFile(io, .{ .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "file2.txt" }), .data = "blah", }); @@ -1298,7 +1303,7 @@ test "makePath but sub_path contains pre-existing file" { defer tmp.cleanup(); try tmp.dir.makeDir("foo"); - try tmp.dir.writeFile(.{ .sub_path = "foo/bar", .data = "" }); + try tmp.dir.writeFile(io, .{ .sub_path = "foo/bar", .data = "" }); try testing.expectError(error.NotDir, tmp.dir.makePath(io, "foo/bar/baz")); } @@ -1400,7 +1405,7 @@ fn testFilenameLimits(io: Io, iterable_dir: Dir, maxed_filename: []const u8) !vo var maxed_dir = try iterable_dir.makeOpenPath(maxed_filename, .{}); defer maxed_dir.close(io); - try maxed_dir.writeFile(.{ .sub_path = maxed_filename, .data = "" }); + try maxed_dir.writeFile(io, .{ .sub_path = maxed_filename, .data = "" }); var walker = try iterable_dir.walk(testing.allocator); defer walker.deinit(); @@ -1513,7 +1518,7 @@ test "setEndPos" { defer tmp.cleanup(); const file_name = "afile.txt"; - try tmp.dir.writeFile(.{ .sub_path = file_name, .data = "ninebytes" }); + try tmp.dir.writeFile(io, .{ .sub_path = file_name, .data = "ninebytes" }); const f = try tmp.dir.openFile(io, file_name, .{ .mode = .read_write }); defer f.close(io); @@ -1563,7 +1568,7 @@ test "access file" { try ctx.dir.makePath(io, dir_path); try testing.expectError(error.FileNotFound, ctx.dir.access(io, file_path, .{})); - try ctx.dir.writeFile(.{ .sub_path = file_path, .data = "" }); + try ctx.dir.writeFile(io, .{ .sub_path = file_path, .data = "" }); try ctx.dir.access(io, file_path, .{}); try ctx.dir.deleteTree(dir_path); } @@ -1659,12 +1664,13 @@ test "sendfile with buffered data" { test "copyFile" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { + const io = ctx.io; const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP"; const src_file = try ctx.transformPath("tmp_test_copy_file.txt"); const dest_file = try ctx.transformPath("tmp_test_copy_file2.txt"); const dest_file2 = try ctx.transformPath("tmp_test_copy_file3.txt"); - try ctx.dir.writeFile(.{ .sub_path = src_file, .data = data }); + try ctx.dir.writeFile(io, .{ .sub_path = src_file, .data = data }); defer ctx.dir.deleteFile(src_file) catch {}; try ctx.dir.copyFile(src_file, ctx.dir, dest_file, .{}); @@ -2050,7 +2056,7 @@ test "'.' and '..' in Io.Dir functions" { renamed_file.close(io); try ctx.dir.deleteFile(rename_path); - try ctx.dir.writeFile(.{ .sub_path = update_path, .data = "something" }); + try ctx.dir.writeFile(io, .{ .sub_path = update_path, .data = "something" }); var dir = ctx.dir; const prev_status = try dir.updateFile(io, file_path, dir, update_path, .{}); try testing.expectEqual(Io.Dir.PrevStatus.stale, prev_status); @@ -2187,7 +2193,7 @@ test "invalid UTF-8/WTF-8 paths" { try testing.expectError(expected_err, ctx.dir.deleteTree(invalid_path)); try testing.expectError(expected_err, ctx.dir.deleteTreeMinStackSize(invalid_path)); - try testing.expectError(expected_err, ctx.dir.writeFile(.{ .sub_path = invalid_path, .data = "" })); + try testing.expectError(expected_err, ctx.dir.writeFile(io, .{ .sub_path = invalid_path, .data = "" })); try testing.expectError(expected_err, ctx.dir.access(invalid_path, .{})); @@ -2304,7 +2310,7 @@ test "seekBy" { var tmp_dir = testing.tmpDir(.{}); defer tmp_dir.cleanup(); - try tmp_dir.dir.writeFile(.{ .sub_path = "blah.txt", .data = "let's test seekBy" }); + try tmp_dir.dir.writeFile(io, .{ .sub_path = "blah.txt", .data = "let's test seekBy" }); const f = try tmp_dir.dir.openFile(io, "blah.txt", .{ .mode = .read_only }); defer f.close(io); var reader = f.readerStreaming(io, &.{}); @@ -2350,7 +2356,7 @@ test "File.Writer sendfile with buffered contents" { defer tmp_dir.cleanup(); { - try tmp_dir.dir.writeFile(.{ .sub_path = "a", .data = "bcd" }); + try tmp_dir.dir.writeFile(io, .{ .sub_path = "a", .data = "bcd" }); const in = try tmp_dir.dir.openFile(io, "a", .{}); defer in.close(io); const out = try tmp_dir.dir.createFile(io, "b", .{}); @@ -2391,11 +2397,13 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { } test "readlinkat" { + const io = testing.io; + var tmp = tmpDir(.{}); defer tmp.cleanup(); // create file - try tmp.dir.writeFile(.{ .sub_path = "file.txt", .data = "nonsense" }); + try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = "nonsense" }); // create a symbolic link if (native_os == .windows) { diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index af17f1f9ff..bacfdacf83 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -145,7 +145,7 @@ test "linkat with different directories" { const subdir = try tmp.dir.makeOpenPath("subdir", .{}); defer tmp.dir.deleteFile(target_name) catch {}; - try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); + try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" }); // Test 1: link from file in subdir back up to target in parent directory try posix.linkat(tmp.dir.handle, target_name, subdir.handle, link_name, 0); diff --git a/src/Compilation.zig b/src/Compilation.zig index 36429a42f8..7fe217b385 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5715,7 +5715,7 @@ pub fn translateC( const out_h_sub_path = tmp_sub_path ++ fs.path.sep_str ++ cimport_basename; const out_h_path = try comp.dirs.local_cache.join(arena, &.{out_h_sub_path}); if (comp.verbose_cimport) log.info("writing C import source to {s}", .{out_h_path}); - try cache_dir.writeFile(.{ .sub_path = out_h_sub_path, .data = c_src }); + try cache_dir.writeFile(io, .{ .sub_path = out_h_sub_path, .data = c_src }); break :path out_h_path; }, .path => |p| p, @@ -6572,7 +6572,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 resource_id, resource_type, fmtRcEscape(src_path), }); - try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input }); + try o_dir.writeFile(io, .{ .sub_path = rc_basename, .data = input }); var argv = std.array_list.Managed([]const u8).init(comp.gpa); defer argv.deinit(); diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index cfd8d5554c..77bd4372d0 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -520,7 +520,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye for (metadata.all_versions[0 .. target_ver_index + 1]) |ver| { try map_contents.print("FBSD_{d}.{d} {{ }};\n", .{ ver.major, ver.minor }); } - try o_directory.handle.writeFile(.{ .sub_path = all_map_basename, .data = map_contents.items }); + try o_directory.handle.writeFile(io, .{ .sub_path = all_map_basename, .data = map_contents.items }); map_contents.deinit(); } @@ -974,7 +974,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "stdthreads", etc. const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; - try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items }); + try o_directory.handle.writeFile(io, .{ .sub_path = asm_file_basename, .data = stubs_asm.items }); try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node); } diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index e3d8ce1f7f..a60dc921be 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -759,7 +759,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye try map_contents.print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch }); } } - try o_directory.handle.writeFile(.{ .sub_path = all_map_basename, .data = map_contents.items }); + try o_directory.handle.writeFile(io, .{ .sub_path = all_map_basename, .data = map_contents.items }); map_contents.deinit(); // The most recent allocation of an arena can be freed :) } @@ -1118,7 +1118,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc. const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; - try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items }); + try o_directory.handle.writeFile(io, .{ .sub_path = asm_file_basename, .data = stubs_asm.items }); try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node); } diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index cb6a80d69d..fd80616e9d 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -628,7 +628,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc. const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; - try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items }); + try o_directory.handle.writeFile(io, .{ .sub_path = asm_file_basename, .data = stubs_asm.items }); try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node); } diff --git a/src/main.zig b/src/main.zig index 99850f5ffe..bef3a3efb5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -7164,7 +7164,7 @@ fn cmdFetch( try ast.render(gpa, &aw.writer, fixups); const rendered = aw.written(); - build_root.directory.handle.writeFile(.{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| { + build_root.directory.handle.writeFile(io, .{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| { fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err }); }; @@ -7207,7 +7207,7 @@ fn createDependenciesModule( { var tmp_dir = try dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}); defer tmp_dir.close(io); - try tmp_dir.writeFile(.{ .sub_path = basename, .data = source }); + try tmp_dir.writeFile(io, .{ .sub_path = basename, .data = source }); } var hh: Cache.HashHelper = .{}; @@ -7438,7 +7438,7 @@ const Templates = struct { i += 1; } - return out_dir.writeFile(.{ + return out_dir.writeFile(io, .{ .sub_path = template_path, .data = templates.buffer.items, .flags = .{ .exclusive = true }, -- cgit v1.2.3 From bee8005fe6817ade9191de0493888b14cdbcac31 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 21:00:04 -0800 Subject: std.heap.DebugAllocator: never detect TTY config instead, allow the user to set it as a field. this fixes a bug where leak printing and error printing would run tty config detection for stderr, and then emit a log, which is not necessary going to print to stderr. however, the nice defaults are gone; the user must explicitly assign the tty_config field during initialization or else the logging will not have color. related: https://github.com/ziglang/zig/issues/24510 --- lib/compiler/build_runner.zig | 2 +- lib/std/Build/Fuzz.zig | 19 ++++++++------- lib/std/Build/Step/InstallArtifact.zig | 2 +- lib/std/Build/Step/WriteFile.zig | 2 +- lib/std/Io/Dir.zig | 8 +++--- lib/std/Io/File.zig | 2 +- lib/std/Io/File/Atomic.zig | 2 +- lib/std/Io/File/Writer.zig | 9 ++++--- lib/std/Io/Threaded.zig | 32 ++++++++++++------------ lib/std/Io/net/HostName.zig | 2 +- lib/std/Io/test.zig | 5 ++-- lib/std/Io/tty.zig | 9 ++++--- lib/std/debug.zig | 4 ++- lib/std/debug/Info.zig | 7 +++--- lib/std/heap/debug_allocator.zig | 39 +++++++++++++++++++++--------- lib/std/process/Child.zig | 2 +- lib/std/zig.zig | 4 +-- src/link/Dwarf.zig | 6 ++--- src/link/MappedFile.zig | 2 +- src/main.zig | 23 +++++++++++------- test/link/macho.zig | 3 ++- test/src/Cases.zig | 5 ++-- test/standalone/ios/build.zig | 4 ++- test/standalone/self_exe_symlink/build.zig | 4 --- test/tests.zig | 2 +- 25 files changed, 113 insertions(+), 86 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 677158645d..05cd21ecdb 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -435,7 +435,7 @@ pub fn main() !void { if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); } - const ttyconf = color.detectTtyConf(); + const ttyconf = color.detectTtyConf(io); const main_progress_node = std.Progress.start(.{ .disable_printing = (color == .off), diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 8fedb7e067..8837d7d527 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -360,12 +360,13 @@ fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void { fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutOfMemory, AlreadyReported, Canceled }!void { assert(fuzz.mode == .forever); const ws = fuzz.mode.forever.ws; + const gpa = fuzz.gpa; const io = fuzz.io; try fuzz.coverage_mutex.lock(io); defer fuzz.coverage_mutex.unlock(io); - const gop = try fuzz.coverage_files.getOrPut(fuzz.gpa, coverage_id); + const gop = try fuzz.coverage_files.getOrPut(gpa, coverage_id); if (gop.found_existing) { // We are fuzzing the same executable with multiple threads. // Perhaps the same unit test; perhaps a different one. In any @@ -383,12 +384,12 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO .entry_points = .{}, .start_timestamp = ws.now(), }; - errdefer gop.value_ptr.coverage.deinit(fuzz.gpa); + errdefer gop.value_ptr.coverage.deinit(gpa); const rebuilt_exe_path = run_step.rebuilt_executable.?; const target = run_step.producer.?.rootModuleTarget(); var debug_info = std.debug.Info.load( - fuzz.gpa, + gpa, io, rebuilt_exe_path, &gop.value_ptr.coverage, @@ -400,7 +401,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO }); return error.AlreadyReported; }; - defer debug_info.deinit(fuzz.gpa); + defer debug_info.deinit(gpa); const coverage_file_path: Build.Cache.Path = .{ .root_dir = run_step.step.owner.cache_root, @@ -434,14 +435,14 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO const header: *const abi.SeenPcsHeader = @ptrCast(mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]); const pcs = header.pcAddrs(); - const source_locations = try fuzz.gpa.alloc(Coverage.SourceLocation, pcs.len); - errdefer fuzz.gpa.free(source_locations); + const source_locations = try gpa.alloc(Coverage.SourceLocation, pcs.len); + errdefer gpa.free(source_locations); // Unfortunately the PCs array that LLVM gives us from the 8-bit PC // counters feature is not sorted. var sorted_pcs: std.MultiArrayList(struct { pc: u64, index: u32, sl: Coverage.SourceLocation }) = .{}; - defer sorted_pcs.deinit(fuzz.gpa); - try sorted_pcs.resize(fuzz.gpa, pcs.len); + defer sorted_pcs.deinit(gpa); + try sorted_pcs.resize(gpa, pcs.len); @memcpy(sorted_pcs.items(.pc), pcs); for (sorted_pcs.items(.index), 0..) |*v, i| v.* = @intCast(i); sorted_pcs.sortUnstable(struct { @@ -452,7 +453,7 @@ fn prepareTables(fuzz: *Fuzz, run_step: *Step.Run, coverage_id: u64) error{ OutO } }{ .addrs = sorted_pcs.items(.pc) }); - debug_info.resolveAddresses(fuzz.gpa, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| { + debug_info.resolveAddresses(gpa, io, sorted_pcs.items(.pc), sorted_pcs.items(.sl)) catch |err| { log.err("failed to resolve addresses to source locations: {t}", .{err}); return error.AlreadyReported; }; diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index 4c1506aa33..019d465f01 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -172,7 +172,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { defer src_dir.close(io); var it = try src_dir.walk(b.allocator); - next_entry: while (try it.next()) |entry| { + next_entry: while (try it.next(io)) |entry| { for (dir.options.exclude_extensions) |ext| { if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry; } diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 3d712fa1d4..53222289e6 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -309,7 +309,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { var it = try already_open_dir.walk(gpa); defer it.deinit(); - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (!dir.options.pathIncluded(entry.path)) continue; const src_entry_path = try src_dir_path.join(arena, entry.path); diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index d6d6aa1be2..6c40e0217e 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -574,7 +574,7 @@ pub fn updateFile( error.WriteFailed => return atomic_file.file_writer.err.?, }; try atomic_file.flush(); - try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime); + try atomic_file.file_writer.file.setTimestamps(io, src_stat.atime, src_stat.mtime); try atomic_file.renameIntoPlace(); return .stale; } @@ -1238,7 +1238,7 @@ pub fn deleteTree(dir: Dir, io: Io, sub_path: []const u8) DeleteTreeError!void { process_stack: while (stack.items.len != 0) { var top = &stack.items[stack.items.len - 1]; - while (try top.iter.next()) |entry| { + while (try top.iter.next(io)) |entry| { var treat_as_dir = entry.kind == .directory; handle_entry: while (true) { if (treat_as_dir) { @@ -1695,9 +1695,9 @@ pub fn atomicFile(parent: Dir, io: Io, dest_path: []const u8, options: AtomicFil else try parent.openDir(io, dirname, .{}); - return .init(path.basename(dest_path), options.permissions, dir, true, options.write_buffer); + return .init(io, path.basename(dest_path), options.permissions, dir, true, options.write_buffer); } else { - return .init(dest_path, options.permissions, parent, false, options.write_buffer); + return .init(io, dest_path, options.permissions, parent, false, options.write_buffer); } } diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index 6d4dd4f323..ead129e3c5 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -460,7 +460,7 @@ pub fn setTimestamps( last_accessed: Io.Timestamp, last_modified: Io.Timestamp, ) SetTimestampsError!void { - return io.vtable.fileUpdateTimes(io.userdata, file, last_accessed, last_modified); + return io.vtable.fileSetTimestamps(io.userdata, file, last_accessed, last_modified); } /// Sets the accessed and modification timestamps of `file` to the current wall diff --git a/lib/std/Io/File/Atomic.zig b/lib/std/Io/File/Atomic.zig index 7d412703ed..340303ca39 100644 --- a/lib/std/Io/File/Atomic.zig +++ b/lib/std/Io/File/Atomic.zig @@ -66,7 +66,7 @@ pub fn deinit(af: *Atomic) void { af.* = undefined; } -pub const FlushError = File.WriteError; +pub const FlushError = File.Writer.Error; pub fn flush(af: *Atomic) FlushError!void { af.file_writer.interface.flush() catch |err| switch (err) { diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index d8c30ddbef..cc971edbf4 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -158,7 +158,7 @@ pub fn sendFile(io_w: *Io.Writer, file_reader: *Io.File.Reader, limit: Io.Limit) fn sendFilePositional(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize { const io = w.io; const header = w.interface.buffered(); - const n = io.vtable.fileSendFilePositional(io.userdata, w.file, header, file_reader, limit, w.pos) catch |err| switch (err) { + const n = io.vtable.fileWriteFilePositional(io.userdata, w.file, header, file_reader, limit, w.pos) catch |err| switch (err) { error.Unseekable => { w.mode = w.mode.toStreaming(); const pos = w.pos; @@ -187,7 +187,7 @@ fn sendFilePositional(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit) fn sendFileStreaming(w: *Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize { const io = w.io; const header = w.interface.buffered(); - const n = io.vtable.fileSendFileStreaming(io.userdata, w.file, header, file_reader, limit) catch |err| switch (err) { + const n = io.vtable.fileWriteFileStreaming(io.userdata, w.file, header, file_reader, limit) catch |err| switch (err) { error.Canceled => { w.err = error.Canceled; return error.WriteFailed; @@ -226,7 +226,7 @@ pub fn seekToUnbuffered(w: *Writer, offset: u64) SeekError!void { } } -pub const EndError = File.SetEndPosError || Io.Writer.Error; +pub const EndError = File.SetLengthError || Io.Writer.Error; /// Flushes any buffered data and sets the end position of the file. /// @@ -236,11 +236,12 @@ pub const EndError = File.SetEndPosError || Io.Writer.Error; /// Flush failure is handled by setting `err` so that it can be handled /// along with other write failures. pub fn end(w: *Writer) EndError!void { + const io = w.io; try w.interface.flush(); switch (w.mode) { .positional, .positional_reading, - => w.file.setLength(w.pos) catch |err| switch (err) { + => w.file.setLength(io, w.pos) catch |err| switch (err) { error.NonResizable => return, else => |e| return e, }, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 16e8930267..945e47d5fd 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -4,8 +4,6 @@ const builtin = @import("builtin"); const native_os = builtin.os.tag; const is_windows = native_os == .windows; const is_darwin = native_os.isDarwin(); -const windows = std.os.windows; -const ws2_32 = std.os.windows.ws2_32; const is_debug = builtin.mode == .Debug; const std = @import("../std.zig"); @@ -19,6 +17,8 @@ const Allocator = std.mem.Allocator; const Alignment = std.mem.Alignment; const assert = std.debug.assert; const posix = std.posix; +const windows = std.os.windows; +const ws2_32 = std.os.windows.ws2_32; /// Thread-safe. allocator: Allocator, @@ -1452,7 +1452,7 @@ const dirMake = switch (native_os) { else => dirMakePosix, }; -fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void { +fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); @@ -1461,7 +1461,7 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir try current_thread.beginSyscall(); while (true) { - switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, mode))) { + switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, permissions.toMode()))) { .SUCCESS => { current_thread.endSyscall(); return; @@ -1498,8 +1498,8 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir } } -fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void { - if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, mode); +fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void { + if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, permissions); const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); try current_thread.beginSyscall(); @@ -1540,13 +1540,13 @@ fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir. } } -fn dirMakeWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void { +fn dirMakeWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); try current_thread.checkCancel(); const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path); - _ = mode; + _ = permissions; // TODO use this value const sub_dir_handle = windows.OpenFile(sub_path_w.span(), .{ .dir = dir.handle, .access_mask = .{ @@ -1570,7 +1570,7 @@ fn dirMakePath( userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, - mode: Dir.Mode, + permissions: Dir.Permissions, ) Dir.MakePathError!Dir.MakePathStatus { const t: *Threaded = @ptrCast(@alignCast(userdata)); @@ -1578,7 +1578,7 @@ fn dirMakePath( var status: Dir.MakePathStatus = .existed; var component = it.last() orelse return error.BadPathName; while (true) { - if (dirMake(t, dir, component.path, mode)) |_| { + if (dirMake(t, dir, component.path, permissions)) |_| { status = .created; } else |err| switch (err) { error.PathAlreadyExists => { @@ -4945,7 +4945,7 @@ fn dirSetTimestamps( sub_path: []const u8, last_accessed: Io.Timestamp, last_modified: Io.Timestamp, - options: File.SetTimestampsOptions, + options: Dir.SetTimestampsOptions, ) File.SetTimestampsError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); @@ -4997,7 +4997,7 @@ fn dirSetTimestampsNow( userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, - options: File.SetTimestampsOptions, + options: Dir.SetTimestampsOptions, ) File.SetTimestampsError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); @@ -6271,7 +6271,7 @@ fn fileWriteStreaming( header: []const u8, data: []const []const u8, splat: usize, -) File.WriteStreamingError!usize { +) File.Writer.Error!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); @@ -9690,7 +9690,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) File.Stat { return .{ .inode = stx.ino, .size = stx.size, - .mode = stx.mode, + .permissions = .fromMode(stx.mode), .kind = switch (stx.mode & std.os.linux.S.IFMT) { std.os.linux.S.IFDIR => .directory, std.os.linux.S.IFCHR => .character_device, @@ -9714,7 +9714,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat { return .{ .inode = st.ino, .size = @bitCast(st.size), - .mode = st.mode, + .permissions = .fromMode(st.mode), .kind = k: { const m = st.mode & posix.S.IFMT; switch (m) { @@ -10019,7 +10019,7 @@ fn lookupHosts( options: HostName.LookupOptions, ) !void { const t_io = io(t); - const file = File.openAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) { + const file = Dir.openFileAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.AccessDenied, diff --git a/lib/std/Io/net/HostName.zig b/lib/std/Io/net/HostName.zig index 628a97d1f8..84484b9dc1 100644 --- a/lib/std/Io/net/HostName.zig +++ b/lib/std/Io/net/HostName.zig @@ -343,7 +343,7 @@ pub const ResolvConf = struct { .attempts = 2, }; - const file = Io.File.openAbsolute(io, "/etc/resolv.conf", .{}) catch |err| switch (err) { + const file = Io.Dir.openFileAbsolute(io, "/etc/resolv.conf", .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.AccessDenied, diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 02bdb591d0..891c751ef2 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -114,7 +114,7 @@ test "setEndPos" { try expect((try file.getPos()) == 100); } -test "updateTimes" { +test "setTimestamps" { const io = testing.io; var tmp = tmpDir(.{}); @@ -126,7 +126,8 @@ test "updateTimes" { const stat_old = try file.stat(io); // Set atime and mtime to 5s before - try file.updateTimes( + try file.setTimestamps( + io, stat_old.atime.subDuration(.fromSeconds(5)), stat_old.mtime.subDuration(.fromSeconds(5)), ); diff --git a/lib/std/Io/tty.zig b/lib/std/Io/tty.zig index d1f8b576c2..65f1b7dad5 100644 --- a/lib/std/Io/tty.zig +++ b/lib/std/Io/tty.zig @@ -2,6 +2,7 @@ const builtin = @import("builtin"); const native_os = builtin.os.tag; const std = @import("std"); +const Io = std.Io; const File = std.Io.File; const process = std.process; const windows = std.os.windows; @@ -39,7 +40,7 @@ pub const Config = union(enum) { /// This includes feature checks for ANSI escape codes and the Windows console API, as well as /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default. /// Will attempt to enable ANSI escape code support if necessary/possible. - pub fn detect(file: File) Config { + pub fn detect(io: Io, file: File) Config { const force_color: ?bool = if (builtin.os.tag == .wasi) null // wasi does not support environment variables else if (process.hasNonEmptyEnvVarConstant("NO_COLOR")) @@ -51,7 +52,7 @@ pub const Config = union(enum) { if (force_color == false) return .no_color; - if (file.enableAnsiEscapeCodes()) |_| { + if (file.enableAnsiEscapeCodes(io)) |_| { return .escape_codes; } else |_| {} @@ -74,9 +75,9 @@ pub const Config = union(enum) { reset_attributes: u16, }; - pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || std.Io.Writer.Error; + pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; - pub fn setColor(conf: Config, w: *std.Io.Writer, color: Color) SetColorError!void { + pub fn setColor(conf: Config, w: *Io.Writer, color: Color) SetColorError!void { nosuspend switch (conf) { .no_color => return, .escape_codes => { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index cb79bb7855..8f1cd50e8e 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -286,11 +286,13 @@ pub fn unlockStdErr() void { pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } { const global = struct { var conf: ?tty.Config = null; + var single_threaded_io: Io.Threaded = .init_single_threaded; }; + const io = global.single_threaded_io.io(); const w = std.Progress.lockStderrWriter(buffer); // The stderr lock also locks access to `global.conf`. if (global.conf == null) { - global.conf = .detect(.stderr()); + global.conf = .detect(io, .stderr()); } return .{ w, global.conf.? }; } diff --git a/lib/std/debug/Info.zig b/lib/std/debug/Info.zig index 6b31f03f72..34e79227d1 100644 --- a/lib/std/debug/Info.zig +++ b/lib/std/debug/Info.zig @@ -42,7 +42,7 @@ pub fn load( var file = try path.root_dir.handle.openFile(io, path.sub_path, .{}); defer file.close(io); - var elf_file: ElfFile = try .load(gpa, file, null, &.none); + var elf_file: ElfFile = try .load(gpa, io, file, null, &.none); errdefer elf_file.deinit(gpa); if (elf_file.dwarf == null) return error.MissingDebugInfo; @@ -58,7 +58,7 @@ pub fn load( const path_str = try path.toString(gpa); defer gpa.free(path_str); - var macho_file: MachOFile = try .load(gpa, path_str, arch); + var macho_file: MachOFile = try .load(gpa, io, path_str, arch); errdefer macho_file.deinit(gpa); return .{ @@ -85,6 +85,7 @@ pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError || error{U pub fn resolveAddresses( info: *Info, gpa: Allocator, + io: Io, /// Asserts the addresses are in ascending order. sorted_pc_addrs: []const u64, /// Asserts its length equals length of `sorted_pc_addrs`. @@ -97,7 +98,7 @@ pub fn resolveAddresses( // Resolving all of the addresses at once unfortunately isn't so easy in Mach-O binaries // due to split debug information. For now, we'll just resolve the addreses one by one. for (sorted_pc_addrs, output) |pc_addr, *src_loc| { - const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, pc_addr) catch |err| switch (err) { + const dwarf, const dwarf_pc_addr = mf.getDwarfForAddress(gpa, io, pc_addr) catch |err| switch (err) { error.InvalidMachO, error.InvalidDwarf => return error.InvalidDebugInfo, else => |e| return e, }; diff --git a/lib/std/heap/debug_allocator.zig b/lib/std/heap/debug_allocator.zig index 3183becd82..27b1b9179f 100644 --- a/lib/std/heap/debug_allocator.zig +++ b/lib/std/heap/debug_allocator.zig @@ -179,6 +179,8 @@ pub fn DebugAllocator(comptime config: Config) type { total_requested_bytes: @TypeOf(total_requested_bytes_init) = total_requested_bytes_init, requested_memory_limit: @TypeOf(requested_memory_limit_init) = requested_memory_limit_init, mutex: @TypeOf(mutex_init) = mutex_init, + /// Set this value differently to affect how errors and leaks are logged. + tty_config: std.Io.tty.Config = .no_color, const Self = @This(); @@ -458,9 +460,9 @@ pub fn DebugAllocator(comptime config: Config) type { /// Emits log messages for leaks and then returns the number of detected leaks (0 if no leaks were detected). pub fn detectLeaks(self: *Self) usize { - var leaks: usize = 0; + const tty_config = self.tty_config; - const tty_config: std.Io.tty.Config = .detect(.stderr()); + var leaks: usize = 0; for (self.buckets, 0..) |init_optional_bucket, size_class_index| { var optional_bucket = init_optional_bucket; @@ -533,10 +535,15 @@ pub fn DebugAllocator(comptime config: Config) type { @memset(addr_buf[@min(st.index, addr_buf.len)..], 0); } - fn reportDoubleFree(ret_addr: usize, alloc_stack_trace: StackTrace, free_stack_trace: StackTrace) void { + fn reportDoubleFree( + tty_config: std.Io.tty.Config, + ret_addr: usize, + alloc_stack_trace: StackTrace, + free_stack_trace: StackTrace, + ) void { + @branchHint(.cold); var addr_buf: [stack_n]usize = undefined; const second_free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf); - const tty_config: std.Io.tty.Config = .detect(.stderr()); log.err("Double free detected. Allocation: {f} First free: {f} Second free: {f}", .{ std.debug.FormatStackTrace{ .stack_trace = alloc_stack_trace, @@ -580,7 +587,7 @@ pub fn DebugAllocator(comptime config: Config) type { if (config.retain_metadata and entry.value_ptr.freed) { if (config.safety) { - reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); + reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); @panic("Unrecoverable double free"); } else { unreachable; @@ -588,9 +595,10 @@ pub fn DebugAllocator(comptime config: Config) type { } if (config.safety and old_mem.len != entry.value_ptr.bytes.len) { + @branchHint(.cold); var addr_buf: [stack_n]usize = undefined; const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf); - const tty_config: std.Io.tty.Config = .detect(.stderr()); + const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ entry.value_ptr.bytes.len, old_mem.len, @@ -693,7 +701,7 @@ pub fn DebugAllocator(comptime config: Config) type { if (config.retain_metadata and entry.value_ptr.freed) { if (config.safety) { - reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); + reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); return; } else { unreachable; @@ -701,9 +709,10 @@ pub fn DebugAllocator(comptime config: Config) type { } if (config.safety and old_mem.len != entry.value_ptr.bytes.len) { + @branchHint(.cold); var addr_buf: [stack_n]usize = undefined; const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf); - const tty_config: std.Io.tty.Config = .detect(.stderr()); + const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ entry.value_ptr.bytes.len, old_mem.len, @@ -915,6 +924,7 @@ pub fn DebugAllocator(comptime config: Config) type { if (!is_used) { if (config.safety) { reportDoubleFree( + self.tty_config, return_address, bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .free), @@ -935,7 +945,8 @@ pub fn DebugAllocator(comptime config: Config) type { var addr_buf: [stack_n]usize = undefined; const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf); if (old_memory.len != requested_size) { - const tty_config: std.Io.tty.Config = .detect(.stderr()); + @branchHint(.cold); + const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ requested_size, old_memory.len, @@ -950,7 +961,8 @@ pub fn DebugAllocator(comptime config: Config) type { }); } if (alignment != slot_alignment) { - const tty_config: std.Io.tty.Config = .detect(.stderr()); + @branchHint(.cold); + const tty_config = self.tty_config; log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{ slot_alignment.toByteUnits(), alignment.toByteUnits(), @@ -1028,6 +1040,7 @@ pub fn DebugAllocator(comptime config: Config) type { const is_used = @as(u1, @truncate(used_byte.* >> used_bit_index)) != 0; if (!is_used) { reportDoubleFree( + self.tty_config, return_address, bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .free), @@ -1044,7 +1057,8 @@ pub fn DebugAllocator(comptime config: Config) type { var addr_buf: [stack_n]usize = undefined; const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf); if (memory.len != requested_size) { - const tty_config: std.Io.tty.Config = .detect(.stderr()); + @branchHint(.cold); + const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ requested_size, memory.len, @@ -1059,7 +1073,8 @@ pub fn DebugAllocator(comptime config: Config) type { }); } if (alignment != slot_alignment) { - const tty_config: std.Io.tty.Config = .detect(.stderr()); + @branchHint(.cold); + const tty_config = self.tty_config; log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{ slot_alignment.toByteUnits(), alignment.toByteUnits(), diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 17139e66b8..05cc4b3944 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -470,7 +470,7 @@ pub fn run(allocator: Allocator, io: Io, args: struct { return .{ .stdout = try stdout.toOwnedSlice(allocator), .stderr = try stderr.toOwnedSlice(allocator), - .term = try child.wait(), + .term = try child.wait(io), }; } diff --git a/lib/std/zig.zig b/lib/std/zig.zig index c8a0dcde3b..ac6a0787de 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -60,9 +60,9 @@ pub const Color = enum { .off => .no_color, }; } - pub fn detectTtyConf(color: Color) Io.tty.Config { + pub fn detectTtyConf(color: Color, io: Io) Io.tty.Config { return switch (color) { - .auto => .detect(.stderr()), + .auto => .detect(io, .stderr()), .on => .escape_codes, .off => .no_color, }; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index cfb02fba38..c675af3e22 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -51,10 +51,10 @@ pub const UpdateError = error{ } || codegen.GenerateSymbolError || Io.File.OpenError || - Io.File.SetEndPosError || + Io.File.LengthError || Io.File.CopyRangeError || - Io.File.PReadError || - Io.File.PWriteError; + Io.File.ReadPositionalError || + Io.File.WritePositionalError; pub const FlushError = UpdateError; diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index a61c6e764c..4e58aae01a 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -28,7 +28,7 @@ writers: std.SinglyLinkedList, pub const growth_factor = 4; -pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.SetEndPosError || error{ +pub const Error = std.posix.MMapError || std.posix.MRemapError || Io.File.LengthError || error{ NotFile, SystemResources, IsDir, diff --git a/src/main.zig b/src/main.zig index 3835e75949..bb940a1fe7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -162,17 +162,20 @@ var debug_allocator: std.heap.DebugAllocator(.{ .stack_trace_frames = build_options.mem_leak_frames, }) = .init; +const use_debug_allocator = build_options.debug_gpa or + (native_os != .wasi and !builtin.link_libc and switch (builtin.mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, + }); + pub fn main() anyerror!void { - const gpa, const is_debug = gpa: { - if (build_options.debug_gpa) break :gpa .{ debug_allocator.allocator(), true }; - if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false }; - if (builtin.link_libc) break :gpa .{ std.heap.c_allocator, false }; - break :gpa switch (builtin.mode) { - .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, - .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, - }; + const gpa = gpa: { + if (use_debug_allocator) break :gpa debug_allocator.allocator(); + if (native_os == .wasi) break :gpa std.heap.wasm_allocator; + if (builtin.link_libc) break :gpa std.heap.c_allocator; + break :gpa std.heap.smp_allocator; }; - defer if (is_debug) { + defer if (use_debug_allocator) { _ = debug_allocator.deinit(); }; var arena_instance = std.heap.ArenaAllocator.init(gpa); @@ -244,6 +247,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { threaded.stack_size = thread_stack_size; const io = threaded.io(); + debug_allocator.tty_config = .detect(io, .stderr()); + const cmd = args[1]; const cmd_args = args[2..]; if (mem.eql(u8, cmd, "build-exe")) { diff --git a/test/link/macho.zig b/test/link/macho.zig index 0f3df8c2d8..12a74e0825 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -868,9 +868,10 @@ fn testLayout(b: *Build, opts: Options) *Step { } fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step { + const io = b.graph.io; const test_step = addTestStep(b, "link-directly-cpp-tbd", opts); - const sdk = std.zig.system.darwin.getSdk(b.allocator, &opts.target.result) orelse + const sdk = std.zig.system.darwin.getSdk(b.allocator, io, &opts.target.result) orelse @panic("macOS SDK is required to run the test"); const exe = addExecutable(b, opts, .{ diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 82b59f722d..f72748e5ef 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -339,7 +339,7 @@ fn addFromDirInner( var it = try iterable_dir.walk(ctx.arena); var filenames: ArrayList([]const u8) = .empty; - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (entry.kind != .file) continue; // Ignore stuff such as .swp files @@ -431,9 +431,10 @@ fn addFromDirInner( } } -pub fn init(gpa: Allocator, arena: Allocator) Cases { +pub fn init(gpa: Allocator, arena: Allocator, io: Io) Cases { return .{ .gpa = gpa, + .io = io, .cases = .init(gpa), .arena = arena, }; diff --git a/test/standalone/ios/build.zig b/test/standalone/ios/build.zig index b07b5b17ea..b87d55993b 100644 --- a/test/standalone/ios/build.zig +++ b/test/standalone/ios/build.zig @@ -23,7 +23,9 @@ pub fn build(b: *std.Build) void { }), }); - if (std.zig.system.darwin.getSdk(b.allocator, &target.result)) |sdk| { + const io = b.graph.io; + + if (std.zig.system.darwin.getSdk(b.allocator, io, &target.result)) |sdk| { b.sysroot = sdk; exe.root_module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) }); exe.root_module.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) }); diff --git a/test/standalone/self_exe_symlink/build.zig b/test/standalone/self_exe_symlink/build.zig index 651740c04b..137848b953 100644 --- a/test/standalone/self_exe_symlink/build.zig +++ b/test/standalone/self_exe_symlink/build.zig @@ -9,10 +9,6 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const target = b.graph.host; - // The test requires getFdPath in order to to get the path of the - // File returned by openSelfExe - if (!std.os.isGetFdPathSupportedOnTarget(target.result.os)) return; - const main = b.addExecutable(.{ .name = "main", .root_module = b.createModule(.{ diff --git a/test/tests.zig b/test/tests.zig index 015c3535bb..ee324329e2 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2632,7 +2632,7 @@ pub fn addCases( const gpa = b.allocator; const io = b.graph.io; - var cases = @import("src/Cases.zig").init(gpa, arena); + var cases = @import("src/Cases.zig").init(gpa, arena, io); var dir = try b.build_root.handle.openDir(io, "test/cases", .{ .iterate = true }); defer dir.close(io); -- cgit v1.2.3 From 90f7259ef17e8e07c2c3f71bea65d103cfe52c07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 21:57:46 -0800 Subject: std.Progress: use a global static Io instance This decision should be audited and discussed. Some factors: * Passing an Io instance into start. * Avoiding reference to global static instance if it won't be used, so that it doesn't bloat the executable. * Being able to use std.debug.print, and related functionality when debugging std.Io instances and std.Progress. --- lib/std/Io/Threaded.zig | 20 +++++++++++++- lib/std/Progress.zig | 71 ++++++++++++++++++++++++++----------------------- lib/std/debug.zig | 11 ++++---- 3 files changed, 62 insertions(+), 40 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 945e47d5fd..459440f5a6 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -771,6 +771,7 @@ pub fn io(t: *Threaded) Io { .windows => netWriteWindows, else => netWritePosix, }, + .netWriteFile = netWriteFile, .netSend = switch (native_os) { .windows => netSendWindows, else => netSendPosix, @@ -872,6 +873,7 @@ pub fn ioBasic(t: *Threaded) Io { .netClose = netCloseUnavailable, .netRead = netReadUnavailable, .netWrite = netWriteUnavailable, + .netWriteFile = netWriteFileUnavailable, .netSend = netSendUnavailable, .netReceive = netReceiveUnavailable, .netInterfaceNameResolve = netInterfaceNameResolveUnavailable, @@ -6782,7 +6784,7 @@ fn netWriteFile( header: []const u8, file_reader: *File.Reader, limit: Io.Limit, -) net.Stream.WriteFileError!usize { +) net.Stream.Writer.WriteFileError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; _ = socket_handle; @@ -6792,6 +6794,22 @@ fn netWriteFile( return error.Unimplemented; // TODO } +fn netWriteFileUnavailable( + userdata: ?*anyopaque, + socket_handle: net.Socket.Handle, + header: []const u8, + file_reader: *File.Reader, + limit: Io.Limit, +) net.Stream.Writer.WriteFileError!usize { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; + _ = socket_handle; + _ = header; + _ = file_reader; + _ = limit; + return error.NetworkDown; +} + fn fileWriteFilePositional( userdata: ?*anyopaque, file: File, diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index d8b22c2db0..a839de8c4f 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -458,13 +458,16 @@ pub fn start(options: Options) Node { if (noop_impl) return Node.none; + const io = static_threaded_io.io(); + if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| { global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{ - @as(posix.fd_t, switch (@typeInfo(posix.fd_t)) { + io, + @as(Io.File, .{ .handle = switch (@typeInfo(posix.fd_t)) { .int => ipc_fd, .pointer => @ptrFromInt(ipc_fd), else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)), - }), + } }), }) catch |err| { std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)}); return Node.none; @@ -476,9 +479,9 @@ pub fn start(options: Options) Node { } const stderr: Io.File = .stderr(); global_progress.terminal = stderr; - if (stderr.enableAnsiEscapeCodes()) |_| { + if (stderr.enableAnsiEscapeCodes(io)) |_| { global_progress.terminal_mode = .ansi_escape_codes; - } else |_| if (is_windows and stderr.isTty()) { + } else |_| if (is_windows and stderr.isTty(io)) { global_progress.terminal_mode = TerminalMode{ .windows_api = .{ .code_page = windows.kernel32.GetConsoleOutputCP(), } }; @@ -499,8 +502,8 @@ pub fn start(options: Options) Node { if (switch (global_progress.terminal_mode) { .off => unreachable, // handled a few lines above - .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{}), - .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{}) else unreachable, + .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{io}), + .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{io}) else unreachable, }) |thread| { global_progress.update_thread = thread; } else |err| { @@ -531,7 +534,7 @@ fn wait(timeout_ns: u64) bool { return resize_flag or (global_progress.cols == 0); } -fn updateThreadRun() void { +fn updateThreadRun(io: Io) void { // Store this data in the thread so that it does not need to be part of the // linker data of the main executable. var serialized_buffer: Serialized.Buffer = undefined; @@ -544,7 +547,7 @@ fn updateThreadRun() void { const buffer, _ = computeRedraw(&serialized_buffer); if (stderr_mutex.tryLock()) { defer stderr_mutex.unlock(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; } } @@ -555,7 +558,7 @@ fn updateThreadRun() void { if (@atomicLoad(bool, &global_progress.done, .monotonic)) { stderr_mutex.lock(); defer stderr_mutex.unlock(); - return clearWrittenWithEscapeCodes() catch {}; + return clearWrittenWithEscapeCodes(io) catch {}; } maybeUpdateSize(resize_flag); @@ -563,7 +566,7 @@ fn updateThreadRun() void { const buffer, _ = computeRedraw(&serialized_buffer); if (stderr_mutex.tryLock()) { defer stderr_mutex.unlock(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; } } @@ -577,7 +580,7 @@ fn windowsApiWriteMarker() void { _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null); } -fn windowsApiUpdateThreadRun() void { +fn windowsApiUpdateThreadRun(io: Io) void { var serialized_buffer: Serialized.Buffer = undefined; { @@ -589,7 +592,7 @@ fn windowsApiUpdateThreadRun() void { if (stderr_mutex.tryLock()) { defer stderr_mutex.unlock(); windowsApiWriteMarker(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; windowsApiMoveToMarker(nl_n) catch return; } @@ -611,7 +614,7 @@ fn windowsApiUpdateThreadRun() void { defer stderr_mutex.unlock(); clearWrittenWindowsApi() catch return; windowsApiWriteMarker(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; windowsApiMoveToMarker(nl_n) catch return; } @@ -624,8 +627,9 @@ fn windowsApiUpdateThreadRun() void { /// /// The lock is recursive; the same thread may hold the lock multiple times. pub fn lockStdErr() void { + const io = stderr_file_writer.io; stderr_mutex.lock(); - clearWrittenWithEscapeCodes() catch {}; + clearWrittenWithEscapeCodes(io) catch {}; } pub fn unlockStdErr() void { @@ -636,10 +640,12 @@ pub fn unlockStdErr() void { const stderr_writer: *Writer = &stderr_file_writer.interface; /// Protected by `stderr_mutex`. var stderr_file_writer: Io.File.Writer = .{ + .io = static_threaded_io.io(), .interface = Io.File.Writer.initInterface(&.{}), .file = if (is_windows) undefined else .stderr(), .mode = .streaming, }; +var static_threaded_io: Io.Threaded = .init_single_threaded; /// Allows the caller to freely write to the returned `Writer`, /// initialized with `buffer`, until `unlockStderrWriter` is called. @@ -647,9 +653,10 @@ var stderr_file_writer: Io.File.Writer = .{ /// During the lock, any `std.Progress` information is cleared from the terminal. /// /// The lock is recursive; the same thread may hold the lock multiple times. -pub fn lockStderrWriter(buffer: []u8) *Writer { +pub fn lockStderrWriter(buffer: []u8) *Io.Writer { + const io = stderr_file_writer.io; stderr_mutex.lock(); - clearWrittenWithEscapeCodes() catch {}; + clearWrittenWithEscapeCodes(io) catch {}; if (is_windows) stderr_file_writer.file = .stderr(); stderr_writer.flush() catch {}; stderr_writer.buffer = buffer; @@ -663,7 +670,7 @@ pub fn unlockStderrWriter() void { stderr_mutex.unlock(); } -fn ipcThreadRun(fd: posix.fd_t) anyerror!void { +fn ipcThreadRun(io: Io, file: Io.File) anyerror!void { // Store this data in the thread so that it does not need to be part of the // linker data of the main executable. var serialized_buffer: Serialized.Buffer = undefined; @@ -675,7 +682,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { return; const serialized = serialize(&serialized_buffer); - writeIpc(fd, serialized) catch |err| switch (err) { + writeIpc(io, file, serialized) catch |err| switch (err) { error.BrokenPipe => return, }; } @@ -687,7 +694,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { return; const serialized = serialize(&serialized_buffer); - writeIpc(fd, serialized) catch |err| switch (err) { + writeIpc(io, file, serialized) catch |err| switch (err) { error.BrokenPipe => return, }; } @@ -786,11 +793,11 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { } } -fn clearWrittenWithEscapeCodes() anyerror!void { +fn clearWrittenWithEscapeCodes(io: Io) anyerror!void { if (noop_impl or !global_progress.need_clear) return; global_progress.need_clear = false; - try write(clear ++ progress_remove); + try write(io, clear ++ progress_remove); } /// U+25BA or ► @@ -1417,13 +1424,13 @@ fn withinRowLimit(p: *Progress, nl_n: usize) bool { return nl_n + 2 < p.rows; } -fn write(buf: []const u8) anyerror!void { - try global_progress.terminal.writeAll(buf); +fn write(io: Io, buf: []const u8) anyerror!void { + try global_progress.terminal.writeStreamingAll(io, buf); } var remaining_write_trash_bytes: usize = 0; -fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { +fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!void { // Byteswap if necessary to ensure little endian over the pipe. This is // needed because the parent or child process might be running in qemu. if (is_big_endian) for (serialized.storage) |*s| s.byteSwap(); @@ -1434,11 +1441,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { const storage = std.mem.sliceAsBytes(serialized.storage); const parents = std.mem.sliceAsBytes(serialized.parents); - var vecs: [3]posix.iovec_const = .{ - .{ .base = header.ptr, .len = header.len }, - .{ .base = storage.ptr, .len = storage.len }, - .{ .base = parents.ptr, .len = parents.len }, - }; + var vecs: [3][]const u8 = .{ header, storage, parents }; // Ensures the packet can fit in the pipe buffer. const upper_bound_msg_len = 1 + node_storage_buffer_len * @sizeOf(Node.Storage) + @@ -1449,7 +1452,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { // We do this in a separate write call to give a better chance for the // writev below to be in a single packet. const n = @min(parents.len, remaining_write_trash_bytes); - if (posix.write(fd, parents[0..n])) |written| { + if (io.vtable.fileWriteStreaming(io.userdata, file, &.{}, &.{parents[0..n]}, 1)) |written| { remaining_write_trash_bytes -= written; continue; } else |err| switch (err) { @@ -1464,7 +1467,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { // If this write would block we do not want to keep trying, but we need to // know if a partial message was written. - if (writevNonblock(fd, &vecs)) |written| { + if (writevNonblock(io, file, &vecs)) |written| { const total = header.len + storage.len + parents.len; if (written < total) { remaining_write_trash_bytes = total - written; @@ -1479,7 +1482,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { } } -fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usize { +fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error!usize { var iov_index: usize = 0; var written: usize = 0; var total_written: usize = 0; @@ -1488,9 +1491,9 @@ fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usi written >= iov[iov_index].len else return total_written) : (iov_index += 1) written -= iov[iov_index].len; - iov[iov_index].base += written; + iov[iov_index].ptr += written; iov[iov_index].len -= written; - written = try posix.writev(fd, iov[iov_index..]); + written = try io.vtable.fileWriteStreaming(io.userdata, file, &.{}, iov, 1); if (written == 0) return total_written; total_written += written; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 8f1cd50e8e..6ffa254271 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -286,13 +286,12 @@ pub fn unlockStdErr() void { pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } { const global = struct { var conf: ?tty.Config = null; - var single_threaded_io: Io.Threaded = .init_single_threaded; }; - const io = global.single_threaded_io.io(); const w = std.Progress.lockStderrWriter(buffer); + const file_writer: *File.Writer = @fieldParentPtr("interface", w); // The stderr lock also locks access to `global.conf`. if (global.conf == null) { - global.conf = .detect(io, .stderr()); + global.conf = .detect(file_writer.io, .stderr()); } return .{ w, global.conf.? }; } @@ -619,13 +618,15 @@ pub const StackUnwindOptions = struct { /// /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace { - var threaded: Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} }; if (!std.options.allow_stack_tracing) return empty_trace; var it: StackIterator = .init(options.context); defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; + + var threaded: Io.Threaded = .init_single_threaded; + const io = threaded.ioBasic(); + var total_frames: usize = 0; var index: usize = 0; var wait_for = options.first_address; -- cgit v1.2.3 From 03526c59d4e2a00f83347cf06c741a3ed4fec520 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Dec 2025 23:14:34 -0800 Subject: std.debug: fix printLineFromFile --- lib/compiler/test_runner.zig | 4 ++-- lib/std/Io/File.zig | 2 +- lib/std/debug.zig | 10 ++++------ lib/std/testing.zig | 6 +++--- 4 files changed, 10 insertions(+), 12 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index 07a6724ec0..b0ef17a7e9 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -75,7 +75,7 @@ pub fn main() void { fn mainServer() !void { @disableInstrumentation(); var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io.io(), &stdin_buffer); - var stdout_writer = Io.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(runner_threaded_io.io(), &stdout_buffer); var server = try std.zig.Server.init(.{ .in = &stdin_reader.interface, .out = &stdout_writer.interface, @@ -228,7 +228,7 @@ fn mainTerminal() void { .root_name = "Test", .estimated_total_items = test_fn_list.len, }); - const have_tty = Io.File.stderr().isTty(); + const have_tty = Io.File.stderr().isTty(runner_threaded_io.io()) catch unreachable; var leaks: usize = 0; for (test_fn_list, 0..) |test_fn, i| { diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index 7718b56f8a..ccf18a8f56 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -280,7 +280,7 @@ pub fn sync(file: File, io: Io) SyncError!void { /// See also: /// * `enableAnsiEscapeCodes` /// * `supportsAnsiEscapeCodes`. -pub fn isTty(file: File, io: Io) bool { +pub fn isTty(file: File, io: Io) Io.Cancelable!bool { return io.vtable.fileIsTty(io.userdata, file); } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6ffa254271..7b111215f3 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1207,9 +1207,8 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) ! var buffer: [4096]u8 = undefined; var file_reader: File.Reader = .init(file, io, &buffer); - const r = &file_reader.interface; var line_index: usize = 0; - while (r.takeDelimiterExclusive('\n')) |line| { + while (try file_reader.interface.takeDelimiter('\n')) |line| { line_index += 1; if (line_index == source_location.line) { // TODO delete hard tabs from the language @@ -1219,9 +1218,8 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) ! try writer.writeByte('\n'); return; } - } else |err| { - return err; } + return error.EndOfStream; } test printLineFromFile { @@ -1248,7 +1246,7 @@ test printLineFromFile { defer gpa.free(path); try test_dir.dir.writeFile(io, .{ .sub_path = "one_line.zig", .data = "no new lines in this file, but one is printed anyway" }); - try expectError(error.EndOfFile, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 })); + try expectError(error.EndOfStream, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 })); try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try expectEqualStrings("no new lines in this file, but one is printed anyway\n", aw.written()); @@ -1317,7 +1315,7 @@ test printLineFromFile { const writer = &file_writer.interface; try writer.splatByteAll('a', 3 * std.heap.page_size_max); - try expectError(error.EndOfFile, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 })); + try expectError(error.EndOfStream, printLineFromFile(io, output_stream, .{ .file_name = path, .line = 2, .column = 0 })); try printLineFromFile(io, output_stream, .{ .file_name = path, .line = 1, .column = 0 }); try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", aw.written()); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 9eb2ad5e96..ab9f1f73c3 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -629,12 +629,12 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); const cwd = Io.Dir.cwd(); - var cache_dir = cwd.makeOpenPath(".zig-cache", .{}) catch + var cache_dir = cwd.makeOpenPath(io, ".zig-cache", .{}) catch @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); defer cache_dir.close(io); - const parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch + const parent_dir = cache_dir.makeOpenPath(io, "tmp", .{}) catch @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); - const dir = parent_dir.makeOpenPath(&sub_path, opts) catch + const dir = parent_dir.makeOpenPath(io, &sub_path, .{ .open_options = opts }) catch @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); return .{ -- cgit v1.2.3 From 78d262d96ee6200c7a6bc0a41fe536d263c24d92 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Dec 2025 18:44:39 -0800 Subject: std: WIP: debug-level stderr writing --- lib/std/Io.zig | 31 ++++++++++ lib/std/Io/Dir.zig | 5 +- lib/std/Io/Threaded.zig | 32 +++++++++++ lib/std/Progress.zig | 149 +++++++++++++++++------------------------------- lib/std/debug.zig | 73 ++++++++++++++++-------- lib/std/log.zig | 16 +++++- 6 files changed, 181 insertions(+), 125 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 193998f037..833cc4ec0f 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -560,6 +560,13 @@ pub const net = @import("Io/net.zig"); userdata: ?*anyopaque, vtable: *const VTable, +/// This is the global, process-wide protection to coordinate stderr writes. +/// +/// The primary motivation for recursive mutex here is so that a panic while +/// stderr mutex is held still dumps the stack trace and other debug +/// information. +pub var stderr_thread_mutex: std.Thread.Mutex.Recursive = .init; + pub const VTable = struct { /// If it returns `null` it means `result` has been already populated and /// `await` will be a no-op. @@ -733,6 +740,10 @@ pub const VTable = struct { netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface, netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name, netLookup: *const fn (?*anyopaque, net.HostName, *Queue(net.HostName.LookupResult), net.HostName.LookupOptions) net.HostName.LookupError!void, + + lockStderrWriter: *const fn (?*anyopaque, buffer: []u8) Cancelable!*Writer, + tryLockStderrWriter: *const fn (?*anyopaque, buffer: []u8) ?*Writer, + unlockStderrWriter: *const fn (?*anyopaque) void, }; pub const Cancelable = error{ @@ -2167,3 +2178,23 @@ pub fn select(io: Io, s: anytype) Cancelable!SelectUnion(@TypeOf(s)) { else => unreachable, } } + +/// For doing application-level writes to the standard error stream. +/// Coordinates also with debug-level writes that are ignorant of Io interface +/// and implementations. When this returns, `stderr_thread_mutex` will be +/// locked. +/// +/// See also: +/// * `tryLockStderrWriter` +pub fn lockStderrWriter(io: Io, buffer: []u8) Cancelable!*Writer { + return io.vtable.lockStderrWriter(io.userdata, buffer); +} + +/// Same as `lockStderrWriter` but uncancelable and non-blocking. +pub fn tryLockStderrWriter(io: Io, buffer: []u8) ?*Writer { + return io.vtable.tryLockStderrWriter(io.userdata, buffer); +} + +pub fn unlockStderrWriter(io: Io) void { + return io.vtable.unlockStderrWriter(io.userdata); +} diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 9c4a1f1df8..a9d28440eb 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -342,7 +342,7 @@ pub const Walker = struct { /// Recursively iterates over a directory. /// -/// `dir` must have been opened with `OpenOptions{.iterate = true}`. +/// `dir` must have been opened with `OpenOptions.iterate` set to `true`. /// /// `Walker.deinit` releases allocated memory and directory handles. /// @@ -350,7 +350,8 @@ pub const Walker = struct { /// /// `dir` will not be closed after walking it. /// -/// See also `walkSelectively`. +/// See also: +/// * `walkSelectively` pub fn walk(dir: Dir, allocator: Allocator) Allocator.Error!Walker { return .{ .inner = try walkSelectively(dir, allocator) }; } diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 639e5cf6cd..9d6d9f979e 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -77,6 +77,8 @@ use_sendfile: UseSendfile = .default, use_copy_file_range: UseCopyFileRange = .default, use_fcopyfile: UseFcopyfile = .default, +stderr_writer: Io.Writer, + pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { enabled, disabled, @@ -9514,6 +9516,36 @@ fn netLookupFallible( return error.OptionUnsupported; } +fn lockStderrWriter(userdata: ?*anyopaque, buffer: []u8) Io.Cancelable!*Io.Writer { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + // Only global mutex since this is Threaded. + Io.stderr_thread_mutex.lock(); + if (is_windows) t.stderr_writer.file = .stderr(); + std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {}; + t.stderr_writer.flush() catch {}; + t.stderr_writer.buffer = buffer; + return &t.stderr_writer; +} + +fn tryLockStderrWriter(userdata: ?*anyopaque, buffer: []u8) ?*Io.Writer { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + // Only global mutex since this is Threaded. + if (!Io.stderr_thread_mutex.tryLock()) return null; + std.Progress.clearWrittenWithEscapeCodes(t.io()) catch {}; + if (is_windows) t.stderr_writer.file = .stderr(); + t.stderr_writer.flush() catch {}; + t.stderr_writer.buffer = buffer; + return &t.stderr_writer; +} + +fn unlockStderrWriter(userdata: ?*anyopaque) void { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + t.stderr_writer.flush() catch {}; + t.stderr_writer.end = 0; + t.stderr_writer.buffer = &.{}; + Io.stderr_thread_mutex.unlock(); +} + pub const PosixAddress = extern union { any: posix.sockaddr, in: posix.sockaddr.in, diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index a839de8c4f..5528591620 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -13,16 +13,18 @@ const assert = std.debug.assert; const posix = std.posix; const Writer = std.Io.Writer; -/// `null` if the current node (and its children) should -/// not print on update() +/// Currently this API only supports this value being set to stderr, which +/// happens automatically inside `start`. terminal: Io.File, +io: Io, + terminal_mode: TerminalMode, -update_thread: ?std.Thread, +update_worker: ?Io.Future(void), /// Atomically set by SIGWINCH as well as the root done() function. -redraw_event: std.Thread.ResetEvent, +redraw_event: Io.ResetEvent, /// Indicates a request to shut down and reset global state. /// Accessed atomically. done: bool, @@ -95,9 +97,9 @@ pub const Options = struct { /// Must be at least 200 bytes. draw_buffer: []u8 = &default_draw_buffer, /// How many nanoseconds between writing updates to the terminal. - refresh_rate_ns: u64 = 80 * std.time.ns_per_ms, + refresh_rate_ns: Io.Duration = .fromMilliseconds(80), /// How many nanoseconds to keep the output hidden - initial_delay_ns: u64 = 200 * std.time.ns_per_ms, + initial_delay_ns: Io.Duration = .fromMilliseconds(200), /// If provided, causes the progress item to have a denominator. /// 0 means unknown. estimated_total_items: usize = 0, @@ -330,7 +332,7 @@ pub const Node = struct { } else { @atomicStore(bool, &global_progress.done, true, .monotonic); global_progress.redraw_event.set(); - if (global_progress.update_thread) |thread| thread.join(); + if (global_progress.update_worker) |worker| worker.await(global_progress.io); } } @@ -391,9 +393,10 @@ pub const Node = struct { }; var global_progress: Progress = .{ + .io = undefined, .terminal = undefined, .terminal_mode = .off, - .update_thread = null, + .update_worker = null, .redraw_event = .unset, .refresh_rate_ns = undefined, .initial_delay_ns = undefined, @@ -403,6 +406,7 @@ var global_progress: Progress = .{ .done = false, .need_clear = false, .status = .working, + .start_failure = .unstarted, .node_parents = &node_parents_buffer, .node_storage = &node_storage_buffer, @@ -411,6 +415,13 @@ var global_progress: Progress = .{ .node_end_index = 0, }; +pub const StartFailure = union(enum) { + unstarted, + spawn_ipc_worker: error{ConcurrencyUnavailable}, + spawn_update_worker: error{ConcurrencyUnavailable}, + parse_env_var: error{}, +}; + const node_storage_buffer_len = 83; var node_parents_buffer: [node_storage_buffer_len]Node.Parent = undefined; var node_storage_buffer: [node_storage_buffer_len]Node.Storage = undefined; @@ -437,7 +448,7 @@ const noop_impl = builtin.single_threaded or switch (builtin.os.tag) { /// Asserts there is only one global Progress instance. /// /// Call `Node.end` when done. -pub fn start(options: Options) Node { +pub fn start(options: Options, io: Io) Node { // Ensure there is only 1 global Progress object. if (global_progress.node_end_index != 0) { debug_start_trace.dump(); @@ -458,10 +469,10 @@ pub fn start(options: Options) Node { if (noop_impl) return Node.none; - const io = static_threaded_io.io(); + global_progress.io = io; if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| { - global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{ + global_progress.update_worker = io.concurrent(ipcThreadRun, .{ io, @as(Io.File, .{ .handle = switch (@typeInfo(posix.fd_t)) { .int => ipc_fd, @@ -469,7 +480,7 @@ pub fn start(options: Options) Node { else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)), } }), }) catch |err| { - std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)}); + global_progress.start_failure = .{ .spawn_ipc_worker = err }; return Node.none; }; } else |env_err| switch (env_err) { @@ -502,17 +513,17 @@ pub fn start(options: Options) Node { if (switch (global_progress.terminal_mode) { .off => unreachable, // handled a few lines above - .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{io}), - .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{io}) else unreachable, - }) |thread| { - global_progress.update_thread = thread; + .ansi_escape_codes => io.concurrent(updateThreadRun, .{io}), + .windows_api => if (is_windows) io.concurrent(windowsApiUpdateThreadRun, .{io}) else unreachable, + }) |future| { + global_progress.update_worker = future; } else |err| { - std.log.warn("unable to spawn thread for printing progress to terminal: {s}", .{@errorName(err)}); + global_progress.start_failure = .{ .spawn_update_worker = err }; return Node.none; } }, else => |e| { - std.log.warn("invalid ZIG_PROGRESS file descriptor integer: {s}", .{@errorName(e)}); + global_progress.start_failure = .{ .parse_env_var = e }; return Node.none; }, } @@ -545,10 +556,10 @@ fn updateThreadRun(io: Io) void { maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (stderr_mutex.tryLock()) { - defer stderr_mutex.unlock(); - write(io, buffer) catch return; + if (io.tryLockStderrWriter(&.{})) |w| { + defer io.unlockStderrWriter(); global_progress.need_clear = true; + w.writeAll(buffer) catch return; } } @@ -556,18 +567,18 @@ fn updateThreadRun(io: Io) void { const resize_flag = wait(global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { - stderr_mutex.lock(); - defer stderr_mutex.unlock(); - return clearWrittenWithEscapeCodes(io) catch {}; + const w = io.lockStderrWriter(&.{}) catch return; + defer io.unlockStderrWriter(); + return clearWrittenWithEscapeCodes(w) catch {}; } maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (stderr_mutex.tryLock()) { - defer stderr_mutex.unlock(); - write(io, buffer) catch return; + if (io.tryLockStderrWriter(&.{})) |w| { + defer io.unlockStderrWriter(); global_progress.need_clear = true; + w.writeAll(buffer) catch return; } } } @@ -589,11 +600,11 @@ fn windowsApiUpdateThreadRun(io: Io) void { maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (stderr_mutex.tryLock()) { - defer stderr_mutex.unlock(); + if (io.tryLockStderrWriter()) |w| { + defer io.unlockStderrWriter(); windowsApiWriteMarker(); - write(io, buffer) catch return; global_progress.need_clear = true; + w.writeAll(buffer) catch return; windowsApiMoveToMarker(nl_n) catch return; } } @@ -602,74 +613,25 @@ fn windowsApiUpdateThreadRun(io: Io) void { const resize_flag = wait(global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { - stderr_mutex.lock(); - defer stderr_mutex.unlock(); + _ = io.lockStderrWriter() catch return; + defer io.unlockStderrWriter(); return clearWrittenWindowsApi() catch {}; } maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (stderr_mutex.tryLock()) { - defer stderr_mutex.unlock(); + if (io.tryLockStderrWriter()) |w| { + defer io.unlockStderrWriter(); clearWrittenWindowsApi() catch return; windowsApiWriteMarker(); - write(io, buffer) catch return; global_progress.need_clear = true; + w.writeAll(buffer) catch return; windowsApiMoveToMarker(nl_n) catch return; } } } -/// Allows the caller to freely write to stderr until `unlockStdErr` is called. -/// -/// During the lock, any `std.Progress` information is cleared from the terminal. -/// -/// The lock is recursive; the same thread may hold the lock multiple times. -pub fn lockStdErr() void { - const io = stderr_file_writer.io; - stderr_mutex.lock(); - clearWrittenWithEscapeCodes(io) catch {}; -} - -pub fn unlockStdErr() void { - stderr_mutex.unlock(); -} - -/// Protected by `stderr_mutex`. -const stderr_writer: *Writer = &stderr_file_writer.interface; -/// Protected by `stderr_mutex`. -var stderr_file_writer: Io.File.Writer = .{ - .io = static_threaded_io.io(), - .interface = Io.File.Writer.initInterface(&.{}), - .file = if (is_windows) undefined else .stderr(), - .mode = .streaming, -}; -var static_threaded_io: Io.Threaded = .init_single_threaded; - -/// Allows the caller to freely write to the returned `Writer`, -/// initialized with `buffer`, until `unlockStderrWriter` is called. -/// -/// During the lock, any `std.Progress` information is cleared from the terminal. -/// -/// The lock is recursive; the same thread may hold the lock multiple times. -pub fn lockStderrWriter(buffer: []u8) *Io.Writer { - const io = stderr_file_writer.io; - stderr_mutex.lock(); - clearWrittenWithEscapeCodes(io) catch {}; - if (is_windows) stderr_file_writer.file = .stderr(); - stderr_writer.flush() catch {}; - stderr_writer.buffer = buffer; - return stderr_writer; -} - -pub fn unlockStderrWriter() void { - stderr_writer.flush() catch {}; - stderr_writer.end = 0; - stderr_writer.buffer = &.{}; - stderr_mutex.unlock(); -} - fn ipcThreadRun(io: Io, file: Io.File) anyerror!void { // Store this data in the thread so that it does not need to be part of the // linker data of the main executable. @@ -793,11 +755,11 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { } } -fn clearWrittenWithEscapeCodes(io: Io) anyerror!void { +fn clearWrittenWithEscapeCodes(w: *Io.Writer) anyerror!void { if (noop_impl or !global_progress.need_clear) return; + try w.writeAll(clear ++ progress_remove); global_progress.need_clear = false; - try write(io, clear ++ progress_remove); } /// U+25BA or ► @@ -997,7 +959,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff const n = posix.read(fd, pipe_buf[bytes_read..]) catch |err| switch (err) { error.WouldBlock => break, else => |e| { - std.log.debug("failed to read child progress data: {s}", .{@errorName(e)}); + std.log.debug("failed to read child progress data: {t}", .{e}); main_storage.completed_count = 0; main_storage.estimated_total_count = 0; continue :main_loop; @@ -1424,10 +1386,6 @@ fn withinRowLimit(p: *Progress, nl_n: usize) bool { return nl_n + 2 < p.rows; } -fn write(io: Io, buf: []const u8) anyerror!void { - try global_progress.terminal.writeStreamingAll(io, buf); -} - var remaining_write_trash_bytes: usize = 0; fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!void { @@ -1459,7 +1417,7 @@ fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!voi error.WouldBlock => return, error.BrokenPipe => return error.BrokenPipe, else => |e| { - std.log.debug("failed to send progress to parent process: {s}", .{@errorName(e)}); + std.log.debug("failed to send progress to parent process: {t}", .{e}); return error.BrokenPipe; }, } @@ -1476,7 +1434,7 @@ fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!voi error.WouldBlock => {}, error.BrokenPipe => return error.BrokenPipe, else => |e| { - std.log.debug("failed to send progress to parent process: {s}", .{@errorName(e)}); + std.log.debug("failed to send progress to parent process: {t}", .{e}); return error.BrokenPipe; }, } @@ -1568,11 +1526,6 @@ const have_sigwinch = switch (builtin.os.tag) { else => false, }; -/// The primary motivation for recursive mutex here is so that a panic while -/// stderr mutex is held still dumps the stack trace and other debug -/// information. -var stderr_mutex = std.Thread.Mutex.Recursive.init; - fn copyAtomicStore(dest: []align(@alignOf(usize)) u8, src: []const u8) void { assert(dest.len == src.len); const chunked_len = dest.len / @sizeOf(usize); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 7b111215f3..7e10c5af32 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -262,17 +262,6 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { else => true, }; -/// Allows the caller to freely write to stderr until `unlockStdErr` is called. -/// -/// During the lock, any `std.Progress` information is cleared from the terminal. -pub fn lockStdErr() void { - std.Progress.lockStdErr(); -} - -pub fn unlockStdErr() void { - std.Progress.unlockStdErr(); -} - /// Allows the caller to freely write to stderr until `unlockStderrWriter` is called. /// /// During the lock, any `std.Progress` information is cleared from the terminal. @@ -281,17 +270,21 @@ pub fn unlockStdErr() void { /// times. The primary motivation is that this allows the panic handler to safely dump the stack /// trace and panic message even if the mutex was held at the panic site. /// -/// The returned `Writer` does not need to be manually flushed: flushing is performed automatically -/// when the matching `unlockStderrWriter` call occurs. +/// The returned `Writer` does not need to be manually flushed: flushing is +/// performed automatically when the matching `unlockStderrWriter` call occurs. +/// +/// This is a low-level debugging primitive that bypasses the `Io` interface, +/// writing directly to stderr using the most basic syscalls available. This +/// function does not switch threads, switch stacks, or suspend. +/// +/// Alternatively, use the higher-level `Io.lockStderrWriter` to integrate with +/// the application's chosen `Io` implementation. pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } { - const global = struct { - var conf: ?tty.Config = null; - }; + Io.stderr_thread_mutex.lock(); const w = std.Progress.lockStderrWriter(buffer); - const file_writer: *File.Writer = @fieldParentPtr("interface", w); // The stderr lock also locks access to `global.conf`. - if (global.conf == null) { - global.conf = .detect(file_writer.io, .stderr()); + if (StderrWriter.singleton.tty_config == null) { + StderrWriter.singleton.tty_config = .detect(io, .stderr()); } return .{ w, global.conf.? }; } @@ -300,11 +293,17 @@ pub fn unlockStderrWriter() void { std.Progress.unlockStderrWriter(); } -/// Print to stderr, silently returning on failure. Intended for use in "printf -/// debugging". Use `std.log` functions for proper logging. +/// Writes to stderr, ignoring errors. +/// +/// This is a low-level debugging primitive that bypasses the `Io` interface, +/// writing directly to stderr using the most basic syscalls available. This +/// function does not switch threads, switch stacks, or suspend. /// /// Uses a 64-byte buffer for formatted printing which is flushed before this /// function returns. +/// +/// Alternatively, use the higher-level `std.log` or `Io.lockStderrWriter` to +/// integrate with the application's chosen `Io` implementation. pub fn print(comptime fmt: []const u8, args: anytype) void { var buffer: [64]u8 = undefined; const bw, _ = lockStderrWriter(&buffer); @@ -312,6 +311,34 @@ pub fn print(comptime fmt: []const u8, args: anytype) void { nosuspend bw.print(fmt, args) catch return; } +const StderrWriter = struct { + interface: Writer, + tty_config: ?tty.Config, + + var singleton: StderrWriter = .{ + .interface = .{ + .buffer = &.{}, + .vtable = &.{ .drain = drain }, + }, + .tty_config = null, + }; + + fn drain(io_w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { + const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); + var n: usize = 0; + const header = w.interface.buffered(); + if (header.len != 0) n += try std.Io.Threaded.debugWrite(header); + for (data[0 .. data.len - 1]) |d| { + if (d.len != 0) n += try std.Io.Threaded.debugWrite(d); + } + const pattern = data[data.len - 1]; + if (pattern.len != 0) { + for (0..splat) |_| n += try std.Io.Threaded.debugWrite(pattern); + } + return io_w.consume(n); + } +}; + /// Marked `inline` to propagate a comptime-known error to callers. pub inline fn getSelfDebugInfo() !*SelfInfo { if (SelfInfo == void) return error.UnsupportedTarget; @@ -767,7 +794,7 @@ pub const FormatStackTrace = struct { stack_trace: StackTrace, tty_config: tty.Config, - pub fn format(context: @This(), writer: *Io.Writer) Io.Writer.Error!void { + pub fn format(context: @This(), writer: *Writer) Writer.Error!void { try writer.writeAll("\n"); try writeStackTrace(&context.stack_trace, writer, context.tty_config); } @@ -1608,7 +1635,7 @@ test "manage resources correctly" { const gpa = std.testing.allocator; var threaded: Io.Threaded = .init_single_threaded; const io = threaded.ioBasic(); - var discarding: Io.Writer.Discarding = .init(&.{}); + var discarding: Writer.Discarding = .init(&.{}); var di: SelfInfo = .init; defer di.deinit(gpa); try printSourceAtAddress( diff --git a/lib/std/log.zig b/lib/std/log.zig index 9568f9ba52..461dfca36e 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -80,6 +80,8 @@ pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool { return @intFromEnum(level) <= @intFromEnum(std.options.log_level); } +var static_threaded_io: std.Io.Threaded = .init_single_threaded; + /// The default implementation for the log function. Custom log functions may /// forward log messages to this function. /// @@ -90,10 +92,20 @@ pub fn defaultLog( comptime scope: @EnumLiteral(), comptime format: []const u8, args: anytype, +) void { + return defaultLogIo(level, scope, format, args, static_threaded_io.io()); +} + +pub fn defaultLogIo( + comptime level: Level, + comptime scope: @EnumLiteral(), + comptime format: []const u8, + args: anytype, + io: std.Io, ) void { var buffer: [64]u8 = undefined; - const stderr, const ttyconf = std.debug.lockStderrWriter(&buffer); - defer std.debug.unlockStderrWriter(); + const stderr, const ttyconf = io.lockStderrWriter(&buffer); + defer io.unlockStderrWriter(); ttyconf.setColor(stderr, switch (level) { .err => .red, .warn => .yellow, -- cgit v1.2.3 From ffcbd48a1220ce6d652ee762001d88baa385de49 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Dec 2025 22:10:12 -0800 Subject: std: rework TTY detection and printing This commit sketches an idea for how to deal with detection of file streams as being terminals. When a File stream is a terminal, writes through the stream should have their escapes stripped unless the programmer explicitly enables terminal escapes. Furthermore, the programmer needs a convenient API for intentionally outputting escapes into the stream. In particular it should be possible to set colors that are silently discarded when the stream is not a terminal. This commit makes `Io.File.Writer` track the terminal mode in the already-existing `mode` field, making it the appropriate place to implement escape stripping. `Io.lockStderrWriter` returns a `*Io.File.Writer` with terminal detection already done by default. This is a higher-level application layer stream for writing to stderr. Meanwhile, `std.debug.lockStderrWriter` also returns a `*Io.File.Writer` but a lower-level one that is hard-coded to use a static single-threaded `std.Io.Threaded` instance. This is the same instance that is used for collecting debug information and iterating the unwind info. --- lib/std/Io.zig | 22 ++-- lib/std/Io/File/Writer.zig | 243 +++++++++++++++++++++++++++++++++++++-- lib/std/Io/Threaded.zig | 47 +++++--- lib/std/Io/tty.zig | 135 ---------------------- lib/std/Progress.zig | 5 +- lib/std/debug.zig | 226 ++++++++++++++++-------------------- lib/std/heap/debug_allocator.zig | 96 +++------------- lib/std/log.zig | 64 ++++++++--- lib/std/process.zig | 8 +- src/main.zig | 2 - 10 files changed, 448 insertions(+), 400 deletions(-) delete mode 100644 lib/std/Io/tty.zig (limited to 'lib/std/debug.zig') diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 833cc4ec0f..fdd813536c 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -82,8 +82,6 @@ pub const Limit = enum(usize) { pub const Reader = @import("Io/Reader.zig"); pub const Writer = @import("Io/Writer.zig"); -pub const tty = @import("Io/tty.zig"); - pub fn poll( gpa: Allocator, comptime StreamEnum: type, @@ -535,7 +533,6 @@ test { _ = net; _ = Reader; _ = Writer; - _ = tty; _ = Evented; _ = Threaded; _ = @import("Io/test.zig"); @@ -720,6 +717,9 @@ pub const VTable = struct { processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File, processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize, + lockStderrWriter: *const fn (?*anyopaque, buffer: []u8) Cancelable!*File.Writer, + tryLockStderrWriter: *const fn (?*anyopaque, buffer: []u8) ?*File.Writer, + unlockStderrWriter: *const fn (?*anyopaque) void, now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, sleep: *const fn (?*anyopaque, Timeout) SleepError!void, @@ -740,10 +740,6 @@ pub const VTable = struct { netInterfaceNameResolve: *const fn (?*anyopaque, *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface, netInterfaceName: *const fn (?*anyopaque, net.Interface) net.Interface.NameError!net.Interface.Name, netLookup: *const fn (?*anyopaque, net.HostName, *Queue(net.HostName.LookupResult), net.HostName.LookupOptions) net.HostName.LookupError!void, - - lockStderrWriter: *const fn (?*anyopaque, buffer: []u8) Cancelable!*Writer, - tryLockStderrWriter: *const fn (?*anyopaque, buffer: []u8) ?*Writer, - unlockStderrWriter: *const fn (?*anyopaque) void, }; pub const Cancelable = error{ @@ -2186,13 +2182,17 @@ pub fn select(io: Io, s: anytype) Cancelable!SelectUnion(@TypeOf(s)) { /// /// See also: /// * `tryLockStderrWriter` -pub fn lockStderrWriter(io: Io, buffer: []u8) Cancelable!*Writer { - return io.vtable.lockStderrWriter(io.userdata, buffer); +pub fn lockStderrWriter(io: Io, buffer: []u8) Cancelable!*File.Writer { + const result = try io.vtable.lockStderrWriter(io.userdata, buffer); + result.io = io; + return result; } /// Same as `lockStderrWriter` but uncancelable and non-blocking. -pub fn tryLockStderrWriter(io: Io, buffer: []u8) ?*Writer { - return io.vtable.tryLockStderrWriter(io.userdata, buffer); +pub fn tryLockStderrWriter(io: Io, buffer: []u8) ?*File.Writer { + const result = io.vtable.tryLockStderrWriter(io.userdata, buffer) orelse return null; + result.io = io; + return result; } pub fn unlockStderrWriter(io: Io) void { diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index ec58824c06..0e995908c4 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -1,4 +1,6 @@ const Writer = @This(); +const builtin = @import("builtin"); +const is_windows = builtin.os.tag == .windows; const std = @import("../../std.zig"); const Io = std.Io; @@ -16,7 +18,144 @@ write_file_err: ?WriteFileError = null, seek_err: ?SeekError = null, interface: Io.Writer, -pub const Mode = File.Reader.Mode; +pub const Mode = union(enum) { + /// Uses `Io.VTable.fileWriteFileStreaming` if possible. Not a terminal. + /// `setColor` does nothing. + streaming, + /// Uses `Io.VTable.fileWriteFilePositional` if possible. Not a terminal. + /// `setColor` does nothing. + positional, + /// Avoids `Io.VTable.fileWriteFileStreaming`. Not a terminal. `setColor` + /// does nothing. + streaming_simple, + /// Avoids `Io.VTable.fileWriteFilePositional`. Not a terminal. `setColor` + /// does nothing. + positional_simple, + /// It's a terminal. Writes are escaped so as to strip escape sequences. + /// Color is enabled. + terminal_escaped, + /// It's a terminal. Colors are enabled via calling + /// SetConsoleTextAttribute. Writes are not escaped. + terminal_winapi: TerminalWinapi, + /// Indicates writing cannot continue because of a seek failure. + failure, + + pub fn toStreaming(m: @This()) @This() { + return switch (m) { + .positional, .streaming => .streaming, + .positional_simple, .streaming_simple => .streaming_simple, + inline else => |_, x| x, + }; + } + + pub fn toSimple(m: @This()) @This() { + return switch (m) { + .positional, .positional_simple => .positional_simple, + .streaming, .streaming_simple => .streaming_simple, + inline else => |x| x, + }; + } + + pub fn toUnescaped(m: @This()) @This() { + return switch (m) { + .terminal_escaped => .streaming_simple, + inline else => |x| x, + }; + } + + pub const TerminalWinapi = if (!is_windows) noreturn else struct { + handle: File.Handle, + reset_attributes: u16, + }; + + /// Detect suitable TTY configuration options for the given file (commonly + /// stdout/stderr). + /// + /// Will attempt to enable ANSI escape code support if necessary/possible. + pub fn detect(io: Io, file: File, want_color: bool, fallback: Mode) Io.Cancelable!Mode { + if (!want_color) return if (try file.isTty(io)) .terminal_escaped else fallback; + + if (file.enableAnsiEscapeCodes(io)) |_| { + return .terminal_escaped; + } else |err| switch (err) { + error.Canceled => return error.Canceled, + error.NotTerminalDevice, error.Unexpected => {}, + } + + if (is_windows and file.isTty(io)) { + const windows = std.os.windows; + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) { + return .{ .terminal_winapi = .{ + .handle = file.handle, + .reset_attributes = info.wAttributes, + } }; + } + return .terminal_escaped; + } + + return fallback; + } + + pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; + + pub fn setColor(mode: Mode, io_w: *Io.Writer, color: Color) Mode.SetColorError!void { + switch (mode) { + .streaming, .positional, .streaming_simple, .positional_simple, .failure => return, + .terminal_escaped => { + const color_string = switch (color) { + .black => "\x1b[30m", + .red => "\x1b[31m", + .green => "\x1b[32m", + .yellow => "\x1b[33m", + .blue => "\x1b[34m", + .magenta => "\x1b[35m", + .cyan => "\x1b[36m", + .white => "\x1b[37m", + .bright_black => "\x1b[90m", + .bright_red => "\x1b[91m", + .bright_green => "\x1b[92m", + .bright_yellow => "\x1b[93m", + .bright_blue => "\x1b[94m", + .bright_magenta => "\x1b[95m", + .bright_cyan => "\x1b[96m", + .bright_white => "\x1b[97m", + .bold => "\x1b[1m", + .dim => "\x1b[2m", + .reset => "\x1b[0m", + }; + try io_w.writeAll(color_string); + }, + .terminal_winapi => |ctx| { + const windows = std.os.windows; + const attributes: windows.WORD = switch (color) { + .black => 0, + .red => windows.FOREGROUND_RED, + .green => windows.FOREGROUND_GREEN, + .yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN, + .blue => windows.FOREGROUND_BLUE, + .magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE, + .cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, + .white => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, + .bright_black => windows.FOREGROUND_INTENSITY, + .bright_red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, + .bright_green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, + .bright_yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, + .bright_blue => windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + .bright_magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + .bright_cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + .bright_white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + // "dim" is not supported using basic character attributes, but let's still make it do *something*. + // This matches the old behavior of TTY.Color before the bright variants were added. + .dim => windows.FOREGROUND_INTENSITY, + .reset => ctx.reset_attributes, + }; + try io_w.flush(); + try windows.SetConsoleTextAttribute(ctx.handle, attributes); + }, + } + } +}; pub const Error = error{ DiskQuota, @@ -74,6 +213,16 @@ pub fn initStreaming(file: File, io: Io, buffer: []u8) Writer { }; } +/// Detects if `file` is terminal and sets the mode accordingly. +pub fn initDetect(file: File, io: Io, buffer: []u8) Io.Cancelable!Writer { + return .{ + .io = io, + .file = file, + .interface = initInterface(buffer), + .mode = try .detect(io, file, true, .positional), + }; +} + pub fn initInterface(buffer: []u8) Io.Writer { return .{ .vtable = &.{ @@ -99,8 +248,9 @@ pub fn moveToReader(w: *Writer) File.Reader { pub fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize { const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); switch (w.mode) { - .positional, .positional_reading => return drainPositional(w, data, splat), - .streaming, .streaming_reading => return drainStreaming(w, data, splat), + .positional, .positional_simple => return drainPositional(w, data, splat), + .streaming, .streaming_simple, .terminal_winapi => return drainStreaming(w, data, splat), + .terminal_escaped => return drainEscaping(w, data, splat), .failure => return error.WriteFailed, } } @@ -141,13 +291,38 @@ fn drainStreaming(w: *Writer, data: []const []const u8, splat: usize) Io.Writer. return w.interface.consume(n); } +fn findTerminalEscape(buffer: []const u8) ?usize { + return std.mem.findScalar(u8, buffer, 0x1b); +} + +fn drainEscaping(w: *Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize { + const io = w.io; + const header = w.interface.buffered(); + if (findTerminalEscape(header)) |i| { + _ = i; + @panic("TODO strip terminal escape sequence"); + } + for (data) |d| { + if (findTerminalEscape(d)) |i| { + _ = i; + @panic("TODO strip terminal escape sequence"); + } + } + const n = io.vtable.fileWriteStreaming(io.userdata, w.file, header, data, splat) catch |err| { + w.err = err; + return error.WriteFailed; + }; + w.pos += n; + return w.interface.consume(n); +} + pub fn sendFile(io_w: *Io.Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize { const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); switch (w.mode) { .positional => return sendFilePositional(w, file_reader, limit), - .positional_reading => return error.Unimplemented, + .positional_simple => return error.Unimplemented, .streaming => return sendFileStreaming(w, file_reader, limit), - .streaming_reading => return error.Unimplemented, + .streaming_simple, .terminal_escaped, .terminal_winapi => return error.Unimplemented, .failure => return error.WriteFailed, } } @@ -214,10 +389,10 @@ pub fn seekToUnbuffered(w: *Writer, offset: u64) SeekError!void { assert(w.interface.buffered().len == 0); const io = w.io; switch (w.mode) { - .positional, .positional_reading => { + .positional, .positional_simple => { w.pos = offset; }, - .streaming, .streaming_reading => { + .streaming, .streaming_simple, .terminal_escaped, .terminal_winapi => { if (w.seek_err) |err| return err; io.vtable.fileSeekTo(io.userdata, w.file, offset) catch |err| { w.seek_err = err; @@ -243,15 +418,65 @@ pub fn end(w: *Writer) EndError!void { try w.interface.flush(); switch (w.mode) { .positional, - .positional_reading, + .positional_simple, => w.file.setLength(io, w.pos) catch |err| switch (err) { error.NonResizable => return, else => |e| return e, }, .streaming, - .streaming_reading, + .streaming_simple, .failure, => {}, } } + +pub const Color = enum { + black, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white, + dim, + bold, + reset, +}; + +pub const SetColorError = Mode.SetColorError; + +pub fn setColor(w: *Writer, color: Color) SetColorError!void { + return w.mode.setColor(&w.interface, color); +} + +pub fn disableEscape(w: *Writer) Mode { + const prev = w.mode; + w.mode = w.mode.toUnescaped(); + return prev; +} + +pub fn restoreEscape(w: *Writer, mode: Mode) void { + w.mode = mode; +} + +pub fn writeAllUnescaped(w: *Writer, bytes: []const u8) Io.Error!void { + const prev_mode = w.disableEscape(); + defer w.restoreEscape(prev_mode); + return w.interface.writeAll(bytes); +} + +pub fn printUnescaped(w: *Writer, comptime fmt: []const u8, args: anytype) Io.Error!void { + const prev_mode = w.disableEscape(); + defer w.restoreEscape(prev_mode); + return w.interface.print(fmt, args); +} diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 9d6d9f979e..c2a3e15f15 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -77,7 +77,13 @@ use_sendfile: UseSendfile = .default, use_copy_file_range: UseCopyFileRange = .default, use_fcopyfile: UseFcopyfile = .default, -stderr_writer: Io.Writer, +stderr_writer: File.Writer = .{ + .io = undefined, + .interface = Io.File.Writer.initInterface(&.{}), + .file = if (is_windows) undefined else .stderr(), + .mode = undefined, +}, +stderr_writer_initialized: bool = false, pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { enabled, @@ -737,6 +743,9 @@ pub fn io(t: *Threaded) Io { .processExecutableOpen = processExecutableOpen, .processExecutablePath = processExecutablePath, + .lockStderrWriter = lockStderrWriter, + .tryLockStderrWriter = tryLockStderrWriter, + .unlockStderrWriter = unlockStderrWriter, .now = now, .sleep = sleep, @@ -864,6 +873,9 @@ pub fn ioBasic(t: *Threaded) Io { .processExecutableOpen = processExecutableOpen, .processExecutablePath = processExecutablePath, + .lockStderrWriter = lockStderrWriter, + .tryLockStderrWriter = tryLockStderrWriter, + .unlockStderrWriter = unlockStderrWriter, .now = now, .sleep = sleep, @@ -9516,33 +9528,42 @@ fn netLookupFallible( return error.OptionUnsupported; } -fn lockStderrWriter(userdata: ?*anyopaque, buffer: []u8) Io.Cancelable!*Io.Writer { +fn lockStderrWriter(userdata: ?*anyopaque, buffer: []u8) Io.Cancelable!*File.Writer { const t: *Threaded = @ptrCast(@alignCast(userdata)); // Only global mutex since this is Threaded. Io.stderr_thread_mutex.lock(); - if (is_windows) t.stderr_writer.file = .stderr(); + if (!t.stderr_writer_initialized) { + if (is_windows) t.stderr_writer.file = .stderr(); + t.stderr_writer.mode = try .detect(ioBasic(t), t.stderr_writer.file, true, .streaming_simple); + t.stderr_writer_initialized = true; + } std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {}; - t.stderr_writer.flush() catch {}; - t.stderr_writer.buffer = buffer; + t.stderr_writer.interface.flush() catch {}; + t.stderr_writer.interface.buffer = buffer; return &t.stderr_writer; } -fn tryLockStderrWriter(userdata: ?*anyopaque, buffer: []u8) ?*Io.Writer { +fn tryLockStderrWriter(userdata: ?*anyopaque, buffer: []u8) ?*File.Writer { const t: *Threaded = @ptrCast(@alignCast(userdata)); // Only global mutex since this is Threaded. if (!Io.stderr_thread_mutex.tryLock()) return null; - std.Progress.clearWrittenWithEscapeCodes(t.io()) catch {}; - if (is_windows) t.stderr_writer.file = .stderr(); - t.stderr_writer.flush() catch {}; - t.stderr_writer.buffer = buffer; + if (!t.stderr_writer_initialized) { + if (is_windows) t.stderr_writer.file = .stderr(); + t.stderr_writer.mode = File.Writer.Mode.detect(ioBasic(t), t.stderr_writer.file, true, .streaming_simple) catch + return null; + t.stderr_writer_initialized = true; + } + std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {}; + t.stderr_writer.interface.flush() catch {}; + t.stderr_writer.interface.buffer = buffer; return &t.stderr_writer; } fn unlockStderrWriter(userdata: ?*anyopaque) void { const t: *Threaded = @ptrCast(@alignCast(userdata)); - t.stderr_writer.flush() catch {}; - t.stderr_writer.end = 0; - t.stderr_writer.buffer = &.{}; + t.stderr_writer.interface.flush() catch {}; + t.stderr_writer.interface.end = 0; + t.stderr_writer.interface.buffer = &.{}; Io.stderr_thread_mutex.unlock(); } diff --git a/lib/std/Io/tty.zig b/lib/std/Io/tty.zig deleted file mode 100644 index 65f1b7dad5..0000000000 --- a/lib/std/Io/tty.zig +++ /dev/null @@ -1,135 +0,0 @@ -const builtin = @import("builtin"); -const native_os = builtin.os.tag; - -const std = @import("std"); -const Io = std.Io; -const File = std.Io.File; -const process = std.process; -const windows = std.os.windows; - -pub const Color = enum { - black, - red, - green, - yellow, - blue, - magenta, - cyan, - white, - bright_black, - bright_red, - bright_green, - bright_yellow, - bright_blue, - bright_magenta, - bright_cyan, - bright_white, - dim, - bold, - reset, -}; - -/// Provides simple functionality for manipulating the terminal in some way, -/// such as coloring text, etc. -pub const Config = union(enum) { - no_color, - escape_codes, - windows_api: if (native_os == .windows) WindowsContext else noreturn, - - /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr). - /// This includes feature checks for ANSI escape codes and the Windows console API, as well as - /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default. - /// Will attempt to enable ANSI escape code support if necessary/possible. - pub fn detect(io: Io, file: File) Config { - const force_color: ?bool = if (builtin.os.tag == .wasi) - null // wasi does not support environment variables - else if (process.hasNonEmptyEnvVarConstant("NO_COLOR")) - false - else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE")) - true - else - null; - - if (force_color == false) return .no_color; - - if (file.enableAnsiEscapeCodes(io)) |_| { - return .escape_codes; - } else |_| {} - - if (native_os == .windows and file.isTty()) { - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) { - return if (force_color == true) .escape_codes else .no_color; - } - return .{ .windows_api = .{ - .handle = file.handle, - .reset_attributes = info.wAttributes, - } }; - } - - return if (force_color == true) .escape_codes else .no_color; - } - - pub const WindowsContext = struct { - handle: File.Handle, - reset_attributes: u16, - }; - - pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; - - pub fn setColor(conf: Config, w: *Io.Writer, color: Color) SetColorError!void { - nosuspend switch (conf) { - .no_color => return, - .escape_codes => { - const color_string = switch (color) { - .black => "\x1b[30m", - .red => "\x1b[31m", - .green => "\x1b[32m", - .yellow => "\x1b[33m", - .blue => "\x1b[34m", - .magenta => "\x1b[35m", - .cyan => "\x1b[36m", - .white => "\x1b[37m", - .bright_black => "\x1b[90m", - .bright_red => "\x1b[91m", - .bright_green => "\x1b[92m", - .bright_yellow => "\x1b[93m", - .bright_blue => "\x1b[94m", - .bright_magenta => "\x1b[95m", - .bright_cyan => "\x1b[96m", - .bright_white => "\x1b[97m", - .bold => "\x1b[1m", - .dim => "\x1b[2m", - .reset => "\x1b[0m", - }; - try w.writeAll(color_string); - }, - .windows_api => |ctx| { - const attributes = switch (color) { - .black => 0, - .red => windows.FOREGROUND_RED, - .green => windows.FOREGROUND_GREEN, - .yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN, - .blue => windows.FOREGROUND_BLUE, - .magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE, - .cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, - .white => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, - .bright_black => windows.FOREGROUND_INTENSITY, - .bright_red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, - .bright_green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, - .bright_yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, - .bright_blue => windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - // "dim" is not supported using basic character attributes, but let's still make it do *something*. - // This matches the old behavior of TTY.Color before the bright variants were added. - .dim => windows.FOREGROUND_INTENSITY, - .reset => ctx.reset_attributes, - }; - try w.flush(); - try windows.SetConsoleTextAttribute(ctx.handle, attributes); - }, - }; - } -}; diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 5528591620..799e3f9c9b 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -755,10 +755,9 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { } } -fn clearWrittenWithEscapeCodes(w: *Io.Writer) anyerror!void { +pub fn clearWrittenWithEscapeCodes(file_writer: *Io.File.Writer) anyerror!void { if (noop_impl or !global_progress.need_clear) return; - - try w.writeAll(clear ++ progress_remove); + try file_writer.interface.writeAllUnescaped(clear ++ progress_remove); global_progress.need_clear = false; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 7e10c5af32..347dbf85ec 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1,7 +1,6 @@ const std = @import("std.zig"); const Io = std.Io; const Writer = std.Io.Writer; -const tty = std.Io.tty; const math = std.math; const mem = std.mem; const posix = std.posix; @@ -262,6 +261,10 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { else => true, }; +/// This is used for debug information and debug printing. It is intentionally +/// separate from the application's `Io` instance. +var static_single_threaded_io: Io.Threaded = .init_single_threaded; + /// Allows the caller to freely write to stderr until `unlockStderrWriter` is called. /// /// During the lock, any `std.Progress` information is cleared from the terminal. @@ -279,18 +282,12 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { /// /// Alternatively, use the higher-level `Io.lockStderrWriter` to integrate with /// the application's chosen `Io` implementation. -pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } { - Io.stderr_thread_mutex.lock(); - const w = std.Progress.lockStderrWriter(buffer); - // The stderr lock also locks access to `global.conf`. - if (StderrWriter.singleton.tty_config == null) { - StderrWriter.singleton.tty_config = .detect(io, .stderr()); - } - return .{ w, global.conf.? }; +pub fn lockStderrWriter(buffer: []u8) *File.Writer { + return static_single_threaded_io.ioBasic().lockStderrWriter(buffer) catch unreachable; } pub fn unlockStderrWriter() void { - std.Progress.unlockStderrWriter(); + static_single_threaded_io.ioBasic().unlockStderrWriter(); } /// Writes to stderr, ignoring errors. @@ -305,39 +302,13 @@ pub fn unlockStderrWriter() void { /// Alternatively, use the higher-level `std.log` or `Io.lockStderrWriter` to /// integrate with the application's chosen `Io` implementation. pub fn print(comptime fmt: []const u8, args: anytype) void { - var buffer: [64]u8 = undefined; - const bw, _ = lockStderrWriter(&buffer); - defer unlockStderrWriter(); - nosuspend bw.print(fmt, args) catch return; -} - -const StderrWriter = struct { - interface: Writer, - tty_config: ?tty.Config, - - var singleton: StderrWriter = .{ - .interface = .{ - .buffer = &.{}, - .vtable = &.{ .drain = drain }, - }, - .tty_config = null, - }; - - fn drain(io_w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { - const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); - var n: usize = 0; - const header = w.interface.buffered(); - if (header.len != 0) n += try std.Io.Threaded.debugWrite(header); - for (data[0 .. data.len - 1]) |d| { - if (d.len != 0) n += try std.Io.Threaded.debugWrite(d); - } - const pattern = data[data.len - 1]; - if (pattern.len != 0) { - for (0..splat) |_| n += try std.Io.Threaded.debugWrite(pattern); - } - return io_w.consume(n); + nosuspend { + var buffer: [64]u8 = undefined; + const stderr = lockStderrWriter(&buffer); + defer unlockStderrWriter(); + stderr.interface.print(fmt, args) catch return; } -}; +} /// Marked `inline` to propagate a comptime-known error to callers. pub inline fn getSelfDebugInfo() !*SelfInfo { @@ -357,16 +328,16 @@ pub fn dumpHex(bytes: []const u8) void { } /// Prints a hexadecimal view of the bytes, returning any error that occurs. -pub fn dumpHexFallible(bw: *Writer, tty_config: tty.Config, bytes: []const u8) !void { +pub fn dumpHexFallible(bw: *Writer, fwm: File.Writer.Mode, bytes: []const u8) !void { var chunks = mem.window(u8, bytes, 16, 16); while (chunks.next()) |window| { // 1. Print the address. const address = (@intFromPtr(bytes.ptr) + 0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, 16) catch unreachable)) - 0x10; - try tty_config.setColor(bw, .dim); + try fwm.setColor(bw, .dim); // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more. // Also, make sure all lines are aligned by padding the address. try bw.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 }); - try tty_config.setColor(bw, .reset); + try fwm.setColor(bw, .reset); // 2. Print the bytes. for (window, 0..) |byte, index| { @@ -386,7 +357,7 @@ pub fn dumpHexFallible(bw: *Writer, tty_config: tty.Config, bytes: []const u8) ! try bw.writeByte(byte); } else { // Related: https://github.com/ziglang/zig/issues/7600 - if (tty_config == .windows_api) { + if (fwm == .terminal_winapi) { try bw.writeByte('.'); continue; } @@ -408,11 +379,11 @@ pub fn dumpHexFallible(bw: *Writer, tty_config: tty.Config, bytes: []const u8) ! test dumpHexFallible { const bytes: []const u8 = &.{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x12, 0x13 }; - var aw: Writer.Allocating = .init(std.testing.allocator); + var aw: Writer.Allocating = .init(testing.allocator); defer aw.deinit(); try dumpHexFallible(&aw.writer, .no_color, bytes); - const expected = try std.fmt.allocPrint(std.testing.allocator, + const expected = try std.fmt.allocPrint(testing.allocator, \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........ \\{x:0>[2]} 01 12 13 ... \\ @@ -421,8 +392,8 @@ test dumpHexFallible { @intFromPtr(bytes.ptr) + 16, @sizeOf(usize) * 2, }); - defer std.testing.allocator.free(expected); - try std.testing.expectEqualStrings(expected, aw.written()); + defer testing.allocator.free(expected); + try testing.expectEqualStrings(expected, aw.written()); } /// The pointer through which a `cpu_context.Native` is received from callers of stack tracing logic. @@ -437,7 +408,7 @@ pub const CpuContextPtr = if (cpu_context.Native == noreturn) noreturn else *con /// away, and in fact the optimizer is able to use the assertion in its /// heuristics. /// -/// Inside a test block, it is best to use the `std.testing` module rather than +/// Inside a test block, it is best to use the `testing` module rather than /// this function, because this function may not detect a test failure in /// ReleaseFast and ReleaseSmall mode. Outside of a test block, this assert /// function is the correct function to use. @@ -574,26 +545,26 @@ pub fn defaultPanic( _ = panicking.fetchAdd(1, .seq_cst); trace: { - const stderr, const tty_config = lockStderrWriter(&.{}); + const stderr = lockStderrWriter(&.{}); defer unlockStderrWriter(); if (builtin.single_threaded) { - stderr.print("panic: ", .{}) catch break :trace; + stderr.interface.print("panic: ", .{}) catch break :trace; } else { const current_thread_id = std.Thread.getCurrentId(); - stderr.print("thread {d} panic: ", .{current_thread_id}) catch break :trace; + stderr.interface.print("thread {d} panic: ", .{current_thread_id}) catch break :trace; } - stderr.print("{s}\n", .{msg}) catch break :trace; + stderr.interface.print("{s}\n", .{msg}) catch break :trace; if (@errorReturnTrace()) |t| if (t.index > 0) { - stderr.writeAll("error return context:\n") catch break :trace; - writeStackTrace(t, stderr, tty_config) catch break :trace; - stderr.writeAll("\nstack trace:\n") catch break :trace; + stderr.interface.writeAll("error return context:\n") catch break :trace; + writeStackTrace(t, &stderr.interface, stderr.mode) catch break :trace; + stderr.interface.writeAll("\nstack trace:\n") catch break :trace; }; writeCurrentStackTrace(.{ .first_address = first_trace_addr orelse @returnAddress(), .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! - }, stderr, tty_config) catch break :trace; + }, &stderr.interface, stderr.mode) catch break :trace; } waitForOtherThreadToFinishPanicking(); @@ -603,8 +574,8 @@ pub fn defaultPanic( // A panic happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - const stderr, _ = lockStderrWriter(&.{}); - stderr.writeAll("aborting due to recursive panic\n") catch {}; + const stderr = lockStderrWriter(&.{}); + stderr.interface.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } @@ -651,8 +622,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; - var threaded: Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); + const io = static_single_threaded_io.ioBasic(); var total_frames: usize = 0; var index: usize = 0; @@ -686,36 +656,34 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: /// Write the current stack trace to `writer`, annotated with source locations. /// /// See `captureCurrentStackTrace` to capture the trace addresses into a buffer instead of printing. -pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_config: tty.Config) Writer.Error!void { - var threaded: Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); - +pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, fwm: File.Writer.Mode) Writer.Error!void { if (!std.options.allow_stack_tracing) { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; return; } const di_gpa = getDebugInfoAllocator(); const di = getSelfDebugInfo() catch |err| switch (err) { error.UnsupportedTarget => { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("Cannot print stack trace: debug info unavailable for target\n", .{}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; return; }, }; var it: StackIterator = .init(options.context); defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("Cannot print stack trace: safe unwind unavailable for target\n", .{}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; return; } var total_frames: usize = 0; var wait_for = options.first_address; var printed_any_frame = false; + const io = static_single_threaded_io.ioBasic(); while (true) switch (it.next(io)) { .switch_to_fp => |unwind_error| { switch (StackIterator.fp_usability) { @@ -733,31 +701,31 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri error.Unexpected => "unexpected error", }; if (it.stratOk(options.allow_unsafe_unwind)) { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print( "Unwind error at address `{s}:0x{x}` ({s}), remaining frames may be incorrect\n", .{ module_name, unwind_error.address, caption }, ); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; } else { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print( "Unwind error at address `{s}:0x{x}` ({s}), stopping trace early\n", .{ module_name, unwind_error.address, caption }, ); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; return; } }, .end => break, .frame => |ret_addr| { if (total_frames > 10_000) { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print( "Stopping trace after {d} frames (large frame count may indicate broken debug info)\n", .{total_frames}, ); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; return; } total_frames += 1; @@ -767,7 +735,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri } // `ret_addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(di_gpa, io, di, writer, ret_addr -| StackIterator.ra_call_offset, tty_config); + try printSourceAtAddress(di_gpa, io, di, writer, ret_addr -| StackIterator.ra_call_offset, fwm); printed_any_frame = true; }, }; @@ -775,7 +743,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri } /// A thin wrapper around `writeCurrentStackTrace` which writes to stderr and ignores write errors. pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void { - const stderr, const tty_config = lockStderrWriter(&.{}); + const stderr = lockStderrWriter(&.{}); defer unlockStderrWriter(); writeCurrentStackTrace(.{ .first_address = a: { @@ -785,33 +753,40 @@ pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void { }, .context = options.context, .allow_unsafe_unwind = options.allow_unsafe_unwind, - }, stderr, tty_config) catch |err| switch (err) { + }, &stderr.interface, stderr.mode) catch |err| switch (err) { error.WriteFailed => {}, }; } pub const FormatStackTrace = struct { stack_trace: StackTrace, - tty_config: tty.Config, - pub fn format(context: @This(), writer: *Writer) Writer.Error!void { - try writer.writeAll("\n"); - try writeStackTrace(&context.stack_trace, writer, context.tty_config); + pub const Decorated = struct { + stack_trace: StackTrace, + file_writer_mode: File.Writer.Mode, + + pub fn format(decorated: Decorated, writer: *Writer) Writer.Error!void { + try writer.writeByte('\n'); + try writeStackTrace(&decorated.stack_trace, writer, decorated.file_writer_mode); + } + }; + + pub fn format(context: FormatStackTrace, writer: *Writer) Writer.Error!void { + return Decorated.format(.{ + .stack_trace = context.stack_trace, + .file_writer_mode = .streaming, + }, writer); } }; /// Write a previously captured stack trace to `writer`, annotated with source locations. -pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, tty_config: tty.Config) Writer.Error!void { +pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, fwm: File.Writer.Mode) Writer.Error!void { if (!std.options.allow_stack_tracing) { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; return; } - // We use an independent Io implementation here in case there was a problem - // with the application's Io implementation itself. - var threaded: Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); // Fetch `st.index` straight away. Aside from avoiding redundant loads, this prevents issues if // `st` is `@errorReturnTrace()` and errors are encountered while writing the stack trace. @@ -820,22 +795,23 @@ pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, tty_config: tty.C const di_gpa = getDebugInfoAllocator(); const di = getSelfDebugInfo() catch |err| switch (err) { error.UnsupportedTarget => { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("Cannot print stack trace: debug info unavailable for target\n\n", .{}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; return; }, }; + const io = static_single_threaded_io.ioBasic(); const captured_frames = @min(n_frames, st.instruction_addresses.len); for (st.instruction_addresses[0..captured_frames]) |ret_addr| { // `ret_addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(di_gpa, io, di, writer, ret_addr -| StackIterator.ra_call_offset, tty_config); + try printSourceAtAddress(di_gpa, io, di, writer, ret_addr -| StackIterator.ra_call_offset, fwm); } if (n_frames > captured_frames) { - tty_config.setColor(writer, .bold) catch {}; + fwm.setColor(writer, .bold) catch {}; try writer.print("({d} additional stack frames skipped...)\n", .{n_frames - captured_frames}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; } } /// A thin wrapper around `writeStackTrace` which writes to stderr and ignores write errors. @@ -1143,7 +1119,7 @@ fn printSourceAtAddress( debug_info: *SelfInfo, writer: *Writer, address: usize, - tty_config: tty.Config, + fwm: File.Writer.Mode, ) Writer.Error!void { const symbol: Symbol = debug_info.getSymbol(gpa, io, address) catch |err| switch (err) { error.MissingDebugInfo, @@ -1151,15 +1127,15 @@ fn printSourceAtAddress( error.InvalidDebugInfo, => .unknown, error.ReadFailed, error.Unexpected, error.Canceled => s: { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; break :s .unknown; }, error.OutOfMemory => s: { - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; break :s .unknown; }, }; @@ -1171,7 +1147,7 @@ fn printSourceAtAddress( address, symbol.name orelse "???", symbol.compile_unit_name orelse debug_info.getModuleName(gpa, address) catch "???", - tty_config, + fwm, ); } fn printLineInfo( @@ -1181,10 +1157,10 @@ fn printLineInfo( address: usize, symbol_name: []const u8, compile_unit_name: []const u8, - tty_config: tty.Config, + fwm: File.Writer.Mode, ) Writer.Error!void { nosuspend { - tty_config.setColor(writer, .bold) catch {}; + fwm.setColor(writer, .bold) catch {}; if (source_location) |*sl| { try writer.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column }); @@ -1192,11 +1168,11 @@ fn printLineInfo( try writer.writeAll("???:?:?"); } - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; try writer.writeAll(": "); - tty_config.setColor(writer, .dim) catch {}; + fwm.setColor(writer, .dim) catch {}; try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; try writer.writeAll("\n"); // Show the matching source code line if possible @@ -1207,9 +1183,9 @@ fn printLineInfo( const space_needed = @as(usize, @intCast(sl.column - 1)); try writer.splatByteAll(' ', space_needed); - tty_config.setColor(writer, .green) catch {}; + fwm.setColor(writer, .green) catch {}; try writer.writeAll("^"); - tty_config.setColor(writer, .reset) catch {}; + fwm.setColor(writer, .reset) catch {}; } try writer.writeAll("\n"); } else |_| { @@ -1250,18 +1226,18 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) ! } test printLineFromFile { - const io = std.testing.io; - const gpa = std.testing.allocator; + const io = testing.io; + const gpa = testing.allocator; var aw: Writer.Allocating = .init(gpa); defer aw.deinit(); const output_stream = &aw.writer; const join = std.fs.path.join; - const expectError = std.testing.expectError; - const expectEqualStrings = std.testing.expectEqualStrings; + const expectError = testing.expectError; + const expectEqualStrings = testing.expectEqualStrings; - var test_dir = std.testing.tmpDir(.{}); + var test_dir = testing.tmpDir(.{}); defer test_dir.cleanup(); // Relies on testing.tmpDir internals which is not ideal, but SourceLocation requires paths. const test_dir_path = try join(gpa, &.{ ".zig-cache", "tmp", test_dir.sub_path[0..] }); @@ -1578,19 +1554,19 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex _ = panicking.fetchAdd(1, .seq_cst); trace: { - const stderr, const tty_config = lockStderrWriter(&.{}); + const stderr = lockStderrWriter(&.{}); defer unlockStderrWriter(); if (addr) |a| { - stderr.print("{s} at address 0x{x}\n", .{ name, a }) catch break :trace; + stderr.interface.print("{s} at address 0x{x}\n", .{ name, a }) catch break :trace; } else { - stderr.print("{s} (no address available)\n", .{name}) catch break :trace; + stderr.interface.print("{s} (no address available)\n", .{name}) catch break :trace; } if (opt_ctx) |context| { writeCurrentStackTrace(.{ .context = context, .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! - }, stderr, tty_config) catch break :trace; + }, &stderr.interface, stderr.mode) catch break :trace; } } }, @@ -1599,8 +1575,8 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex // A segfault happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - const stderr, _ = lockStderrWriter(&.{}); - stderr.writeAll("aborting due to recursive panic\n") catch {}; + const stderr = lockStderrWriter(&.{}); + stderr.interface.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } @@ -1632,9 +1608,9 @@ test "manage resources correctly" { return @returnAddress(); } }; - const gpa = std.testing.allocator; - var threaded: Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); + const gpa = testing.allocator; + const io = testing.io; + var discarding: Writer.Discarding = .init(&.{}); var di: SelfInfo = .init; defer di.deinit(gpa); diff --git a/lib/std/heap/debug_allocator.zig b/lib/std/heap/debug_allocator.zig index 27b1b9179f..66ff59a711 100644 --- a/lib/std/heap/debug_allocator.zig +++ b/lib/std/heap/debug_allocator.zig @@ -179,8 +179,6 @@ pub fn DebugAllocator(comptime config: Config) type { total_requested_bytes: @TypeOf(total_requested_bytes_init) = total_requested_bytes_init, requested_memory_limit: @TypeOf(requested_memory_limit_init) = requested_memory_limit_init, mutex: @TypeOf(mutex_init) = mutex_init, - /// Set this value differently to affect how errors and leaks are logged. - tty_config: std.Io.tty.Config = .no_color, const Self = @This(); @@ -427,7 +425,6 @@ pub fn DebugAllocator(comptime config: Config) type { bucket: *BucketHeader, size_class_index: usize, used_bits_count: usize, - tty_config: std.Io.tty.Config, ) usize { const size_class = @as(usize, 1) << @as(Log2USize, @intCast(size_class_index)); const slot_count = slot_counts[size_class_index]; @@ -444,11 +441,7 @@ pub fn DebugAllocator(comptime config: Config) type { const page_addr = @intFromPtr(bucket) & ~(page_size - 1); const addr = page_addr + slot_index * size_class; log.err("memory address 0x{x} leaked: {f}", .{ - addr, - std.debug.FormatStackTrace{ - .stack_trace = stack_trace, - .tty_config = tty_config, - }, + addr, std.debug.FormatStackTrace{ .stack_trace = stack_trace }, }); leaks += 1; } @@ -460,8 +453,6 @@ pub fn DebugAllocator(comptime config: Config) type { /// Emits log messages for leaks and then returns the number of detected leaks (0 if no leaks were detected). pub fn detectLeaks(self: *Self) usize { - const tty_config = self.tty_config; - var leaks: usize = 0; for (self.buckets, 0..) |init_optional_bucket, size_class_index| { @@ -469,7 +460,7 @@ pub fn DebugAllocator(comptime config: Config) type { const slot_count = slot_counts[size_class_index]; const used_bits_count = usedBitsCount(slot_count); while (optional_bucket) |bucket| { - leaks += detectLeaksInBucket(bucket, size_class_index, used_bits_count, tty_config); + leaks += detectLeaksInBucket(bucket, size_class_index, used_bits_count); optional_bucket = bucket.prev; } } @@ -480,10 +471,7 @@ pub fn DebugAllocator(comptime config: Config) type { const stack_trace = large_alloc.getStackTrace(.alloc); log.err("memory address 0x{x} leaked: {f}", .{ @intFromPtr(large_alloc.bytes.ptr), - std.debug.FormatStackTrace{ - .stack_trace = stack_trace, - .tty_config = tty_config, - }, + std.debug.FormatStackTrace{ .stack_trace = stack_trace }, }); leaks += 1; } @@ -535,28 +523,14 @@ pub fn DebugAllocator(comptime config: Config) type { @memset(addr_buf[@min(st.index, addr_buf.len)..], 0); } - fn reportDoubleFree( - tty_config: std.Io.tty.Config, - ret_addr: usize, - alloc_stack_trace: StackTrace, - free_stack_trace: StackTrace, - ) void { + fn reportDoubleFree(ret_addr: usize, alloc_stack_trace: StackTrace, free_stack_trace: StackTrace) void { @branchHint(.cold); var addr_buf: [stack_n]usize = undefined; const second_free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf); log.err("Double free detected. Allocation: {f} First free: {f} Second free: {f}", .{ - std.debug.FormatStackTrace{ - .stack_trace = alloc_stack_trace, - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = free_stack_trace, - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = second_free_stack_trace, - .tty_config = tty_config, - }, + std.debug.FormatStackTrace{ .stack_trace = alloc_stack_trace }, + std.debug.FormatStackTrace{ .stack_trace = free_stack_trace }, + std.debug.FormatStackTrace{ .stack_trace = second_free_stack_trace }, }); } @@ -587,7 +561,7 @@ pub fn DebugAllocator(comptime config: Config) type { if (config.retain_metadata and entry.value_ptr.freed) { if (config.safety) { - reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); + reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); @panic("Unrecoverable double free"); } else { unreachable; @@ -598,18 +572,11 @@ pub fn DebugAllocator(comptime config: Config) type { @branchHint(.cold); var addr_buf: [stack_n]usize = undefined; const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf); - const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ entry.value_ptr.bytes.len, old_mem.len, - std.debug.FormatStackTrace{ - .stack_trace = entry.value_ptr.getStackTrace(.alloc), - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = free_stack_trace, - .tty_config = tty_config, - }, + std.debug.FormatStackTrace{ .stack_trace = entry.value_ptr.getStackTrace(.alloc) }, + std.debug.FormatStackTrace{ .stack_trace = free_stack_trace }, }); } @@ -701,7 +668,7 @@ pub fn DebugAllocator(comptime config: Config) type { if (config.retain_metadata and entry.value_ptr.freed) { if (config.safety) { - reportDoubleFree(self.tty_config, ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); + reportDoubleFree(ret_addr, entry.value_ptr.getStackTrace(.alloc), entry.value_ptr.getStackTrace(.free)); return; } else { unreachable; @@ -712,18 +679,11 @@ pub fn DebugAllocator(comptime config: Config) type { @branchHint(.cold); var addr_buf: [stack_n]usize = undefined; const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = ret_addr }, &addr_buf); - const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ entry.value_ptr.bytes.len, old_mem.len, - std.debug.FormatStackTrace{ - .stack_trace = entry.value_ptr.getStackTrace(.alloc), - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = free_stack_trace, - .tty_config = tty_config, - }, + std.debug.FormatStackTrace{ .stack_trace = entry.value_ptr.getStackTrace(.alloc) }, + std.debug.FormatStackTrace{ .stack_trace = free_stack_trace }, }); } @@ -924,7 +884,6 @@ pub fn DebugAllocator(comptime config: Config) type { if (!is_used) { if (config.safety) { reportDoubleFree( - self.tty_config, return_address, bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .free), @@ -946,34 +905,24 @@ pub fn DebugAllocator(comptime config: Config) type { const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf); if (old_memory.len != requested_size) { @branchHint(.cold); - const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ requested_size, old_memory.len, std.debug.FormatStackTrace{ .stack_trace = bucketStackTrace(bucket, slot_count, slot_index, .alloc), - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = free_stack_trace, - .tty_config = tty_config, }, + std.debug.FormatStackTrace{ .stack_trace = free_stack_trace }, }); } if (alignment != slot_alignment) { @branchHint(.cold); - const tty_config = self.tty_config; log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{ slot_alignment.toByteUnits(), alignment.toByteUnits(), std.debug.FormatStackTrace{ .stack_trace = bucketStackTrace(bucket, slot_count, slot_index, .alloc), - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = free_stack_trace, - .tty_config = tty_config, }, + std.debug.FormatStackTrace{ .stack_trace = free_stack_trace }, }); } } @@ -1040,7 +989,6 @@ pub fn DebugAllocator(comptime config: Config) type { const is_used = @as(u1, @truncate(used_byte.* >> used_bit_index)) != 0; if (!is_used) { reportDoubleFree( - self.tty_config, return_address, bucketStackTrace(bucket, slot_count, slot_index, .alloc), bucketStackTrace(bucket, slot_count, slot_index, .free), @@ -1058,34 +1006,24 @@ pub fn DebugAllocator(comptime config: Config) type { const free_stack_trace = std.debug.captureCurrentStackTrace(.{ .first_address = return_address }, &addr_buf); if (memory.len != requested_size) { @branchHint(.cold); - const tty_config = self.tty_config; log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {f} Free: {f}", .{ requested_size, memory.len, std.debug.FormatStackTrace{ .stack_trace = bucketStackTrace(bucket, slot_count, slot_index, .alloc), - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = free_stack_trace, - .tty_config = tty_config, }, + std.debug.FormatStackTrace{ .stack_trace = free_stack_trace }, }); } if (alignment != slot_alignment) { @branchHint(.cold); - const tty_config = self.tty_config; log.err("Allocation alignment {d} does not match free alignment {d}. Allocation: {f} Free: {f}", .{ slot_alignment.toByteUnits(), alignment.toByteUnits(), std.debug.FormatStackTrace{ .stack_trace = bucketStackTrace(bucket, slot_count, slot_index, .alloc), - .tty_config = tty_config, - }, - std.debug.FormatStackTrace{ - .stack_trace = free_stack_trace, - .tty_config = tty_config, }, + std.debug.FormatStackTrace{ .stack_trace = free_stack_trace }, }); } } diff --git a/lib/std/log.zig b/lib/std/log.zig index 461dfca36e..029f724f44 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -15,7 +15,7 @@ //! //! For an example implementation of the `logFn` function, see `defaultLog`, //! which is the default implementation. It outputs to stderr, using color if -//! the detected `std.Io.tty.Config` supports it. Its output looks like this: +//! supported. Its output looks like this: //! ``` //! error: this is an error //! error(scope): this is an error with a non-default scope @@ -80,8 +80,6 @@ pub fn logEnabled(comptime level: Level, comptime scope: @EnumLiteral()) bool { return @intFromEnum(level) <= @intFromEnum(std.options.log_level); } -var static_threaded_io: std.Io.Threaded = .init_single_threaded; - /// The default implementation for the log function. Custom log functions may /// forward log messages to this function. /// @@ -93,36 +91,64 @@ pub fn defaultLog( comptime format: []const u8, args: anytype, ) void { - return defaultLogIo(level, scope, format, args, static_threaded_io.io()); + var buffer: [64]u8 = undefined; + const stderr = std.debug.lockStderrWriter(&buffer); + defer std.debug.unlockStderrWriter(); + return defaultLogFileWriter(level, scope, format, args, stderr); } -pub fn defaultLogIo( +pub fn defaultLogFileWriter( comptime level: Level, comptime scope: @EnumLiteral(), comptime format: []const u8, args: anytype, - io: std.Io, + fw: *std.Io.File.Writer, ) void { - var buffer: [64]u8 = undefined; - const stderr, const ttyconf = io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - ttyconf.setColor(stderr, switch (level) { + fw.setColor(switch (level) { .err => .red, .warn => .yellow, .info => .green, .debug => .magenta, }) catch {}; - ttyconf.setColor(stderr, .bold) catch {}; - stderr.writeAll(level.asText()) catch return; - ttyconf.setColor(stderr, .reset) catch {}; - ttyconf.setColor(stderr, .dim) catch {}; - ttyconf.setColor(stderr, .bold) catch {}; + fw.setColor(.bold) catch {}; + fw.interface.writeAll(level.asText()) catch return; + fw.setColor(.reset) catch {}; + fw.setColor(.dim) catch {}; + fw.setColor(.bold) catch {}; if (scope != .default) { - stderr.print("({s})", .{@tagName(scope)}) catch return; + fw.interface.print("({s})", .{@tagName(scope)}) catch return; + } + fw.interface.writeAll(": ") catch return; + fw.setColor(.reset) catch {}; + fw.interface.print(format ++ "\n", decorateArgs(args, fw.mode)) catch return; +} + +fn DecorateArgs(comptime Args: type) type { + const fields = @typeInfo(Args).@"struct".fields; + var new_fields: [fields.len]type = undefined; + for (fields, &new_fields) |old, *new| { + if (old.type == std.debug.FormatStackTrace) { + new.* = std.debug.FormatStackTrace.Decorated; + } else { + new.* = old.type; + } + } + return @Tuple(&new_fields); +} + +fn decorateArgs(args: anytype, file_writer_mode: std.Io.File.Writer.Mode) DecorateArgs(@TypeOf(args)) { + var new_args: DecorateArgs(@TypeOf(args)) = undefined; + inline for (args, &new_args) |old, *new| { + if (@TypeOf(old) == std.debug.FormatStackTrace) { + new.* = .{ + .stack_trace = old.stack_trace, + .file_writer_mode = file_writer_mode, + }; + } else { + new.* = old; + } } - stderr.writeAll(": ") catch return; - ttyconf.setColor(stderr, .reset) catch {}; - stderr.print(format ++ "\n", args) catch return; + return new_args; } /// Returns a scoped logging namespace that logs all messages using the scope diff --git a/lib/std/process.zig b/lib/std/process.zig index f7ecf5fdc2..5c6e6f89eb 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -439,25 +439,25 @@ pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError } /// On Windows, `key` must be valid WTF-8. -pub fn hasEnvVarConstant(comptime key: []const u8) bool { +pub inline fn hasEnvVarConstant(comptime key: []const u8) bool { if (native_os == .windows) { const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key); return getenvW(key_w) != null; } else if (native_os == .wasi and !builtin.link_libc) { - @compileError("hasEnvVarConstant is not supported for WASI without libc"); + return false; } else { return posix.getenv(key) != null; } } /// On Windows, `key` must be valid WTF-8. -pub fn hasNonEmptyEnvVarConstant(comptime key: []const u8) bool { +pub inline fn hasNonEmptyEnvVarConstant(comptime key: []const u8) bool { if (native_os == .windows) { const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key); const value = getenvW(key_w) orelse return false; return value.len != 0; } else if (native_os == .wasi and !builtin.link_libc) { - @compileError("hasNonEmptyEnvVarConstant is not supported for WASI without libc"); + return false; } else { const value = posix.getenv(key) orelse return false; return value.len != 0; diff --git a/src/main.zig b/src/main.zig index bb940a1fe7..2eebb6d060 100644 --- a/src/main.zig +++ b/src/main.zig @@ -247,8 +247,6 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { threaded.stack_size = thread_stack_size; const io = threaded.io(); - debug_allocator.tty_config = .detect(io, .stderr()); - const cmd = args[1]; const cmd_args = args[2..]; if (mem.eql(u8, cmd, "build-exe")) { -- cgit v1.2.3 From 95b0399d1bf7e12aefeba79652141d202f805fe4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Dec 2025 18:14:43 -0800 Subject: std: finish implementing futexWait with timer --- lib/std/Io/Threaded.zig | 1 - lib/std/Progress.zig | 61 ++++++++++++++++++++++++++----------------------- lib/std/debug.zig | 12 +++++----- 3 files changed, 39 insertions(+), 35 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index c2a3e15f15..41e93cee5b 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -1147,7 +1147,6 @@ const GroupClosure = struct { const group = gc.group; const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); const event: *Io.Event = @ptrCast(&group.context); - current_thread.current_closure = closure; current_thread.cancel_protection = .unblocked; diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 63fdddc231..603f53091e 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -24,7 +24,7 @@ terminal_mode: TerminalMode, update_worker: ?Io.Future(void), /// Atomically set by SIGWINCH as well as the root done() function. -redraw_event: Io.ResetEvent, +redraw_event: Io.Event, /// Indicates a request to shut down and reset global state. /// Accessed atomically. done: bool, @@ -333,8 +333,9 @@ pub const Node = struct { } } else { @atomicStore(bool, &global_progress.done, true, .monotonic); - global_progress.redraw_event.set(); - if (global_progress.update_worker) |worker| worker.await(global_progress.io); + const io = global_progress.io; + global_progress.redraw_event.set(io); + if (global_progress.update_worker) |*worker| worker.await(io); } } @@ -421,7 +422,7 @@ pub const StartFailure = union(enum) { unstarted, spawn_ipc_worker: error{ConcurrencyUnavailable}, spawn_update_worker: error{ConcurrencyUnavailable}, - parse_env_var: error{}, + parse_env_var: error{ InvalidCharacter, Overflow }, }; const node_storage_buffer_len = 83; @@ -452,7 +453,7 @@ const noop_impl = builtin.single_threaded or switch (builtin.os.tag) { /// Call `Node.end` when done. /// /// If an error occurs, `start_failure` will be populated. -pub fn start(options: Options, io: Io) Node { +pub fn start(io: Io, options: Options) Node { // Ensure there is only 1 global Progress object. if (global_progress.node_end_index != 0) { debug_start_trace.dump(); @@ -467,8 +468,8 @@ pub fn start(options: Options, io: Io) Node { assert(options.draw_buffer.len >= 200); global_progress.draw_buffer = options.draw_buffer; - global_progress.refresh_rate_ns = options.refresh_rate_ns; - global_progress.initial_delay_ns = options.initial_delay_ns; + global_progress.refresh_rate_ns = @intCast(options.refresh_rate_ns.toNanoseconds()); + global_progress.initial_delay_ns = @intCast(options.initial_delay_ns.toNanoseconds()); if (noop_impl) return Node.none; @@ -541,9 +542,13 @@ pub fn setStatus(new_status: Status) void { } /// Returns whether a resize is needed to learn the terminal size. -fn wait(timeout_ns: u64) bool { - const resize_flag = if (global_progress.redraw_event.timedWait(timeout_ns)) |_| true else |err| switch (err) { - error.Timeout => false, +fn wait(io: Io, timeout_ns: u64) bool { + const timeout: Io.Timeout = .{ .duration = .{ + .clock = .awake, + .raw = .fromNanoseconds(timeout_ns), + } }; + const resize_flag = if (global_progress.redraw_event.waitTimeout(io, timeout)) |_| true else |err| switch (err) { + error.Timeout, error.Canceled => false, }; global_progress.redraw_event.reset(); return resize_flag or (global_progress.cols == 0); @@ -555,34 +560,34 @@ fn updateThreadRun(io: Io) void { var serialized_buffer: Serialized.Buffer = undefined; { - const resize_flag = wait(global_progress.initial_delay_ns); + const resize_flag = wait(io, global_progress.initial_delay_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter(&.{})) |w| { + if (io.tryLockStderrWriter(&.{})) |fw| { defer io.unlockStderrWriter(); global_progress.need_clear = true; - w.writeAll(buffer) catch return; + fw.writeAllUnescaped(buffer) catch return; } } while (true) { - const resize_flag = wait(global_progress.refresh_rate_ns); + const resize_flag = wait(io, global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { - const w = io.lockStderrWriter(&.{}) catch return; + const fw = io.lockStderrWriter(&.{}) catch return; defer io.unlockStderrWriter(); - return clearWrittenWithEscapeCodes(w) catch {}; + return clearWrittenWithEscapeCodes(fw) catch {}; } maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter(&.{})) |w| { + if (io.tryLockStderrWriter(&.{})) |fw| { defer io.unlockStderrWriter(); global_progress.need_clear = true; - w.writeAll(buffer) catch return; + fw.writeAllUnescaped(buffer) catch return; } } } @@ -599,22 +604,22 @@ fn windowsApiUpdateThreadRun(io: Io) void { var serialized_buffer: Serialized.Buffer = undefined; { - const resize_flag = wait(global_progress.initial_delay_ns); + const resize_flag = wait(io, global_progress.initial_delay_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter()) |w| { + if (io.tryLockStderrWriter()) |fw| { defer io.unlockStderrWriter(); windowsApiWriteMarker(); global_progress.need_clear = true; - w.writeAll(buffer) catch return; + fw.writeAllUnescaped(buffer) catch return; windowsApiMoveToMarker(nl_n) catch return; } } while (true) { - const resize_flag = wait(global_progress.refresh_rate_ns); + const resize_flag = wait(io, global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { _ = io.lockStderrWriter() catch return; @@ -625,24 +630,24 @@ fn windowsApiUpdateThreadRun(io: Io) void { maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter()) |w| { + if (io.tryLockStderrWriter()) |fw| { defer io.unlockStderrWriter(); clearWrittenWindowsApi() catch return; windowsApiWriteMarker(); global_progress.need_clear = true; - w.writeAll(buffer) catch return; + fw.writeAllUnescaped(buffer) catch return; windowsApiMoveToMarker(nl_n) catch return; } } } -fn ipcThreadRun(io: Io, file: Io.File) anyerror!void { +fn ipcThreadRun(io: Io, file: Io.File) void { // Store this data in the thread so that it does not need to be part of the // linker data of the main executable. var serialized_buffer: Serialized.Buffer = undefined; { - _ = wait(global_progress.initial_delay_ns); + _ = wait(io, global_progress.initial_delay_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; @@ -654,7 +659,7 @@ fn ipcThreadRun(io: Io, file: Io.File) anyerror!void { } while (true) { - _ = wait(global_progress.refresh_rate_ns); + _ = wait(io, global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; @@ -1504,7 +1509,7 @@ fn handleSigWinch(sig: posix.SIG, info: *const posix.siginfo_t, ctx_ptr: ?*anyop _ = info; _ = ctx_ptr; assert(sig == .WINCH); - global_progress.redraw_event.set(); + global_progress.redraw_event.set(global_progress.io); } const have_sigwinch = switch (builtin.os.tag) { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 347dbf85ec..69d8c942c4 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -816,9 +816,9 @@ pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, fwm: File.Writer. } /// A thin wrapper around `writeStackTrace` which writes to stderr and ignores write errors. pub fn dumpStackTrace(st: *const StackTrace) void { - const stderr, const tty_config = lockStderrWriter(&.{}); + const stderr = lockStderrWriter(&.{}); defer unlockStderrWriter(); - writeStackTrace(st, stderr, tty_config) catch |err| switch (err) { + writeStackTrace(st, &stderr.interface, stderr.mode) catch |err| switch (err) { error.WriteFailed => {}, }; } @@ -1682,21 +1682,21 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize pub fn dump(t: @This()) void { if (!enabled) return; - const stderr, const tty_config = lockStderrWriter(&.{}); + const stderr = lockStderrWriter(&.{}); defer unlockStderrWriter(); const end = @min(t.index, size); for (t.addrs[0..end], 0..) |frames_array, i| { - stderr.print("{s}:\n", .{t.notes[i]}) catch return; + stderr.interface.print("{s}:\n", .{t.notes[i]}) catch return; var frames_array_mutable = frames_array; const frames = mem.sliceTo(frames_array_mutable[0..], 0); const stack_trace: StackTrace = .{ .index = frames.len, .instruction_addresses = frames, }; - writeStackTrace(&stack_trace, stderr, tty_config) catch return; + writeStackTrace(&stack_trace, &stderr.interface, stderr.mode) catch return; } if (t.index > end) { - stderr.print("{d} more traces not shown; consider increasing trace size\n", .{ + stderr.interface.print("{d} more traces not shown; consider increasing trace size\n", .{ t.index - end, }) catch return; } -- cgit v1.2.3 From b042e935228db5d46271d4d3d17afeb9ba5d7ce3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Dec 2025 20:55:11 -0800 Subject: std: update tty config references in the build system --- lib/compiler/build_runner.zig | 208 ++++++++++++++++++++--------------------- lib/std/Build.zig | 60 ++++++------ lib/std/Build/Fuzz.zig | 20 ++-- lib/std/Build/Step.zig | 9 +- lib/std/Build/Step/Compile.zig | 10 +- lib/std/Build/Step/Run.zig | 68 +++++++------- lib/std/Build/WebServer.zig | 4 - lib/std/Io/File/Writer.zig | 41 +++++++- lib/std/debug.zig | 2 +- lib/std/log.zig | 30 +----- lib/std/testing.zig | 42 ++++----- lib/std/zig.zig | 12 +-- lib/std/zig/ErrorBundle.zig | 59 ++++++------ 13 files changed, 271 insertions(+), 294 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 05cd21ecdb..acba1a4621 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -14,7 +14,6 @@ const WebServer = std.Build.WebServer; const Allocator = std.mem.Allocator; const fatal = std.process.fatal; const Writer = std.Io.Writer; -const tty = std.Io.tty; pub const root = @import("@build"); pub const dependencies = @import("@dependencies"); @@ -435,9 +434,7 @@ pub fn main() !void { if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); } - const ttyconf = color.detectTtyConf(io); - - const main_progress_node = std.Progress.start(.{ + const main_progress_node = std.Progress.start(io, .{ .disable_printing = (color == .off), }); defer main_progress_node.end(); @@ -509,8 +506,6 @@ pub fn main() !void { .error_style = error_style, .multiline_errors = multiline_errors, .summary = summary orelse if (watch or webui_listen != null) .line else .failures, - - .ttyconf = ttyconf, }; defer { run.memory_blocked_steps.deinit(gpa); @@ -524,10 +519,10 @@ pub fn main() !void { prepare(arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) { error.DependencyLoopDetected => { - // Perhaps in the future there could be an Advanced Options flag such as - // --debug-build-runner-leaks which would make this code return instead of - // calling exit. - std.debug.lockStdErr(); + // Perhaps in the future there could be an Advanced Options flag + // such as --debug-build-runner-leaks which would make this code + // return instead of calling exit. + _ = std.debug.lockStderrWriter(&.{}); process.exit(1); }, else => |e| return e, @@ -545,7 +540,6 @@ pub fn main() !void { if (builtin.single_threaded) unreachable; // `fatal` above break :ws .init(.{ .gpa = gpa, - .ttyconf = ttyconf, .graph = &graph, .all_steps = run.step_stack.keys(), .root_prog_node = main_progress_node, @@ -560,9 +554,9 @@ pub fn main() !void { } rebuild: while (true) : (if (run.error_style.clearOnUpdate()) { - const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const stderr = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); - try bw.writeAll("\x1B[2J\x1B[3J\x1B[H"); + try stderr.writeAllUnescaped("\x1B[2J\x1B[3J\x1B[H"); }) { if (run.web_server) |*ws| ws.startBuild(); @@ -663,9 +657,6 @@ const Run = struct { memory_blocked_steps: std.ArrayList(*Step), /// Allocated into `gpa`. step_stack: std.AutoArrayHashMapUnmanaged(*Step, void), - /// Similar to the `tty.Config` returned by `std.debug.lockStderrWriter`, - /// but also respects the '--color' flag. - ttyconf: tty.Config, claimed_rss: usize, error_style: ErrorStyle, @@ -839,7 +830,6 @@ fn runStepNames( var f = std.Build.Fuzz.init( gpa, io, - run.ttyconf, step_stack.keys(), parent_prog_node, mode, @@ -866,18 +856,20 @@ fn runStepNames( .none => break :summary, } - const w, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const stderr = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); - const ttyconf = run.ttyconf; + + const w = &stderr.interface; + const fwm = stderr.mode; const total_count = success_count + failure_count + pending_count + skipped_count; - ttyconf.setColor(w, .cyan) catch {}; - ttyconf.setColor(w, .bold) catch {}; + fwm.setColor(w, .cyan) catch {}; + fwm.setColor(w, .bold) catch {}; w.writeAll("Build Summary: ") catch {}; - ttyconf.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; { - ttyconf.setColor(w, .dim) catch {}; + fwm.setColor(w, .dim) catch {}; var first = true; if (skipped_count > 0) { w.print("{s}{d} skipped", .{ if (first) " (" else ", ", skipped_count }) catch {}; @@ -888,12 +880,12 @@ fn runStepNames( first = false; } if (!first) w.writeByte(')') catch {}; - ttyconf.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } if (test_count > 0) { w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; - ttyconf.setColor(w, .dim) catch {}; + fwm.setColor(w, .dim) catch {}; var first = true; if (test_skip_count > 0) { w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {}; @@ -912,7 +904,7 @@ fn runStepNames( first = false; } if (!first) w.writeByte(')') catch {}; - ttyconf.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } w.writeAll("\n") catch {}; @@ -926,7 +918,7 @@ fn runStepNames( var print_node: PrintNode = .{ .parent = null }; if (step_names.len == 0) { print_node.last = true; - printTreeStep(b, b.default_step, run, w, ttyconf, &print_node, &step_stack_copy) catch {}; + printTreeStep(b, b.default_step, run, w, fwm, &print_node, &step_stack_copy) catch {}; } else { const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: { var i: usize = step_names.len; @@ -945,7 +937,7 @@ fn runStepNames( for (step_names, 0..) |step_name, i| { const tls = b.top_level_steps.get(step_name).?; print_node.last = i + 1 == last_index; - printTreeStep(b, &tls.step, run, w, ttyconf, &print_node, &step_stack_copy) catch {}; + printTreeStep(b, &tls.step, run, w, fwm, &print_node, &step_stack_copy) catch {}; } } w.writeByte('\n') catch {}; @@ -962,7 +954,7 @@ fn runStepNames( if (run.error_style.verboseContext()) break :code 1; // failure; print build command break :code 2; // failure; do not print build command }; - std.debug.lockStdErr(); + _ = std.debug.lockStderrWriter(&.{}); process.exit(code); } @@ -971,31 +963,31 @@ const PrintNode = struct { last: bool = false, }; -fn printPrefix(node: *PrintNode, stderr: *Writer, ttyconf: tty.Config) !void { +fn printPrefix(node: *PrintNode, w: *Writer, fwm: File.Writer.Mode) !void { const parent = node.parent orelse return; if (parent.parent == null) return; - try printPrefix(parent, stderr, ttyconf); + try printPrefix(parent, w, fwm); if (parent.last) { - try stderr.writeAll(" "); + try w.writeAll(" "); } else { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "| ", - .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ + try w.writeAll(switch (fwm) { + .terminal_escaped => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ + else => "| ", }); } } -fn printChildNodePrefix(stderr: *Writer, ttyconf: tty.Config) !void { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "+- ", - .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ +fn printChildNodePrefix(w: *Writer, fwm: File.Writer.Mode) !void { + try w.writeAll(switch (fwm) { + .terminal_escaped => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ + else => "+- ", }); } fn printStepStatus( s: *Step, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, run: *const Run, ) !void { switch (s.state) { @@ -1005,13 +997,13 @@ fn printStepStatus( .running => unreachable, .dependency_failure => { - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); try stderr.writeAll(" transitive failure\n"); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); }, .success => { - try ttyconf.setColor(stderr, .green); + try fwm.setColor(stderr, .green); if (s.result_cached) { try stderr.writeAll(" cached"); } else if (s.test_results.test_count > 0) { @@ -1019,19 +1011,19 @@ fn printStepStatus( assert(s.test_results.test_count == pass_count + s.test_results.skip_count); try stderr.print(" {d} pass", .{pass_count}); if (s.test_results.skip_count > 0) { - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); try stderr.print("{d} skip", .{s.test_results.skip_count}); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); try stderr.print(" ({d} total)", .{s.test_results.test_count}); } else { try stderr.writeAll(" success"); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); if (s.result_duration_ns) |ns| { - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); if (ns >= std.time.ns_per_min) { try stderr.print(" {d}m", .{ns / std.time.ns_per_min}); } else if (ns >= std.time.ns_per_s) { @@ -1043,11 +1035,11 @@ fn printStepStatus( } else { try stderr.print(" {d}ns", .{ns}); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } if (s.result_peak_rss != 0) { const rss = s.result_peak_rss; - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); if (rss >= 1000_000_000) { try stderr.print(" MaxRSS:{d}G", .{rss / 1000_000_000}); } else if (rss >= 1000_000) { @@ -1057,25 +1049,25 @@ fn printStepStatus( } else { try stderr.print(" MaxRSS:{d}B", .{rss}); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } try stderr.writeAll("\n"); }, .skipped, .skipped_oom => |skip| { - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); try stderr.writeAll(" skipped"); if (skip == .skipped_oom) { try stderr.writeAll(" (not enough memory)"); - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); try stderr.print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); } try stderr.writeAll("\n"); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); }, .failure => { - try printStepFailure(s, stderr, ttyconf, false); - try ttyconf.setColor(stderr, .reset); + try printStepFailure(s, stderr, fwm, false); + try fwm.setColor(stderr, .reset); }, } } @@ -1083,48 +1075,48 @@ fn printStepStatus( fn printStepFailure( s: *Step, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, dim: bool, ) !void { if (s.result_error_bundle.errorMessageCount() > 0) { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print(" {d} errors\n", .{ s.result_error_bundle.errorMessageCount(), }); } else if (!s.test_results.isSuccess()) { // These first values include all of the test "statuses". Every test is either passsed, // skipped, failed, crashed, or timed out. - try ttyconf.setColor(stderr, .green); + try fwm.setColor(stderr, .green); try stderr.print(" {d} pass", .{s.test_results.passCount()}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); if (s.test_results.skip_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); try stderr.print("{d} skip", .{s.test_results.skip_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } if (s.test_results.fail_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} fail", .{s.test_results.fail_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } if (s.test_results.crash_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} crash", .{s.test_results.crash_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } if (s.test_results.timeout_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} timeout", .{s.test_results.timeout_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } try stderr.print(" ({d} total)", .{s.test_results.test_count}); @@ -1134,10 +1126,10 @@ fn printStepFailure( // 2 pass, 1 skip, 2 fail (5 total); 2 leaks if (s.test_results.leak_count > 0) { try stderr.writeAll("; "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} leaks", .{s.test_results.leak_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } // It's usually not helpful to know how many error logs there were because they tend to @@ -1151,19 +1143,19 @@ fn printStepFailure( }; if (show_err_logs) { try stderr.writeAll("; "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} error logs", .{s.test_results.log_err_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } try stderr.writeAll("\n"); } else if (s.result_error_msgs.items.len > 0) { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll(" failure\n"); } else { assert(s.result_stderr.len > 0); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll(" stderr\n"); } } @@ -1173,7 +1165,7 @@ fn printTreeStep( s: *Step, run: *const Run, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, parent_node: *PrintNode, step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), ) !void { @@ -1186,26 +1178,26 @@ fn printTreeStep( .failures => s.state == .success, }; if (skip) return; - try printPrefix(parent_node, stderr, ttyconf); + try printPrefix(parent_node, stderr, fwm); if (parent_node.parent != null) { if (parent_node.last) { - try printChildNodePrefix(stderr, ttyconf); + try printChildNodePrefix(stderr, fwm); } else { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "+- ", - .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ + try stderr.writeAll(switch (fwm) { + .terminal_escaped => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ + else => "+- ", }); } } - if (!first) try ttyconf.setColor(stderr, .dim); + if (!first) try fwm.setColor(stderr, .dim); // dep_prefix omitted here because it is redundant with the tree. try stderr.writeAll(s.name); if (first) { - try printStepStatus(s, stderr, ttyconf, run); + try printStepStatus(s, stderr, fwm, run); const last_index = if (summary == .all) s.dependencies.items.len -| 1 else blk: { var i: usize = s.dependencies.items.len; @@ -1227,7 +1219,7 @@ fn printTreeStep( .parent = parent_node, .last = i == last_index, }; - try printTreeStep(b, dep, run, stderr, ttyconf, &print_node, step_stack); + try printTreeStep(b, dep, run, stderr, fwm, &print_node, step_stack); } } else { if (s.dependencies.items.len == 0) { @@ -1237,7 +1229,7 @@ fn printTreeStep( s.dependencies.items.len, }); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } } @@ -1368,7 +1360,6 @@ fn workerMakeOneStep( .progress_node = sub_prog_node, .watch = run.watch, .web_server = if (run.web_server) |*ws| ws else null, - .ttyconf = run.ttyconf, .unit_test_timeout_ns = run.unit_test_timeout_ns, .gpa = gpa, }); @@ -1378,10 +1369,9 @@ fn workerMakeOneStep( const show_error_msgs = s.result_error_msgs.items.len > 0; const show_stderr = s.result_stderr.len > 0; if (show_error_msgs or show_compile_errors or show_stderr) { - const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const stderr = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); - const ttyconf = run.ttyconf; - printErrorMessages(gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {}; + printErrorMessages(gpa, s, .{}, &stderr.interface, stderr.mode, run.error_style, run.multiline_errors) catch {}; } handle_result: { @@ -1449,7 +1439,7 @@ pub fn printErrorMessages( failing_step: *Step, options: std.zig.ErrorBundle.RenderOptions, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, error_style: ErrorStyle, multiline_errors: MultilineErrors, ) !void { @@ -1464,29 +1454,29 @@ pub fn printErrorMessages( } // Now, `step_stack` has the subtree that we want to print, in reverse order. - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); var indent: usize = 0; while (step_stack.pop()) |s| : (indent += 1) { if (indent > 0) { try stderr.splatByteAll(' ', (indent - 1) * 3); - try printChildNodePrefix(stderr, ttyconf); + try printChildNodePrefix(stderr, fwm); } try stderr.writeAll(s.name); if (s == failing_step) { - try printStepFailure(s, stderr, ttyconf, true); + try printStepFailure(s, stderr, fwm, true); } else { try stderr.writeAll("\n"); } } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } else { // Just print the failing step itself. - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); try stderr.writeAll(failing_step.name); - try printStepFailure(failing_step, stderr, ttyconf, true); - try ttyconf.setColor(stderr, .reset); + try printStepFailure(failing_step, stderr, fwm, true); + try fwm.setColor(stderr, .reset); } if (failing_step.result_stderr.len > 0) { @@ -1496,12 +1486,12 @@ pub fn printErrorMessages( } } - try failing_step.result_error_bundle.renderToWriter(options, stderr, ttyconf); + try failing_step.result_error_bundle.renderToWriter(options, stderr, fwm); for (failing_step.result_error_msgs.items) |msg| { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll("error:"); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); if (std.mem.indexOfScalar(u8, msg, '\n') == null) { try stderr.print(" {s}\n", .{msg}); } else switch (multiline_errors) { @@ -1519,9 +1509,9 @@ pub fn printErrorMessages( if (error_style.verboseContext()) { if (failing_step.result_failed_command) |cmd_str| { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll("failed command: "); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); try stderr.writeAll(cmd_str); try stderr.writeByte('\n'); } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index ae2ab1c4d0..085254fdde 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -5,9 +5,8 @@ const std = @import("std.zig"); const Io = std.Io; const fs = std.fs; const mem = std.mem; -const debug = std.debug; const panic = std.debug.panic; -const assert = debug.assert; +const assert = std.debug.assert; const log = std.log; const StringHashMap = std.StringHashMap; const Allocator = std.mem.Allocator; @@ -2090,7 +2089,7 @@ pub fn dependencyFromBuildZig( } const full_path = b.pathFromRoot("build.zig.zon"); - debug.panic("'{}' is not a build.zig struct of a dependency in '{s}'", .{ build_zig, full_path }); + std.debug.panic("'{}' is not a build.zig struct of a dependency in '{s}'", .{ build_zig, full_path }); } fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool { @@ -2249,9 +2248,9 @@ pub const GeneratedFile = struct { pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 { return gen.path orelse { - const w, const ttyconf = debug.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.step, w, ttyconf, src_builder, asking_step) catch {}; - debug.unlockStderrWriter(); + const stderr = std.debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.step, &stderr.interface, stderr.mode, src_builder, asking_step) catch {}; + std.debug.unlockStderrWriter(); @panic("misconfigured build script"); }; } @@ -2458,9 +2457,9 @@ pub const LazyPath = union(enum) { var file_path: Cache.Path = .{ .root_dir = Cache.Directory.cwd(), .sub_path = gen.file.path orelse { - const w, const ttyconf = debug.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.file.step, w, ttyconf, src_builder, asking_step) catch {}; - debug.unlockStderrWriter(); + const stderr = std.debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.file.step, &stderr.interface, stderr.mode, src_builder, asking_step) catch {}; + std.debug.unlockStderrWriter(); @panic("misconfigured build script"); }, }; @@ -2550,37 +2549,40 @@ fn dumpBadDirnameHelp( comptime msg: []const u8, args: anytype, ) anyerror!void { - const w, const tty_config = debug.lockStderrWriter(&.{}); - defer debug.unlockStderrWriter(); + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + + const w = &stderr.interface; + const fwm = stderr.mode; try w.print(msg, args); if (fail_step) |s| { - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" The step was created by this stack trace:\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; - s.dump(w, tty_config); + s.dump(w, fwm); } if (asking_step) |as| { - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; - as.dump(w, tty_config); + as.dump(w, fwm); } - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" Hope that helps. Proceeding to panic.\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } /// In this function the stderr mutex has already been locked. pub fn dumpBadGetPathHelp( s: *Step, - w: *std.Io.Writer, - tty_config: std.Io.tty.Config, + w: *Io.Writer, + fwm: File.Writer.Mode, src_builder: *Build, asking_step: ?*Step, ) anyerror!void { @@ -2594,21 +2596,21 @@ pub fn dumpBadGetPathHelp( s.name, }); - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" The step was created by this stack trace:\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; - s.dump(w, tty_config); + s.dump(w, fwm); if (asking_step) |as| { - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; - as.dump(w, tty_config); + as.dump(w, fwm); } - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" Hope that helps. Proceeding to panic.\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } pub const InstallDir = union(enum) { diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 8837d7d527..c2b7d1e9e9 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -9,14 +9,12 @@ const Allocator = std.mem.Allocator; const log = std.log; const Coverage = std.debug.Coverage; const abi = Build.abi.fuzz; -const tty = std.Io.tty; const Fuzz = @This(); const build_runner = @import("root"); gpa: Allocator, io: Io, -ttyconf: tty.Config, mode: Mode, /// Allocated into `gpa`. @@ -77,7 +75,6 @@ const CoverageMap = struct { pub fn init( gpa: Allocator, io: Io, - ttyconf: tty.Config, all_steps: []const *Build.Step, root_prog_node: std.Progress.Node, mode: Mode, @@ -95,7 +92,7 @@ pub fn init( if (run.producer == null) continue; if (run.fuzz_tests.items.len == 0) continue; try steps.append(gpa, run); - rebuild_group.async(io, rebuildTestsWorkerRun, .{ run, gpa, ttyconf, rebuild_node }); + rebuild_group.async(io, rebuildTestsWorkerRun, .{ run, gpa, rebuild_node }); } if (steps.items.len == 0) fatal("no fuzz tests found", .{}); @@ -115,7 +112,6 @@ pub fn init( return .{ .gpa = gpa, .io = io, - .ttyconf = ttyconf, .mode = mode, .run_steps = run_steps, .group = .init, @@ -154,14 +150,14 @@ pub fn deinit(fuzz: *Fuzz) void { fuzz.gpa.free(fuzz.run_steps); } -fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) void { - rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| { +fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, parent_prog_node: std.Progress.Node) void { + rebuildTestsWorkerRunFallible(run, gpa, parent_prog_node) catch |err| { const compile = run.producer.?; log.err("step '{s}': failed to rebuild in fuzz mode: {t}", .{ compile.step.name, err }); }; } -fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) !void { +fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_node: std.Progress.Node) !void { const compile = run.producer.?; const prog_node = parent_prog_node.start(compile.step.name, 0); defer prog_node.end(); @@ -174,9 +170,9 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: tty.Co if (show_error_msgs or show_compile_errors or show_stderr) { var buf: [256]u8 = undefined; - const w, _ = std.debug.lockStderrWriter(&buf); + const stderr = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &compile.step, .{}, w, ttyconf, .verbose, .indent) catch {}; + build_runner.printErrorMessages(gpa, &compile.step, .{}, &stderr.interface, stderr.mode, .verbose, .indent) catch {}; } const rebuilt_bin_path = result catch |err| switch (err) { @@ -200,9 +196,9 @@ fn fuzzWorkerRun( run.rerunInFuzzMode(fuzz, unit_test_index, prog_node) catch |err| switch (err) { error.MakeFailed => { var buf: [256]u8 = undefined; - const w, _ = std.debug.lockStderrWriter(&buf); + const stderr = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &run.step, .{}, w, fuzz.ttyconf, .verbose, .indent) catch {}; + build_runner.printErrorMessages(gpa, &run.step, .{}, &stderr.interface, stderr.mode, .verbose, .indent) catch {}; return; }, else => { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 9c7fcc757f..0df58b24b7 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -117,7 +117,6 @@ pub const MakeOptions = struct { // it currently breaks because `std.net.Address` doesn't work there. Work around for now. .wasm32 => void, }, - ttyconf: std.Io.tty.Config, /// If set, this is a timeout to enforce on all individual unit tests, in nanoseconds. unit_test_timeout_ns: ?u64, /// Not to be confused with `Build.allocator`, which is an alias of `Build.graph.arena`. @@ -329,16 +328,16 @@ pub fn cast(step: *Step, comptime T: type) ?*T { } /// For debugging purposes, prints identifying information about this Step. -pub fn dump(step: *Step, w: *Io.Writer, tty_config: Io.tty.Config) void { +pub fn dump(step: *Step, w: *Io.Writer, fwm: Io.File.Writer.Mode) void { if (step.debug_stack_trace.instruction_addresses.len > 0) { w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; - std.debug.writeStackTrace(&step.debug_stack_trace, w, tty_config) catch {}; + std.debug.writeStackTrace(&step.debug_stack_trace, w, fwm) catch {}; } else { const field = "debug_stack_frames_count"; comptime assert(@hasField(Build, field)); - tty_config.setColor(w, .yellow) catch {}; + fwm.setColor(w, .yellow) catch {}; w.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {}; - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } } diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index dfa5460981..d703e55b87 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -926,15 +926,15 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking const maybe_path: ?*GeneratedFile = @field(compile, tag_name); const generated_file = maybe_path orelse { - const w, const ttyconf = std.debug.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {}; + const stderr = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, &stderr.interface, stderr.mode, compile.step.owner, asking_step) catch {}; std.debug.unlockStderrWriter(); @panic("missing emit option for " ++ tag_name); }; const path = generated_file.path orelse { - const w, const ttyconf = std.debug.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {}; + const stderr = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, &stderr.interface, stderr.mode, compile.step.owner, asking_step) catch {}; std.debug.unlockStderrWriter(); @panic(tag_name ++ " is null. Is there a missing step dependency?"); }; @@ -1904,7 +1904,7 @@ fn checkCompileErrors(compile: *Compile) !void { try actual_eb.renderToWriter(.{ .include_reference_trace = false, .include_source_line = false, - }, &aw.writer, .no_color); + }, &aw.writer, .streaming); break :ae try aw.toOwnedSlice(); }; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 95024061d8..8aafc77820 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -5,7 +5,7 @@ const std = @import("std"); const Io = std.Io; const Build = std.Build; const Step = std.Build.Step; -const fs = std.fs; +const Dir = std.Io.Dir; const mem = std.mem; const process = std.process; const EnvMap = std.process.EnvMap; @@ -26,19 +26,7 @@ cwd: ?Build.LazyPath, env_map: ?*EnvMap, /// Controls the `NO_COLOR` and `CLICOLOR_FORCE` environment variables. -color: enum { - /// `CLICOLOR_FORCE` is set, and `NO_COLOR` is unset. - enable, - /// `NO_COLOR` is set, and `CLICOLOR_FORCE` is unset. - disable, - /// If the build runner is using color, equivalent to `.enable`. Otherwise, equivalent to `.disable`. - inherit, - /// If stderr is captured or checked, equivalent to `.disable`. Otherwise, equivalent to `.inherit`. - auto, - /// The build runner does not modify the `CLICOLOR_FORCE` or `NO_COLOR` environment variables. - /// They are treated like normal variables, so can be controlled through `setEnvironmentVariable`. - manual, -} = .auto, +color: Color = .auto, /// When `true` prevents `ZIG_PROGRESS` environment variable from being passed /// to the child process, which otherwise would be used for the child to send @@ -112,6 +100,20 @@ rebuilt_executable: ?Path, /// If this Run step was produced by a Compile step, it is tracked here. producer: ?*Step.Compile, +pub const Color = enum { + /// `CLICOLOR_FORCE` is set, and `NO_COLOR` is unset. + enable, + /// `NO_COLOR` is set, and `CLICOLOR_FORCE` is unset. + disable, + /// If the build runner is using color, equivalent to `.enable`. Otherwise, equivalent to `.disable`. + inherit, + /// If stderr is captured or checked, equivalent to `.disable`. Otherwise, equivalent to `.inherit`. + auto, + /// The build runner does not modify the `CLICOLOR_FORCE` or `NO_COLOR` environment variables. + /// They are treated like normal variables, so can be controlled through `setEnvironmentVariable`. + manual, +}; + pub const StdIn = union(enum) { none, bytes: []const u8, @@ -565,7 +567,7 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void { if (prev_path) |pp| { const new_path = b.fmt("{s}{c}{s}", .{ pp, - if (use_wine) fs.path.delimiter_windows else fs.path.delimiter, + if (use_wine) Dir.path.delimiter_windows else Dir.path.delimiter, search_path, }); env_map.put(key, new_path) catch @panic("OOM"); @@ -748,7 +750,7 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { const b = run.step.owner; const path_str = path.toString(b.graph.arena) catch @panic("OOM"); - if (std.fs.path.isAbsolute(path_str)) { + if (Dir.path.isAbsolute(path_str)) { // Absolute paths don't need changing. return path_str; } @@ -756,19 +758,19 @@ fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { const child_lazy_cwd = run.cwd orelse break :rel path_str; const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(b.graph.arena) catch @panic("OOM"); // Convert it from relative to *our* cwd, to relative to the *child's* cwd. - break :rel std.fs.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM"); + break :rel Dir.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM"); }; // Not every path can be made relative, e.g. if the path and the child cwd are on different // disk designators on Windows. In that case, `relative` will return an absolute path which we can // just return. - if (std.fs.path.isAbsolute(child_cwd_rel)) { + if (Dir.path.isAbsolute(child_cwd_rel)) { return child_cwd_rel; } // We're not done yet. In some cases this path must be prefixed with './': // * On POSIX, the executable name cannot be a single component like 'foo' // * Some executables might treat a leading '-' like a flag, which we must avoid // There's no harm in it, so just *always* apply this prefix. - return std.fs.path.join(b.graph.arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); + return Dir.path.join(b.graph.arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); } const IndexedOutput = struct { @@ -965,11 +967,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { &digest, ); - const output_dir_path = "o" ++ fs.path.sep_str ++ &digest; + const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest; for (output_placeholders.items) |placeholder| { const output_sub_path = b.pathJoin(&.{ output_dir_path, placeholder.output.basename }); const output_sub_dir_path = switch (placeholder.tag) { - .output_file => fs.path.dirname(output_sub_path).?, + .output_file => Dir.path.dirname(output_sub_path).?, .output_directory => output_sub_path, else => unreachable, }; @@ -995,13 +997,13 @@ fn make(step: *Step, options: Step.MakeOptions) !void { // We do not know the final output paths yet, use temp paths to run the command. const rand_int = std.crypto.random.int(u64); - const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); for (output_placeholders.items) |placeholder| { const output_components = .{ tmp_dir_path, placeholder.output.basename }; const output_sub_path = b.pathJoin(&output_components); const output_sub_dir_path = switch (placeholder.tag) { - .output_file => fs.path.dirname(output_sub_path).?, + .output_file => Dir.path.dirname(output_sub_path).?, .output_directory => output_sub_path, else => unreachable, }; @@ -1023,7 +1025,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, options, null); - const dep_file_dir = Io.Dir.cwd(); + const dep_file_dir = Dir.cwd(); const dep_file_basename = dep_output_file.generated_file.getPath2(b, step); if (has_side_effects) try man.addDepFile(dep_file_dir, dep_file_basename) @@ -1040,7 +1042,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { // Rename into place if (any_output) { - const o_sub_path = "o" ++ fs.path.sep_str ++ &digest; + const o_sub_path = "o" ++ Dir.path.sep_str ++ &digest; b.cache_root.handle.rename(tmp_dir_path, b.cache_root.handle, o_sub_path, io) catch |err| { if (err == error.PathAlreadyExists) { @@ -1139,12 +1141,11 @@ pub fn rerunInFuzzMode( const has_side_effects = false; const rand_int = std.crypto.random.int(u64); - const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{ .progress_node = prog_node, .watch = undefined, // not used by `runCommand` .web_server = null, // only needed for time reports - .ttyconf = fuzz.ttyconf, .unit_test_timeout_ns = null, // don't time out fuzz tests for now .gpa = fuzz.gpa, }, .{ @@ -1266,10 +1267,7 @@ fn runCommand( try env_map.put("NO_COLOR", "1"); env_map.remove("CLICOLOR_FORCE"); }, - .inherit => switch (options.ttyconf) { - .no_color, .windows_api => continue :color .disable, - .escape_codes => continue :color .enable, - }, + .inherit => {}, .auto => { const capture_stderr = run.captured_stderr != null or switch (run.stdio) { .check => |checks| checksContainStderr(checks.items), @@ -1464,7 +1462,7 @@ fn runCommand( captured.output.generated_file.path = output_path; const sub_path = b.pathJoin(&output_components); - const sub_path_dirname = fs.path.dirname(sub_path).?; + const sub_path_dirname = Dir.path.dirname(sub_path).?; b.cache_root.handle.makePath(io, sub_path_dirname) catch |err| { return step.fail("unable to make path '{f}{s}': {s}", .{ b.cache_root, sub_path_dirname, @errorName(err), @@ -1650,8 +1648,8 @@ fn spawnChildAndCollect( if (!run.disable_zig_progress and !inherit) { child.progress_node = options.progress_node; } - if (inherit) std.debug.lockStdErr(); - defer if (inherit) std.debug.unlockStdErr(); + if (inherit) _ = std.debug.lockStderrWriter(&.{}); + defer if (inherit) std.debug.unlockStderrWriter(); var timer = try std.time.Timer.start(); const res = try evalGeneric(run, &child); run.step.result_duration_ns = timer.read(); @@ -2277,7 +2275,7 @@ fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void { if (compile.root_module.resolved_target.?.result.os.tag == .windows and compile.isDynamicLibrary()) { - addPathDir(run, fs.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?); + addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?); } } } diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 5f633c5948..ccb7159192 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -2,7 +2,6 @@ gpa: Allocator, graph: *const Build.Graph, all_steps: []const *Build.Step, listen_address: net.IpAddress, -ttyconf: Io.tty.Config, root_prog_node: std.Progress.Node, watch: bool, @@ -52,7 +51,6 @@ pub fn notifyUpdate(ws: *WebServer) void { pub const Options = struct { gpa: Allocator, - ttyconf: Io.tty.Config, graph: *const std.Build.Graph, all_steps: []const *Build.Step, root_prog_node: std.Progress.Node, @@ -98,7 +96,6 @@ pub fn init(opts: Options) WebServer { return .{ .gpa = opts.gpa, - .ttyconf = opts.ttyconf, .graph = opts.graph, .all_steps = all_steps, .listen_address = opts.listen_address, @@ -233,7 +230,6 @@ pub fn finishBuild(ws: *WebServer, opts: struct { ws.fuzz = Fuzz.init( ws.gpa, ws.graph.io, - ws.ttyconf, ws.all_steps, ws.root_prog_node, .{ .forever = .{ .ws = ws } }, diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index 8f9f573a4b..03e4ad8a15 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -99,7 +99,7 @@ pub const Mode = union(enum) { pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; - pub fn setColor(mode: Mode, io_w: *Io.Writer, color: Color) Mode.SetColorError!void { + pub fn setColor(mode: Mode, io_w: *Io.Writer, color: Color) SetColorError!void { switch (mode) { .streaming, .positional, .streaming_simple, .positional_simple, .failure => return, .terminal_escaped => { @@ -155,6 +155,34 @@ pub const Mode = union(enum) { }, } } + + fn DecorateArgs(comptime Args: type) type { + const fields = @typeInfo(Args).@"struct".fields; + var new_fields: [fields.len]type = undefined; + for (fields, &new_fields) |old, *new| { + if (old.type == std.debug.FormatStackTrace) { + new.* = std.debug.FormatStackTrace.Decorated; + } else { + new.* = old.type; + } + } + return @Tuple(&new_fields); + } + + pub fn decorateArgs(file_writer_mode: std.Io.File.Writer.Mode, args: anytype) DecorateArgs(@TypeOf(args)) { + var new_args: DecorateArgs(@TypeOf(args)) = undefined; + inline for (args, &new_args) |old, *new| { + if (@TypeOf(old) == std.debug.FormatStackTrace) { + new.* = .{ + .stack_trace = old.stack_trace, + .file_writer_mode = file_writer_mode, + }; + } else { + new.* = old; + } + } + return new_args; + } }; pub const Error = error{ @@ -426,6 +454,8 @@ pub fn end(w: *Writer) EndError!void { .streaming, .streaming_simple, + .terminal_escaped, + .terminal_winapi, .failure, => {}, } @@ -453,10 +483,11 @@ pub const Color = enum { reset, }; -pub const SetColorError = Mode.SetColorError; - -pub fn setColor(w: *Writer, color: Color) SetColorError!void { - return w.mode.setColor(&w.interface, color); +pub fn setColor(w: *Writer, color: Color) Io.Writer.Error!void { + return w.mode.setColor(&w.interface, color) catch |err| switch (err) { + error.WriteFailed => |e| return e, + else => |e| w.err = e, + }; } pub fn disableEscape(w: *Writer) Mode { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 69d8c942c4..67f7d3a9fe 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -306,7 +306,7 @@ pub fn print(comptime fmt: []const u8, args: anytype) void { var buffer: [64]u8 = undefined; const stderr = lockStderrWriter(&buffer); defer unlockStderrWriter(); - stderr.interface.print(fmt, args) catch return; + stderr.interface.print(fmt, stderr.mode.decorateArgs(args)) catch return; } } diff --git a/lib/std/log.zig b/lib/std/log.zig index 029f724f44..b75bada580 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -120,35 +120,7 @@ pub fn defaultLogFileWriter( } fw.interface.writeAll(": ") catch return; fw.setColor(.reset) catch {}; - fw.interface.print(format ++ "\n", decorateArgs(args, fw.mode)) catch return; -} - -fn DecorateArgs(comptime Args: type) type { - const fields = @typeInfo(Args).@"struct".fields; - var new_fields: [fields.len]type = undefined; - for (fields, &new_fields) |old, *new| { - if (old.type == std.debug.FormatStackTrace) { - new.* = std.debug.FormatStackTrace.Decorated; - } else { - new.* = old.type; - } - } - return @Tuple(&new_fields); -} - -fn decorateArgs(args: anytype, file_writer_mode: std.Io.File.Writer.Mode) DecorateArgs(@TypeOf(args)) { - var new_args: DecorateArgs(@TypeOf(args)) = undefined; - inline for (args, &new_args) |old, *new| { - if (@TypeOf(old) == std.debug.FormatStackTrace) { - new.* = .{ - .stack_trace = old.stack_trace, - .file_writer_mode = file_writer_mode, - }; - } else { - new.* = old; - } - } - return new_args; + fw.interface.print(format ++ "\n", fw.mode.decorateArgs(args)) catch return; } /// Returns a scoped logging namespace that logs all messages using the scope diff --git a/lib/std/testing.zig b/lib/std/testing.zig index ab9f1f73c3..092b65d71a 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -354,11 +354,10 @@ test expectApproxEqRel { } } -/// This function is intended to be used only in tests. When the two slices are not -/// equal, prints diagnostics to stderr to show exactly how they are not equal (with -/// the differences highlighted in red), then returns a test failure error. -/// The colorized output is optional and controlled by the return of `Io.tty.Config.detect`. -/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. +/// This function is intended to be used only in tests. When the two slices are +/// not equal, prints diagnostics to stderr to show exactly how they are not +/// equal (with the differences highlighted in red), then returns a test +/// failure error. pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void { const diff_index: usize = diff_index: { const shortest = @min(expected.len, actual.len); @@ -369,20 +368,13 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const break :diff_index if (expected.len == actual.len) return else shortest; }; if (!backend_can_print) return error.TestExpectedEqual; - const stderr_w, const ttyconf = std.debug.lockStderrWriter(&.{}); + const stderr = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - failEqualSlices(T, expected, actual, diff_index, stderr_w, ttyconf) catch {}; + failEqualSlices(T, expected, actual, diff_index, &stderr.interface, stderr.mode) catch {}; return error.TestExpectedEqual; } -fn failEqualSlices( - comptime T: type, - expected: []const T, - actual: []const T, - diff_index: usize, - w: *Io.Writer, - ttyconf: Io.tty.Config, -) !void { +fn failEqualSlices(comptime T: type, expected: []const T, actual: []const T, diff_index: usize, w: *Io.Writer, fwm: Io.File.Writer.Mode) !void { try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); // TODO: Should this be configurable by the caller? @@ -404,12 +396,12 @@ fn failEqualSlices( var differ = if (T == u8) BytesDiffer{ .expected = expected_window, .actual = actual_window, - .ttyconf = ttyconf, + .file_writer_mode = fwm, } else SliceDiffer(T){ .start_index = window_start, .expected = expected_window, .actual = actual_window, - .ttyconf = ttyconf, + .file_writer_mode = fwm, }; // Print indexes as hex for slices of u8 since it's more likely to be binary data where @@ -466,7 +458,7 @@ fn SliceDiffer(comptime T: type) type { start_index: usize, expected: []const T, actual: []const T, - ttyconf: Io.tty.Config, + file_writer_mode: Io.File.Writer.Mode, const Self = @This(); @@ -474,13 +466,13 @@ fn SliceDiffer(comptime T: type) type { for (self.expected, 0..) |value, i| { const full_index = self.start_index + i; const diff = if (i < self.actual.len) !std.meta.eql(self.actual[i], value) else true; - if (diff) try self.ttyconf.setColor(writer, .red); + if (diff) try self.file_writer_mode.setColor(writer, .red); if (@typeInfo(T) == .pointer) { try writer.print("[{}]{*}: {any}\n", .{ full_index, value, value }); } else { try writer.print("[{}]: {any}\n", .{ full_index, value }); } - if (diff) try self.ttyconf.setColor(writer, .reset); + if (diff) try self.file_writer_mode.setColor(writer, .reset); } } }; @@ -489,7 +481,7 @@ fn SliceDiffer(comptime T: type) type { const BytesDiffer = struct { expected: []const u8, actual: []const u8, - ttyconf: Io.tty.Config, + file_writer_mode: Io.File.Writer.Mode, pub fn write(self: BytesDiffer, writer: *Io.Writer) !void { var expected_iterator = std.mem.window(u8, self.expected, 16, 16); @@ -516,7 +508,7 @@ const BytesDiffer = struct { try self.writeDiff(writer, "{c}", .{byte}, diff); } else { // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed - if (self.ttyconf == .windows_api) { + if (self.file_writer_mode == .terminal_winapi) { try self.writeDiff(writer, ".", .{}, diff); continue; } @@ -538,9 +530,9 @@ const BytesDiffer = struct { } fn writeDiff(self: BytesDiffer, writer: *Io.Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void { - if (diff) try self.ttyconf.setColor(writer, .red); + if (diff) try self.file_writer_mode.setColor(writer, .red); try writer.print(fmt, args); - if (diff) try self.ttyconf.setColor(writer, .reset); + if (diff) try self.file_writer_mode.setColor(writer, .reset); } }; @@ -1162,7 +1154,6 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime } else |err| switch (err) { error.OutOfMemory => { if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { - const tty_config: Io.tty.Config = .detect(.stderr()); print( "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}", .{ @@ -1174,7 +1165,6 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime failing_allocator_inst.deallocations, std.debug.FormatStackTrace{ .stack_trace = failing_allocator_inst.getStackTrace(), - .tty_config = tty_config, }, }, ); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index ac6a0787de..b6203326f2 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -53,18 +53,18 @@ pub const Color = enum { /// Assume stderr is a terminal. on, - pub fn getTtyConf(color: Color, detected: Io.tty.Config) Io.tty.Config { + pub fn getTtyConf(color: Color, detected: Io.File.Writer.Mode) Io.File.Writer.Mode { return switch (color) { .auto => detected, - .on => .escape_codes, - .off => .no_color, + .on => .terminal_escaped, + .off => .streaming, }; } - pub fn detectTtyConf(color: Color, io: Io) Io.tty.Config { + pub fn detectTtyConf(color: Color, io: Io) Io.File.Writer.Mode { return switch (color) { .auto => .detect(io, .stderr()), - .on => .escape_codes, - .off => .no_color, + .on => .terminal_escaped, + .off => .streaming, }; } }; diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index ef6203cbe8..cc6182c8aa 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -164,15 +164,20 @@ pub const RenderOptions = struct { pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions, color: std.zig.Color) void { var buffer: [256]u8 = undefined; - const w, const ttyconf = std.debug.lockStderrWriter(&buffer); + const stderr = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); - renderToWriter(eb, options, w, color.getTtyConf(ttyconf)) catch return; + renderToWriter(eb, options, &stderr.interface, color.getTtyConf(stderr.mode)) catch return; } -pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer, ttyconf: Io.tty.Config) (Writer.Error || std.posix.UnexpectedError)!void { +pub fn renderToWriter( + eb: ErrorBundle, + options: RenderOptions, + w: *Writer, + fwm: Io.File.Writer.Mode, +) Io.File.Writer.Mode.SetColorError!void { if (eb.extra.len == 0) return; for (eb.getMessages()) |err_msg| { - try renderErrorMessageToWriter(eb, options, err_msg, w, ttyconf, "error", .red, 0); + try renderErrorMessageToWriter(eb, options, err_msg, w, fwm, "error", .red, 0); } if (options.include_log_text) { @@ -189,18 +194,18 @@ fn renderErrorMessageToWriter( options: RenderOptions, err_msg_index: MessageIndex, w: *Writer, - ttyconf: Io.tty.Config, + fwm: Io.File.Writer.Mode, kind: []const u8, - color: Io.tty.Color, + color: Io.File.Writer.Color, indent: usize, -) (Writer.Error || std.posix.UnexpectedError)!void { +) Io.File.Writer.Mode.SetColorError!void { const err_msg = eb.getErrorMessage(err_msg_index); if (err_msg.src_loc != .none) { const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc)); var prefix: Writer.Discarding = .init(&.{}); try w.splatByteAll(' ', indent); prefix.count += indent; - try ttyconf.setColor(w, .bold); + try fwm.setColor(w, .bold); try w.print("{s}:{d}:{d}: ", .{ eb.nullTerminatedString(src.data.src_path), src.data.line + 1, @@ -211,7 +216,7 @@ fn renderErrorMessageToWriter( src.data.line + 1, src.data.column + 1, }); - try ttyconf.setColor(w, color); + try fwm.setColor(w, color); try w.writeAll(kind); prefix.count += kind.len; try w.writeAll(": "); @@ -219,17 +224,17 @@ fn renderErrorMessageToWriter( // This is the length of the part before the error message: // e.g. "file.zig:4:5: error: " const prefix_len: usize = @intCast(prefix.count); - try ttyconf.setColor(w, .reset); - try ttyconf.setColor(w, .bold); + try fwm.setColor(w, .reset); + try fwm.setColor(w, .bold); if (err_msg.count == 1) { try writeMsg(eb, err_msg, w, prefix_len); try w.writeByte('\n'); } else { try writeMsg(eb, err_msg, w, prefix_len); - try ttyconf.setColor(w, .dim); + try fwm.setColor(w, .dim); try w.print(" ({d} times)\n", .{err_msg.count}); } - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); if (src.data.source_line != 0 and options.include_source_line) { const line = eb.nullTerminatedString(src.data.source_line); for (line) |b| switch (b) { @@ -242,19 +247,19 @@ fn renderErrorMessageToWriter( // -1 since span.main includes the caret const after_caret = src.data.span_end -| src.data.span_main -| 1; try w.splatByteAll(' ', src.data.column - before_caret); - try ttyconf.setColor(w, .green); + try fwm.setColor(w, .green); try w.splatByteAll('~', before_caret); try w.writeByte('^'); try w.splatByteAll('~', after_caret); try w.writeByte('\n'); - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); } for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, ttyconf, "note", .cyan, indent); + try renderErrorMessageToWriter(eb, options, note, w, fwm, "note", .cyan, indent); } if (src.data.reference_trace_len > 0 and options.include_reference_trace) { - try ttyconf.setColor(w, .reset); - try ttyconf.setColor(w, .dim); + try fwm.setColor(w, .reset); + try fwm.setColor(w, .dim); try w.print("referenced by:\n", .{}); var ref_index = src.end; for (0..src.data.reference_trace_len) |_| { @@ -281,25 +286,25 @@ fn renderErrorMessageToWriter( ); } } - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); } } else { - try ttyconf.setColor(w, color); + try fwm.setColor(w, color); try w.splatByteAll(' ', indent); try w.writeAll(kind); try w.writeAll(": "); - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); const msg = eb.nullTerminatedString(err_msg.msg); if (err_msg.count == 1) { try w.print("{s}\n", .{msg}); } else { try w.print("{s}", .{msg}); - try ttyconf.setColor(w, .dim); + try fwm.setColor(w, .dim); try w.print(" ({d} times)\n", .{err_msg.count}); } - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, ttyconf, "note", .cyan, indent + 4); + try renderErrorMessageToWriter(eb, options, note, w, fwm, "note", .cyan, indent + 4); } } } @@ -806,12 +811,10 @@ pub const Wip = struct { }; defer bundle.deinit(std.testing.allocator); - const ttyconf: Io.tty.Config = .no_color; - var bundle_buf: Writer.Allocating = .init(std.testing.allocator); const bundle_bw = &bundle_buf.interface; defer bundle_buf.deinit(); - try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_bw); + try bundle.renderToWriter(bundle_bw); var copy = copy: { var wip: ErrorBundle.Wip = undefined; @@ -827,7 +830,7 @@ pub const Wip = struct { var copy_buf: Writer.Allocating = .init(std.testing.allocator); const copy_bw = ©_buf.interface; defer copy_buf.deinit(); - try copy.renderToWriter(.{ .ttyconf = ttyconf }, copy_bw); + try copy.renderToWriter(copy_bw); try std.testing.expectEqualStrings(bundle_bw.written(), copy_bw.written()); } -- cgit v1.2.3 From aa57793b680b3da05f1d888b4df15807905e57c8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Dec 2025 15:47:33 -0800 Subject: std: rework locking stderr --- lib/std/Io.zig | 46 +++++---- lib/std/Io/File/Reader.zig | 34 +++--- lib/std/Io/File/Writer.zig | 250 +-------------------------------------------- lib/std/Io/Terminal.zig | 154 ++++++++++++++++++++++++++++ lib/std/Io/Threaded.zig | 83 +++++++++------ lib/std/Io/Writer.zig | 2 +- lib/std/Progress.zig | 36 +++---- lib/std/debug.zig | 232 +++++++++++++++++++++-------------------- lib/std/log.zig | 34 +++--- lib/std/process.zig | 7 ++ 10 files changed, 409 insertions(+), 469 deletions(-) create mode 100644 lib/std/Io/Terminal.zig (limited to 'lib/std/debug.zig') diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 83a2e940bf..6fa05b16d2 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -557,13 +557,6 @@ pub const net = @import("Io/net.zig"); userdata: ?*anyopaque, vtable: *const VTable, -/// This is the global, process-wide protection to coordinate stderr writes. -/// -/// The primary motivation for recursive mutex here is so that a panic while -/// stderr mutex is held still dumps the stack trace and other debug -/// information. -pub var stderr_thread_mutex: std.Thread.Mutex.Recursive = .init; - pub const VTable = struct { /// If it returns `null` it means `result` has been already populated and /// `await` will be a no-op. @@ -719,9 +712,9 @@ pub const VTable = struct { processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File, processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize, - lockStderrWriter: *const fn (?*anyopaque, buffer: []u8) Cancelable!*File.Writer, - tryLockStderrWriter: *const fn (?*anyopaque, buffer: []u8) ?*File.Writer, - unlockStderrWriter: *const fn (?*anyopaque) void, + lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, + tryLockStderr: *const fn (?*anyopaque, buffer: []u8) Cancelable!?LockedStderr, + unlockStderr: *const fn (?*anyopaque) void, now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, sleep: *const fn (?*anyopaque, Timeout) SleepError!void, @@ -763,6 +756,7 @@ pub const UnexpectedError = error{ pub const Dir = @import("Io/Dir.zig"); pub const File = @import("Io/File.zig"); +pub const Terminal = @import("Io/Terminal.zig"); pub const Clock = enum { /// A settable system-wide clock that measures real (i.e. wall-clock) @@ -2177,22 +2171,34 @@ pub fn select(io: Io, s: anytype) Cancelable!SelectUnion(@TypeOf(s)) { } } +pub const LockedStderr = struct { + file_writer: *File.Writer, + terminal_mode: Terminal.Mode, + + pub fn terminal(ls: LockedStderr) Terminal { + return .{ + .writer = &ls.file_writer.interface, + .mode = ls.terminal_mode, + }; + } +}; + /// For doing application-level writes to the standard error stream. /// Coordinates also with debug-level writes that are ignorant of Io interface -/// and implementations. When this returns, `stderr_thread_mutex` will be -/// locked. +/// and implementations. When this returns, `std.process.stderr_thread_mutex` +/// will be locked. /// /// See also: -/// * `tryLockStderrWriter` -pub fn lockStderrWriter(io: Io, buffer: []u8) Cancelable!*File.Writer { - return io.vtable.lockStderrWriter(io.userdata, buffer); +/// * `tryLockStderr` +pub fn lockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancelable!LockedStderr { + return io.vtable.lockStderr(io.userdata, buffer, terminal_mode); } -/// Same as `lockStderrWriter` but uncancelable and non-blocking. -pub fn tryLockStderrWriter(io: Io, buffer: []u8) ?*File.Writer { - return io.vtable.tryLockStderrWriter(io.userdata, buffer); +/// Same as `lockStderr` but non-blocking. +pub fn tryLockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancelable!?LockedStderr { + return io.vtable.tryLockStderr(io.userdata, buffer, terminal_mode); } -pub fn unlockStderrWriter(io: Io) void { - return io.vtable.unlockStderrWriter(io.userdata); +pub fn unlockStderr(io: Io) void { + return io.vtable.unlockStderr(io.userdata); } diff --git a/lib/std/Io/File/Reader.zig b/lib/std/Io/File/Reader.zig index 8b2074a0aa..792b20a716 100644 --- a/lib/std/Io/File/Reader.zig +++ b/lib/std/Io/File/Reader.zig @@ -64,24 +64,24 @@ pub const Mode = enum { streaming, positional, /// Avoid syscalls other than `read` and `readv`. - streaming_reading, + streaming_simple, /// Avoid syscalls other than `pread` and `preadv`. - positional_reading, + positional_simple, /// Indicates reading cannot continue because of a seek failure. failure, pub fn toStreaming(m: @This()) @This() { return switch (m) { .positional, .streaming => .streaming, - .positional_reading, .streaming_reading => .streaming_reading, + .positional_simple, .streaming_simple => .streaming_simple, .failure => .failure, }; } - pub fn toReading(m: @This()) @This() { + pub fn toSimple(m: @This()) @This() { return switch (m) { - .positional, .positional_reading => .positional_reading, - .streaming, .streaming_reading => .streaming_reading, + .positional, .positional_simple => .positional_simple, + .streaming, .streaming_simple => .streaming_simple, .failure => .failure, }; } @@ -153,10 +153,10 @@ pub fn getSize(r: *Reader) SizeError!u64 { pub fn seekBy(r: *Reader, offset: i64) SeekError!void { const io = r.io; switch (r.mode) { - .positional, .positional_reading => { + .positional, .positional_simple => { setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); }, - .streaming, .streaming_reading => { + .streaming, .streaming_simple => { const seek_err = r.seek_err orelse e: { if (io.vtable.fileSeekBy(io.userdata, r.file, offset)) |_| { setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); @@ -183,10 +183,10 @@ pub fn seekBy(r: *Reader, offset: i64) SeekError!void { pub fn seekTo(r: *Reader, offset: u64) SeekError!void { const io = r.io; switch (r.mode) { - .positional, .positional_reading => { + .positional, .positional_simple => { setLogicalPos(r, offset); }, - .streaming, .streaming_reading => { + .streaming, .streaming_simple => { const logical_pos = logicalPos(r); if (offset >= logical_pos) return seekBy(r, @intCast(offset - logical_pos)); if (r.seek_err) |err| return err; @@ -225,19 +225,19 @@ pub fn streamMode(r: *Reader, w: *Io.Writer, limit: Io.Limit, mode: Mode) Io.Rea switch (mode) { .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { error.Unimplemented => { - r.mode = r.mode.toReading(); + r.mode = r.mode.toSimple(); return 0; }, else => |e| return e, }, - .positional_reading => { + .positional_simple => { const dest = limit.slice(try w.writableSliceGreedy(1)); var data: [1][]u8 = .{dest}; const n = try readVecPositional(r, &data); w.advance(n); return n; }, - .streaming_reading => { + .streaming_simple => { const dest = limit.slice(try w.writableSliceGreedy(1)); var data: [1][]u8 = .{dest}; const n = try readVecStreaming(r, &data); @@ -251,8 +251,8 @@ pub fn streamMode(r: *Reader, w: *Io.Writer, limit: Io.Limit, mode: Mode) Io.Rea fn readVec(io_reader: *Io.Reader, data: [][]u8) Io.Reader.Error!usize { const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); switch (r.mode) { - .positional, .positional_reading => return readVecPositional(r, data), - .streaming, .streaming_reading => return readVecStreaming(r, data), + .positional, .positional_simple => return readVecPositional(r, data), + .streaming, .streaming_simple => return readVecStreaming(r, data), .failure => return error.ReadFailed, } } @@ -320,7 +320,7 @@ fn discard(io_reader: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { const io = r.io; const file = r.file; switch (r.mode) { - .positional, .positional_reading => { + .positional, .positional_simple => { const size = r.getSize() catch { r.mode = r.mode.toStreaming(); return 0; @@ -330,7 +330,7 @@ fn discard(io_reader: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { setLogicalPos(r, logical_pos + delta); return delta; }, - .streaming, .streaming_reading => { + .streaming, .streaming_simple => { // Unfortunately we can't seek forward without knowing the // size because the seek syscalls provided to us will not // return the true end position if a seek would exceed the diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index 03e4ad8a15..1d0494c674 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -18,172 +18,7 @@ write_file_err: ?WriteFileError = null, seek_err: ?SeekError = null, interface: Io.Writer, -pub const Mode = union(enum) { - /// Uses `Io.VTable.fileWriteFileStreaming` if possible. Not a terminal. - /// `setColor` does nothing. - streaming, - /// Uses `Io.VTable.fileWriteFilePositional` if possible. Not a terminal. - /// `setColor` does nothing. - positional, - /// Avoids `Io.VTable.fileWriteFileStreaming`. Not a terminal. `setColor` - /// does nothing. - streaming_simple, - /// Avoids `Io.VTable.fileWriteFilePositional`. Not a terminal. `setColor` - /// does nothing. - positional_simple, - /// It's a terminal. Writes are escaped so as to strip escape sequences. - /// Color is enabled. - terminal_escaped, - /// It's a terminal. Colors are enabled via calling - /// SetConsoleTextAttribute. Writes are not escaped. - terminal_winapi: TerminalWinapi, - /// Indicates writing cannot continue because of a seek failure. - failure, - - pub fn toStreaming(m: @This()) @This() { - return switch (m) { - .positional, .streaming => .streaming, - .positional_simple, .streaming_simple => .streaming_simple, - inline else => |_, x| x, - }; - } - - pub fn toSimple(m: @This()) @This() { - return switch (m) { - .positional, .positional_simple => .positional_simple, - .streaming, .streaming_simple => .streaming_simple, - inline else => |_, x| x, - }; - } - - pub fn toUnescaped(m: @This()) @This() { - return switch (m) { - .terminal_escaped => .streaming_simple, - inline else => |_, x| x, - }; - } - - pub const TerminalWinapi = if (!is_windows) noreturn else struct { - handle: File.Handle, - reset_attributes: u16, - }; - - /// Detect suitable TTY configuration options for the given file (commonly - /// stdout/stderr). - /// - /// Will attempt to enable ANSI escape code support if necessary/possible. - pub fn detect(io: Io, file: File, want_color: bool, fallback: Mode) Io.Cancelable!Mode { - if (!want_color) return if (try file.isTty(io)) .terminal_escaped else fallback; - - if (file.enableAnsiEscapeCodes(io)) |_| { - return .terminal_escaped; - } else |err| switch (err) { - error.Canceled => return error.Canceled, - error.NotTerminalDevice, error.Unexpected => {}, - } - - if (is_windows and file.isTty(io)) { - const windows = std.os.windows; - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) { - return .{ .terminal_winapi = .{ - .handle = file.handle, - .reset_attributes = info.wAttributes, - } }; - } - return .terminal_escaped; - } - - return fallback; - } - - pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; - - pub fn setColor(mode: Mode, io_w: *Io.Writer, color: Color) SetColorError!void { - switch (mode) { - .streaming, .positional, .streaming_simple, .positional_simple, .failure => return, - .terminal_escaped => { - const color_string = switch (color) { - .black => "\x1b[30m", - .red => "\x1b[31m", - .green => "\x1b[32m", - .yellow => "\x1b[33m", - .blue => "\x1b[34m", - .magenta => "\x1b[35m", - .cyan => "\x1b[36m", - .white => "\x1b[37m", - .bright_black => "\x1b[90m", - .bright_red => "\x1b[91m", - .bright_green => "\x1b[92m", - .bright_yellow => "\x1b[93m", - .bright_blue => "\x1b[94m", - .bright_magenta => "\x1b[95m", - .bright_cyan => "\x1b[96m", - .bright_white => "\x1b[97m", - .bold => "\x1b[1m", - .dim => "\x1b[2m", - .reset => "\x1b[0m", - }; - try io_w.writeAll(color_string); - }, - .terminal_winapi => |ctx| { - const windows = std.os.windows; - const attributes: windows.WORD = switch (color) { - .black => 0, - .red => windows.FOREGROUND_RED, - .green => windows.FOREGROUND_GREEN, - .yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN, - .blue => windows.FOREGROUND_BLUE, - .magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE, - .cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, - .white => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, - .bright_black => windows.FOREGROUND_INTENSITY, - .bright_red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, - .bright_green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, - .bright_yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, - .bright_blue => windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - // "dim" is not supported using basic character attributes, but let's still make it do *something*. - // This matches the old behavior of TTY.Color before the bright variants were added. - .dim => windows.FOREGROUND_INTENSITY, - .reset => ctx.reset_attributes, - }; - try io_w.flush(); - try windows.SetConsoleTextAttribute(ctx.handle, attributes); - }, - } - } - - fn DecorateArgs(comptime Args: type) type { - const fields = @typeInfo(Args).@"struct".fields; - var new_fields: [fields.len]type = undefined; - for (fields, &new_fields) |old, *new| { - if (old.type == std.debug.FormatStackTrace) { - new.* = std.debug.FormatStackTrace.Decorated; - } else { - new.* = old.type; - } - } - return @Tuple(&new_fields); - } - - pub fn decorateArgs(file_writer_mode: std.Io.File.Writer.Mode, args: anytype) DecorateArgs(@TypeOf(args)) { - var new_args: DecorateArgs(@TypeOf(args)) = undefined; - inline for (args, &new_args) |old, *new| { - if (@TypeOf(old) == std.debug.FormatStackTrace) { - new.* = .{ - .stack_trace = old.stack_trace, - .file_writer_mode = file_writer_mode, - }; - } else { - new.* = old; - } - } - return new_args; - } -}; +pub const Mode = File.Reader.Mode; pub const Error = error{ DiskQuota, @@ -277,8 +112,7 @@ pub fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); switch (w.mode) { .positional, .positional_simple => return drainPositional(w, data, splat), - .streaming, .streaming_simple, .terminal_winapi => return drainStreaming(w, data, splat), - .terminal_escaped => return drainEscaping(w, data, splat), + .streaming, .streaming_simple => return drainStreaming(w, data, splat), .failure => return error.WriteFailed, } } @@ -319,38 +153,13 @@ fn drainStreaming(w: *Writer, data: []const []const u8, splat: usize) Io.Writer. return w.interface.consume(n); } -fn findTerminalEscape(buffer: []const u8) ?usize { - return std.mem.findScalar(u8, buffer, 0x1b); -} - -fn drainEscaping(w: *Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize { - const io = w.io; - const header = w.interface.buffered(); - if (findTerminalEscape(header)) |i| { - _ = i; - // TODO strip terminal escape sequences here - } - for (data) |d| { - if (findTerminalEscape(d)) |i| { - _ = i; - // TODO strip terminal escape sequences here - } - } - const n = io.vtable.fileWriteStreaming(io.userdata, w.file, header, data, splat) catch |err| { - w.err = err; - return error.WriteFailed; - }; - w.pos += n; - return w.interface.consume(n); -} - pub fn sendFile(io_w: *Io.Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize { const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); switch (w.mode) { .positional => return sendFilePositional(w, file_reader, limit), .positional_simple => return error.Unimplemented, .streaming => return sendFileStreaming(w, file_reader, limit), - .streaming_simple, .terminal_escaped, .terminal_winapi => return error.Unimplemented, + .streaming_simple => return error.Unimplemented, .failure => return error.WriteFailed, } } @@ -454,60 +263,7 @@ pub fn end(w: *Writer) EndError!void { .streaming, .streaming_simple, - .terminal_escaped, - .terminal_winapi, .failure, => {}, } } - -pub const Color = enum { - black, - red, - green, - yellow, - blue, - magenta, - cyan, - white, - bright_black, - bright_red, - bright_green, - bright_yellow, - bright_blue, - bright_magenta, - bright_cyan, - bright_white, - dim, - bold, - reset, -}; - -pub fn setColor(w: *Writer, color: Color) Io.Writer.Error!void { - return w.mode.setColor(&w.interface, color) catch |err| switch (err) { - error.WriteFailed => |e| return e, - else => |e| w.err = e, - }; -} - -pub fn disableEscape(w: *Writer) Mode { - const prev = w.mode; - w.mode = w.mode.toUnescaped(); - return prev; -} - -pub fn restoreEscape(w: *Writer, mode: Mode) void { - w.mode = mode; -} - -pub fn writeAllUnescaped(w: *Writer, bytes: []const u8) Io.Writer.Error!void { - const prev_mode = w.disableEscape(); - defer w.restoreEscape(prev_mode); - return w.interface.writeAll(bytes); -} - -pub fn printUnescaped(w: *Writer, comptime fmt: []const u8, args: anytype) Io.Writer.Error!void { - const prev_mode = w.disableEscape(); - defer w.restoreEscape(prev_mode); - return w.interface.print(fmt, args); -} diff --git a/lib/std/Io/Terminal.zig b/lib/std/Io/Terminal.zig new file mode 100644 index 0000000000..cd0ec56caa --- /dev/null +++ b/lib/std/Io/Terminal.zig @@ -0,0 +1,154 @@ +/// Abstraction for writing to a stream that might support terminal escape +/// codes. +const Terminal = @This(); + +const builtin = @import("builtin"); +const is_windows = builtin.os.tag == .windows; + +const std = @import("std"); +const Io = std.Io; +const File = std.Io.File; + +writer: *Io.Writer, +mode: Mode, + +pub const Color = enum { + black, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white, + dim, + bold, + reset, +}; + +pub const Mode = union(enum) { + no_color, + escape_codes, + windows_api: WindowsApi, + + pub const WindowsApi = if (!is_windows) noreturn else struct { + handle: File.Handle, + reset_attributes: u16, + }; + + /// Detect suitable TTY configuration options for the given file (commonly + /// stdout/stderr). + /// + /// Will attempt to enable ANSI escape code support if necessary/possible. + pub fn detect(io: Io, file: File) Io.Cancelable!Mode { + if (file.enableAnsiEscapeCodes(io)) |_| { + return .escape_codes; + } else |err| switch (err) { + error.Canceled => return error.Canceled, + error.NotTerminalDevice, error.Unexpected => {}, + } + + if (is_windows and file.isTty(io)) { + const windows = std.os.windows; + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != 0) { + return .{ .terminal_winapi = .{ + .handle = file.handle, + .reset_attributes = info.wAttributes, + } }; + } + return .escape_codes; + } + + return .no_color; + } +}; + +pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; + +pub fn setColor(t: Terminal, color: Color) Io.Writer.Error!void { + switch (t.mode) { + .no_color => return, + .escape_codes => { + const color_string = switch (color) { + .black => "\x1b[30m", + .red => "\x1b[31m", + .green => "\x1b[32m", + .yellow => "\x1b[33m", + .blue => "\x1b[34m", + .magenta => "\x1b[35m", + .cyan => "\x1b[36m", + .white => "\x1b[37m", + .bright_black => "\x1b[90m", + .bright_red => "\x1b[91m", + .bright_green => "\x1b[92m", + .bright_yellow => "\x1b[93m", + .bright_blue => "\x1b[94m", + .bright_magenta => "\x1b[95m", + .bright_cyan => "\x1b[96m", + .bright_white => "\x1b[97m", + .bold => "\x1b[1m", + .dim => "\x1b[2m", + .reset => "\x1b[0m", + }; + try t.writer.writeAll(color_string); + }, + .windows_api => |wa| { + const windows = std.os.windows; + const attributes: windows.WORD = switch (color) { + .black => 0, + .red => windows.FOREGROUND_RED, + .green => windows.FOREGROUND_GREEN, + .yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN, + .blue => windows.FOREGROUND_BLUE, + .magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE, + .cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, + .white => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, + .bright_black => windows.FOREGROUND_INTENSITY, + .bright_red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, + .bright_green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, + .bright_yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, + .bright_blue => windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + .bright_magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + .bright_cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + .bright_white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, + // "dim" is not supported using basic character attributes, but let's still make it do *something*. + // This matches the old behavior of TTY.Color before the bright variants were added. + .dim => windows.FOREGROUND_INTENSITY, + .reset => wa.reset_attributes, + }; + try t.writer.flush(); + try windows.SetConsoleTextAttribute(wa.handle, attributes); + }, + } +} + +pub fn disableEscape(t: *Terminal) Mode { + const prev = t.mode; + t.mode = t.mode.toUnescaped(); + return prev; +} + +pub fn restoreEscape(t: *Terminal, mode: Mode) void { + t.mode = mode; +} + +pub fn writeAllUnescaped(t: *Terminal, bytes: []const u8) Io.Writer.Error!void { + const prev_mode = t.disableEscape(); + defer t.restoreEscape(prev_mode); + return t.interface.writeAll(bytes); +} + +pub fn printUnescaped(t: *Terminal, comptime fmt: []const u8, args: anytype) Io.Writer.Error!void { + const prev_mode = t.disableEscape(); + defer t.restoreEscape(prev_mode); + return t.interface.print(fmt, args); +} diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index f375bbcb7c..3e7b6950d4 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -82,8 +82,8 @@ stderr_writer: File.Writer = .{ .io = undefined, .interface = Io.File.Writer.initInterface(&.{}), .file = if (is_windows) undefined else .stderr(), - .mode = undefined, }, +stderr_mode: Io.Terminal.Mode = .no_color, stderr_writer_initialized: bool = false, pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { @@ -755,9 +755,9 @@ pub fn io(t: *Threaded) Io { .processExecutableOpen = processExecutableOpen, .processExecutablePath = processExecutablePath, - .lockStderrWriter = lockStderrWriter, - .tryLockStderrWriter = tryLockStderrWriter, - .unlockStderrWriter = unlockStderrWriter, + .lockStderr = lockStderr, + .tryLockStderr = tryLockStderr, + .unlockStderr = unlockStderr, .now = now, .sleep = sleep, @@ -887,9 +887,9 @@ pub fn ioBasic(t: *Threaded) Io { .processExecutableOpen = processExecutableOpen, .processExecutablePath = processExecutablePath, - .lockStderrWriter = lockStderrWriter, - .tryLockStderrWriter = tryLockStderrWriter, - .unlockStderrWriter = unlockStderrWriter, + .lockStderr = lockStderr, + .tryLockStderr = tryLockStderr, + .unlockStderr = unlockStderr, .now = now, .sleep = sleep, @@ -10090,47 +10090,70 @@ fn netLookupFallible( return error.OptionUnsupported; } -fn lockStderrWriter(userdata: ?*anyopaque, buffer: []u8) Io.Cancelable!*File.Writer { +fn lockStderr( + userdata: ?*anyopaque, + buffer: []u8, + terminal_mode: ?Io.Terminal.Mode, +) Io.Cancelable!Io.LockedStderr { const t: *Threaded = @ptrCast(@alignCast(userdata)); // Only global mutex since this is Threaded. - Io.stderr_thread_mutex.lock(); - if (!t.stderr_writer_initialized) { - const io_t = ioBasic(t); - if (is_windows) t.stderr_writer.file = .stderr(); - t.stderr_writer.io = io_t; - t.stderr_writer.mode = try .detect(io_t, t.stderr_writer.file, true, .streaming_simple); - t.stderr_writer_initialized = true; - } - std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {}; - t.stderr_writer.interface.flush() catch {}; - t.stderr_writer.interface.buffer = buffer; - return &t.stderr_writer; + std.process.stderr_thread_mutex.lock(); + return initLockedStderr(t, buffer, terminal_mode); } -fn tryLockStderrWriter(userdata: ?*anyopaque, buffer: []u8) ?*File.Writer { +fn tryLockStderr( + userdata: ?*anyopaque, + buffer: []u8, + terminal_mode: ?Io.Terminal.Mode, +) Io.Cancelable!?Io.LockedStderr { const t: *Threaded = @ptrCast(@alignCast(userdata)); // Only global mutex since this is Threaded. - if (!Io.stderr_thread_mutex.tryLock()) return null; + if (!std.process.stderr_thread_mutex.tryLock()) return null; + return try initLockedStderr(t, buffer, terminal_mode); +} + +fn initLockedStderr( + t: *Threaded, + buffer: []u8, + terminal_mode: ?Io.Terminal.Mode, +) Io.Cancelable!Io.LockedStderr { if (!t.stderr_writer_initialized) { const io_t = ioBasic(t); if (is_windows) t.stderr_writer.file = .stderr(); t.stderr_writer.io = io_t; - t.stderr_writer.mode = File.Writer.Mode.detect(io_t, t.stderr_writer.file, true, .streaming_simple) catch - return null; t.stderr_writer_initialized = true; + t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file); } - std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {}; - t.stderr_writer.interface.flush() catch {}; + std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch |err| switch (err) { + error.WriteFailed => switch (t.stderr_writer.err.?) { + error.Canceled => |e| return e, + else => {}, + }, + }; + t.stderr_writer.interface.flush() catch |err| switch (err) { + error.WriteFailed => switch (t.stderr_writer.err.?) { + error.Canceled => |e| return e, + else => {}, + }, + }; t.stderr_writer.interface.buffer = buffer; - return &t.stderr_writer; + return .{ + .file_writer = &t.stderr_writer, + .terminal_mode = t.stderr_mode, + }; } -fn unlockStderrWriter(userdata: ?*anyopaque) void { +fn unlockStderr(userdata: ?*anyopaque) void { const t: *Threaded = @ptrCast(@alignCast(userdata)); - t.stderr_writer.interface.flush() catch {}; + t.stderr_writer.interface.flush() catch |err| switch (err) { + error.WriteFailed => switch (t.stderr_writer.err.?) { + error.Canceled => @panic("TODO make this uncancelable"), + else => {}, + }, + }; t.stderr_writer.interface.end = 0; t.stderr_writer.interface.buffer = &.{}; - Io.stderr_thread_mutex.unlock(); + std.process.stderr_thread_mutex.unlock(); } pub const PosixAddress = extern union { diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 63ae6d93a0..5349eef104 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -961,7 +961,7 @@ pub fn sendFileAll(w: *Writer, file_reader: *File.Reader, limit: Limit) FileAllE const n = sendFile(w, file_reader, .limited(remaining)) catch |err| switch (err) { error.EndOfStream => break, error.Unimplemented => { - file_reader.mode = file_reader.mode.toReading(); + file_reader.mode = file_reader.mode.toSimple(); remaining -= try w.sendFileReadingAll(file_reader, .limited(remaining)); break; }, diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 6edbf9e471..54b52d48af 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -565,10 +565,10 @@ fn updateThreadRun(io: Io) void { maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter(&.{})) |fw| { - defer io.unlockStderrWriter(); + if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + defer io.unlockStderr(); global_progress.need_clear = true; - fw.writeAllUnescaped(buffer) catch return; + locked_stderr.file_writer.interface.writeAll(buffer) catch return; } } @@ -576,18 +576,18 @@ fn updateThreadRun(io: Io) void { const resize_flag = wait(io, global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { - const fw = io.lockStderrWriter(&.{}) catch return; - defer io.unlockStderrWriter(); - return clearWrittenWithEscapeCodes(fw) catch {}; + const stderr = io.lockStderr(&.{}, null) catch return; + defer io.unlockStderr(); + return clearWrittenWithEscapeCodes(stderr.file_writer) catch {}; } maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter(&.{})) |fw| { - defer io.unlockStderrWriter(); + if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + defer io.unlockStderr(); global_progress.need_clear = true; - fw.writeAllUnescaped(buffer) catch return; + locked_stderr.file_writer.interface.writeAll(buffer) catch return; } } } @@ -609,11 +609,11 @@ fn windowsApiUpdateThreadRun(io: Io) void { maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter()) |fw| { - defer io.unlockStderrWriter(); + if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + defer io.unlockStderr(); windowsApiWriteMarker(); global_progress.need_clear = true; - fw.writeAllUnescaped(buffer) catch return; + locked_stderr.file_writer.interface.writeAll(buffer) catch return; windowsApiMoveToMarker(nl_n) catch return; } } @@ -622,20 +622,20 @@ fn windowsApiUpdateThreadRun(io: Io) void { const resize_flag = wait(io, global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { - _ = io.lockStderrWriter() catch return; - defer io.unlockStderrWriter(); + _ = io.lockStderr(&.{}, null) catch return; + defer io.unlockStderr(); return clearWrittenWindowsApi() catch {}; } maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (io.tryLockStderrWriter()) |fw| { - defer io.unlockStderrWriter(); + if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + defer io.unlockStderr(); clearWrittenWindowsApi() catch return; windowsApiWriteMarker(); global_progress.need_clear = true; - fw.writeAllUnescaped(buffer) catch return; + locked_stderr.file_writer.interface.writeAll(buffer) catch return; windowsApiMoveToMarker(nl_n) catch return; } } @@ -766,7 +766,7 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { pub fn clearWrittenWithEscapeCodes(file_writer: *Io.File.Writer) Io.Writer.Error!void { if (noop_impl or !global_progress.need_clear) return; - try file_writer.writeAllUnescaped(clear ++ progress_remove); + try file_writer.interface.writeAll(clear ++ progress_remove); global_progress.need_clear = false; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 67f7d3a9fe..fd3a456ff7 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -265,29 +265,34 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { /// separate from the application's `Io` instance. var static_single_threaded_io: Io.Threaded = .init_single_threaded; -/// Allows the caller to freely write to stderr until `unlockStderrWriter` is called. +/// Allows the caller to freely write to stderr until `unlockStderr` is called. /// /// During the lock, any `std.Progress` information is cleared from the terminal. /// -/// The lock is recursive, so it is valid for the same thread to call `lockStderrWriter` multiple -/// times. The primary motivation is that this allows the panic handler to safely dump the stack -/// trace and panic message even if the mutex was held at the panic site. +/// The lock is recursive, so it is valid for the same thread to call +/// `lockStderr` multiple times, allowing the panic handler to safely +/// dump the stack trace and panic message even if the mutex was held at the +/// panic site. /// /// The returned `Writer` does not need to be manually flushed: flushing is -/// performed automatically when the matching `unlockStderrWriter` call occurs. +/// performed automatically when the matching `unlockStderr` call occurs. /// /// This is a low-level debugging primitive that bypasses the `Io` interface, /// writing directly to stderr using the most basic syscalls available. This /// function does not switch threads, switch stacks, or suspend. /// -/// Alternatively, use the higher-level `Io.lockStderrWriter` to integrate with -/// the application's chosen `Io` implementation. -pub fn lockStderrWriter(buffer: []u8) *File.Writer { - return static_single_threaded_io.ioBasic().lockStderrWriter(buffer) catch unreachable; +/// Alternatively, use the higher-level `Io.lockStderr` to integrate with the +/// application's chosen `Io` implementation. +pub fn lockStderr(buffer: []u8) Io.Terminal { + return (static_single_threaded_io.ioBasic().lockStderr(buffer, null) catch |err| switch (err) { + // Impossible to cancel because no calls to cancel using + // `static_single_threaded_io` exist. + error.Canceled => unreachable, + }).terminal(); } -pub fn unlockStderrWriter() void { - static_single_threaded_io.ioBasic().unlockStderrWriter(); +pub fn unlockStderr() void { + static_single_threaded_io.ioBasic().unlockStderr(); } /// Writes to stderr, ignoring errors. @@ -299,14 +304,14 @@ pub fn unlockStderrWriter() void { /// Uses a 64-byte buffer for formatted printing which is flushed before this /// function returns. /// -/// Alternatively, use the higher-level `std.log` or `Io.lockStderrWriter` to +/// Alternatively, use the higher-level `std.log` or `Io.lockStderr` to /// integrate with the application's chosen `Io` implementation. pub fn print(comptime fmt: []const u8, args: anytype) void { nosuspend { var buffer: [64]u8 = undefined; - const stderr = lockStderrWriter(&buffer); - defer unlockStderrWriter(); - stderr.interface.print(fmt, stderr.mode.decorateArgs(args)) catch return; + const stderr = lockStderr(&buffer); + defer unlockStderr(); + stderr.writer.print(fmt, args) catch return; } } @@ -322,43 +327,44 @@ pub inline fn getSelfDebugInfo() !*SelfInfo { /// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned. /// Obtains the stderr mutex while dumping. pub fn dumpHex(bytes: []const u8) void { - const bw, const ttyconf = lockStderrWriter(&.{}); - defer unlockStderrWriter(); - dumpHexFallible(bw, ttyconf, bytes) catch {}; + const stderr = lockStderr(&.{}); + defer unlockStderr(); + dumpHexFallible(stderr, bytes) catch {}; } /// Prints a hexadecimal view of the bytes, returning any error that occurs. -pub fn dumpHexFallible(bw: *Writer, fwm: File.Writer.Mode, bytes: []const u8) !void { +pub fn dumpHexFallible(t: Io.Terminal, bytes: []const u8) !void { + const w = t.writer; var chunks = mem.window(u8, bytes, 16, 16); while (chunks.next()) |window| { // 1. Print the address. const address = (@intFromPtr(bytes.ptr) + 0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, 16) catch unreachable)) - 0x10; - try fwm.setColor(bw, .dim); + try t.setColor(.dim); // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more. // Also, make sure all lines are aligned by padding the address. - try bw.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 }); - try fwm.setColor(bw, .reset); + try w.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 }); + try t.setColor(.reset); // 2. Print the bytes. for (window, 0..) |byte, index| { - try bw.print("{X:0>2} ", .{byte}); - if (index == 7) try bw.writeByte(' '); + try w.print("{X:0>2} ", .{byte}); + if (index == 7) try w.writeByte(' '); } - try bw.writeByte(' '); + try w.writeByte(' '); if (window.len < 16) { var missing_columns = (16 - window.len) * 3; if (window.len < 8) missing_columns += 1; - try bw.splatByteAll(' ', missing_columns); + try w.splatByteAll(' ', missing_columns); } // 3. Print the characters. for (window) |byte| { if (std.ascii.isPrint(byte)) { - try bw.writeByte(byte); + try w.writeByte(byte); } else { // Related: https://github.com/ziglang/zig/issues/7600 - if (fwm == .terminal_winapi) { - try bw.writeByte('.'); + if (t.mode == .windows_api) { + try w.writeByte('.'); continue; } @@ -366,14 +372,14 @@ pub fn dumpHexFallible(bw: *Writer, fwm: File.Writer.Mode, bytes: []const u8) !v // We don't want to do this for all control codes because most control codes apart from // the ones that Zig has escape sequences for are likely not very useful to print as symbols. switch (byte) { - '\n' => try bw.writeAll("␊"), - '\r' => try bw.writeAll("␍"), - '\t' => try bw.writeAll("␉"), - else => try bw.writeByte('.'), + '\n' => try w.writeAll("␊"), + '\r' => try w.writeAll("␍"), + '\t' => try w.writeAll("␉"), + else => try w.writeByte('.'), } } } - try bw.writeByte('\n'); + try w.writeByte('\n'); } } @@ -545,26 +551,27 @@ pub fn defaultPanic( _ = panicking.fetchAdd(1, .seq_cst); trace: { - const stderr = lockStderrWriter(&.{}); - defer unlockStderrWriter(); + const stderr = lockStderr(&.{}); + defer unlockStderr(); + const writer = stderr.writer; if (builtin.single_threaded) { - stderr.interface.print("panic: ", .{}) catch break :trace; + writer.print("panic: ", .{}) catch break :trace; } else { const current_thread_id = std.Thread.getCurrentId(); - stderr.interface.print("thread {d} panic: ", .{current_thread_id}) catch break :trace; + writer.print("thread {d} panic: ", .{current_thread_id}) catch break :trace; } - stderr.interface.print("{s}\n", .{msg}) catch break :trace; + writer.print("{s}\n", .{msg}) catch break :trace; if (@errorReturnTrace()) |t| if (t.index > 0) { - stderr.interface.writeAll("error return context:\n") catch break :trace; - writeStackTrace(t, &stderr.interface, stderr.mode) catch break :trace; - stderr.interface.writeAll("\nstack trace:\n") catch break :trace; + writer.writeAll("error return context:\n") catch break :trace; + writeStackTrace(t, stderr) catch break :trace; + writer.writeAll("\nstack trace:\n") catch break :trace; }; writeCurrentStackTrace(.{ .first_address = first_trace_addr orelse @returnAddress(), .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! - }, &stderr.interface, stderr.mode) catch break :trace; + }, stderr) catch break :trace; } waitForOtherThreadToFinishPanicking(); @@ -574,8 +581,8 @@ pub fn defaultPanic( // A panic happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - const stderr = lockStderrWriter(&.{}); - stderr.interface.writeAll("aborting due to recursive panic\n") catch {}; + const stderr = lockStderr(&.{}); + stderr.writer.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } @@ -656,28 +663,29 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: /// Write the current stack trace to `writer`, annotated with source locations. /// /// See `captureCurrentStackTrace` to capture the trace addresses into a buffer instead of printing. -pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, fwm: File.Writer.Mode) Writer.Error!void { +pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Terminal) Writer.Error!void { + const writer = t.writer; if (!std.options.allow_stack_tracing) { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; return; } const di_gpa = getDebugInfoAllocator(); const di = getSelfDebugInfo() catch |err| switch (err) { error.UnsupportedTarget => { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print("Cannot print stack trace: debug info unavailable for target\n", .{}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; return; }, }; var it: StackIterator = .init(options.context); defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print("Cannot print stack trace: safe unwind unavailable for target\n", .{}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; return; } var total_frames: usize = 0; @@ -701,31 +709,31 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri error.Unexpected => "unexpected error", }; if (it.stratOk(options.allow_unsafe_unwind)) { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print( "Unwind error at address `{s}:0x{x}` ({s}), remaining frames may be incorrect\n", .{ module_name, unwind_error.address, caption }, ); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; } else { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print( "Unwind error at address `{s}:0x{x}` ({s}), stopping trace early\n", .{ module_name, unwind_error.address, caption }, ); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; return; } }, .end => break, .frame => |ret_addr| { if (total_frames > 10_000) { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print( "Stopping trace after {d} frames (large frame count may indicate broken debug info)\n", .{total_frames}, ); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; return; } total_frames += 1; @@ -735,7 +743,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri } // `ret_addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(di_gpa, io, di, writer, ret_addr -| StackIterator.ra_call_offset, fwm); + try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset); printed_any_frame = true; }, }; @@ -743,8 +751,8 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri } /// A thin wrapper around `writeCurrentStackTrace` which writes to stderr and ignores write errors. pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void { - const stderr = lockStderrWriter(&.{}); - defer unlockStderrWriter(); + const stderr = lockStderr(&.{}); + defer unlockStderr(); writeCurrentStackTrace(.{ .first_address = a: { if (options.first_address) |a| break :a a; @@ -753,38 +761,28 @@ pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void { }, .context = options.context, .allow_unsafe_unwind = options.allow_unsafe_unwind, - }, &stderr.interface, stderr.mode) catch |err| switch (err) { + }, stderr) catch |err| switch (err) { error.WriteFailed => {}, }; } pub const FormatStackTrace = struct { stack_trace: StackTrace, + terminal_mode: Io.Terminal.Mode = .no_color, - pub const Decorated = struct { - stack_trace: StackTrace, - file_writer_mode: File.Writer.Mode, - - pub fn format(decorated: Decorated, writer: *Writer) Writer.Error!void { - try writer.writeByte('\n'); - try writeStackTrace(&decorated.stack_trace, writer, decorated.file_writer_mode); - } - }; - - pub fn format(context: FormatStackTrace, writer: *Writer) Writer.Error!void { - return Decorated.format(.{ - .stack_trace = context.stack_trace, - .file_writer_mode = .streaming, - }, writer); + pub fn format(fst: FormatStackTrace, writer: *Writer) Writer.Error!void { + try writer.writeByte('\n'); + try writeStackTrace(&fst.stack_trace, .{ .writer = writer, .mode = fst.terminal_mode }); } }; /// Write a previously captured stack trace to `writer`, annotated with source locations. -pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, fwm: File.Writer.Mode) Writer.Error!void { +pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void { + const writer = t.writer; if (!std.options.allow_stack_tracing) { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print("Cannot print stack trace: stack tracing is disabled\n", .{}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; return; } @@ -795,9 +793,9 @@ pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, fwm: File.Writer. const di_gpa = getDebugInfoAllocator(); const di = getSelfDebugInfo() catch |err| switch (err) { error.UnsupportedTarget => { - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print("Cannot print stack trace: debug info unavailable for target\n\n", .{}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; return; }, }; @@ -806,19 +804,19 @@ pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, fwm: File.Writer. for (st.instruction_addresses[0..captured_frames]) |ret_addr| { // `ret_addr` is the return address, which is *after* the function call. // Subtract 1 to get an address *in* the function call for a better source location. - try printSourceAtAddress(di_gpa, io, di, writer, ret_addr -| StackIterator.ra_call_offset, fwm); + try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset); } if (n_frames > captured_frames) { - fwm.setColor(writer, .bold) catch {}; + t.setColor(.bold) catch {}; try writer.print("({d} additional stack frames skipped...)\n", .{n_frames - captured_frames}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; } } /// A thin wrapper around `writeStackTrace` which writes to stderr and ignores write errors. pub fn dumpStackTrace(st: *const StackTrace) void { - const stderr = lockStderrWriter(&.{}); - defer unlockStderrWriter(); - writeStackTrace(st, &stderr.interface, stderr.mode) catch |err| switch (err) { + const stderr = lockStderr(&.{}); + defer unlockStderr(); + writeStackTrace(st, stderr) catch |err| switch (err) { error.WriteFailed => {}, }; } @@ -1117,9 +1115,8 @@ fn printSourceAtAddress( gpa: Allocator, io: Io, debug_info: *SelfInfo, - writer: *Writer, + t: Io.Terminal, address: usize, - fwm: File.Writer.Mode, ) Writer.Error!void { const symbol: Symbol = debug_info.getSymbol(gpa, io, address) catch |err| switch (err) { error.MissingDebugInfo, @@ -1127,40 +1124,39 @@ fn printSourceAtAddress( error.InvalidDebugInfo, => .unknown, error.ReadFailed, error.Unexpected, error.Canceled => s: { - fwm.setColor(writer, .dim) catch {}; - try writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.dim) catch {}; + try t.writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); + t.setColor(.reset) catch {}; break :s .unknown; }, error.OutOfMemory => s: { - fwm.setColor(writer, .dim) catch {}; - try writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.dim) catch {}; + try t.writer.print("Ran out of memory loading debug info, trace may be incomplete\n\n", .{}); + t.setColor(.reset) catch {}; break :s .unknown; }, }; defer if (symbol.source_location) |sl| gpa.free(sl.file_name); return printLineInfo( io, - writer, + t, symbol.source_location, address, symbol.name orelse "???", symbol.compile_unit_name orelse debug_info.getModuleName(gpa, address) catch "???", - fwm, ); } fn printLineInfo( io: Io, - writer: *Writer, + t: Io.Terminal, source_location: ?SourceLocation, address: usize, symbol_name: []const u8, compile_unit_name: []const u8, - fwm: File.Writer.Mode, ) Writer.Error!void { nosuspend { - fwm.setColor(writer, .bold) catch {}; + const writer = t.writer; + t.setColor(.bold) catch {}; if (source_location) |*sl| { try writer.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column }); @@ -1168,11 +1164,11 @@ fn printLineInfo( try writer.writeAll("???:?:?"); } - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; try writer.writeAll(": "); - fwm.setColor(writer, .dim) catch {}; + t.setColor(.dim) catch {}; try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; try writer.writeAll("\n"); // Show the matching source code line if possible @@ -1183,9 +1179,9 @@ fn printLineInfo( const space_needed = @as(usize, @intCast(sl.column - 1)); try writer.splatByteAll(' ', space_needed); - fwm.setColor(writer, .green) catch {}; + t.setColor(.green) catch {}; try writer.writeAll("^"); - fwm.setColor(writer, .reset) catch {}; + t.setColor(.reset) catch {}; } try writer.writeAll("\n"); } else |_| { @@ -1554,19 +1550,19 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex _ = panicking.fetchAdd(1, .seq_cst); trace: { - const stderr = lockStderrWriter(&.{}); - defer unlockStderrWriter(); + const stderr = lockStderr(&.{}); + defer unlockStderr(); if (addr) |a| { - stderr.interface.print("{s} at address 0x{x}\n", .{ name, a }) catch break :trace; + stderr.writer.print("{s} at address 0x{x}\n", .{ name, a }) catch break :trace; } else { - stderr.interface.print("{s} (no address available)\n", .{name}) catch break :trace; + stderr.writer.print("{s} (no address available)\n", .{name}) catch break :trace; } if (opt_ctx) |context| { writeCurrentStackTrace(.{ .context = context, .allow_unsafe_unwind = true, // we're crashing anyway, give it our all! - }, &stderr.interface, stderr.mode) catch break :trace; + }, stderr) catch break :trace; } } }, @@ -1575,8 +1571,8 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex // A segfault happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - const stderr = lockStderrWriter(&.{}); - stderr.interface.writeAll("aborting due to recursive panic\n") catch {}; + const stderr = lockStderr(&.{}); + stderr.writer.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. } @@ -1682,21 +1678,21 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize pub fn dump(t: @This()) void { if (!enabled) return; - const stderr = lockStderrWriter(&.{}); - defer unlockStderrWriter(); + const stderr = lockStderr(&.{}); + defer unlockStderr(); const end = @min(t.index, size); for (t.addrs[0..end], 0..) |frames_array, i| { - stderr.interface.print("{s}:\n", .{t.notes[i]}) catch return; + stderr.writer.print("{s}:\n", .{t.notes[i]}) catch return; var frames_array_mutable = frames_array; const frames = mem.sliceTo(frames_array_mutable[0..], 0); const stack_trace: StackTrace = .{ .index = frames.len, .instruction_addresses = frames, }; - writeStackTrace(&stack_trace, &stderr.interface, stderr.mode) catch return; + writeStackTrace(&stack_trace, stderr) catch return; } if (t.index > end) { - stderr.interface.print("{d} more traces not shown; consider increasing trace size\n", .{ + stderr.writer.print("{d} more traces not shown; consider increasing trace size\n", .{ t.index - end, }) catch return; } diff --git a/lib/std/log.zig b/lib/std/log.zig index b75bada580..8579bb04e0 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -92,35 +92,33 @@ pub fn defaultLog( args: anytype, ) void { var buffer: [64]u8 = undefined; - const stderr = std.debug.lockStderrWriter(&buffer); - defer std.debug.unlockStderrWriter(); - return defaultLogFileWriter(level, scope, format, args, stderr); + const stderr = std.debug.lockStderr(&buffer); + defer std.debug.unlockStderr(); + return defaultLogFileTerminal(level, scope, format, args, stderr) catch {}; } -pub fn defaultLogFileWriter( +pub fn defaultLogFileTerminal( comptime level: Level, comptime scope: @EnumLiteral(), comptime format: []const u8, args: anytype, - fw: *std.Io.File.Writer, -) void { - fw.setColor(switch (level) { + t: std.Io.Terminal, +) std.Io.Writer.Error!void { + t.setColor(switch (level) { .err => .red, .warn => .yellow, .info => .green, .debug => .magenta, }) catch {}; - fw.setColor(.bold) catch {}; - fw.interface.writeAll(level.asText()) catch return; - fw.setColor(.reset) catch {}; - fw.setColor(.dim) catch {}; - fw.setColor(.bold) catch {}; - if (scope != .default) { - fw.interface.print("({s})", .{@tagName(scope)}) catch return; - } - fw.interface.writeAll(": ") catch return; - fw.setColor(.reset) catch {}; - fw.interface.print(format ++ "\n", fw.mode.decorateArgs(args)) catch return; + t.setColor(.bold) catch {}; + try t.writer.writeAll(level.asText()); + t.setColor(.reset) catch {}; + t.setColor(.dim) catch {}; + t.setColor(.bold) catch {}; + if (scope != .default) try t.writer.print("({t})", .{scope}); + try t.writer.writeAll(": "); + t.setColor(.reset) catch {}; + try t.writer.print(format ++ "\n", args); } /// Returns a scoped logging namespace that logs all messages using the scope diff --git a/lib/std/process.zig b/lib/std/process.zig index 4b4c27fe20..45cd575133 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -21,6 +21,13 @@ pub const changeCurDirZ = posix.chdirZ; pub const GetCwdError = posix.GetCwdError; +/// This is the global, process-wide protection to coordinate stderr writes. +/// +/// The primary motivation for recursive mutex here is so that a panic while +/// stderr mutex is held still dumps the stack trace and other debug +/// information. +pub var stderr_thread_mutex: std.Thread.Mutex.Recursive = .init; + /// The result is a slice of `out_buffer`, from index `0`. /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. -- cgit v1.2.3 From 608145c2f07d90c46cdaa8bc2013f31b965a5b8b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 17 Dec 2025 17:00:41 -0800 Subject: fix more fallout from locking stderr --- lib/compiler/aro/aro/Diagnostics.zig | 28 ++- lib/compiler/build_runner.zig | 338 +++++++++++++++++------------------ lib/compiler/resinator/cli.zig | 10 +- lib/compiler/resinator/errors.zig | 6 +- lib/compiler/resinator/main.zig | 31 ++-- lib/compiler/test_runner.zig | 8 +- lib/std/Build.zig | 73 ++++---- lib/std/Build/Fuzz.zig | 18 +- lib/std/Build/Step.zig | 9 +- lib/std/Build/Step/Compile.zig | 17 +- lib/std/Build/Step/Run.zig | 22 +-- lib/std/Io.zig | 2 +- lib/std/Io/File/Writer.zig | 2 +- lib/std/Io/Terminal.zig | 22 --- lib/std/Io/Threaded.zig | 6 +- lib/std/debug.zig | 24 +-- lib/std/debug/simple_panic.zig | 6 +- lib/std/json/dynamic.zig | 6 +- lib/std/log.zig | 2 +- lib/std/process.zig | 2 +- lib/std/testing.zig | 21 +-- lib/std/zig.zig | 21 +-- lib/std/zig/ErrorBundle.zig | 72 ++++---- lib/std/zig/parser_test.zig | 16 +- src/Air/print.zig | 12 +- src/Compilation.zig | 45 +++-- src/InternPool.zig | 10 +- src/Zcu/PerThread.zig | 8 +- src/codegen/aarch64/Select.zig | 7 +- src/crash_report.zig | 6 +- src/libs/mingw.zig | 24 +-- src/libs/mingw/def.zig | 6 +- src/link/Coff.zig | 6 +- src/link/Elf2.zig | 6 +- src/main.zig | 12 +- 35 files changed, 446 insertions(+), 458 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/aro/aro/Diagnostics.zig b/lib/compiler/aro/aro/Diagnostics.zig index bebcf4e3d2..b33efa3f85 100644 --- a/lib/compiler/aro/aro/Diagnostics.zig +++ b/lib/compiler/aro/aro/Diagnostics.zig @@ -24,20 +24,21 @@ pub const Message = struct { @"fatal error", }; - pub fn write(msg: Message, w: *std.Io.Writer, config: std.Io.File.Writer.Mode, details: bool) std.Io.tty.Config.SetColorError!void { - try config.setColor(w, .bold); + pub fn write(msg: Message, t: std.Io.Terminal, details: bool) std.Io.Terminal.SetColorError!void { + const w = t.writer; + try t.setColor(.bold); if (msg.location) |loc| { try w.print("{s}:{d}:{d}: ", .{ loc.path, loc.line_no, loc.col }); } switch (msg.effective_kind) { - .@"fatal error", .@"error" => try config.setColor(w, .bright_red), - .note => try config.setColor(w, .bright_cyan), - .warning => try config.setColor(w, .bright_magenta), + .@"fatal error", .@"error" => try t.setColor(.bright_red), + .note => try t.setColor(.bright_cyan), + .warning => try t.setColor(.bright_magenta), .off => unreachable, } try w.print("{s}: ", .{@tagName(msg.effective_kind)}); - try config.setColor(w, .white); + try t.setColor(.white); try w.writeAll(msg.text); if (msg.opt) |some| { if (msg.effective_kind == .@"error" and msg.kind != .@"error") { @@ -55,17 +56,17 @@ pub const Message = struct { if (!details or msg.location == null) { try w.writeAll("\n"); - try config.setColor(w, .reset); + try t.setColor(.reset); } else { const loc = msg.location.?; const trailer = if (loc.end_with_splice) "\\ " else ""; - try config.setColor(w, .reset); + try t.setColor(.reset); try w.print("\n{s}{s}\n", .{ loc.line, trailer }); try w.splatByteAll(' ', loc.width); - try config.setColor(w, .bold); - try config.setColor(w, .bright_green); + try t.setColor(.bold); + try t.setColor(.bright_green); try w.writeAll("^\n"); - try config.setColor(w, .reset); + try t.setColor(.reset); } try w.flush(); } @@ -290,10 +291,7 @@ pub const State = struct { const Diagnostics = @This(); output: union(enum) { - to_writer: struct { - writer: *std.Io.Writer, - color: std.Io.File.Writer.Mode, - }, + to_writer: std.Io.Terminal, to_list: struct { messages: std.ArrayList(Message) = .empty, arena: std.heap.ArenaAllocator, diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index d284d85747..1c30ecaadc 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -286,8 +286,8 @@ pub fn main() !void { const next_arg = nextArg(args, &arg_idx) orelse fatalWithHint("expected u16 after '{s}'", .{arg}); debounce_interval_ms = std.fmt.parseUnsigned(u16, next_arg, 0) catch |err| { - fatal("unable to parse debounce interval '{s}' as unsigned 16-bit integer: {s}\n", .{ - next_arg, @errorName(err), + fatal("unable to parse debounce interval '{s}' as unsigned 16-bit integer: {t}\n", .{ + next_arg, err, }); }; } else if (mem.eql(u8, arg, "--webui")) { @@ -429,6 +429,12 @@ pub fn main() !void { } } + graph.stderr_mode = switch (color) { + .auto => try .detect(io, .stderr()), + .on => .escape_codes, + .off => .no_color, + }; + if (webui_listen != null) { if (watch) fatal("using '--webui' and '--watch' together is not yet supported; consider omitting '--watch' in favour of the web UI \"Rebuild\" button", .{}); if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); @@ -522,7 +528,7 @@ pub fn main() !void { // Perhaps in the future there could be an Advanced Options flag // such as --debug-build-runner-leaks which would make this code // return instead of calling exit. - _ = io.lockStderrWriter(&.{}) catch {}; + _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; process.exit(1); }, else => |e| return e, @@ -554,9 +560,9 @@ pub fn main() !void { } rebuild: while (true) : (if (run.error_style.clearOnUpdate()) { - const stderr = try io.lockStderrWriter(&stdio_buffer_allocation); - defer io.unlockStderrWriter(); - try stderr.writeAllUnescaped("\x1B[2J\x1B[3J\x1B[H"); + const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode); + defer io.unlockStderr(); + try stderr.file_writer.interface.writeAll("\x1B[2J\x1B[3J\x1B[H"); }) { if (run.web_server) |*ws| ws.startBuild(); @@ -730,7 +736,8 @@ fn runStepNames( fuzz: ?std.Build.Fuzz.Mode, ) !void { const gpa = run.gpa; - const io = b.graph.io; + const graph = b.graph; + const io = graph.io; const step_stack = &run.step_stack; { @@ -856,20 +863,19 @@ fn runStepNames( .none => break :summary, } - const stderr = try io.lockStderrWriter(&stdio_buffer_allocation); - defer io.unlockStderrWriter(); - - const w = &stderr.interface; - const fwm = stderr.mode; + const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode); + defer io.unlockStderr(); + const t = stderr.terminal(); + const w = &stderr.file_writer.interface; const total_count = success_count + failure_count + pending_count + skipped_count; - fwm.setColor(w, .cyan) catch {}; - fwm.setColor(w, .bold) catch {}; + t.setColor(.cyan) catch {}; + t.setColor(.bold) catch {}; w.writeAll("Build Summary: ") catch {}; - fwm.setColor(w, .reset) catch {}; + t.setColor(.reset) catch {}; w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; { - fwm.setColor(w, .dim) catch {}; + t.setColor(.dim) catch {}; var first = true; if (skipped_count > 0) { w.print("{s}{d} skipped", .{ if (first) " (" else ", ", skipped_count }) catch {}; @@ -880,12 +886,12 @@ fn runStepNames( first = false; } if (!first) w.writeByte(')') catch {}; - fwm.setColor(w, .reset) catch {}; + t.setColor(.reset) catch {}; } if (test_count > 0) { w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; - fwm.setColor(w, .dim) catch {}; + t.setColor(.dim) catch {}; var first = true; if (test_skip_count > 0) { w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {}; @@ -904,7 +910,7 @@ fn runStepNames( first = false; } if (!first) w.writeByte(')') catch {}; - fwm.setColor(w, .reset) catch {}; + t.setColor(.reset) catch {}; } w.writeAll("\n") catch {}; @@ -918,7 +924,7 @@ fn runStepNames( var print_node: PrintNode = .{ .parent = null }; if (step_names.len == 0) { print_node.last = true; - printTreeStep(b, b.default_step, run, w, fwm, &print_node, &step_stack_copy) catch {}; + printTreeStep(b, b.default_step, run, t, &print_node, &step_stack_copy) catch {}; } else { const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: { var i: usize = step_names.len; @@ -937,7 +943,7 @@ fn runStepNames( for (step_names, 0..) |step_name, i| { const tls = b.top_level_steps.get(step_name).?; print_node.last = i + 1 == last_index; - printTreeStep(b, &tls.step, run, w, fwm, &print_node, &step_stack_copy) catch {}; + printTreeStep(b, &tls.step, run, t, &print_node, &step_stack_copy) catch {}; } } w.writeByte('\n') catch {}; @@ -954,7 +960,7 @@ fn runStepNames( if (run.error_style.verboseContext()) break :code 1; // failure; print build command break :code 2; // failure; do not print build command }; - _ = io.lockStderrWriter(&.{}) catch {}; + _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; process.exit(code); } @@ -963,33 +969,30 @@ const PrintNode = struct { last: bool = false, }; -fn printPrefix(node: *PrintNode, w: *Writer, fwm: File.Writer.Mode) !void { +fn printPrefix(node: *PrintNode, stderr: Io.Terminal) !void { const parent = node.parent orelse return; + const writer = stderr.writer; if (parent.parent == null) return; - try printPrefix(parent, w, fwm); + try printPrefix(parent, stderr); if (parent.last) { - try w.writeAll(" "); + try writer.writeAll(" "); } else { - try w.writeAll(switch (fwm) { - .terminal_escaped => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ + try writer.writeAll(switch (stderr.mode) { + .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ else => "| ", }); } } -fn printChildNodePrefix(w: *Writer, fwm: File.Writer.Mode) !void { - try w.writeAll(switch (fwm) { - .terminal_escaped => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ +fn printChildNodePrefix(stderr: Io.Terminal) !void { + try stderr.writer.writeAll(switch (stderr.mode) { + .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ else => "+- ", }); } -fn printStepStatus( - s: *Step, - stderr: *Writer, - fwm: File.Writer.Mode, - run: *const Run, -) !void { +fn printStepStatus(s: *Step, stderr: Io.Terminal, run: *const Run) !void { + const writer = stderr.writer; switch (s.state) { .precheck_unstarted => unreachable, .precheck_started => unreachable, @@ -997,139 +1000,135 @@ fn printStepStatus( .running => unreachable, .dependency_failure => { - try fwm.setColor(stderr, .dim); - try stderr.writeAll(" transitive failure\n"); - try fwm.setColor(stderr, .reset); + try stderr.setColor(.dim); + try writer.writeAll(" transitive failure\n"); + try stderr.setColor(.reset); }, .success => { - try fwm.setColor(stderr, .green); + try stderr.setColor(.green); if (s.result_cached) { - try stderr.writeAll(" cached"); + try writer.writeAll(" cached"); } else if (s.test_results.test_count > 0) { const pass_count = s.test_results.passCount(); assert(s.test_results.test_count == pass_count + s.test_results.skip_count); - try stderr.print(" {d} pass", .{pass_count}); + try writer.print(" {d} pass", .{pass_count}); if (s.test_results.skip_count > 0) { - try fwm.setColor(stderr, .reset); - try stderr.writeAll(", "); - try fwm.setColor(stderr, .yellow); - try stderr.print("{d} skip", .{s.test_results.skip_count}); + try stderr.setColor(.reset); + try writer.writeAll(", "); + try stderr.setColor(.yellow); + try writer.print("{d} skip", .{s.test_results.skip_count}); } - try fwm.setColor(stderr, .reset); - try stderr.print(" ({d} total)", .{s.test_results.test_count}); + try stderr.setColor(.reset); + try writer.print(" ({d} total)", .{s.test_results.test_count}); } else { - try stderr.writeAll(" success"); + try writer.writeAll(" success"); } - try fwm.setColor(stderr, .reset); + try stderr.setColor(.reset); if (s.result_duration_ns) |ns| { - try fwm.setColor(stderr, .dim); + try stderr.setColor(.dim); if (ns >= std.time.ns_per_min) { - try stderr.print(" {d}m", .{ns / std.time.ns_per_min}); + try writer.print(" {d}m", .{ns / std.time.ns_per_min}); } else if (ns >= std.time.ns_per_s) { - try stderr.print(" {d}s", .{ns / std.time.ns_per_s}); + try writer.print(" {d}s", .{ns / std.time.ns_per_s}); } else if (ns >= std.time.ns_per_ms) { - try stderr.print(" {d}ms", .{ns / std.time.ns_per_ms}); + try writer.print(" {d}ms", .{ns / std.time.ns_per_ms}); } else if (ns >= std.time.ns_per_us) { - try stderr.print(" {d}us", .{ns / std.time.ns_per_us}); + try writer.print(" {d}us", .{ns / std.time.ns_per_us}); } else { - try stderr.print(" {d}ns", .{ns}); + try writer.print(" {d}ns", .{ns}); } - try fwm.setColor(stderr, .reset); + try stderr.setColor(.reset); } if (s.result_peak_rss != 0) { const rss = s.result_peak_rss; - try fwm.setColor(stderr, .dim); + try stderr.setColor(.dim); if (rss >= 1000_000_000) { - try stderr.print(" MaxRSS:{d}G", .{rss / 1000_000_000}); + try writer.print(" MaxRSS:{d}G", .{rss / 1000_000_000}); } else if (rss >= 1000_000) { - try stderr.print(" MaxRSS:{d}M", .{rss / 1000_000}); + try writer.print(" MaxRSS:{d}M", .{rss / 1000_000}); } else if (rss >= 1000) { - try stderr.print(" MaxRSS:{d}K", .{rss / 1000}); + try writer.print(" MaxRSS:{d}K", .{rss / 1000}); } else { - try stderr.print(" MaxRSS:{d}B", .{rss}); + try writer.print(" MaxRSS:{d}B", .{rss}); } - try fwm.setColor(stderr, .reset); + try stderr.setColor(.reset); } - try stderr.writeAll("\n"); + try writer.writeAll("\n"); }, .skipped, .skipped_oom => |skip| { - try fwm.setColor(stderr, .yellow); - try stderr.writeAll(" skipped"); + try stderr.setColor(.yellow); + try writer.writeAll(" skipped"); if (skip == .skipped_oom) { - try stderr.writeAll(" (not enough memory)"); - try fwm.setColor(stderr, .dim); - try stderr.print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); - try fwm.setColor(stderr, .yellow); + try writer.writeAll(" (not enough memory)"); + try stderr.setColor(.dim); + try writer.print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); + try stderr.setColor(.yellow); } - try stderr.writeAll("\n"); - try fwm.setColor(stderr, .reset); + try writer.writeAll("\n"); + try stderr.setColor(.reset); }, .failure => { - try printStepFailure(s, stderr, fwm, false); - try fwm.setColor(stderr, .reset); + try printStepFailure(s, stderr, false); + try stderr.setColor(.reset); }, } } -fn printStepFailure( - s: *Step, - stderr: *Writer, - fwm: File.Writer.Mode, - dim: bool, -) !void { +fn printStepFailure(s: *Step, stderr: Io.Terminal, dim: bool) !void { + const w = stderr.writer; if (s.result_error_bundle.errorMessageCount() > 0) { - try fwm.setColor(stderr, .red); - try stderr.print(" {d} errors\n", .{ + try stderr.setColor(.red); + try w.print(" {d} errors\n", .{ s.result_error_bundle.errorMessageCount(), }); } else if (!s.test_results.isSuccess()) { // These first values include all of the test "statuses". Every test is either passsed, // skipped, failed, crashed, or timed out. - try fwm.setColor(stderr, .green); - try stderr.print(" {d} pass", .{s.test_results.passCount()}); - try fwm.setColor(stderr, .reset); - if (dim) try fwm.setColor(stderr, .dim); + try stderr.setColor(.green); + try w.print(" {d} pass", .{s.test_results.passCount()}); + try stderr.setColor(.reset); + if (dim) try stderr.setColor(.dim); if (s.test_results.skip_count > 0) { - try stderr.writeAll(", "); - try fwm.setColor(stderr, .yellow); - try stderr.print("{d} skip", .{s.test_results.skip_count}); - try fwm.setColor(stderr, .reset); - if (dim) try fwm.setColor(stderr, .dim); + try w.writeAll(", "); + try stderr.setColor(.yellow); + try w.print("{d} skip", .{s.test_results.skip_count}); + try stderr.setColor(.reset); + if (dim) try stderr.setColor(.dim); } if (s.test_results.fail_count > 0) { - try stderr.writeAll(", "); - try fwm.setColor(stderr, .red); - try stderr.print("{d} fail", .{s.test_results.fail_count}); - try fwm.setColor(stderr, .reset); - if (dim) try fwm.setColor(stderr, .dim); + try w.writeAll(", "); + try stderr.setColor(.red); + try w.print("{d} fail", .{s.test_results.fail_count}); + try stderr.setColor(.reset); + if (dim) try stderr.setColor(.dim); } if (s.test_results.crash_count > 0) { - try stderr.writeAll(", "); - try fwm.setColor(stderr, .red); - try stderr.print("{d} crash", .{s.test_results.crash_count}); - try fwm.setColor(stderr, .reset); - if (dim) try fwm.setColor(stderr, .dim); + try w.writeAll(", "); + try stderr.setColor(.red); + try w.print("{d} crash", .{s.test_results.crash_count}); + try stderr.setColor(.reset); + if (dim) try stderr.setColor(.dim); } if (s.test_results.timeout_count > 0) { - try stderr.writeAll(", "); - try fwm.setColor(stderr, .red); - try stderr.print("{d} timeout", .{s.test_results.timeout_count}); - try fwm.setColor(stderr, .reset); - if (dim) try fwm.setColor(stderr, .dim); + try w.writeAll(", "); + try stderr.setColor(.red); + try w.print("{d} timeout", .{s.test_results.timeout_count}); + try stderr.setColor(.reset); + if (dim) try stderr.setColor(.dim); } - try stderr.print(" ({d} total)", .{s.test_results.test_count}); + try w.print(" ({d} total)", .{s.test_results.test_count}); // Memory leaks are intentionally written after the total, because is isn't a test *status*, // but just a flag that any tests -- even passed ones -- can have. We also use a different // separator, so it looks like: // 2 pass, 1 skip, 2 fail (5 total); 2 leaks if (s.test_results.leak_count > 0) { - try stderr.writeAll("; "); - try fwm.setColor(stderr, .red); - try stderr.print("{d} leaks", .{s.test_results.leak_count}); - try fwm.setColor(stderr, .reset); - if (dim) try fwm.setColor(stderr, .dim); + try w.writeAll("; "); + try stderr.setColor(.red); + try w.print("{d} leaks", .{s.test_results.leak_count}); + try stderr.setColor(.reset); + if (dim) try stderr.setColor(.dim); } // It's usually not helpful to know how many error logs there were because they tend to @@ -1142,21 +1141,21 @@ fn printStepFailure( break :show alt_results.isSuccess(); }; if (show_err_logs) { - try stderr.writeAll("; "); - try fwm.setColor(stderr, .red); - try stderr.print("{d} error logs", .{s.test_results.log_err_count}); - try fwm.setColor(stderr, .reset); - if (dim) try fwm.setColor(stderr, .dim); + try w.writeAll("; "); + try stderr.setColor(.red); + try w.print("{d} error logs", .{s.test_results.log_err_count}); + try stderr.setColor(.reset); + if (dim) try stderr.setColor(.dim); } - try stderr.writeAll("\n"); + try w.writeAll("\n"); } else if (s.result_error_msgs.items.len > 0) { - try fwm.setColor(stderr, .red); - try stderr.writeAll(" failure\n"); + try stderr.setColor(.red); + try w.writeAll(" failure\n"); } else { assert(s.result_stderr.len > 0); - try fwm.setColor(stderr, .red); - try stderr.writeAll(" stderr\n"); + try stderr.setColor(.red); + try w.writeAll(" w\n"); } } @@ -1164,11 +1163,11 @@ fn printTreeStep( b: *std.Build, s: *Step, run: *const Run, - stderr: *Writer, - fwm: File.Writer.Mode, + stderr: Io.Terminal, parent_node: *PrintNode, step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), ) !void { + const writer = stderr.writer; const first = step_stack.swapRemove(s); const summary = run.summary; const skip = switch (summary) { @@ -1178,26 +1177,26 @@ fn printTreeStep( .failures => s.state == .success, }; if (skip) return; - try printPrefix(parent_node, stderr, fwm); + try printPrefix(parent_node, stderr); if (parent_node.parent != null) { if (parent_node.last) { - try printChildNodePrefix(stderr, fwm); + try printChildNodePrefix(stderr); } else { - try stderr.writeAll(switch (fwm) { - .terminal_escaped => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ + try writer.writeAll(switch (stderr.mode) { + .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ else => "+- ", }); } } - if (!first) try fwm.setColor(stderr, .dim); + if (!first) try stderr.setColor(.dim); // dep_prefix omitted here because it is redundant with the tree. - try stderr.writeAll(s.name); + try writer.writeAll(s.name); if (first) { - try printStepStatus(s, stderr, fwm, run); + try printStepStatus(s, stderr, run); const last_index = if (summary == .all) s.dependencies.items.len -| 1 else blk: { var i: usize = s.dependencies.items.len; @@ -1219,17 +1218,17 @@ fn printTreeStep( .parent = parent_node, .last = i == last_index, }; - try printTreeStep(b, dep, run, stderr, fwm, &print_node, step_stack); + try printTreeStep(b, dep, run, stderr, &print_node, step_stack); } } else { if (s.dependencies.items.len == 0) { - try stderr.writeAll(" (reused)\n"); + try writer.writeAll(" (reused)\n"); } else { - try stderr.print(" (+{d} more reused dependencies)\n", .{ + try writer.print(" (+{d} more reused dependencies)\n", .{ s.dependencies.items.len, }); } - try fwm.setColor(stderr, .reset); + try stderr.setColor(.reset); } } @@ -1300,7 +1299,8 @@ fn workerMakeOneStep( prog_node: std.Progress.Node, run: *Run, ) void { - const io = b.graph.io; + const graph = b.graph; + const io = graph.io; const gpa = run.gpa; // First, check the conditions for running this step. If they are not met, @@ -1369,11 +1369,11 @@ fn workerMakeOneStep( const show_error_msgs = s.result_error_msgs.items.len > 0; const show_stderr = s.result_stderr.len > 0; if (show_error_msgs or show_compile_errors or show_stderr) { - const stderr = io.lockStderrWriter(&stdio_buffer_allocation) catch |err| switch (err) { + const stderr = io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode) catch |err| switch (err) { error.Canceled => return, }; - defer io.unlockStderrWriter(); - printErrorMessages(gpa, s, .{}, &stderr.interface, stderr.mode, run.error_style, run.multiline_errors) catch {}; + defer io.unlockStderr(); + printErrorMessages(gpa, s, .{}, stderr.terminal(), run.error_style, run.multiline_errors) catch {}; } handle_result: { @@ -1440,11 +1440,11 @@ pub fn printErrorMessages( gpa: Allocator, failing_step: *Step, options: std.zig.ErrorBundle.RenderOptions, - stderr: *Writer, - fwm: File.Writer.Mode, + stderr: Io.Terminal, error_style: ErrorStyle, multiline_errors: MultilineErrors, ) !void { + const writer = stderr.writer; if (error_style.verboseContext()) { // Provide context for where these error messages are coming from by // printing the corresponding Step subtree. @@ -1456,70 +1456,70 @@ pub fn printErrorMessages( } // Now, `step_stack` has the subtree that we want to print, in reverse order. - try fwm.setColor(stderr, .dim); + try stderr.setColor(.dim); var indent: usize = 0; while (step_stack.pop()) |s| : (indent += 1) { if (indent > 0) { - try stderr.splatByteAll(' ', (indent - 1) * 3); - try printChildNodePrefix(stderr, fwm); + try writer.splatByteAll(' ', (indent - 1) * 3); + try printChildNodePrefix(stderr); } - try stderr.writeAll(s.name); + try writer.writeAll(s.name); if (s == failing_step) { - try printStepFailure(s, stderr, fwm, true); + try printStepFailure(s, stderr, true); } else { - try stderr.writeAll("\n"); + try writer.writeAll("\n"); } } - try fwm.setColor(stderr, .reset); + try stderr.setColor(.reset); } else { // Just print the failing step itself. - try fwm.setColor(stderr, .dim); - try stderr.writeAll(failing_step.name); - try printStepFailure(failing_step, stderr, fwm, true); - try fwm.setColor(stderr, .reset); + try stderr.setColor(.dim); + try writer.writeAll(failing_step.name); + try printStepFailure(failing_step, stderr, true); + try stderr.setColor(.reset); } if (failing_step.result_stderr.len > 0) { - try stderr.writeAll(failing_step.result_stderr); + try writer.writeAll(failing_step.result_stderr); if (!mem.endsWith(u8, failing_step.result_stderr, "\n")) { - try stderr.writeAll("\n"); + try writer.writeAll("\n"); } } - try failing_step.result_error_bundle.renderToWriter(options, stderr, fwm); + try failing_step.result_error_bundle.renderToTerminal(options, stderr); for (failing_step.result_error_msgs.items) |msg| { - try fwm.setColor(stderr, .red); - try stderr.writeAll("error:"); - try fwm.setColor(stderr, .reset); + try stderr.setColor(.red); + try writer.writeAll("error:"); + try stderr.setColor(.reset); if (std.mem.indexOfScalar(u8, msg, '\n') == null) { - try stderr.print(" {s}\n", .{msg}); + try writer.print(" {s}\n", .{msg}); } else switch (multiline_errors) { .indent => { var it = std.mem.splitScalar(u8, msg, '\n'); - try stderr.print(" {s}\n", .{it.first()}); + try writer.print(" {s}\n", .{it.first()}); while (it.next()) |line| { - try stderr.print(" {s}\n", .{line}); + try writer.print(" {s}\n", .{line}); } }, - .newline => try stderr.print("\n{s}\n", .{msg}), - .none => try stderr.print(" {s}\n", .{msg}), + .newline => try writer.print("\n{s}\n", .{msg}), + .none => try writer.print(" {s}\n", .{msg}), } } if (error_style.verboseContext()) { if (failing_step.result_failed_command) |cmd_str| { - try fwm.setColor(stderr, .red); - try stderr.writeAll("failed command: "); - try fwm.setColor(stderr, .reset); - try stderr.writeAll(cmd_str); - try stderr.writeByte('\n'); + try stderr.setColor(.red); + try writer.writeAll("failed command: "); + try stderr.setColor(.reset); + try writer.writeAll(cmd_str); + try writer.writeByte('\n'); } } - try stderr.writeByte('\n'); + try writer.writeByte('\n'); } fn printSteps(builder: *std.Build, w: *Writer) !void { diff --git a/lib/compiler/resinator/cli.zig b/lib/compiler/resinator/cli.zig index 08befbe1fa..ddf3ca18b7 100644 --- a/lib/compiler/resinator/cli.zig +++ b/lib/compiler/resinator/cli.zig @@ -126,14 +126,14 @@ pub const Diagnostics = struct { } pub fn renderToStderr(self: *Diagnostics, io: Io, args: []const []const u8) void { - const stderr = io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); - self.renderToWriter(args, &stderr.interface, stderr.mode) catch return; + const stderr = io.lockStderr(&.{}, null); + defer io.unlockStderr(); + self.renderToWriter(args, stderr.terminal()) catch return; } - pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: *std.Io.Writer, config: std.Io.tty.Config) !void { + pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, t: Io.Terminal) !void { for (self.errors.items) |err_details| { - try renderErrorMessage(writer, config, err_details, args); + try renderErrorMessage(t, err_details, args); } } diff --git a/lib/compiler/resinator/errors.zig b/lib/compiler/resinator/errors.zig index 031b5d3574..6146f777cd 100644 --- a/lib/compiler/resinator/errors.zig +++ b/lib/compiler/resinator/errors.zig @@ -69,10 +69,10 @@ pub const Diagnostics = struct { pub fn renderToStderr(self: *Diagnostics, cwd: Io.Dir, source: []const u8, source_mappings: ?SourceMappings) void { const io = self.io; - const stderr = io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); + const stderr = io.lockStderr(&.{}, null); + defer io.unlockStderr(); for (self.errors.items) |err_details| { - renderErrorMessage(io, &stderr.interface, stderr.mode, cwd, err_details, source, self.strings.items, source_mappings) catch return; + renderErrorMessage(io, stderr.terminal(), cwd, err_details, source, self.strings.items, source_mappings) catch return; } } diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig index bd2fcc912e..81a3a8689a 100644 --- a/lib/compiler/resinator/main.zig +++ b/lib/compiler/resinator/main.zig @@ -35,8 +35,8 @@ pub fn main() !void { const args = try std.process.argsAlloc(arena); if (args.len < 2) { - const stderr = io.lockStderrWriter(&.{}); - try renderErrorMessage(&stderr.interface, stderr.mode, .err, "expected zig lib dir as first argument", .{}); + const stderr = try io.lockStderr(&.{}, null); + try renderErrorMessage(stderr.terminal(), .err, "expected zig lib dir as first argument", .{}); std.process.exit(1); } const zig_lib_dir = args[1]; @@ -80,9 +80,9 @@ pub fn main() !void { // so that there is a clear separation between the cli diagnostics and whatever // gets printed after if (cli_diagnostics.errors.items.len > 0) { - const stderr = io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); - try stderr.interface.writeByte('\n'); + const stderr = try io.lockStderr(&.{}, null); + defer io.unlockStderr(); + try stderr.file_writer.interface.writeByte('\n'); } } break :options options; @@ -130,15 +130,12 @@ pub fn main() !void { var stderr_buf: [512]u8 = undefined; var diagnostics: aro.Diagnostics = .{ .output = output: { if (zig_integration) break :output .{ .to_list = .{ .arena = .init(gpa) } }; - const stderr = io.lockStderrWriter(&stderr_buf); - break :output .{ .to_writer = .{ - .writer = &stderr.interface, - .color = stderr.mode, - } }; + const stderr = try io.lockStderr(&stderr_buf, null); + break :output .{ .to_writer = stderr.terminal() }; } }; defer { diagnostics.deinit(); - if (!zig_integration) std.debug.unlockStderrWriter(); + if (!zig_integration) std.debug.unlockStderr(); } var comp = aro.Compilation.init(aro_arena, aro_arena, io, &diagnostics, Io.Dir.cwd()); @@ -699,9 +696,9 @@ const ErrorHandler = union(enum) { }, .stderr => { // aro errors have already been emitted - const stderr = io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); - try renderErrorMessage(&stderr.interface, stderr.mode, .err, "{s}", .{fail_msg}); + const stderr = io.lockStderr(&.{}, null); + defer io.unlockStderr(); + try renderErrorMessage(stderr.terminal(), .err, "{s}", .{fail_msg}); }, } } @@ -745,9 +742,9 @@ const ErrorHandler = union(enum) { try server.serveErrorBundle(error_bundle); }, .stderr => { - const stderr = io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); - try renderErrorMessage(&stderr.interface, stderr.mode, msg_type, format, args); + const stderr = try io.lockStderr(&.{}, null); + defer io.unlockStderr(); + try renderErrorMessage(stderr.terminal(), msg_type, format, args); }, } } diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index 3a135ab8fd..3d97944a7f 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -405,18 +405,18 @@ pub fn fuzz( testOne(ctx, input.toSlice()) catch |err| switch (err) { error.SkipZigTest => return, else => { - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr = std.debug.lockStderr(&.{}, null).terminal(); p: { if (@errorReturnTrace()) |trace| { - std.debug.writeStackTrace(trace, &stderr.interface, stderr.mode) catch break :p; + std.debug.writeStackTrace(trace, stderr) catch break :p; } - stderr.interface.print("failed with error.{t}\n", .{err}) catch break :p; + stderr.writer.print("failed with error.{t}\n", .{err}) catch break :p; } std.process.exit(1); }, }; if (log_err_count != 0) { - const stderr = std.debug.lockStderrWriter(&.{}); + const stderr = std.debug.lockStderr(&.{}, .no_color); stderr.interface.print("error logs detected\n", .{}) catch {}; std.process.exit(1); } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index de2985d2b5..cf0b9e5b0d 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -129,6 +129,9 @@ pub const Graph = struct { dependency_cache: InitializedDepMap = .empty, allow_so_scripts: ?bool = null, time_report: bool, + /// Similar to the `Io.Terminal.Mode` returned by `Io.lockStderr`, but also + /// respects the '--color' flag. + stderr_mode: ?Io.Terminal.Mode = null, }; const AvailableDeps = []const struct { []const u8, []const u8 }; @@ -2255,9 +2258,10 @@ pub const GeneratedFile = struct { pub fn getPath3(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) Io.Cancelable![]const u8 { return gen.path orelse { - const io = gen.step.owner.graph.io; - const stderr = try io.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.step, &stderr.interface, stderr.mode, src_builder, asking_step) catch {}; + const graph = gen.step.owner.graph; + const io = graph.io; + const stderr = try io.lockStderr(&.{}, graph.stderr_mode); + dumpBadGetPathHelp(gen.step, stderr.terminal(), src_builder, asking_step) catch {}; @panic("misconfigured build script"); }; } @@ -2468,13 +2472,15 @@ pub const LazyPath = union(enum) { // TODO make gen.file.path not be absolute and use that as the // basis for not traversing up too many directories. + const graph = src_builder.graph; + var file_path: Cache.Path = .{ .root_dir = Cache.Directory.cwd(), .sub_path = gen.file.path orelse { - const io = src_builder.graph.io; - const stderr = try io.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.file.step, &stderr.interface, stderr.mode, src_builder, asking_step) catch {}; - io.unlockStderrWriter(); + const io = graph.io; + const stderr = try io.lockStderr(&.{}, graph.stderr_mode); + dumpBadGetPathHelp(gen.file.step, stderr.terminal(), src_builder, asking_step) catch {}; + io.unlockStderr(); @panic("misconfigured build script"); }, }; @@ -2564,43 +2570,36 @@ fn dumpBadDirnameHelp( comptime msg: []const u8, args: anytype, ) anyerror!void { - const stderr = std.debug.lockStderrWriter(&.{}); - defer std.debug.unlockStderrWriter(); - - const w = &stderr.interface; - const fwm = stderr.mode; + const stderr = std.debug.lockStderr(&.{}).terminal(); + defer std.debug.unlockStderr(); + const w = stderr.writer; try w.print(msg, args); if (fail_step) |s| { - fwm.setColor(w, .red) catch {}; + stderr.setColor(.red) catch {}; try w.writeAll(" The step was created by this stack trace:\n"); - fwm.setColor(w, .reset) catch {}; + stderr.setColor(.reset) catch {}; - s.dump(w, fwm); + s.dump(stderr); } if (asking_step) |as| { - fwm.setColor(w, .red) catch {}; + stderr.setColor(.red) catch {}; try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); - fwm.setColor(w, .reset) catch {}; + stderr.setColor(.reset) catch {}; - as.dump(w, fwm); + as.dump(stderr); } - fwm.setColor(w, .red) catch {}; - try w.writeAll(" Hope that helps. Proceeding to panic.\n"); - fwm.setColor(w, .reset) catch {}; + stderr.setColor(.red) catch {}; + try w.writeAll(" Proceeding to panic.\n"); + stderr.setColor(.reset) catch {}; } /// In this function the stderr mutex has already been locked. -pub fn dumpBadGetPathHelp( - s: *Step, - w: *Io.Writer, - fwm: File.Writer.Mode, - src_builder: *Build, - asking_step: ?*Step, -) anyerror!void { +pub fn dumpBadGetPathHelp(s: *Step, t: Io.Terminal, src_builder: *Build, asking_step: ?*Step) anyerror!void { + const w = t.writer; try w.print( \\getPath() was called on a GeneratedFile that wasn't built yet. \\ source package path: {s} @@ -2611,21 +2610,21 @@ pub fn dumpBadGetPathHelp( s.name, }); - fwm.setColor(w, .red) catch {}; + t.setColor(.red) catch {}; try w.writeAll(" The step was created by this stack trace:\n"); - fwm.setColor(w, .reset) catch {}; + t.setColor(.reset) catch {}; - s.dump(w, fwm); + s.dump(t); if (asking_step) |as| { - fwm.setColor(w, .red) catch {}; + t.setColor(.red) catch {}; try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name}); - fwm.setColor(w, .reset) catch {}; + t.setColor(.reset) catch {}; - as.dump(w, fwm); + as.dump(t); } - fwm.setColor(w, .red) catch {}; - try w.writeAll(" Hope that helps. Proceeding to panic.\n"); - fwm.setColor(w, .reset) catch {}; + t.setColor(.red) catch {}; + try w.writeAll(" Proceeding to panic.\n"); + t.setColor(.reset) catch {}; } pub const InstallDir = union(enum) { diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 4770e03a0c..d308efdf70 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -158,7 +158,8 @@ fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, parent_prog_node: std.P } fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_node: std.Progress.Node) !void { - const io = run.step.owner.graph.io; + const graph = run.step.owner.graph; + const io = graph.io; const compile = run.producer.?; const prog_node = parent_prog_node.start(compile.step.name, 0); defer prog_node.end(); @@ -171,9 +172,9 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_nod if (show_error_msgs or show_compile_errors or show_stderr) { var buf: [256]u8 = undefined; - const stderr = try io.lockStderrWriter(&buf); - defer io.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &compile.step, .{}, &stderr.interface, stderr.mode, .verbose, .indent) catch {}; + const stderr = try io.lockStderr(&buf, graph.stderr_mode); + defer io.unlockStderr(); + build_runner.printErrorMessages(gpa, &compile.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; } const rebuilt_bin_path = result catch |err| switch (err) { @@ -186,7 +187,8 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_nod fn fuzzWorkerRun(fuzz: *Fuzz, run: *Step.Run, unit_test_index: u32) void { const owner = run.step.owner; const gpa = owner.allocator; - const io = owner.graph.io; + const graph = owner.graph; + const io = graph.io; const test_name = run.cached_test_metadata.?.testName(unit_test_index); const prog_node = fuzz.prog_node.start(test_name, 0); @@ -195,11 +197,11 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: *Step.Run, unit_test_index: u32) void { run.rerunInFuzzMode(fuzz, unit_test_index, prog_node) catch |err| switch (err) { error.MakeFailed => { var buf: [256]u8 = undefined; - const stderr = io.lockStderrWriter(&buf) catch |e| switch (e) { + const stderr = io.lockStderr(&buf, graph.stderr_mode) catch |e| switch (e) { error.Canceled => return, }; - defer io.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &run.step, .{}, &stderr.interface, stderr.mode, .verbose, .indent) catch {}; + defer io.unlockStderr(); + build_runner.printErrorMessages(gpa, &run.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; return; }, else => { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 0df58b24b7..74b41634a7 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -328,16 +328,17 @@ pub fn cast(step: *Step, comptime T: type) ?*T { } /// For debugging purposes, prints identifying information about this Step. -pub fn dump(step: *Step, w: *Io.Writer, fwm: Io.File.Writer.Mode) void { +pub fn dump(step: *Step, t: Io.Terminal) void { + const w = t.writer; if (step.debug_stack_trace.instruction_addresses.len > 0) { w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; - std.debug.writeStackTrace(&step.debug_stack_trace, w, fwm) catch {}; + std.debug.writeStackTrace(&step.debug_stack_trace, t) catch {}; } else { const field = "debug_stack_frames_count"; comptime assert(@hasField(Build, field)); - fwm.setColor(w, .yellow) catch {}; + t.setColor(.yellow) catch {}; w.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {}; - fwm.setColor(w, .reset) catch {}; + t.setColor(.reset) catch {}; } } diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 888775884d..4752046089 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -925,20 +925,21 @@ const CliNamedModules = struct { fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) ![]const u8 { const step = &compile.step; const b = step.owner; - const io = b.graph.io; + const graph = b.graph; + const io = graph.io; const maybe_path: ?*GeneratedFile = @field(compile, tag_name); const generated_file = maybe_path orelse { - const stderr = try io.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, &stderr.interface, stderr.mode, compile.step.owner, asking_step) catch {}; - io.unlockStderrWriter(); + const stderr = try io.lockStderr(&.{}, graph.stderr_mode); + std.Build.dumpBadGetPathHelp(&compile.step, stderr.terminal(), compile.step.owner, asking_step) catch {}; + io.unlockStderr(); @panic("missing emit option for " ++ tag_name); }; const path = generated_file.path orelse { - const stderr = try io.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, &stderr.interface, stderr.mode, compile.step.owner, asking_step) catch {}; - io.unlockStderrWriter(); + const stderr = try io.lockStderr(&.{}, graph.stderr_mode); + std.Build.dumpBadGetPathHelp(&compile.step, stderr.terminal(), compile.step.owner, asking_step) catch {}; + io.unlockStderr(); @panic(tag_name ++ " is null. Is there a missing step dependency?"); }; @@ -1907,7 +1908,7 @@ fn checkCompileErrors(compile: *Compile) !void { try actual_eb.renderToWriter(.{ .include_reference_trace = false, .include_source_line = false, - }, &aw.writer, .streaming); + }, &aw.writer); break :ae try aw.toOwnedSlice(); }; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 67cf201374..157a0292e7 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -1559,7 +1559,8 @@ fn spawnChildAndCollect( ) !?EvalGenericResult { const b = run.step.owner; const arena = b.allocator; - const io = b.graph.io; + const graph = b.graph; + const io = graph.io; if (fuzz_context != null) { assert(!has_side_effects); @@ -1625,11 +1626,12 @@ fn spawnChildAndCollect( if (!run.disable_zig_progress and !inherit) { child.progress_node = options.progress_node; } - if (inherit) { - const stderr = try io.lockStderrWriter(&.{}); - try setColorEnvironmentVariables(run, env_map, stderr.mode); - } - defer if (inherit) io.unlockStderrWriter(); + const terminal_mode: Io.Terminal.Mode = if (inherit) m: { + const stderr = try io.lockStderr(&.{}, graph.stderr_mode); + break :m stderr.terminal_mode; + } else .no_color; + defer if (inherit) io.unlockStderr(); + try setColorEnvironmentVariables(run, env_map, terminal_mode); var timer = try std.time.Timer.start(); const res = try evalGeneric(run, &child); run.step.result_duration_ns = timer.read(); @@ -1637,7 +1639,7 @@ fn spawnChildAndCollect( } } -fn setColorEnvironmentVariables(run: *Run, env_map: *EnvMap, fwm: Io.File.Writer.Mode) !void { +fn setColorEnvironmentVariables(run: *Run, env_map: *EnvMap, terminal_mode: Io.Terminal.Mode) !void { color: switch (run.color) { .manual => {}, .enable => { @@ -1648,9 +1650,9 @@ fn setColorEnvironmentVariables(run: *Run, env_map: *EnvMap, fwm: Io.File.Writer try env_map.put("NO_COLOR", "1"); env_map.remove("CLICOLOR_FORCE"); }, - .inherit => switch (fwm) { - .terminal_escaped => continue :color .enable, - else => continue :color .disable, + .inherit => switch (terminal_mode) { + .no_color, .windows_api => continue :color .disable, + .escape_codes => continue :color .enable, }, .auto => { const capture_stderr = run.captured_stderr != null or switch (run.stdio) { diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 6fa05b16d2..78049633a0 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -713,7 +713,7 @@ pub const VTable = struct { processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File, processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize, lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, - tryLockStderr: *const fn (?*anyopaque, buffer: []u8) Cancelable!?LockedStderr, + tryLockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!?LockedStderr, unlockStderr: *const fn (?*anyopaque) void, now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index 1d0494c674..56a1c09340 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -229,7 +229,7 @@ pub fn seekToUnbuffered(w: *Writer, offset: u64) SeekError!void { .positional, .positional_simple => { w.pos = offset; }, - .streaming, .streaming_simple, .terminal_escaped, .terminal_winapi => { + .streaming, .streaming_simple => { if (w.seek_err) |err| return err; io.vtable.fileSeekTo(io.userdata, w.file, offset) catch |err| { w.seek_err = err; diff --git a/lib/std/Io/Terminal.zig b/lib/std/Io/Terminal.zig index cd0ec56caa..a39f2175d2 100644 --- a/lib/std/Io/Terminal.zig +++ b/lib/std/Io/Terminal.zig @@ -130,25 +130,3 @@ pub fn setColor(t: Terminal, color: Color) Io.Writer.Error!void { }, } } - -pub fn disableEscape(t: *Terminal) Mode { - const prev = t.mode; - t.mode = t.mode.toUnescaped(); - return prev; -} - -pub fn restoreEscape(t: *Terminal, mode: Mode) void { - t.mode = mode; -} - -pub fn writeAllUnescaped(t: *Terminal, bytes: []const u8) Io.Writer.Error!void { - const prev_mode = t.disableEscape(); - defer t.restoreEscape(prev_mode); - return t.interface.writeAll(bytes); -} - -pub fn printUnescaped(t: *Terminal, comptime fmt: []const u8, args: anytype) Io.Writer.Error!void { - const prev_mode = t.disableEscape(); - defer t.restoreEscape(prev_mode); - return t.interface.print(fmt, args); -} diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 3e7b6950d4..b07a9bb9a8 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -7178,7 +7178,7 @@ fn fileWriteFileStreaming( break :o .{ &off, @min(@intFromEnum(limit), size - file_reader.pos, max_count) }; }, .streaming => .{ null, limit.minInt(max_count) }, - .streaming_reading, .positional_reading => break :sf, + .streaming_simple, .positional_simple => break :sf, .failure => return error.ReadFailed, }; const current_thread = Thread.getCurrent(t); @@ -7251,7 +7251,7 @@ fn fileWriteFileStreaming( } var off_in: i64 = undefined; const off_in_ptr: ?*i64 = switch (file_reader.mode) { - .positional_reading, .streaming_reading => return error.Unimplemented, + .positional_simple, .streaming_simple => return error.Unimplemented, .positional => p: { off_in = @intCast(file_reader.pos); break :p &off_in; @@ -7427,7 +7427,7 @@ fn fileWriteFilePositional( } var off_in: i64 = undefined; const off_in_ptr: ?*i64 = switch (file_reader.mode) { - .positional_reading, .streaming_reading => return error.Unimplemented, + .positional_simple, .streaming_simple => return error.Unimplemented, .positional => p: { off_in = @intCast(file_reader.pos); break :p &off_in; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index fd3a456ff7..e33a2bd575 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -283,12 +283,12 @@ var static_single_threaded_io: Io.Threaded = .init_single_threaded; /// /// Alternatively, use the higher-level `Io.lockStderr` to integrate with the /// application's chosen `Io` implementation. -pub fn lockStderr(buffer: []u8) Io.Terminal { - return (static_single_threaded_io.ioBasic().lockStderr(buffer, null) catch |err| switch (err) { +pub fn lockStderr(buffer: []u8) Io.LockedStderr { + return static_single_threaded_io.ioBasic().lockStderr(buffer, null) catch |err| switch (err) { // Impossible to cancel because no calls to cancel using // `static_single_threaded_io` exist. error.Canceled => unreachable, - }).terminal(); + }; } pub fn unlockStderr() void { @@ -311,7 +311,7 @@ pub fn print(comptime fmt: []const u8, args: anytype) void { var buffer: [64]u8 = undefined; const stderr = lockStderr(&buffer); defer unlockStderr(); - stderr.writer.print(fmt, args) catch return; + stderr.file_writer.interface.print(fmt, args) catch return; } } @@ -327,7 +327,7 @@ pub inline fn getSelfDebugInfo() !*SelfInfo { /// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned. /// Obtains the stderr mutex while dumping. pub fn dumpHex(bytes: []const u8) void { - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); defer unlockStderr(); dumpHexFallible(stderr, bytes) catch {}; } @@ -551,7 +551,7 @@ pub fn defaultPanic( _ = panicking.fetchAdd(1, .seq_cst); trace: { - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); defer unlockStderr(); const writer = stderr.writer; @@ -581,7 +581,7 @@ pub fn defaultPanic( // A panic happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); stderr.writer.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. @@ -751,7 +751,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin } /// A thin wrapper around `writeCurrentStackTrace` which writes to stderr and ignores write errors. pub fn dumpCurrentStackTrace(options: StackUnwindOptions) void { - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); defer unlockStderr(); writeCurrentStackTrace(.{ .first_address = a: { @@ -814,7 +814,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void } /// A thin wrapper around `writeStackTrace` which writes to stderr and ignores write errors. pub fn dumpStackTrace(st: *const StackTrace) void { - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); defer unlockStderr(); writeStackTrace(st, stderr) catch |err| switch (err) { error.WriteFailed => {}, @@ -1550,7 +1550,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex _ = panicking.fetchAdd(1, .seq_cst); trace: { - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); defer unlockStderr(); if (addr) |a| { @@ -1571,7 +1571,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex // A segfault happened while trying to print a previous panic message. // We're still holding the mutex but that's fine as we're going to // call abort(). - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); stderr.writer.writeAll("aborting due to recursive panic\n") catch {}; }, else => {}, // Panicked while printing the recursive panic message. @@ -1678,7 +1678,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize pub fn dump(t: @This()) void { if (!enabled) return; - const stderr = lockStderr(&.{}); + const stderr = lockStderr(&.{}).terminal(); defer unlockStderr(); const end = @min(t.index, size); for (t.addrs[0..end], 0..) |frames_array, i| { diff --git a/lib/std/debug/simple_panic.zig b/lib/std/debug/simple_panic.zig index 2d0e5abf4e..34e8f7b43c 100644 --- a/lib/std/debug/simple_panic.zig +++ b/lib/std/debug/simple_panic.zig @@ -14,9 +14,9 @@ const std = @import("../std.zig"); pub fn call(msg: []const u8, ra: ?usize) noreturn { @branchHint(.cold); _ = ra; - const stderr = std.debug.lockStderrWriter(&.{}); - stderr.interface.writeAll(msg) catch {}; - stderr.interface.flush(msg) catch {}; + const stderr_writer = std.debug.lockStderr(&.{}, null).terminal().writer; + stderr_writer.writeAll(msg) catch {}; + stderr_writer.flush(msg) catch {}; @trap(); } diff --git a/lib/std/json/dynamic.zig b/lib/std/json/dynamic.zig index f85916670b..e38ea9cb17 100644 --- a/lib/std/json/dynamic.zig +++ b/lib/std/json/dynamic.zig @@ -47,9 +47,9 @@ pub const Value = union(enum) { } pub fn dump(v: Value) void { - const stderr = std.debug.lockStderrWriter(&.{}); - defer std.debug.unlockStderrWriter(); - json.Stringify.value(v, .{}, &stderr.interface) catch return; + const stderr = std.debug.lockStderr(&.{}, null); + defer std.debug.unlockStderr(); + json.Stringify.value(v, .{}, &stderr.file_writer.interface) catch return; } pub fn jsonStringify(value: @This(), jws: anytype) !void { diff --git a/lib/std/log.zig b/lib/std/log.zig index 8579bb04e0..81bfc4ebb6 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -92,7 +92,7 @@ pub fn defaultLog( args: anytype, ) void { var buffer: [64]u8 = undefined; - const stderr = std.debug.lockStderr(&buffer); + const stderr = std.debug.lockStderr(&buffer).terminal(); defer std.debug.unlockStderr(); return defaultLogFileTerminal(level, scope, format, args, stderr) catch {}; } diff --git a/lib/std/process.zig b/lib/std/process.zig index 45cd575133..23a3d92e9b 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1856,7 +1856,7 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { /// and does not return. pub fn cleanExit(io: Io) void { if (builtin.mode == .Debug) return; - _ = io.lockStderrWriter(&.{}) catch {}; + _ = io.lockStderr(&.{}, .no_color) catch {}; exit(0); } diff --git a/lib/std/testing.zig b/lib/std/testing.zig index be6f316804..98fca2f2d5 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -368,8 +368,8 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const break :diff_index if (expected.len == actual.len) return else shortest; }; if (!backend_can_print) return error.TestExpectedEqual; - if (io.lockStderrWriter(&.{})) |stderr| { - defer io.unlockStderrWriter(); + if (io.lockStderr(&.{}, null)) |stderr| { + defer io.unlockStderr(); failEqualSlices(T, expected, actual, diff_index, &stderr.interface, stderr.mode) catch {}; } else |_| {} return error.TestExpectedEqual; @@ -381,7 +381,7 @@ fn failEqualSlices( actual: []const T, diff_index: usize, w: *Io.Writer, - fwm: Io.File.Writer.Mode, + terminal_mode: Io.Terminal.Mode, ) !void { try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); @@ -404,12 +404,12 @@ fn failEqualSlices( var differ = if (T == u8) BytesDiffer{ .expected = expected_window, .actual = actual_window, - .file_writer_mode = fwm, + .terminal_mode = terminal_mode, } else SliceDiffer(T){ .start_index = window_start, .expected = expected_window, .actual = actual_window, - .file_writer_mode = fwm, + .terminal_mode = terminal_mode, }; // Print indexes as hex for slices of u8 since it's more likely to be binary data where @@ -466,21 +466,22 @@ fn SliceDiffer(comptime T: type) type { start_index: usize, expected: []const T, actual: []const T, - file_writer_mode: Io.File.Writer.Mode, + terminal_mode: Io.Terminal.Mode, const Self = @This(); pub fn write(self: Self, writer: *Io.Writer) !void { + const t: Io.Terminal = .{ .writer = writer, .mode = self.terminal_mode }; for (self.expected, 0..) |value, i| { const full_index = self.start_index + i; const diff = if (i < self.actual.len) !std.meta.eql(self.actual[i], value) else true; - if (diff) try self.file_writer_mode.setColor(writer, .red); + if (diff) try t.setColor(writer, .red); if (@typeInfo(T) == .pointer) { try writer.print("[{}]{*}: {any}\n", .{ full_index, value, value }); } else { try writer.print("[{}]: {any}\n", .{ full_index, value }); } - if (diff) try self.file_writer_mode.setColor(writer, .reset); + if (diff) try t.setColor(writer, .reset); } } }; @@ -489,7 +490,7 @@ fn SliceDiffer(comptime T: type) type { const BytesDiffer = struct { expected: []const u8, actual: []const u8, - file_writer_mode: Io.File.Writer.Mode, + terminal_mode: Io.Terminal.Mode, pub fn write(self: BytesDiffer, writer: *Io.Writer) !void { var expected_iterator = std.mem.window(u8, self.expected, 16, 16); @@ -516,7 +517,7 @@ const BytesDiffer = struct { try self.writeDiff(writer, "{c}", .{byte}, diff); } else { // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed - if (self.file_writer_mode == .terminal_winapi) { + if (self.terminal_mode == .windows_api) { try self.writeDiff(writer, ".", .{}, diff); continue; } diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 6e89c75d91..6212264005 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -46,25 +46,18 @@ pub const SrcHasher = std.crypto.hash.Blake3; pub const SrcHash = [16]u8; pub const Color = enum { - /// Determine whether stderr is a terminal or not automatically. + /// Auto-detect whether stream supports terminal colors. auto, - /// Assume stderr is not a terminal. + /// Force-enable colors. off, - /// Assume stderr is a terminal. + /// Suppress colors. on, - pub fn getTtyConf(color: Color, detected: Io.File.Writer.Mode) Io.File.Writer.Mode { + pub fn terminalMode(color: Color) ?Io.Terminal.Mode { return switch (color) { - .auto => detected, - .on => .terminal_escaped, - .off => .streaming, - }; - } - pub fn detectTtyConf(color: Color, io: Io) Io.File.Writer.Mode { - return switch (color) { - .auto => .detect(io, .stderr()), - .on => .terminal_escaped, - .off => .streaming, + .auto => null, + .on => .escape_codes, + .off => .no_color, }; } }; diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 556b1167a3..c5275729ae 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -166,51 +166,53 @@ pub const RenderToStderrError = Io.Cancelable || Io.File.Writer.Error; pub fn renderToStderr(eb: ErrorBundle, io: Io, options: RenderOptions, color: std.zig.Color) RenderToStderrError!void { var buffer: [256]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - renderToWriter(eb, options, &stderr.interface, color.getTtyConf(stderr.mode)) catch |err| switch (err) { - error.WriteFailed => return stderr.err.?, + const stderr = try io.lockStderr(&buffer, color.terminalMode()); + defer io.unlockStderr(); + renderToTerminal(eb, options, stderr.terminal()) catch |err| switch (err) { + error.WriteFailed => return stderr.file_writer.err.?, else => |e| return e, }; } -pub fn renderToWriter( - eb: ErrorBundle, - options: RenderOptions, - w: *Writer, - fwm: Io.File.Writer.Mode, -) Io.File.Writer.Mode.SetColorError!void { +pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer) Writer.Error!void { + return renderToTerminal(eb, options, .{ .writer = w, .mode = .no_color }) catch |err| switch (err) { + error.WriteFailed => |e| return e, + else => unreachable, + }; +} + +pub fn renderToTerminal(eb: ErrorBundle, options: RenderOptions, t: Io.Terminal) Io.Terminal.SetColorError!void { if (eb.extra.len == 0) return; for (eb.getMessages()) |err_msg| { - try renderErrorMessageToWriter(eb, options, err_msg, w, fwm, "error", .red, 0); + try renderErrorMessage(eb, options, err_msg, t, "error", .red, 0); } if (options.include_log_text) { const log_text = eb.getCompileLogOutput(); if (log_text.len != 0) { - try w.writeAll("\nCompile Log Output:\n"); - try w.writeAll(log_text); + try t.writer.writeAll("\nCompile Log Output:\n"); + try t.writer.writeAll(log_text); } } } -fn renderErrorMessageToWriter( +fn renderErrorMessage( eb: ErrorBundle, options: RenderOptions, err_msg_index: MessageIndex, - w: *Writer, - fwm: Io.File.Writer.Mode, + t: Io.Terminal, kind: []const u8, - color: Io.File.Writer.Color, + color: Io.Terminal.Color, indent: usize, -) Io.File.Writer.Mode.SetColorError!void { +) Io.Terminal.SetColorError!void { + const w = t.writer; const err_msg = eb.getErrorMessage(err_msg_index); if (err_msg.src_loc != .none) { const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc)); var prefix: Writer.Discarding = .init(&.{}); try w.splatByteAll(' ', indent); prefix.count += indent; - try fwm.setColor(w, .bold); + try t.setColor(.bold); try w.print("{s}:{d}:{d}: ", .{ eb.nullTerminatedString(src.data.src_path), src.data.line + 1, @@ -221,7 +223,7 @@ fn renderErrorMessageToWriter( src.data.line + 1, src.data.column + 1, }); - try fwm.setColor(w, color); + try t.setColor(color); try w.writeAll(kind); prefix.count += kind.len; try w.writeAll(": "); @@ -229,17 +231,17 @@ fn renderErrorMessageToWriter( // This is the length of the part before the error message: // e.g. "file.zig:4:5: error: " const prefix_len: usize = @intCast(prefix.count); - try fwm.setColor(w, .reset); - try fwm.setColor(w, .bold); + try t.setColor(.reset); + try t.setColor(.bold); if (err_msg.count == 1) { try writeMsg(eb, err_msg, w, prefix_len); try w.writeByte('\n'); } else { try writeMsg(eb, err_msg, w, prefix_len); - try fwm.setColor(w, .dim); + try t.setColor(.dim); try w.print(" ({d} times)\n", .{err_msg.count}); } - try fwm.setColor(w, .reset); + try t.setColor(.reset); if (src.data.source_line != 0 and options.include_source_line) { const line = eb.nullTerminatedString(src.data.source_line); for (line) |b| switch (b) { @@ -252,19 +254,19 @@ fn renderErrorMessageToWriter( // -1 since span.main includes the caret const after_caret = src.data.span_end -| src.data.span_main -| 1; try w.splatByteAll(' ', src.data.column - before_caret); - try fwm.setColor(w, .green); + try t.setColor(.green); try w.splatByteAll('~', before_caret); try w.writeByte('^'); try w.splatByteAll('~', after_caret); try w.writeByte('\n'); - try fwm.setColor(w, .reset); + try t.setColor(.reset); } for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, fwm, "note", .cyan, indent); + try renderErrorMessage(eb, options, note, t, "note", .cyan, indent); } if (src.data.reference_trace_len > 0 and options.include_reference_trace) { - try fwm.setColor(w, .reset); - try fwm.setColor(w, .dim); + try t.setColor(.reset); + try t.setColor(.dim); try w.print("referenced by:\n", .{}); var ref_index = src.end; for (0..src.data.reference_trace_len) |_| { @@ -291,25 +293,25 @@ fn renderErrorMessageToWriter( ); } } - try fwm.setColor(w, .reset); + try t.setColor(.reset); } } else { - try fwm.setColor(w, color); + try t.setColor(color); try w.splatByteAll(' ', indent); try w.writeAll(kind); try w.writeAll(": "); - try fwm.setColor(w, .reset); + try t.setColor(.reset); const msg = eb.nullTerminatedString(err_msg.msg); if (err_msg.count == 1) { try w.print("{s}\n", .{msg}); } else { try w.print("{s}", .{msg}); - try fwm.setColor(w, .dim); + try t.setColor(.dim); try w.print(" ({d} times)\n", .{err_msg.count}); } - try fwm.setColor(w, .reset); + try t.setColor(.reset); for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, fwm, "note", .cyan, indent + 4); + try renderErrorMessage(eb, options, note, t, "note", .cyan, indent + 4); } } } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 6c11c3021b..94d877b406 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -6333,25 +6333,25 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(io: Io, source: [:0]const u8, allocator: Allocator, anything_changed: *bool) ![]u8 { var buffer: [64]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); var tree = try std.zig.Ast.parse(allocator, source, .zig); defer tree.deinit(allocator); for (tree.errors) |parse_error| { const loc = tree.tokenLocation(0, parse_error.token); - try stderr.printUnescaped("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); - try tree.renderError(parse_error, &stderr.interface); - try stderr.interface.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); + try stderr.writer.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); + try tree.renderError(parse_error, stderr.writer); + try stderr.writer.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { - try stderr.writeAllUnescaped(" "); + try stderr.writer.writeAll(" "); } - try stderr.writeAllUnescaped("^"); + try stderr.writer.writeAll("^"); } - try stderr.writeAllUnescaped("\n"); + try stderr.writer.writeAll("\n"); } if (tree.errors.len != 0) { return error.ParseError; diff --git a/src/Air/print.zig b/src/Air/print.zig index 05614a0ed3..98b0a0b242 100644 --- a/src/Air/print.zig +++ b/src/Air/print.zig @@ -76,9 +76,9 @@ pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { const comp = pt.zcu.comp; const io = comp.io; var buffer: [512]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; air.write(w, pt, liveness); } @@ -86,9 +86,9 @@ pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Ai const comp = pt.zcu.comp; const io = comp.io; var buffer: [512]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; air.writeInst(w, inst, pt, liveness); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 37e15ab171..f3fcef40a0 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2092,14 +2092,16 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, } if (options.verbose_llvm_cpu_features) { - if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: { - const stderr = try io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); - const w = &stderr.interface; - w.print("compilation: {s}\n", .{options.root_name}) catch break :print; - w.print(" target: {s}\n", .{try target.zigTriple(arena)}) catch break :print; - w.print(" cpu: {s}\n", .{target.cpu.model.name}) catch break :print; - w.print(" features: {s}\n", .{cf}) catch {}; + if (options.root_mod.resolved_target.llvm_cpu_features) |cf| { + const stderr = try io.lockStderr(&.{}, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; + printVerboseLlvmCpuFeatures(w, arena, options.root_name, target, cf) catch |err| switch (err) { + error.WriteFailed => switch (stderr.file_writer.err.?) { + error.Canceled => |e| return e, + else => {}, + }, + }; } } @@ -2700,6 +2702,19 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, return comp; } +fn printVerboseLlvmCpuFeatures( + w: *Writer, + arena: Allocator, + root_name: []const u8, + target: *const std.Target, + cf: [*:0]const u8, +) Writer.Error!void { + try w.print("compilation: {s}\n", .{root_name}); + try w.print(" target: {s}\n", .{try target.zigTriple(arena)}); + try w.print(" cpu: {s}\n", .{target.cpu.model.name}); + try w.print(" features: {s}\n", .{cf}); +} + pub fn destroy(comp: *Compilation) void { const gpa = comp.gpa; const io = comp.io; @@ -4259,9 +4274,9 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { // However, we haven't reported any such error. // This is a compiler bug. print_ctx: { - const stderr = std.debug.lockStderrWriter(&.{}); - defer std.debug.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = std.debug.lockStderr(&.{}).terminal(); + defer std.debug.unlockStderr(); + const w = stderr.writer; w.writeAll("referenced transitive analysis errors, but none actually emitted\n") catch break :print_ctx; w.print("{f} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}) catch break :print_ctx; while (ref) |r| { @@ -7772,11 +7787,11 @@ pub fn lockAndSetMiscFailure( pub fn dumpArgv(io: Io, argv: []const []const u8) Io.Cancelable!void { var buffer: [64]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&buffer); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; return dumpArgvWriter(w, argv) catch |err| switch (err) { - error.WriteFailed => switch (stderr.err.?) { + error.WriteFailed => switch (stderr.file_writer.err.?) { error.Canceled => return error.Canceled, else => return, }, diff --git a/src/InternPool.zig b/src/InternPool.zig index 539cb441c5..98bde244c5 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -11169,9 +11169,9 @@ pub fn mutateVarInit(ip: *InternPool, io: Io, index: Index, init_index: Index) v pub fn dump(ip: *const InternPool, io: Io) Io.Cancelable!void { var buffer: [4096]u8 = undefined; - const stderr_writer = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr_writer.interface; + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; try dumpStatsFallible(ip, w, std.heap.page_allocator); try dumpAllFallible(ip, w); } @@ -11536,8 +11536,8 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { pub fn dumpGenericInstances(ip: *const InternPool, io: Io, allocator: Allocator) Io.Cancelable!void { var buffer: [4096]u8 = undefined; - const stderr_writer = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); + const stderr_writer = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); const w = &stderr_writer.interface; try ip.dumpGenericInstancesFallible(allocator, w); } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 76ab3e229c..103cbaaaae 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -4560,10 +4560,10 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e if (build_options.enable_debug_extensions and comp.verbose_air) p: { const io = comp.io; - const stderr = try io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); - printVerboseAir(pt, liveness, fqn, air, &stderr.interface) catch |err| switch (err) { - error.WriteFailed => switch (stderr.err.?) { + const stderr = try io.lockStderr(&.{}, null); + defer io.unlockStderr(); + printVerboseAir(pt, liveness, fqn, air, &stderr.file_writer.interface) catch |err| switch (err) { + error.WriteFailed => switch (stderr.file_writer.err.?) { error.Canceled => |e| return e, else => break :p, }, diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 138c70fecf..93a6e0a768 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -11274,16 +11274,15 @@ fn initValueAdvanced( } pub fn dumpValues(isel: *Select, which: enum { only_referenced, all }) void { const zcu = isel.pt.zcu; - const io = zcu.comp.io; const gpa = zcu.gpa; const ip = &zcu.intern_pool; const nav = ip.getNav(isel.nav_index); errdefer |err| @panic(@errorName(err)); - const stderr_writer = io.lockStderrWriter(&.{}) catch return; - defer io.unlockStderrWriter(); - const stderr = &stderr_writer.interface; + const locked_stderr = std.debug.lockStderr(&.{}, null); + defer std.debug.unlockStderr(); + const stderr = &locked_stderr.file_writer.interface; var reverse_live_values: std.AutoArrayHashMapUnmanaged(Value.Index, std.ArrayList(Air.Inst.Index)) = .empty; defer { diff --git a/src/crash_report.zig b/src/crash_report.zig index f23806b758..e56bc7cec5 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -95,9 +95,9 @@ fn dumpCrashContext() Io.Writer.Error!void { // TODO: this does mean that a different thread could grab the stderr mutex between the context // and the actual panic printing, which would be quite confusing. - const stderr = std.debug.lockStderrWriter(&.{}); - defer std.debug.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = std.debug.lockStderr(&.{}); + defer std.debug.unlockStderr(); + const w = &stderr.file_writer.interface; try w.writeAll("Compiler crash context:\n"); diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index a76fec9237..8cf1dfa303 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -314,14 +314,14 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { if (comp.verbose_cc) { var buffer: [256]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; w.print("def file: {s}\n", .{def_file_path}) catch |err| switch (err) { - error.WriteFailed => return stderr.err.?, + error.WriteFailed => return stderr.file_writer.err.?, }; w.print("include dir: {s}\n", .{include_dir}) catch |err| switch (err) { - error.WriteFailed => return stderr.err.?, + error.WriteFailed => return stderr.file_writer.err.?, }; } @@ -339,12 +339,12 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { if (aro_comp.diagnostics.output.to_list.messages.items.len != 0) { var buffer: [64]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); for (aro_comp.diagnostics.output.to_list.messages.items) |msg| { if (msg.kind == .@"fatal error" or msg.kind == .@"error") { - msg.write(&stderr.interface, stderr.mode, true) catch |err| switch (err) { - error.WriteFailed => return stderr.err.?, + msg.write(stderr.terminal(), true) catch |err| switch (err) { + error.WriteFailed => return stderr.file_writer.err.?, }; return error.AroPreprocessorFailed; } @@ -365,9 +365,9 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { error.OutOfMemory => |e| return e, error.ParseError => { var buffer: [64]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; try w.writeAll("error: "); try def_diagnostics.writeMsg(w, input); try w.writeByte('\n'); diff --git a/src/libs/mingw/def.zig b/src/libs/mingw/def.zig index 9a105b6182..f1c112d16e 100644 --- a/src/libs/mingw/def.zig +++ b/src/libs/mingw/def.zig @@ -1039,9 +1039,9 @@ fn testParse( const module = parse(std.testing.allocator, source, machine_type, .mingw, &diagnostics) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.ParseError => { - const stderr = try io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&.{}, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; try diagnostics.writeMsg(w, source); try w.writeByte('\n'); return err; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index d379669613..03b757f5b4 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -2382,9 +2382,9 @@ pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) Io.Cancelable!void { const comp = coff.base.comp; const io = comp.io; var buffer: [512]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&buffer, null); + defer io.unlockStderr(); + const w = &stderr.file_writer.interface; coff.printNode(tid, w, .root, 0) catch |err| switch (err) { error.WriteFailed => return stderr.err.?, }; diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 3c511f1ee9..bbdb439385 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -3733,9 +3733,9 @@ pub fn dump(elf: *Elf, tid: Zcu.PerThread.Id) Io.Cancelable!void { const comp = elf.base.comp; const io = comp.io; var buffer: [512]u8 = undefined; - const stderr = try io.lockStderrWriter(&buffer); - defer io.unlockStderrWriter(); - const w = &stderr.interface; + const stderr = try io.lockStderr(&buffer, null); + defer io.lockStderr(); + const w = &stderr.file_writer.interface; elf.printNode(tid, w, .root, 0) catch |err| switch (err) { error.WriteFailed => return stderr.err.?, }; diff --git a/src/main.zig b/src/main.zig index c5ed278921..8e30febb81 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4429,9 +4429,9 @@ fn runOrTest( // the error message and invocation below. if (process.can_execv and arg_mode == .run) { // execv releases the locks; no need to destroy the Compilation here. - _ = try io.lockStderrWriter(&.{}); + _ = try io.lockStderr(&.{}, .no_color); const err = process.execve(gpa, argv.items, &env_map); - io.unlockStderrWriter(); + io.unlockStderr(); try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); @@ -4448,8 +4448,8 @@ fn runOrTest( comp_destroyed.* = true; const term_result = t: { - _ = try io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); + _ = try io.lockStderr(&.{}, .no_color); + defer io.unlockStderr(); break :t child.spawnAndWait(io); }; const term = term_result catch |err| { @@ -5418,8 +5418,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) child.stderr_behavior = .Inherit; const term = t: { - _ = try io.lockStderrWriter(&.{}); - defer io.unlockStderrWriter(); + _ = try io.lockStderr(&.{}, .no_color); + defer io.unlockStderr(); break :t child.spawnAndWait(io) catch |err| fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err }); }; -- cgit v1.2.3 From 21d0264c61ac29724b98187aa87d192f97b52425 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 18 Dec 2025 21:32:34 -0800 Subject: std.dynamic_library: use a global static single threaded Io See #30150 --- lib/std/Build/Watch.zig | 2 +- lib/std/Io/File/Reader.zig | 4 +++- lib/std/Io/Writer.zig | 6 +++--- lib/std/debug.zig | 10 ++-------- lib/std/dynamic_library.zig | 40 +++++++++++++++++++++------------------- lib/std/fs/test.zig | 8 +++++--- lib/std/posix.zig | 12 ++++++------ 7 files changed, 41 insertions(+), 41 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/Build/Watch.zig b/lib/std/Build/Watch.zig index f7ac47961e..9042241b55 100644 --- a/lib/std/Build/Watch.zig +++ b/lib/std/Build/Watch.zig @@ -684,7 +684,7 @@ const Os = switch (builtin.os.tag) { path.root_dir.handle.handle else posix.openat(path.root_dir.handle.handle, path.sub_path, dir_open_flags, 0) catch |err| { - fatal("failed to open directory {f}: {s}", .{ path, @errorName(err) }); + fatal("failed to open directory {f}: {t}", .{ path, err }); }; // Empirically the dir has to stay open or else no events are triggered. errdefer if (!skip_open_dir) posix.close(dir_fd); diff --git a/lib/std/Io/File/Reader.zig b/lib/std/Io/File/Reader.zig index 792b20a716..2359fad722 100644 --- a/lib/std/Io/File/Reader.zig +++ b/lib/std/Io/File/Reader.zig @@ -43,7 +43,9 @@ pub const Error = error{ /// In WASI, this error occurs when the file descriptor does /// not hold the required rights to read from it. AccessDenied, - /// Unable to read file due to lock. + /// Unable to read file due to lock. Depending on the `Io` implementation, + /// reading from a locked file may return this error, or may ignore the + /// lock. LockViolation, } || Io.Cancelable || Io.UnexpectedError; diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index fab40001a5..49ca6056d4 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -2838,7 +2838,7 @@ test "discarding sendFile" { const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [256]u8 = undefined; - var file_writer: File.Writer = .init(file, &r_buffer); + var file_writer: File.Writer = .init(file, io, &r_buffer); try file_writer.interface.writeByte('h'); try file_writer.interface.flush(); @@ -2860,7 +2860,7 @@ test "allocating sendFile" { const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [2]u8 = undefined; - var file_writer: File.Writer = .init(file, &r_buffer); + var file_writer: File.Writer = .init(file, io, &r_buffer); try file_writer.interface.writeAll("abcd"); try file_writer.interface.flush(); @@ -2884,7 +2884,7 @@ test sendFileReading { const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true }); defer file.close(io); var r_buffer: [2]u8 = undefined; - var file_writer: File.Writer = .init(file, &r_buffer); + var file_writer: File.Writer = .init(file, io, &r_buffer); try file_writer.interface.writeAll("abcd"); try file_writer.interface.flush(); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index e33a2bd575..3850ec3e8c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1610,14 +1610,8 @@ test "manage resources correctly" { var discarding: Writer.Discarding = .init(&.{}); var di: SelfInfo = .init; defer di.deinit(gpa); - try printSourceAtAddress( - gpa, - io, - &di, - &discarding.writer, - S.showMyTrace(), - .no_color, - ); + const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color }; + try printSourceAtAddress(gpa, io, &di, t, S.showMyTrace()); } /// This API helps you track where a value originated and where it was mutated, diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index a52d64ac3f..18db4ad8c6 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -55,11 +55,11 @@ pub const DynLib = struct { // An iterator is provided in order to traverse the linked list in a idiomatic // fashion. const LinkMap = extern struct { - l_addr: usize, - l_name: [*:0]const u8, - l_ld: ?*elf.Dyn, - l_next: ?*LinkMap, - l_prev: ?*LinkMap, + addr: usize, + name: [*:0]const u8, + ld: ?*elf.Dyn, + next: ?*LinkMap, + prev: ?*LinkMap, pub const Iterator = struct { current: ?*LinkMap, @@ -70,7 +70,7 @@ const LinkMap = extern struct { pub fn next(self: *Iterator) ?*LinkMap { if (self.current) |it| { - self.current = it.l_next; + self.current = it.next; return it; } return null; @@ -79,10 +79,10 @@ const LinkMap = extern struct { }; const RDebug = extern struct { - r_version: i32, - r_map: ?*LinkMap, - r_brk: usize, - r_ldbase: usize, + version: i32, + map: ?*LinkMap, + brk: usize, + ldbase: usize, }; /// TODO fix comparisons of extern symbol pointers so we don't need this helper function. @@ -107,8 +107,8 @@ pub fn linkmap_iterator() error{InvalidExe}!LinkMap.Iterator { elf.DT_DEBUG => { const ptr = @as(?*RDebug, @ptrFromInt(_DYNAMIC[i].d_val)); if (ptr) |r_debug| { - if (r_debug.r_version != 1) return error.InvalidExe; - break :init r_debug.r_map; + if (r_debug.version != 1) return error.InvalidExe; + break :init r_debug.map; } }, elf.DT_PLTGOT => { @@ -142,6 +142,8 @@ const ElfDynLibError = error{ Streaming, } || posix.OpenError || posix.MMapError; +var static_single_threaded_io: Io.Threaded = .init_single_threaded; + pub const ElfDynLib = struct { strings: [*:0]u8, syms: [*]elf.Sym, @@ -157,7 +159,7 @@ pub const ElfDynLib = struct { dt_gnu_hash: *elf.gnu_hash.Header, }; - fn openPath(path: []const u8, io: Io) !Io.Dir { + fn openPath(io: Io, path: []const u8) !Io.Dir { if (path.len == 0) return error.NotDir; var parts = std.mem.tokenizeScalar(u8, path, '/'); var parent = if (path[0] == '/') try Io.Dir.cwd().openDir(io, "/", .{}) else Io.Dir.cwd(); @@ -172,7 +174,7 @@ pub const ElfDynLib = struct { fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { var paths = std.mem.tokenizeScalar(u8, search_path, delim); while (paths.next()) |p| { - var dir = openPath(p) catch continue; + var dir = openPath(io, p) catch continue; defer dir.close(io); const fd = posix.openat(dir.handle, file_name, .{ .ACCMODE = .RDONLY, @@ -221,7 +223,9 @@ pub const ElfDynLib = struct { } /// Trusts the file. Malicious file will be able to execute arbitrary code. - pub fn open(io: Io, path: []const u8) Error!ElfDynLib { + pub fn open(path: []const u8) Error!ElfDynLib { + const io = static_single_threaded_io.ioBasic(); + const fd = try resolveFromName(io, path); defer posix.close(fd); @@ -551,11 +555,9 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str } test "ElfDynLib" { - if (native_os != .linux) { - return error.SkipZigTest; - } - + if (native_os != .linux) return error.SkipZigTest; try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so")); + try testing.expectError(error.FileNotFound, ElfDynLib.openZ("invalid_so.so")); } /// Separated to avoid referencing `WindowsDynLib`, because its field types may not diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 92d3630ac7..8c60ffc343 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1796,7 +1796,7 @@ test "read from locked file" { const f = try ctx.dir.createFile(io, filename, .{ .read = true }); defer f.close(io); var buffer: [1]u8 = undefined; - _ = try f.read(&buffer); + _ = try f.readPositional(io, &.{&buffer}, 0); } { const f = try ctx.dir.createFile(io, filename, .{ @@ -1806,11 +1806,13 @@ test "read from locked file" { defer f.close(io); const f2 = try ctx.dir.openFile(io, filename, .{}); defer f2.close(io); + // On POSIX locks may be ignored, however on Windows they cause + // LockViolation. var buffer: [1]u8 = undefined; if (builtin.os.tag == .windows) { - try expectError(error.LockViolation, f2.read(&buffer)); + try expectError(error.LockViolation, f2.readPositional(io, &.{&buffer}, 0)); } else { - try expectEqual(0, f2.read(&buffer)); + try expectEqual(0, f2.readPositional(io, &.{&buffer}, 0)); } } } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 61ef8bb3af..3997bc70cd 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -777,7 +777,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O .NFILE => return error.SystemFdQuotaExceeded, .NODEV => return error.NoDevice, .NOENT => return error.FileNotFound, - .SRCH => return error.ProcessNotFound, + .SRCH => return error.FileNotFound, .NOMEM => return error.SystemResources, .NOSPC => return error.NoSpaceLeft, .NOTDIR => return error.NotDir, @@ -2759,16 +2759,16 @@ pub fn dl_iterate_phdr( // Last return value from the callback function. while (it.next()) |entry| { - const phdrs: []elf.ElfN.Phdr = if (entry.l_addr != 0) phdrs: { - const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.l_addr); + const phdrs: []elf.ElfN.Phdr = if (entry.addr != 0) phdrs: { + const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.addr); assert(mem.eql(u8, ehdr.ident[0..4], elf.MAGIC)); - const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.l_addr + ehdr.phoff); + const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.addr + ehdr.phoff); break :phdrs phdrs[0..ehdr.phnum]; } else getSelfPhdrs(); var info: dl_phdr_info = .{ - .addr = entry.l_addr, - .name = entry.l_name, + .addr = entry.addr, + .name = entry.name, .phdr = phdrs.ptr, .phnum = @intCast(phdrs.len), }; -- cgit v1.2.3 From 7ce5ee2e92bf1bf1f39ccc08df19f9a1044e9f2c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 18 Dec 2025 22:03:10 -0800 Subject: std: update remaining unit tests for std.Io API changes --- lib/std/Build/Cache.zig | 12 ++++++------ lib/std/Io/File/Reader.zig | 4 ++-- lib/std/Io/File/Writer.zig | 8 ++++++-- lib/std/Io/test.zig | 39 ++++++++++++++++++--------------------- lib/std/Thread.zig | 18 ++++++++++-------- lib/std/debug.zig | 9 +++++---- 6 files changed, 47 insertions(+), 43 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index e2c848b6fd..43f8691000 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -1384,8 +1384,8 @@ test "check that changing a file makes cache fail" { try tmp.dir.writeFile(io, .{ .sub_path = temp_file, .data = original_temp_file_contents }); // Wait for file timestamps to tick - const initial_time = try testGetCurrentFileTimestamp(tmp.dir); - while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time.nanoseconds) { + const initial_time = try testGetCurrentFileTimestamp(io, tmp.dir); + while ((try testGetCurrentFileTimestamp(io, tmp.dir)).nanoseconds == initial_time.nanoseconds) { try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io); } @@ -1502,8 +1502,8 @@ test "Manifest with files added after initial hash work" { try tmp.dir.writeFile(io, .{ .sub_path = temp_file2, .data = "Hello world the second!\n" }); // Wait for file timestamps to tick - const initial_time = try testGetCurrentFileTimestamp(tmp.dir); - while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time.nanoseconds) { + const initial_time = try testGetCurrentFileTimestamp(io, tmp.dir); + while ((try testGetCurrentFileTimestamp(io, tmp.dir)).nanoseconds == initial_time.nanoseconds) { try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io); } @@ -1553,8 +1553,8 @@ test "Manifest with files added after initial hash work" { try tmp.dir.writeFile(io, .{ .sub_path = temp_file2, .data = "Hello world the second, updated\n" }); // Wait for file timestamps to tick - const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir); - while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time2.nanoseconds) { + const initial_time2 = try testGetCurrentFileTimestamp(io, tmp.dir); + while ((try testGetCurrentFileTimestamp(io, tmp.dir)).nanoseconds == initial_time2.nanoseconds) { try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io); } diff --git a/lib/std/Io/File/Reader.zig b/lib/std/Io/File/Reader.zig index 2359fad722..0c573c9ae1 100644 --- a/lib/std/Io/File/Reader.zig +++ b/lib/std/Io/File/Reader.zig @@ -18,8 +18,8 @@ io: Io, file: File, err: ?Error = null, mode: Mode = .positional, -/// Tracks the true seek position in the file. To obtain the logical -/// position, use `logicalPos`. +/// Tracks the true seek position in the file. To obtain the logical position, +/// use `logicalPos`. pos: u64 = 0, size: ?u64 = null, size_err: ?SizeError = null, diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index 56a1c09340..3487416719 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -11,8 +11,8 @@ io: Io, file: File, err: ?Error = null, mode: Mode = .positional, -/// Tracks the true seek position in the file. To obtain the logical -/// position, add the buffer size to this value. +/// Tracks the true seek position in the file. To obtain the logical position, +/// use `logicalPos`. pos: u64 = 0, write_file_err: ?WriteFileError = null, seek_err: ?SeekError = null, @@ -221,6 +221,10 @@ pub fn seekTo(w: *Writer, offset: u64) (SeekError || Io.Writer.Error)!void { try seekToUnbuffered(w, offset); } +pub fn logicalPos(w: *const Writer) u64 { + return w.pos + w.interface.end; +} + /// Asserts that no data is currently buffered. pub fn seekToUnbuffered(w: *Writer, offset: u64) SeekError!void { assert(w.interface.buffered().len == 0); diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 8f32555b52..ef0d45d953 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -64,33 +64,28 @@ test "write a file, read it, then delete it" { try tmp.dir.deleteFile(io, tmp_file_name); } -test "File seek ops" { +test "File.Writer.seekTo" { var tmp = tmpDir(.{}); defer tmp.cleanup(); const io = testing.io; + var data: [8192]u8 = undefined; + @memset(&data, 0x55); + const tmp_file_name = "temp_test_file.txt"; var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); - try file.writeAll(&([_]u8{0x55} ** 8192)); - - // Seek to the end - try file.seekFromEnd(0); - try expect((try file.getPos()) == try file.length(io)); - // Negative delta - try file.seekBy(-4096); - try expect((try file.getPos()) == 4096); - // Positive delta - try file.seekBy(10); - try expect((try file.getPos()) == 4106); - // Absolute position - try file.seekTo(1234); - try expect((try file.getPos()) == 1234); + var fw = file.writerStreaming(io, &.{}); + + try fw.interface.writeAll(&data); + try expect(fw.logicalPos() == try file.length(io)); + try fw.seekTo(1234); + try expect(fw.logicalPos() == 1234); } -test "setLength" { +test "File.setLength" { const io = testing.io; var tmp = tmpDir(.{}); @@ -100,19 +95,21 @@ test "setLength" { var file = try tmp.dir.createFile(io, tmp_file_name, .{}); defer file.close(io); + var fw = file.writerStreaming(io, &.{}); + // Verify that the file size changes and the file offset is not moved try expect((try file.length(io)) == 0); - try expect((try file.getPos()) == 0); + try expect(fw.logicalPos() == 0); try file.setLength(io, 8192); try expect((try file.length(io)) == 8192); - try expect((try file.getPos()) == 0); - try file.seekTo(100); + try expect(fw.logicalPos() == 0); + try fw.seekTo(100); try file.setLength(io, 4096); try expect((try file.length(io)) == 4096); - try expect((try file.getPos()) == 100); + try expect(fw.logicalPos() == 100); try file.setLength(io, 0); try expect((try file.length(io)) == 0); - try expect((try file.getPos()) == 100); + try expect(fw.logicalPos() == 100); } test "legacy setLength" { diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 8453bc4c81..fbce1cd000 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -211,7 +211,7 @@ pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { const file = try Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }); defer file.close(io); - try file.writeAll(name); + try file.writeStreamingAll(io, name); return; }, .windows => { @@ -1676,14 +1676,14 @@ const LinuxThreadImpl = struct { } }; -fn testThreadName(thread: *Thread) !void { +fn testThreadName(io: Io, thread: *Thread) !void { const testCases = &[_][]const u8{ "mythread", "b" ** max_name_len, }; inline for (testCases) |tc| { - try thread.setName(tc); + try thread.setName(io, tc); var name_buffer: [max_name_len:0]u8 = undefined; @@ -1698,6 +1698,8 @@ fn testThreadName(thread: *Thread) !void { test "setName, getName" { if (builtin.single_threaded) return error.SkipZigTest; + const io = testing.io; + const Context = struct { start_wait_event: ResetEvent = .unset, test_done_event: ResetEvent = .unset, @@ -1711,11 +1713,11 @@ test "setName, getName" { ctx.start_wait_event.wait(); switch (native_os) { - .windows => testThreadName(&ctx.thread) catch |err| switch (err) { + .windows => testThreadName(io, &ctx.thread) catch |err| switch (err) { error.Unsupported => return error.SkipZigTest, else => return err, }, - else => try testThreadName(&ctx.thread), + else => try testThreadName(io, &ctx.thread), } // Signal our test is done @@ -1735,14 +1737,14 @@ test "setName, getName" { switch (native_os) { .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => { - const res = thread.setName("foobar"); + const res = thread.setName(io, "foobar"); try std.testing.expectError(error.Unsupported, res); }, - .windows => testThreadName(&thread) catch |err| switch (err) { + .windows => testThreadName(io, &thread) catch |err| switch (err) { error.Unsupported => return error.SkipZigTest, else => return err, }, - else => try testThreadName(&thread), + else => try testThreadName(io, &thread), } context.thread_done_event.set(); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 3850ec3e8c..cef4233495 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -384,12 +384,13 @@ pub fn dumpHexFallible(t: Io.Terminal, bytes: []const u8) !void { } test dumpHexFallible { + const gpa = testing.allocator; const bytes: []const u8 = &.{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x12, 0x13 }; - var aw: Writer.Allocating = .init(testing.allocator); + var aw: Writer.Allocating = .init(gpa); defer aw.deinit(); - try dumpHexFallible(&aw.writer, .no_color, bytes); - const expected = try std.fmt.allocPrint(testing.allocator, + try dumpHexFallible(.{ .writer = &aw.writer, .mode = .no_color }, bytes); + const expected = try std.fmt.allocPrint(gpa, \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........ \\{x:0>[2]} 01 12 13 ... \\ @@ -398,7 +399,7 @@ test dumpHexFallible { @intFromPtr(bytes.ptr) + 16, @sizeOf(usize) * 2, }); - defer testing.allocator.free(expected); + defer gpa.free(expected); try testing.expectEqualStrings(expected, aw.written()); } -- cgit v1.2.3 From 1381f9f612a5e03895615ade253411d86bce31a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 19 Dec 2025 12:10:21 -0800 Subject: std.debug: fix printLineFromFile by using streamDelimiter and discardDelimiter functions that don't depend on the buffer size being large enough --- lib/std/debug.zig | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index cef4233495..26631db501 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1203,23 +1203,21 @@ fn printLineFromFile(io: Io, writer: *Writer, source_location: SourceLocation) ! const cwd: Io.Dir = .cwd(); var file = try cwd.openFile(io, source_location.file_name, .{}); defer file.close(io); - // TODO fstat and make sure that the file has the correct size var buffer: [4096]u8 = undefined; var file_reader: File.Reader = .init(file, io, &buffer); var line_index: usize = 0; - while (try file_reader.interface.takeDelimiter('\n')) |line| { + const r = &file_reader.interface; + while (true) { line_index += 1; if (line_index == source_location.line) { // TODO delete hard tabs from the language - mem.replaceScalar(u8, line, '\t', ' '); - try writer.writeAll(line); - // Make sure printing last line of file inserts extra newline. + _ = try r.streamDelimiterEnding(writer, '\n'); try writer.writeByte('\n'); return; } + _ = try r.discardDelimiterInclusive('\n'); } - return error.EndOfStream; } test printLineFromFile { -- cgit v1.2.3 From 77d2ad8c929680ed35fcfe6646f940518a07e7e4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 19 Dec 2025 20:15:50 -0800 Subject: std: consolidate all instances of std.Io.Threaded into a singleton It's better to avoid references to this global variable, but, in the cases where it's needed, such as in std.debug.print and collecting stack traces, better to share the same instance. --- lib/compiler/test_runner.zig | 10 +++++----- lib/std/Io/Threaded.zig | 13 +++++++++++++ lib/std/Thread.zig | 3 +-- lib/std/debug.zig | 12 ++++++------ lib/std/debug/SelfInfo/Windows.zig | 4 ++-- lib/std/dynamic_library.zig | 4 +--- lib/std/process/Child.zig | 15 +++++++-------- test/incremental/no_change_preserves_tag_names | 4 ++-- test/standalone/cmakedefine/check.zig | 3 +-- test/standalone/dirname/exists_in.zig | 3 +-- test/standalone/dirname/touch.zig | 3 +-- test/standalone/entry_point/check_differ.zig | 3 +-- test/standalone/install_headers/check_exists.zig | 3 +-- test/standalone/posix/relpaths.zig | 3 +-- test/standalone/run_cwd/check_file_exists.zig | 3 +-- test/standalone/run_output_caching/main.zig | 3 +-- test/standalone/run_output_paths/create_file.zig | 3 +-- test/standalone/self_exe_symlink/create-symlink.zig | 3 +-- test/standalone/simple/hello_world/hello.zig | 11 ++++++++--- test/standalone/windows_paths/test.zig | 3 +-- 20 files changed, 56 insertions(+), 53 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index d16fc3ae82..84664feb13 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -17,7 +17,7 @@ var fba: std.heap.FixedBufferAllocator = .init(&fba_buffer); var fba_buffer: [8192]u8 = undefined; var stdin_buffer: [4096]u8 = undefined; var stdout_buffer: [4096]u8 = undefined; -var runner_threaded_io: Io.Threaded = .init_single_threaded; +const runner_threaded_io: Io = Io.Threaded.global_single_threaded.ioBasic(); /// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether /// the test runner will communicate with the build runner via `std.zig.Server`. @@ -74,8 +74,8 @@ pub fn main() void { fn mainServer() !void { @disableInstrumentation(); - var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io.io(), &stdin_buffer); - var stdout_writer = Io.File.stdout().writerStreaming(runner_threaded_io.io(), &stdout_buffer); + var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io, &stdin_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(runner_threaded_io, &stdout_buffer); var server = try std.zig.Server.init(.{ .in = &stdin_reader.interface, .out = &stdout_writer.interface, @@ -224,11 +224,11 @@ fn mainTerminal() void { var skip_count: usize = 0; var fail_count: usize = 0; var fuzz_count: usize = 0; - const root_node = if (builtin.fuzz) std.Progress.Node.none else std.Progress.start(runner_threaded_io.io(), .{ + const root_node = if (builtin.fuzz) std.Progress.Node.none else std.Progress.start(runner_threaded_io, .{ .root_name = "Test", .estimated_total_items = test_fn_list.len, }); - const have_tty = Io.File.stderr().isTty(runner_threaded_io.io()) catch unreachable; + const have_tty = Io.File.stderr().isTty(runner_threaded_io) catch unreachable; var leaks: usize = 0; for (test_fn_list, 0..) |test_fn, i| { diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index db4c5f3fda..182a8f4b6d 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -626,6 +626,19 @@ pub const init_single_threaded: Threaded = .{ }, }; +var global_single_threaded_instance: Threaded = .init_single_threaded; + +/// In general, the application is responsible for choosing the `Io` +/// implementation and library code should accept an `Io` parameter rather than +/// accessing this declaration. Most code should avoid referencing this +/// declaration entirely. +/// +/// However, in some cases such as debugging, it is desirable to hardcode a +/// reference to this `Io` implementation. +/// +/// This instance does not support concurrency or cancelation. +pub const global_single_threaded: *Threaded = &global_single_threaded_instance; + pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { t.mutex.lock(); defer t.mutex.unlock(); diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index fbce1cd000..f25c664000 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -322,8 +322,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co var buf: [32]u8 = undefined; const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); + const io = Io.Threaded.global_single_threaded.ioBasic(); const file = try Io.Dir.cwd().openFile(io, path, .{}); defer file.close(io); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 26631db501..0e804e2348 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -263,7 +263,7 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { /// This is used for debug information and debug printing. It is intentionally /// separate from the application's `Io` instance. -var static_single_threaded_io: Io.Threaded = .init_single_threaded; +const static_single_threaded_io = Io.Threaded.global_single_threaded.ioBasic(); /// Allows the caller to freely write to stderr until `unlockStderr` is called. /// @@ -284,7 +284,7 @@ var static_single_threaded_io: Io.Threaded = .init_single_threaded; /// Alternatively, use the higher-level `Io.lockStderr` to integrate with the /// application's chosen `Io` implementation. pub fn lockStderr(buffer: []u8) Io.LockedStderr { - return static_single_threaded_io.ioBasic().lockStderr(buffer, null) catch |err| switch (err) { + return static_single_threaded_io.lockStderr(buffer, null) catch |err| switch (err) { // Impossible to cancel because no calls to cancel using // `static_single_threaded_io` exist. error.Canceled => unreachable, @@ -292,7 +292,7 @@ pub fn lockStderr(buffer: []u8) Io.LockedStderr { } pub fn unlockStderr() void { - static_single_threaded_io.ioBasic().unlockStderr(); + static_single_threaded_io.unlockStderr(); } /// Writes to stderr, ignoring errors. @@ -630,7 +630,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; - const io = static_single_threaded_io.ioBasic(); + const io = static_single_threaded_io; var total_frames: usize = 0; var index: usize = 0; @@ -692,7 +692,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin var total_frames: usize = 0; var wait_for = options.first_address; var printed_any_frame = false; - const io = static_single_threaded_io.ioBasic(); + const io = static_single_threaded_io; while (true) switch (it.next(io)) { .switch_to_fp => |unwind_error| { switch (StackIterator.fp_usability) { @@ -800,7 +800,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void return; }, }; - const io = static_single_threaded_io.ioBasic(); + const io = static_single_threaded_io; const captured_frames = @min(n_frames, st.instruction_addresses.len); for (st.instruction_addresses[0..captured_frames]) |ret_addr| { // `ret_addr` is the return address, which is *after* the function call. diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index c34c60f3ec..99d3e9f926 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -315,8 +315,8 @@ const Module = struct { ); if (len == 0) return error.MissingDebugInfo; const name_w = name_buffer[0 .. len + 4 :0]; - var threaded: Io.Threaded = .init_single_threaded; - const coff_file = threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) { + // TODO eliminate the reference to Io.Threaded.global_single_threaded here + const coff_file = Io.Threaded.global_single_threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) { error.Canceled => |e| return e, error.Unexpected => |e| return e, error.FileNotFound => return error.MissingDebugInfo, diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 18db4ad8c6..70b236f3e5 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -142,8 +142,6 @@ const ElfDynLibError = error{ Streaming, } || posix.OpenError || posix.MMapError; -var static_single_threaded_io: Io.Threaded = .init_single_threaded; - pub const ElfDynLib = struct { strings: [*:0]u8, syms: [*]elf.Sym, @@ -224,7 +222,7 @@ pub const ElfDynLib = struct { /// Trusts the file. Malicious file will be able to execute arbitrary code. pub fn open(path: []const u8) Error!ElfDynLib { - const io = static_single_threaded_io.ioBasic(); + const io = Io.Threaded.global_single_threaded.ioBasic(); const fd = try resolveFromName(io, path); defer posix.close(fd); diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 05cc4b3944..0db02aa824 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -266,7 +266,7 @@ pub fn spawn(self: *Child, io: Io) SpawnError!void { } if (native_os == .windows) { - return self.spawnWindows(); + return self.spawnWindows(io); } else { return self.spawnPosix(io); } @@ -750,7 +750,7 @@ fn spawnPosix(self: *Child, io: Io) SpawnError!void { self.progress_node.setIpcFd(prog_pipe[0]); } -fn spawnWindows(self: *Child) SpawnError!void { +fn spawnWindows(self: *Child, io: Io) SpawnError!void { var saAttr = windows.SECURITY_ATTRIBUTES{ .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), .bInheritHandle = windows.TRUE, @@ -953,7 +953,7 @@ fn spawnWindows(self: *Child) SpawnError!void { try dir_buf.appendSlice(self.allocator, app_dir); } - windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| { + windowsCreateProcessPathExt(self.allocator, io, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| { const original_err = switch (no_path_err) { // argv[0] contains unsupported characters that will never resolve to a valid exe. error.InvalidArg0 => return error.FileNotFound, @@ -977,7 +977,7 @@ fn spawnWindows(self: *Child) SpawnError!void { dir_buf.clearRetainingCapacity(); try dir_buf.appendSlice(self.allocator, search_path); - if (windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) { + if (windowsCreateProcessPathExt(self.allocator, io, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) { break :run; } else |err| switch (err) { // argv[0] contains unsupported characters that will never resolve to a valid exe. @@ -1079,6 +1079,7 @@ const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); /// Note: If the dir is the cwd, dir_buf should be empty (len = 0). fn windowsCreateProcessPathExt( allocator: Allocator, + io: Io, dir_buf: *ArrayList(u16), app_buf: *ArrayList(u16), pathext: [:0]const u16, @@ -1122,16 +1123,14 @@ fn windowsCreateProcessPathExt( // Under those conditions, here we will have access to lower level directory // opening function knowing which implementation we are in. Here, we imitate // that scenario. - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); - var dir = dir: { // needs to be null-terminated try dir_buf.append(allocator, 0); defer dir_buf.shrinkRetainingCapacity(dir_path_len); const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z); - break :dir threaded.dirOpenDirWindows(.cwd(), prefixed_path.span(), .{ + // TODO eliminate this reference + break :dir Io.Threaded.global_single_threaded.dirOpenDirWindows(.cwd(), prefixed_path.span(), .{ .iterate = true, }) catch return error.FileNotFound; }; diff --git a/test/incremental/no_change_preserves_tag_names b/test/incremental/no_change_preserves_tag_names index e399e083e1..6675d74166 100644 --- a/test/incremental/no_change_preserves_tag_names +++ b/test/incremental/no_change_preserves_tag_names @@ -8,7 +8,7 @@ const std = @import("std"); var some_enum: enum { first, second } = .first; pub fn main() !void { - try std.Io.File.stdout().writeAll(@tagName(some_enum)); + try std.Io.File.stdout().writeStreamingAll(std.Io.Threaded.global_single_threaded.ioBasic(), @tagName(some_enum)); } #expect_stdout="first" #update=no change @@ -16,6 +16,6 @@ pub fn main() !void { const std = @import("std"); var some_enum: enum { first, second } = .first; pub fn main() !void { - try std.Io.File.stdout().writeAll(@tagName(some_enum)); + try std.Io.File.stdout().writeStreamingAll(std.Io.Threaded.global_single_threaded.ioBasic(), @tagName(some_enum)); } #expect_stdout="first" diff --git a/test/standalone/cmakedefine/check.zig b/test/standalone/cmakedefine/check.zig index d0751913d7..c2f89ad112 100644 --- a/test/standalone/cmakedefine/check.zig +++ b/test/standalone/cmakedefine/check.zig @@ -9,8 +9,7 @@ pub fn main() !void { const actual_path = args[1]; const expected_path = args[2]; - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); const actual = try std.Io.Dir.cwd().readFileAlloc(io, actual_path, arena, .limited(1024 * 1024)); const expected = try std.Io.Dir.cwd().readFileAlloc(io, expected_path, arena, .limited(1024 * 1024)); diff --git a/test/standalone/dirname/exists_in.zig b/test/standalone/dirname/exists_in.zig index 7bddc4e613..b321f46c55 100644 --- a/test/standalone/dirname/exists_in.zig +++ b/test/standalone/dirname/exists_in.zig @@ -34,8 +34,7 @@ fn run(allocator: std.mem.Allocator) !void { return error.BadUsage; }; - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); defer dir.close(io); diff --git a/test/standalone/dirname/touch.zig b/test/standalone/dirname/touch.zig index 9255d27c72..d7f9b71553 100644 --- a/test/standalone/dirname/touch.zig +++ b/test/standalone/dirname/touch.zig @@ -29,8 +29,7 @@ fn run(allocator: std.mem.Allocator) !void { const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable; const basename = std.Io.Dir.path.basename(path); - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); defer dir.close(io); diff --git a/test/standalone/entry_point/check_differ.zig b/test/standalone/entry_point/check_differ.zig index bba45e5f8c..29b333632f 100644 --- a/test/standalone/entry_point/check_differ.zig +++ b/test/standalone/entry_point/check_differ.zig @@ -6,8 +6,7 @@ pub fn main() !void { const args = try std.process.argsAlloc(arena); if (args.len != 3) return error.BadUsage; // usage: 'check_differ ' - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); const contents_1 = try std.Io.Dir.cwd().readFileAlloc(io, args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty const contents_2 = try std.Io.Dir.cwd().readFileAlloc(io, args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty diff --git a/test/standalone/install_headers/check_exists.zig b/test/standalone/install_headers/check_exists.zig index 8a7104e4f2..22638cf167 100644 --- a/test/standalone/install_headers/check_exists.zig +++ b/test/standalone/install_headers/check_exists.zig @@ -11,8 +11,7 @@ pub fn main() !void { var arg_it = try std.process.argsWithAllocator(arena); _ = arg_it.next(); - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); const cwd = std.Io.Dir.cwd(); const cwd_realpath = try cwd.realPathAlloc(io, arena, "."); diff --git a/test/standalone/posix/relpaths.zig b/test/standalone/posix/relpaths.zig index ed143ccdc2..447285c444 100644 --- a/test/standalone/posix/relpaths.zig +++ b/test/standalone/posix/relpaths.zig @@ -14,8 +14,7 @@ pub fn main() !void { const gpa = debug_allocator.allocator(); defer std.debug.assert(debug_allocator.deinit() == .ok); - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); // TODO this API isn't supposed to be used outside of unit testing. make it compilation error if used // outside of unit testing. diff --git a/test/standalone/run_cwd/check_file_exists.zig b/test/standalone/run_cwd/check_file_exists.zig index 8830cc115a..a885c7dafd 100644 --- a/test/standalone/run_cwd/check_file_exists.zig +++ b/test/standalone/run_cwd/check_file_exists.zig @@ -8,8 +8,7 @@ pub fn main() !void { if (args.len != 2) return error.BadUsage; const path = args[1]; - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); std.Io.Dir.cwd().access(io, path, .{}) catch return error.AccessFailed; } diff --git a/test/standalone/run_output_caching/main.zig b/test/standalone/run_output_caching/main.zig index c21d8d0f6b..66e64beda2 100644 --- a/test/standalone/run_output_caching/main.zig +++ b/test/standalone/run_output_caching/main.zig @@ -1,8 +1,7 @@ const std = @import("std"); pub fn main() !void { - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); var args = try std.process.argsWithAllocator(std.heap.page_allocator); _ = args.skip(); const filename = args.next().?; diff --git a/test/standalone/run_output_paths/create_file.zig b/test/standalone/run_output_paths/create_file.zig index 4830790c79..27f741ecab 100644 --- a/test/standalone/run_output_paths/create_file.zig +++ b/test/standalone/run_output_paths/create_file.zig @@ -1,8 +1,7 @@ const std = @import("std"); pub fn main() !void { - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); var args = try std.process.argsWithAllocator(std.heap.page_allocator); _ = args.skip(); const dir_name = args.next().?; diff --git a/test/standalone/self_exe_symlink/create-symlink.zig b/test/standalone/self_exe_symlink/create-symlink.zig index 6ccf6596aa..d725207320 100644 --- a/test/standalone/self_exe_symlink/create-symlink.zig +++ b/test/standalone/self_exe_symlink/create-symlink.zig @@ -15,8 +15,7 @@ pub fn main() anyerror!void { const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path); defer allocator.free(exe_rel_path); - var threaded: std.Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); try std.Io.Dir.cwd().symLink(io, exe_rel_path, symlink_path, .{}); } diff --git a/test/standalone/simple/hello_world/hello.zig b/test/standalone/simple/hello_world/hello.zig index d708394230..6bce841344 100644 --- a/test/standalone/simple/hello_world/hello.zig +++ b/test/standalone/simple/hello_world/hello.zig @@ -1,8 +1,13 @@ const std = @import("std"); -var static_single_threaded_io: std.Io.Threaded = .init_single_threaded; -const io = static_single_threaded_io.ioBasic(); - pub fn main() !void { + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer _ = debug_allocator.deinit(); + const gpa = debug_allocator.allocator(); + + var threaded: std.Io.Threaded = .init(gpa); + defer threaded.deinit(); + const io = threaded.io(); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } diff --git a/test/standalone/windows_paths/test.zig b/test/standalone/windows_paths/test.zig index f5fa594766..ed4069dc61 100644 --- a/test/standalone/windows_paths/test.zig +++ b/test/standalone/windows_paths/test.zig @@ -10,8 +10,7 @@ pub fn main() anyerror!void { if (args.len < 2) return error.MissingArgs; - var threaded: Io.Threaded = .init_single_threaded; - const io = threaded.io(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); const exe_path = args[1]; -- cgit v1.2.3 From fd0c324cb05d0d30f8ca5ae44f6db5072975b125 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 21 Dec 2025 17:06:02 -0800 Subject: std.debug: fix simple_panic --- lib/std/debug.zig | 5 +---- lib/std/debug/simple_panic.zig | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 0e804e2348..5129a70b02 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -489,10 +489,7 @@ const use_trap_panic = switch (builtin.zig_backend) { }; /// Dumps a stack trace to standard error, then aborts. -pub fn defaultPanic( - msg: []const u8, - first_trace_addr: ?usize, -) noreturn { +pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn { @branchHint(.cold); if (use_trap_panic) @trap(); diff --git a/lib/std/debug/simple_panic.zig b/lib/std/debug/simple_panic.zig index 34e8f7b43c..a5a09fa116 100644 --- a/lib/std/debug/simple_panic.zig +++ b/lib/std/debug/simple_panic.zig @@ -14,9 +14,8 @@ const std = @import("../std.zig"); pub fn call(msg: []const u8, ra: ?usize) noreturn { @branchHint(.cold); _ = ra; - const stderr_writer = std.debug.lockStderr(&.{}, null).terminal().writer; + const stderr_writer = &std.debug.lockStderr(&.{}).file_writer.interface; stderr_writer.writeAll(msg) catch {}; - stderr_writer.flush(msg) catch {}; @trap(); } -- cgit v1.2.3 From 3c2f5adf41f0e75fd5e8f6661891dd7d4fa770a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 22 Dec 2025 14:37:41 -0800 Subject: std: integrate Io.Threaded with environment variables * std.option allows overriding the debug Io instance * if the default is used, start code initializes environ and argv0 also fix some places that needed recancel(), thanks mlugg! See #30562 --- lib/compiler/build_runner.zig | 5 ++- lib/std/Io/File.zig | 2 +- lib/std/Io/Terminal.zig | 14 ++++-- lib/std/Io/Threaded.zig | 99 +++++++++++++++++++++++++------------------ lib/std/Thread.zig | 2 +- lib/std/debug.zig | 20 ++++----- lib/std/dynamic_library.zig | 2 +- lib/std/start.zig | 10 +++++ lib/std/std.zig | 19 ++++++++- 9 files changed, 111 insertions(+), 62 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index e982a2f316..135cf2dced 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -429,8 +429,11 @@ pub fn main() !void { } } + const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(); + const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(); + graph.stderr_mode = switch (color) { - .auto => try .detect(io, .stderr()), + .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE), .on => .escape_codes, .off => .no_color, }; diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index ae6c69285f..804b0c9155 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -391,7 +391,7 @@ pub fn setOwner(file: File, io: Io, owner: ?Uid, group: ?Gid) SetOwnerError!void /// On POSIX systems this corresponds to "mode" and on Windows this corresponds to "attributes". /// /// Overridable via `std.options`. -pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(std.os.windows.DWORD) { +pub const Permissions = std.io_options.FilePermissions orelse if (is_windows) enum(std.os.windows.DWORD) { default_file = 0, _, diff --git a/lib/std/Io/Terminal.zig b/lib/std/Io/Terminal.zig index 549adcf207..beacc4d301 100644 --- a/lib/std/Io/Terminal.zig +++ b/lib/std/Io/Terminal.zig @@ -48,7 +48,15 @@ pub const Mode = union(enum) { /// stdout/stderr). /// /// Will attempt to enable ANSI escape code support if necessary/possible. - pub fn detect(io: Io, file: File) Io.Cancelable!Mode { + /// + /// * `NO_COLOR` indicates whether "NO_COLOR" environment variable is + /// present and non-empty. + /// * `CLICOLOR_FORCE` indicates whether "CLICOLOR_FORCE" environment + /// variable is present and non-empty. + pub fn detect(io: Io, file: File, NO_COLOR: bool, CLICOLOR_FORCE: bool) Io.Cancelable!Mode { + const force_color: ?bool = if (NO_COLOR) false else if (CLICOLOR_FORCE) true else null; + if (force_color == false) return .no_color; + if (file.enableAnsiEscapeCodes(io)) |_| { return .escape_codes; } else |err| switch (err) { @@ -65,10 +73,8 @@ pub const Mode = union(enum) { .reset_attributes = info.wAttributes, } }; } - return .escape_codes; } - - return .no_color; + return if (force_color == true) .escape_codes else .no_color; } }; diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 054752fb03..4f1fe48310 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -60,30 +60,34 @@ stderr_writer: File.Writer = .{ stderr_mode: Io.Terminal.Mode = .no_color, stderr_writer_initialized: bool = false, +argv0: Argv0, environ: Environ, -args: Args, -pub const Environ = switch (native_os) { +pub const Argv0 = switch (native_os) { .openbsd, .haiku => struct { - PATH: ?[]const u8, - - pub const empty: @This() = .{ - .PATH = null, - }; - }, - else => struct { - pub const empty: @This() = .{}; + value: ?[*:0]const u8 = null, }, + else => struct {}, }; -pub const Args = switch (native_os) { - .openbsd, .haiku => struct { - list: []const []const u8, - pub const empty: @This() = .{ .list = &.{} }; - }, - else => struct { - pub const empty: @This() = .{}; - }, +pub const Environ = struct { + /// Unmodified data directly from the OS. + block: Block = &.{}, + /// Protected by `mutex`. Determines whether the other fields have been + /// memoized based on `block`. + initialized: bool = false, + /// Protected by `mutex`. Memoized based on `block`. Tracks whether the + /// environment variables are present and non-empty. + exist: struct { + NO_COLOR: bool = false, + CLICOLOR_FORCE: bool = false, + } = .{}, + /// Protected by `mutex`. Memoized based on `block`. + string: struct { + PATH: ?[:0]const u8 = null, + } = .{}, + + pub const Block = []const [*:0]const u8; }; pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { @@ -591,18 +595,11 @@ pub const InitOptions = struct { robust_cancel: RobustCancel = .disabled, /// Affects the following operations: /// * `processExecutablePath` on OpenBSD and Haiku. - /// - /// The default value causes this to be a compile error on systems that need to - /// initialize this field. `Environ.empty` can be used to omit this field on - /// all targets. - environ: Environ = .{}, + argv0: Argv0 = .{}, /// Affects the following operations: - /// * `processExecutablePath` on OpenBSD and Haiku. - /// - /// The default value causes this to be a compile error on systems that need to - /// initialize this field. `Args.empty` can be used to omit this field on all - /// targets. - args: Args = .{}, + /// * `fileIsTty` + /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH"). + environ: Environ = .{}, }; /// Related: @@ -636,8 +633,8 @@ pub fn init( .current_closure = null, .cancel_protection = undefined, }, + .argv0 = options.argv0, .environ = options.environ, - .args = options.args, .robust_cancel = options.robust_cancel, }; @@ -678,8 +675,8 @@ pub const init_single_threaded: Threaded = .{ .cancel_protection = undefined, }, .robust_cancel = .disabled, - .environ = .empty, - .args = .empty, + .argv0 = .{}, + .environ = .{}, }; var global_single_threaded_instance: Threaded = .init_single_threaded; @@ -7242,17 +7239,16 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex } }, .openbsd, .haiku => { - // The best we can do on these operating systems is check based on CLI args. - const argv = t.args.list; - if (argv.len == 0) return error.OperationUnsupported; - const argv0 = argv[0]; + // The best we can do on these operating systems is check based on + // the first process argument. + const argv0 = t.argv0.value orelse return error.OperationUnsupported; if (std.mem.findScalar(u8, argv0, '/') != null) { // argv[0] is a path (relative or absolute): use realpath(3) directly const current_thread = Thread.getCurrent(t); var resolved_buf: [std.c.PATH_MAX]u8 = undefined; try current_thread.beginSyscall(); while (true) { - if (std.c.realpath(argv[0], &resolved_buf)) |p| { + if (std.c.realpath(argv0, &resolved_buf)) |p| { assert(p == &resolved_buf); break current_thread.endSyscall(); } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) { @@ -7283,13 +7279,14 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex return resolved.len; } else if (argv0.len != 0) { // argv[0] is not empty (and not a path): search PATH + t.scanEnviron(); + const PATH = t.environ.string.PATH orelse return error.FileNotFound; const current_thread = Thread.getCurrent(t); - const PATH = t.environ.PATH orelse return error.FileNotFound; var it = std.mem.tokenizeScalar(u8, PATH, ':'); it: while (it.next()) |dir| { var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined; const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{ - dir, argv[0], + dir, argv0, }, 0) catch continue; var resolved_buf: [std.c.PATH_MAX]u8 = undefined; @@ -10752,7 +10749,10 @@ fn initLockedStderr( if (is_windows) t.stderr_writer.file = .stderr(); t.stderr_writer.io = io_t; t.stderr_writer_initialized = true; - t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file); + t.scanEnviron(); + const NO_COLOR = t.environ.exist.NO_COLOR; + const CLICOLOR_FORCE = t.environ.exist.CLICOLOR_FORCE; + t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file, NO_COLOR, CLICOLOR_FORCE); } std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch |err| switch (err) { error.WriteFailed => switch (t.stderr_writer.err.?) { @@ -10777,7 +10777,7 @@ fn unlockStderr(userdata: ?*anyopaque) void { const t: *Threaded = @ptrCast(@alignCast(userdata)); t.stderr_writer.interface.flush() catch |err| switch (err) { error.WriteFailed => switch (t.stderr_writer.err.?) { - error.Canceled => @panic("TODO make this uncancelable"), + error.Canceled => recancel(t), else => {}, }, }; @@ -11910,6 +11910,23 @@ const pthreads_futex = struct { } }; +fn scanEnviron(t: *Threaded) void { + t.mutex.lock(); + defer t.mutex.unlock(); + + if (t.environ.initialized) return; + t.environ.initialized = true; + + if (native_os == .wasi) { + @panic("TODO"); + } + + for (t.environ.block) |kv| { + _ = kv; + @panic("TODO"); + } +} + test { _ = @import("Threaded/test.zig"); } diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index f25c664000..1900225099 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -322,7 +322,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co var buf: [32]u8 = undefined; const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); - const io = Io.Threaded.global_single_threaded.ioBasic(); + const io = std.options.debug_io; const file = try Io.Dir.cwd().openFile(io, path, .{}); defer file.close(io); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 5129a70b02..25a0bc120d 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -261,10 +261,6 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { else => true, }; -/// This is used for debug information and debug printing. It is intentionally -/// separate from the application's `Io` instance. -const static_single_threaded_io = Io.Threaded.global_single_threaded.ioBasic(); - /// Allows the caller to freely write to stderr until `unlockStderr` is called. /// /// During the lock, any `std.Progress` information is cleared from the terminal. @@ -284,15 +280,15 @@ const static_single_threaded_io = Io.Threaded.global_single_threaded.ioBasic(); /// Alternatively, use the higher-level `Io.lockStderr` to integrate with the /// application's chosen `Io` implementation. pub fn lockStderr(buffer: []u8) Io.LockedStderr { - return static_single_threaded_io.lockStderr(buffer, null) catch |err| switch (err) { - // Impossible to cancel because no calls to cancel using - // `static_single_threaded_io` exist. - error.Canceled => unreachable, + const io = std.options.debug_io; + return io.lockStderr(buffer, null) catch |err| switch (err) { + error.Canceled => io.recancel(), }; } pub fn unlockStderr() void { - static_single_threaded_io.unlockStderr(); + const io = std.options.debug_io; + io.unlockStderr(); } /// Writes to stderr, ignoring errors. @@ -627,7 +623,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; - const io = static_single_threaded_io; + const io = std.options.debug_io; var total_frames: usize = 0; var index: usize = 0; @@ -689,7 +685,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin var total_frames: usize = 0; var wait_for = options.first_address; var printed_any_frame = false; - const io = static_single_threaded_io; + const io = std.options.debug_io; while (true) switch (it.next(io)) { .switch_to_fp => |unwind_error| { switch (StackIterator.fp_usability) { @@ -797,7 +793,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void return; }, }; - const io = static_single_threaded_io; + const io = std.options.debug_io; const captured_frames = @min(n_frames, st.instruction_addresses.len); for (st.instruction_addresses[0..captured_frames]) |ret_addr| { // `ret_addr` is the return address, which is *after* the function call. diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 70b236f3e5..b4acc8ae70 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -222,7 +222,7 @@ pub const ElfDynLib = struct { /// Trusts the file. Malicious file will be able to execute arbitrary code. pub fn open(path: []const u8) Error!ElfDynLib { - const io = Io.Threaded.global_single_threaded.ioBasic(); + const io = std.options.debug_io; const fd = try resolveFromName(io, path); defer posix.close(fd); diff --git a/lib/std/start.zig b/lib/std/start.zig index 64a1c17175..5a3e39e24a 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -669,6 +669,11 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; + if (std.io_options.debug_threaded_io) |t| { + if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; + t.environ = .{ .block = envp }; + } + std.debug.maybeEnableSegfaultHandler(); return callMain(); @@ -691,6 +696,11 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; + + if (@sizeOf(std.Io.Threaded.Argv0) != 0) { + if (std.io_options.debug_threaded_io) |t| t.argv0.value = std.os.argv[0]; + } + return callMain(); } diff --git a/lib/std/std.zig b/lib/std/std.zig index 1690c0575c..46f6415d09 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -108,8 +108,11 @@ pub const start = @import("start.zig"); const root = @import("root"); -/// Stdlib-wide options that can be overridden by the root file. +/// Compile-time known settings overridable by the root source file. pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options else .{}; +/// Minimal set of `options` moved here to avoid dependency loop compilation +/// errors. +pub const io_options: IoOptions = if (@hasDecl(root, "std_io_options")) root.std_io_options else .{}; pub const Options = struct { enable_segfault_handler: bool = debug.default_enable_segfault_handler, @@ -174,8 +177,22 @@ 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, + /// The `Io` instance that `std.debug` uses for `std.debug.print`, + /// capturing stack traces, loading debug info, finding the executable's + /// own path, and environment variables that affect terminal mode + /// detection. The default is to use statically initialized singleton that + /// is independent from the application's `Io` instance in order to make + /// debugging more straightforward. For example, while debugging an `Io` + /// implementation based on coroutines, one likely wants `std.debug.print` + /// to directly write to stderr without trying to interact with the code + /// being debugged. + debug_io: Io = io_options.debug_threaded_io.?.ioBasic(), +}; + +pub const IoOptions = struct { /// Overrides `std.Io.File.Permissions`. FilePermissions: ?type = null, + debug_threaded_io: ?*Io.Threaded = Io.Threaded.global_single_threaded, }; // This forces the start.zig file to be imported, and the comptime logic inside that -- cgit v1.2.3 From 78c4fcfcd89e80ef70cf4cbb13f5100c84432496 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 22 Dec 2025 15:13:48 -0800 Subject: std.debug.lockStderr: cancel protection rather than recancel because we need to return the value --- lib/std/debug.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 25a0bc120d..88af16666f 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -281,8 +281,10 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { /// application's chosen `Io` implementation. pub fn lockStderr(buffer: []u8) Io.LockedStderr { const io = std.options.debug_io; + const prev = io.swapCancelProtection(.blocked); + defer _ = io.swapCancelProtection(prev); return io.lockStderr(buffer, null) catch |err| switch (err) { - error.Canceled => io.recancel(), + error.Canceled => unreachable, // Cancel protection enabled above. }; } -- cgit v1.2.3 From 0992f1204e6e1ebf4437d98647f4336aa1c5c95e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 22 Dec 2025 15:14:59 -0800 Subject: std.debug: delete nosuspend blocks now that the application can choose an Io implementation these might actually suspend. --- lib/std/debug.zig | 70 ++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 37 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 88af16666f..44a0a489da 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -305,12 +305,10 @@ pub fn unlockStderr() void { /// Alternatively, use the higher-level `std.log` or `Io.lockStderr` to /// integrate with the application's chosen `Io` implementation. pub fn print(comptime fmt: []const u8, args: anytype) void { - nosuspend { - var buffer: [64]u8 = undefined; - const stderr = lockStderr(&buffer); - defer unlockStderr(); - stderr.file_writer.interface.print(fmt, args) catch return; - } + var buffer: [64]u8 = undefined; + const stderr = lockStderr(&buffer); + defer unlockStderr(); + stderr.file_writer.interface.print(fmt, args) catch return; } /// Marked `inline` to propagate a comptime-known error to callers. @@ -1150,40 +1148,38 @@ fn printLineInfo( symbol_name: []const u8, compile_unit_name: []const u8, ) Writer.Error!void { - nosuspend { - const writer = t.writer; - t.setColor(.bold) catch {}; + const writer = t.writer; + t.setColor(.bold) catch {}; - if (source_location) |*sl| { - try writer.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column }); - } else { - try writer.writeAll("???:?:?"); - } + if (source_location) |*sl| { + try writer.print("{s}:{d}:{d}", .{ sl.file_name, sl.line, sl.column }); + } else { + try writer.writeAll("???:?:?"); + } - t.setColor(.reset) catch {}; - try writer.writeAll(": "); - t.setColor(.dim) catch {}; - try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); - t.setColor(.reset) catch {}; - try writer.writeAll("\n"); - - // Show the matching source code line if possible - if (source_location) |sl| { - if (printLineFromFile(io, writer, sl)) { - if (sl.column > 0) { - // The caret already takes one char - const space_needed = @as(usize, @intCast(sl.column - 1)); - - try writer.splatByteAll(' ', space_needed); - t.setColor(.green) catch {}; - try writer.writeAll("^"); - t.setColor(.reset) catch {}; - } - try writer.writeAll("\n"); - } else |_| { - // Ignore all errors; it's a better UX to just print the source location without the - // corresponding line number. The user can always open the source file themselves. + t.setColor(.reset) catch {}; + try writer.writeAll(": "); + t.setColor(.dim) catch {}; + try writer.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); + t.setColor(.reset) catch {}; + try writer.writeAll("\n"); + + // Show the matching source code line if possible + if (source_location) |sl| { + if (printLineFromFile(io, writer, sl)) { + if (sl.column > 0) { + // The caret already takes one char + const space_needed = @as(usize, @intCast(sl.column - 1)); + + try writer.splatByteAll(' ', space_needed); + t.setColor(.green) catch {}; + try writer.writeAll("^"); + t.setColor(.reset) catch {}; } + try writer.writeAll("\n"); + } else |_| { + // Ignore all errors; it's a better UX to just print the source location without the + // corresponding line number. The user can always open the source file themselves. } } } -- cgit v1.2.3 From 7c1236e267e536379f8b91148117fb0b8e965334 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 22 Dec 2025 16:10:43 -0800 Subject: std: different way of doing some options to avoid dependency loops --- lib/std/Io/File.zig | 2 +- lib/std/Thread.zig | 2 +- lib/std/debug.zig | 10 +++++----- lib/std/dynamic_library.zig | 2 +- lib/std/start.zig | 4 ++-- lib/std/std.zig | 14 +++++++------- 6 files changed, 17 insertions(+), 17 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index 30cda7a74c..a972ce2fd3 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -389,7 +389,7 @@ pub fn setOwner(file: File, io: Io, owner: ?Uid, group: ?Gid) SetOwnerError!void /// Cross-platform representation of permissions on a file. /// /// On POSIX systems this corresponds to "mode" and on Windows this corresponds to "attributes". -pub const Permissions = if (is_windows) enum(std.os.windows.DWORD) { +pub const Permissions = std.Options.FilePermissions orelse if (is_windows) enum(std.os.windows.DWORD) { default_file = 0, _, diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 1900225099..312cc25a99 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -322,7 +322,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co var buf: [32]u8 = undefined; const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); - const io = std.options.debug_io; + const io = std.Options.debug_io; const file = try Io.Dir.cwd().openFile(io, path, .{}); defer file.close(io); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 44a0a489da..d000bda62e 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -280,7 +280,7 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { /// Alternatively, use the higher-level `Io.lockStderr` to integrate with the /// application's chosen `Io` implementation. pub fn lockStderr(buffer: []u8) Io.LockedStderr { - const io = std.options.debug_io; + const io = std.Options.debug_io; const prev = io.swapCancelProtection(.blocked); defer _ = io.swapCancelProtection(prev); return io.lockStderr(buffer, null) catch |err| switch (err) { @@ -289,7 +289,7 @@ pub fn lockStderr(buffer: []u8) Io.LockedStderr { } pub fn unlockStderr() void { - const io = std.options.debug_io; + const io = std.Options.debug_io; io.unlockStderr(); } @@ -623,7 +623,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; - const io = std.options.debug_io; + const io = std.Options.debug_io; var total_frames: usize = 0; var index: usize = 0; @@ -685,7 +685,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin var total_frames: usize = 0; var wait_for = options.first_address; var printed_any_frame = false; - const io = std.options.debug_io; + const io = std.Options.debug_io; while (true) switch (it.next(io)) { .switch_to_fp => |unwind_error| { switch (StackIterator.fp_usability) { @@ -793,7 +793,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void return; }, }; - const io = std.options.debug_io; + const io = std.Options.debug_io; const captured_frames = @min(n_frames, st.instruction_addresses.len); for (st.instruction_addresses[0..captured_frames]) |ret_addr| { // `ret_addr` is the return address, which is *after* the function call. diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index b4acc8ae70..ccd60ecd97 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -222,7 +222,7 @@ pub const ElfDynLib = struct { /// Trusts the file. Malicious file will be able to execute arbitrary code. pub fn open(path: []const u8) Error!ElfDynLib { - const io = std.options.debug_io; + const io = std.Options.debug_io; const fd = try resolveFromName(io, path); defer posix.close(fd); diff --git a/lib/std/start.zig b/lib/std/start.zig index 5a3e39e24a..c6a9f06724 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -669,7 +669,7 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; - if (std.io_options.debug_threaded_io) |t| { + if (std.Options.debug_threaded_io) |t| { if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; t.environ = .{ .block = envp }; } @@ -698,7 +698,7 @@ fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; if (@sizeOf(std.Io.Threaded.Argv0) != 0) { - if (std.io_options.debug_threaded_io) |t| t.argv0.value = std.os.argv[0]; + if (std.Options.debug_threaded_io) |t| t.argv0.value = std.os.argv[0]; } return callMain(); diff --git a/lib/std/std.zig b/lib/std/std.zig index dccaffb75f..93d83b4807 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -110,9 +110,6 @@ const root = @import("root"); /// Compile-time known settings overridable by the root source file. pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options else .{}; -/// Minimal set of `options` moved here to avoid dependency loop compilation -/// errors. -pub const io_options: IoOptions = if (@hasDecl(root, "std_io_options")) root.std_io_options else .{}; pub const Options = struct { enable_segfault_handler: bool = debug.default_enable_segfault_handler, @@ -177,6 +174,10 @@ 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, + pub const debug_threaded_io: ?*Io.Threaded = if (@hasDecl(root, "std_options_debug_threaded_io")) + root.std_options_debug_threaded_io + else + Io.Threaded.global_single_threaded; /// The `Io` instance that `std.debug` uses for `std.debug.print`, /// capturing stack traces, loading debug info, finding the executable's /// own path, and environment variables that affect terminal mode @@ -186,11 +187,10 @@ pub const Options = struct { /// implementation based on coroutines, one likely wants `std.debug.print` /// to directly write to stderr without trying to interact with the code /// being debugged. - debug_io: Io = io_options.debug_threaded_io.?.ioBasic(), -}; + pub const debug_io: Io = if (@hasDecl(root, "std_options_debug_io")) root.std_options_debug_io else debug_threaded_io.?.ioBasic(); -pub const IoOptions = struct { - debug_threaded_io: ?*Io.Threaded = Io.Threaded.global_single_threaded, + /// Overrides `std.Io.File.Permissions`. + pub const FilePermissions: ?type = if (@hasDecl(root, "std_options_FilePermissions")) root.std_options_FilePermissions else null; }; // This forces the start.zig file to be imported, and the comptime logic inside that -- cgit v1.2.3