| author | |
| committer | |
| log | 3c2f5adf41f0e75fd5e8f6661891dd7d4fa770a9 |
| tree | 5ae7765b93dfde9f5638b83ba2436fbbdd0da24d |
| parent | 86e9e32cf0d5a028d6ebb32f8d0f3d0a23e717b6 |
* 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 #305629 files changed, 111 insertions(+), 62 deletions(-)
lib/compiler/build_runner.zig+4-1| ... | @@ -429,8 +429,11 @@ pub fn main() !void { | ... | @@ -429,8 +429,11 @@ pub fn main() !void { |
| 429 | } | 429 | } |
| 430 | } | 430 | } |
| 431 | 431 | ||
| 432 | const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(); | ||
| 433 | const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(); | ||
| 434 | |||
| 432 | graph.stderr_mode = switch (color) { | 435 | graph.stderr_mode = switch (color) { |
| 433 | .auto => try .detect(io, .stderr()), | 436 | .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE), |
| 434 | .on => .escape_codes, | 437 | .on => .escape_codes, |
| 435 | .off => .no_color, | 438 | .off => .no_color, |
| 436 | }; | 439 | }; |
lib/std/Io/File.zig+1-1| ... | @@ -391,7 +391,7 @@ pub fn setOwner(file: File, io: Io, owner: ?Uid, group: ?Gid) SetOwnerError!void | ... | @@ -391,7 +391,7 @@ pub fn setOwner(file: File, io: Io, owner: ?Uid, group: ?Gid) SetOwnerError!void |
| 391 | /// On POSIX systems this corresponds to "mode" and on Windows this corresponds to "attributes". | 391 | /// On POSIX systems this corresponds to "mode" and on Windows this corresponds to "attributes". |
| 392 | /// | 392 | /// |
| 393 | /// Overridable via `std.options`. | 393 | /// Overridable via `std.options`. |
| 394 | pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(std.os.windows.DWORD) { | 394 | pub const Permissions = std.io_options.FilePermissions orelse if (is_windows) enum(std.os.windows.DWORD) { |
| 395 | default_file = 0, | 395 | default_file = 0, |
| 396 | _, | 396 | _, |
| 397 | 397 |
lib/std/Io/Terminal.zig+10-4| ... | @@ -48,7 +48,15 @@ pub const Mode = union(enum) { | ... | @@ -48,7 +48,15 @@ pub const Mode = union(enum) { |
| 48 | /// stdout/stderr). | 48 | /// stdout/stderr). |
| 49 | /// | 49 | /// |
| 50 | /// Will attempt to enable ANSI escape code support if necessary/possible. | 50 | /// Will attempt to enable ANSI escape code support if necessary/possible. |
| 51 | pub fn detect(io: Io, file: File) Io.Cancelable!Mode { | 51 | /// |
| 52 | /// * `NO_COLOR` indicates whether "NO_COLOR" environment variable is | ||
| 53 | /// present and non-empty. | ||
| 54 | /// * `CLICOLOR_FORCE` indicates whether "CLICOLOR_FORCE" environment | ||
| 55 | /// variable is present and non-empty. | ||
| 56 | pub fn detect(io: Io, file: File, NO_COLOR: bool, CLICOLOR_FORCE: bool) Io.Cancelable!Mode { | ||
| 57 | const force_color: ?bool = if (NO_COLOR) false else if (CLICOLOR_FORCE) true else null; | ||
| 58 | if (force_color == false) return .no_color; | ||
| 59 | |||
| 52 | if (file.enableAnsiEscapeCodes(io)) |_| { | 60 | if (file.enableAnsiEscapeCodes(io)) |_| { |
| 53 | return .escape_codes; | 61 | return .escape_codes; |
| 54 | } else |err| switch (err) { | 62 | } else |err| switch (err) { |
| ... | @@ -65,10 +73,8 @@ pub const Mode = union(enum) { | ... | @@ -65,10 +73,8 @@ pub const Mode = union(enum) { |
| 65 | .reset_attributes = info.wAttributes, | 73 | .reset_attributes = info.wAttributes, |
| 66 | } }; | 74 | } }; |
| 67 | } | 75 | } |
| 68 | return .escape_codes; | ||
| 69 | } | 76 | } |
| 70 | 77 | return if (force_color == true) .escape_codes else .no_color; | |
| 71 | return .no_color; | ||
| 72 | } | 78 | } |
| 73 | }; | 79 | }; |
| 74 | 80 |
lib/std/Io/Threaded.zig+58-41| ... | @@ -60,30 +60,34 @@ stderr_writer: File.Writer = .{ | ... | @@ -60,30 +60,34 @@ stderr_writer: File.Writer = .{ |
| 60 | stderr_mode: Io.Terminal.Mode = .no_color, | 60 | stderr_mode: Io.Terminal.Mode = .no_color, |
| 61 | stderr_writer_initialized: bool = false, | 61 | stderr_writer_initialized: bool = false, |
| 62 | 62 | ||
| 63 | argv0: Argv0, | ||
| 63 | environ: Environ, | 64 | environ: Environ, |
| 64 | args: Args, | ||
| 65 | 65 | ||
| 66 | pub const Environ = switch (native_os) { | 66 | pub const Argv0 = switch (native_os) { |
| 67 | .openbsd, .haiku => struct { | 67 | .openbsd, .haiku => struct { |
| 68 | PATH: ?[]const u8, | 68 | value: ?[*:0]const u8 = null, |
| 69 | |||
| 70 | pub const empty: @This() = .{ | ||
| 71 | .PATH = null, | ||
| 72 | }; | ||
| 73 | }, | ||
| 74 | else => struct { | ||
| 75 | pub const empty: @This() = .{}; | ||
| 76 | }, | 69 | }, |
| 70 | else => struct {}, | ||
| 77 | }; | 71 | }; |
| 78 | 72 | ||
| 79 | pub const Args = switch (native_os) { | 73 | pub const Environ = struct { |
| 80 | .openbsd, .haiku => struct { | 74 | /// Unmodified data directly from the OS. |
| 81 | list: []const []const u8, | 75 | block: Block = &.{}, |
| 82 | pub const empty: @This() = .{ .list = &.{} }; | 76 | /// Protected by `mutex`. Determines whether the other fields have been |
| 83 | }, | 77 | /// memoized based on `block`. |
| 84 | else => struct { | 78 | initialized: bool = false, |
| 85 | pub const empty: @This() = .{}; | 79 | /// Protected by `mutex`. Memoized based on `block`. Tracks whether the |
| 86 | }, | 80 | /// environment variables are present and non-empty. |
| 81 | exist: struct { | ||
| 82 | NO_COLOR: bool = false, | ||
| 83 | CLICOLOR_FORCE: bool = false, | ||
| 84 | } = .{}, | ||
| 85 | /// Protected by `mutex`. Memoized based on `block`. | ||
| 86 | string: struct { | ||
| 87 | PATH: ?[:0]const u8 = null, | ||
| 88 | } = .{}, | ||
| 89 | |||
| 90 | pub const Block = []const [*:0]const u8; | ||
| 87 | }; | 91 | }; |
| 88 | 92 | ||
| 89 | pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { | 93 | pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { |
| ... | @@ -591,18 +595,11 @@ pub const InitOptions = struct { | ... | @@ -591,18 +595,11 @@ pub const InitOptions = struct { |
| 591 | robust_cancel: RobustCancel = .disabled, | 595 | robust_cancel: RobustCancel = .disabled, |
| 592 | /// Affects the following operations: | 596 | /// Affects the following operations: |
| 593 | /// * `processExecutablePath` on OpenBSD and Haiku. | 597 | /// * `processExecutablePath` on OpenBSD and Haiku. |
| 594 | /// | 598 | argv0: Argv0 = .{}, |
| 595 | /// The default value causes this to be a compile error on systems that need to | ||
| 596 | /// initialize this field. `Environ.empty` can be used to omit this field on | ||
| 597 | /// all targets. | ||
| 598 | environ: Environ = .{}, | ||
| 599 | /// Affects the following operations: | 599 | /// Affects the following operations: |
| 600 | /// * `processExecutablePath` on OpenBSD and Haiku. | 600 | /// * `fileIsTty` |
| 601 | /// | 601 | /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH"). |
| 602 | /// The default value causes this to be a compile error on systems that need to | 602 | environ: Environ = .{}, |
| 603 | /// initialize this field. `Args.empty` can be used to omit this field on all | ||
| 604 | /// targets. | ||
| 605 | args: Args = .{}, | ||
| 606 | }; | 603 | }; |
| 607 | 604 | ||
| 608 | /// Related: | 605 | /// Related: |
| ... | @@ -636,8 +633,8 @@ pub fn init( | ... | @@ -636,8 +633,8 @@ pub fn init( |
| 636 | .current_closure = null, | 633 | .current_closure = null, |
| 637 | .cancel_protection = undefined, | 634 | .cancel_protection = undefined, |
| 638 | }, | 635 | }, |
| 636 | .argv0 = options.argv0, | ||
| 639 | .environ = options.environ, | 637 | .environ = options.environ, |
| 640 | .args = options.args, | ||
| 641 | .robust_cancel = options.robust_cancel, | 638 | .robust_cancel = options.robust_cancel, |
| 642 | }; | 639 | }; |
| 643 | 640 | ||
| ... | @@ -678,8 +675,8 @@ pub const init_single_threaded: Threaded = .{ | ... | @@ -678,8 +675,8 @@ pub const init_single_threaded: Threaded = .{ |
| 678 | .cancel_protection = undefined, | 675 | .cancel_protection = undefined, |
| 679 | }, | 676 | }, |
| 680 | .robust_cancel = .disabled, | 677 | .robust_cancel = .disabled, |
| 681 | .environ = .empty, | 678 | .argv0 = .{}, |
| 682 | .args = .empty, | 679 | .environ = .{}, |
| 683 | }; | 680 | }; |
| 684 | 681 | ||
| 685 | var global_single_threaded_instance: Threaded = .init_single_threaded; | 682 | var global_single_threaded_instance: Threaded = .init_single_threaded; |
| ... | @@ -7242,17 +7239,16 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex | ... | @@ -7242,17 +7239,16 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex |
| 7242 | } | 7239 | } |
| 7243 | }, | 7240 | }, |
| 7244 | .openbsd, .haiku => { | 7241 | .openbsd, .haiku => { |
| 7245 | // The best we can do on these operating systems is check based on CLI args. | 7242 | // The best we can do on these operating systems is check based on |
| 7246 | const argv = t.args.list; | 7243 | // the first process argument. |
| 7247 | if (argv.len == 0) return error.OperationUnsupported; | 7244 | const argv0 = t.argv0.value orelse return error.OperationUnsupported; |
| 7248 | const argv0 = argv[0]; | ||
| 7249 | if (std.mem.findScalar(u8, argv0, '/') != null) { | 7245 | if (std.mem.findScalar(u8, argv0, '/') != null) { |
| 7250 | // argv[0] is a path (relative or absolute): use realpath(3) directly | 7246 | // argv[0] is a path (relative or absolute): use realpath(3) directly |
| 7251 | const current_thread = Thread.getCurrent(t); | 7247 | const current_thread = Thread.getCurrent(t); |
| 7252 | var resolved_buf: [std.c.PATH_MAX]u8 = undefined; | 7248 | var resolved_buf: [std.c.PATH_MAX]u8 = undefined; |
| 7253 | try current_thread.beginSyscall(); | 7249 | try current_thread.beginSyscall(); |
| 7254 | while (true) { | 7250 | while (true) { |
| 7255 | if (std.c.realpath(argv[0], &resolved_buf)) |p| { | 7251 | if (std.c.realpath(argv0, &resolved_buf)) |p| { |
| 7256 | assert(p == &resolved_buf); | 7252 | assert(p == &resolved_buf); |
| 7257 | break current_thread.endSyscall(); | 7253 | break current_thread.endSyscall(); |
| 7258 | } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) { | 7254 | } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) { |
| ... | @@ -7283,13 +7279,14 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex | ... | @@ -7283,13 +7279,14 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex |
| 7283 | return resolved.len; | 7279 | return resolved.len; |
| 7284 | } else if (argv0.len != 0) { | 7280 | } else if (argv0.len != 0) { |
| 7285 | // argv[0] is not empty (and not a path): search PATH | 7281 | // argv[0] is not empty (and not a path): search PATH |
| 7282 | t.scanEnviron(); | ||
| 7283 | const PATH = t.environ.string.PATH orelse return error.FileNotFound; | ||
| 7286 | const current_thread = Thread.getCurrent(t); | 7284 | const current_thread = Thread.getCurrent(t); |
| 7287 | const PATH = t.environ.PATH orelse return error.FileNotFound; | ||
| 7288 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); | 7285 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); |
| 7289 | it: while (it.next()) |dir| { | 7286 | it: while (it.next()) |dir| { |
| 7290 | var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined; | 7287 | var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined; |
| 7291 | const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{ | 7288 | const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{ |
| 7292 | dir, argv[0], | 7289 | dir, argv0, |
| 7293 | }, 0) catch continue; | 7290 | }, 0) catch continue; |
| 7294 | 7291 | ||
| 7295 | var resolved_buf: [std.c.PATH_MAX]u8 = undefined; | 7292 | var resolved_buf: [std.c.PATH_MAX]u8 = undefined; |
| ... | @@ -10752,7 +10749,10 @@ fn initLockedStderr( | ... | @@ -10752,7 +10749,10 @@ fn initLockedStderr( |
| 10752 | if (is_windows) t.stderr_writer.file = .stderr(); | 10749 | if (is_windows) t.stderr_writer.file = .stderr(); |
| 10753 | t.stderr_writer.io = io_t; | 10750 | t.stderr_writer.io = io_t; |
| 10754 | t.stderr_writer_initialized = true; | 10751 | t.stderr_writer_initialized = true; |
| 10755 | t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file); | 10752 | t.scanEnviron(); |
| 10753 | const NO_COLOR = t.environ.exist.NO_COLOR; | ||
| 10754 | const CLICOLOR_FORCE = t.environ.exist.CLICOLOR_FORCE; | ||
| 10755 | t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file, NO_COLOR, CLICOLOR_FORCE); | ||
| 10756 | } | 10756 | } |
| 10757 | std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch |err| switch (err) { | 10757 | std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch |err| switch (err) { |
| 10758 | error.WriteFailed => switch (t.stderr_writer.err.?) { | 10758 | error.WriteFailed => switch (t.stderr_writer.err.?) { |
| ... | @@ -10777,7 +10777,7 @@ fn unlockStderr(userdata: ?*anyopaque) void { | ... | @@ -10777,7 +10777,7 @@ fn unlockStderr(userdata: ?*anyopaque) void { |
| 10777 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 10777 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 10778 | t.stderr_writer.interface.flush() catch |err| switch (err) { | 10778 | t.stderr_writer.interface.flush() catch |err| switch (err) { |
| 10779 | error.WriteFailed => switch (t.stderr_writer.err.?) { | 10779 | error.WriteFailed => switch (t.stderr_writer.err.?) { |
| 10780 | error.Canceled => @panic("TODO make this uncancelable"), | 10780 | error.Canceled => recancel(t), |
| 10781 | else => {}, | 10781 | else => {}, |
| 10782 | }, | 10782 | }, |
| 10783 | }; | 10783 | }; |
| ... | @@ -11910,6 +11910,23 @@ const pthreads_futex = struct { | ... | @@ -11910,6 +11910,23 @@ const pthreads_futex = struct { |
| 11910 | } | 11910 | } |
| 11911 | }; | 11911 | }; |
| 11912 | 11912 | ||
| 11913 | fn scanEnviron(t: *Threaded) void { | ||
| 11914 | t.mutex.lock(); | ||
| 11915 | defer t.mutex.unlock(); | ||
| 11916 | |||
| 11917 | if (t.environ.initialized) return; | ||
| 11918 | t.environ.initialized = true; | ||
| 11919 | |||
| 11920 | if (native_os == .wasi) { | ||
| 11921 | @panic("TODO"); | ||
| 11922 | } | ||
| 11923 | |||
| 11924 | for (t.environ.block) |kv| { | ||
| 11925 | _ = kv; | ||
| 11926 | @panic("TODO"); | ||
| 11927 | } | ||
| 11928 | } | ||
| 11929 | |||
| 11913 | test { | 11930 | test { |
| 11914 | _ = @import("Threaded/test.zig"); | 11931 | _ = @import("Threaded/test.zig"); |
| 11915 | } | 11932 | } |
lib/std/Thread.zig+1-1| ... | @@ -322,7 +322,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co | ... | @@ -322,7 +322,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co |
| 322 | var buf: [32]u8 = undefined; | 322 | var buf: [32]u8 = undefined; |
| 323 | const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); | 323 | const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()}); |
| 324 | 324 | ||
| 325 | const io = Io.Threaded.global_single_threaded.ioBasic(); | 325 | const io = std.options.debug_io; |
| 326 | 326 | ||
| 327 | const file = try Io.Dir.cwd().openFile(io, path, .{}); | 327 | const file = try Io.Dir.cwd().openFile(io, path, .{}); |
| 328 | defer file.close(io); | 328 | defer file.close(io); |
lib/std/debug.zig+8-12| ... | @@ -261,10 +261,6 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { | ... | @@ -261,10 +261,6 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { |
| 261 | else => true, | 261 | else => true, |
| 262 | }; | 262 | }; |
| 263 | 263 | ||
| 264 | /// This is used for debug information and debug printing. It is intentionally | ||
| 265 | /// separate from the application's `Io` instance. | ||
| 266 | const static_single_threaded_io = Io.Threaded.global_single_threaded.ioBasic(); | ||
| 267 | |||
| 268 | /// Allows the caller to freely write to stderr until `unlockStderr` is called. | 264 | /// Allows the caller to freely write to stderr until `unlockStderr` is called. |
| 269 | /// | 265 | /// |
| 270 | /// During the lock, any `std.Progress` information is cleared from the terminal. | 266 | /// 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(); | ... | @@ -284,15 +280,15 @@ const static_single_threaded_io = Io.Threaded.global_single_threaded.ioBasic(); |
| 284 | /// Alternatively, use the higher-level `Io.lockStderr` to integrate with the | 280 | /// Alternatively, use the higher-level `Io.lockStderr` to integrate with the |
| 285 | /// application's chosen `Io` implementation. | 281 | /// application's chosen `Io` implementation. |
| 286 | pub fn lockStderr(buffer: []u8) Io.LockedStderr { | 282 | pub fn lockStderr(buffer: []u8) Io.LockedStderr { |
| 287 | return static_single_threaded_io.lockStderr(buffer, null) catch |err| switch (err) { | 283 | const io = std.options.debug_io; |
| 288 | // Impossible to cancel because no calls to cancel using | 284 | return io.lockStderr(buffer, null) catch |err| switch (err) { |
| 289 | // `static_single_threaded_io` exist. | 285 | error.Canceled => io.recancel(), |
| 290 | error.Canceled => unreachable, | ||
| 291 | }; | 286 | }; |
| 292 | } | 287 | } |
| 293 | 288 | ||
| 294 | pub fn unlockStderr() void { | 289 | pub fn unlockStderr() void { |
| 295 | static_single_threaded_io.unlockStderr(); | 290 | const io = std.options.debug_io; |
| 291 | io.unlockStderr(); | ||
| 296 | } | 292 | } |
| 297 | 293 | ||
| 298 | /// Writes to stderr, ignoring errors. | 294 | /// Writes to stderr, ignoring errors. |
| ... | @@ -627,7 +623,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: | ... | @@ -627,7 +623,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: |
| 627 | defer it.deinit(); | 623 | defer it.deinit(); |
| 628 | if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; | 624 | if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; |
| 629 | 625 | ||
| 630 | const io = static_single_threaded_io; | 626 | const io = std.options.debug_io; |
| 631 | 627 | ||
| 632 | var total_frames: usize = 0; | 628 | var total_frames: usize = 0; |
| 633 | var index: usize = 0; | 629 | var index: usize = 0; |
| ... | @@ -689,7 +685,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin | ... | @@ -689,7 +685,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin |
| 689 | var total_frames: usize = 0; | 685 | var total_frames: usize = 0; |
| 690 | var wait_for = options.first_address; | 686 | var wait_for = options.first_address; |
| 691 | var printed_any_frame = false; | 687 | var printed_any_frame = false; |
| 692 | const io = static_single_threaded_io; | 688 | const io = std.options.debug_io; |
| 693 | while (true) switch (it.next(io)) { | 689 | while (true) switch (it.next(io)) { |
| 694 | .switch_to_fp => |unwind_error| { | 690 | .switch_to_fp => |unwind_error| { |
| 695 | switch (StackIterator.fp_usability) { | 691 | switch (StackIterator.fp_usability) { |
| ... | @@ -797,7 +793,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void | ... | @@ -797,7 +793,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void |
| 797 | return; | 793 | return; |
| 798 | }, | 794 | }, |
| 799 | }; | 795 | }; |
| 800 | const io = static_single_threaded_io; | 796 | const io = std.options.debug_io; |
| 801 | const captured_frames = @min(n_frames, st.instruction_addresses.len); | 797 | const captured_frames = @min(n_frames, st.instruction_addresses.len); |
| 802 | for (st.instruction_addresses[0..captured_frames]) |ret_addr| { | 798 | for (st.instruction_addresses[0..captured_frames]) |ret_addr| { |
| 803 | // `ret_addr` is the return address, which is *after* the function call. | 799 | // `ret_addr` is the return address, which is *after* the function call. |
lib/std/dynamic_library.zig+1-1| ... | @@ -222,7 +222,7 @@ pub const ElfDynLib = struct { | ... | @@ -222,7 +222,7 @@ pub const ElfDynLib = struct { |
| 222 | 222 | ||
| 223 | /// Trusts the file. Malicious file will be able to execute arbitrary code. | 223 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 224 | pub fn open(path: []const u8) Error!ElfDynLib { | 224 | pub fn open(path: []const u8) Error!ElfDynLib { |
| 225 | const io = Io.Threaded.global_single_threaded.ioBasic(); | 225 | const io = std.options.debug_io; |
| 226 | 226 | ||
| 227 | const fd = try resolveFromName(io, path); | 227 | const fd = try resolveFromName(io, path); |
| 228 | defer posix.close(fd); | 228 | defer posix.close(fd); |
lib/std/start.zig+10| ... | @@ -669,6 +669,11 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { | ... | @@ -669,6 +669,11 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { |
| 669 | std.os.argv = argv[0..argc]; | 669 | std.os.argv = argv[0..argc]; |
| 670 | std.os.environ = envp; | 670 | std.os.environ = envp; |
| 671 | 671 | ||
| 672 | if (std.io_options.debug_threaded_io) |t| { | ||
| 673 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; | ||
| 674 | t.environ = .{ .block = envp }; | ||
| 675 | } | ||
| 676 | |||
| 672 | std.debug.maybeEnableSegfaultHandler(); | 677 | std.debug.maybeEnableSegfaultHandler(); |
| 673 | 678 | ||
| 674 | return callMain(); | 679 | return callMain(); |
| ... | @@ -691,6 +696,11 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal | ... | @@ -691,6 +696,11 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal |
| 691 | 696 | ||
| 692 | fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { | 697 | fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { |
| 693 | std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; | 698 | std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; |
| 699 | |||
| 700 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) { | ||
| 701 | if (std.io_options.debug_threaded_io) |t| t.argv0.value = std.os.argv[0]; | ||
| 702 | } | ||
| 703 | |||
| 694 | return callMain(); | 704 | return callMain(); |
| 695 | } | 705 | } |
| 696 | 706 |
lib/std/std.zig+18-1| ... | @@ -108,8 +108,11 @@ pub const start = @import("start.zig"); | ... | @@ -108,8 +108,11 @@ pub const start = @import("start.zig"); |
| 108 | 108 | ||
| 109 | const root = @import("root"); | 109 | const root = @import("root"); |
| 110 | 110 | ||
| 111 | /// Stdlib-wide options that can be overridden by the root file. | 111 | /// Compile-time known settings overridable by the root source file. |
| 112 | pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options else .{}; | 112 | pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options else .{}; |
| 113 | /// Minimal set of `options` moved here to avoid dependency loop compilation | ||
| 114 | /// errors. | ||
| 115 | pub const io_options: IoOptions = if (@hasDecl(root, "std_io_options")) root.std_io_options else .{}; | ||
| 113 | 116 | ||
| 114 | pub const Options = struct { | 117 | pub const Options = struct { |
| 115 | enable_segfault_handler: bool = debug.default_enable_segfault_handler, | 118 | enable_segfault_handler: bool = debug.default_enable_segfault_handler, |
| ... | @@ -174,8 +177,22 @@ pub const Options = struct { | ... | @@ -174,8 +177,22 @@ pub const Options = struct { |
| 174 | /// stack traces will just print an error to the relevant `Io.Writer` and return. | 177 | /// stack traces will just print an error to the relevant `Io.Writer` and return. |
| 175 | allow_stack_tracing: bool = !@import("builtin").strip_debug_info, | 178 | allow_stack_tracing: bool = !@import("builtin").strip_debug_info, |
| 176 | 179 | ||
| 180 | /// The `Io` instance that `std.debug` uses for `std.debug.print`, | ||
| 181 | /// capturing stack traces, loading debug info, finding the executable's | ||
| 182 | /// own path, and environment variables that affect terminal mode | ||
| 183 | /// detection. The default is to use statically initialized singleton that | ||
| 184 | /// is independent from the application's `Io` instance in order to make | ||
| 185 | /// debugging more straightforward. For example, while debugging an `Io` | ||
| 186 | /// implementation based on coroutines, one likely wants `std.debug.print` | ||
| 187 | /// to directly write to stderr without trying to interact with the code | ||
| 188 | /// being debugged. | ||
| 189 | debug_io: Io = io_options.debug_threaded_io.?.ioBasic(), | ||
| 190 | }; | ||
| 191 | |||
| 192 | pub const IoOptions = struct { | ||
| 177 | /// Overrides `std.Io.File.Permissions`. | 193 | /// Overrides `std.Io.File.Permissions`. |
| 178 | FilePermissions: ?type = null, | 194 | FilePermissions: ?type = null, |
| 195 | debug_threaded_io: ?*Io.Threaded = Io.Threaded.global_single_threaded, | ||
| 179 | }; | 196 | }; |
| 180 | 197 | ||
| 181 | // This forces the start.zig file to be imported, and the comptime logic inside that | 198 | // This forces the start.zig file to be imported, and the comptime logic inside that |