std: collect all options under one namespace

This commit is contained in:
Veikka Tuominen 2023-01-03 19:37:11 +02:00 committed by Andrew Kelley
parent fe2bd9dda8
commit f83834993e
17 changed files with 164 additions and 97 deletions

View file

@ -5,7 +5,6 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const root = @import("root");
const mem = std.mem; const mem = std.mem;
const os = std.os; const os = std.os;
@ -67,8 +66,8 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
// Allow applications to decide they would prefer to have every call to // Allow applications to decide they would prefer to have every call to
// std.crypto.random always make an OS syscall, rather than rely on an // std.crypto.random always make an OS syscall, rather than rely on an
// application implementation of a CSPRNG. // application implementation of a CSPRNG.
if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) { if (std.options.crypto_always_getrandom) {
return fillWithOsEntropy(buffer); return defaultRandomSeed(buffer);
} }
if (wipe_mem.len == 0) { if (wipe_mem.len == 0) {
@ -86,7 +85,7 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
) catch { ) catch {
// Could not allocate memory for the local state, fall back to // Could not allocate memory for the local state, fall back to
// the OS syscall. // the OS syscall.
return fillWithOsEntropy(buffer); return std.options.cryptoRandomSeed(buffer);
}; };
// The memory is already zero-initialized. // The memory is already zero-initialized.
} else { } else {
@ -128,14 +127,14 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
// Since we failed to set up fork safety, we fall back to always // Since we failed to set up fork safety, we fall back to always
// calling getrandom every time. // calling getrandom every time.
ctx.init_state = .failed; ctx.init_state = .failed;
return fillWithOsEntropy(buffer); return std.options.cryptoRandomSeed(buffer);
}, },
.initialized => { .initialized => {
return fillWithCsprng(buffer); return fillWithCsprng(buffer);
}, },
.failed => { .failed => {
if (want_fork_safety) { if (want_fork_safety) {
return fillWithOsEntropy(buffer); return std.options.cryptoRandomSeed(buffer);
} else { } else {
unreachable; unreachable;
} }
@ -165,7 +164,7 @@ fn fillWithCsprng(buffer: []u8) void {
mem.set(u8, ctx.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0); mem.set(u8, ctx.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
} }
fn fillWithOsEntropy(buffer: []u8) void { pub fn defaultRandomSeed(buffer: []u8) void {
os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy"); os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
} }
@ -174,12 +173,8 @@ fn initAndFill(buffer: []u8) void {
// Because we panic on getrandom() failing, we provide the opportunity // Because we panic on getrandom() failing, we provide the opportunity
// to override the default seed function. This also makes // to override the default seed function. This also makes
// `std.crypto.random` available on freestanding targets, provided that // `std.crypto.random` available on freestanding targets, provided that
// the `cryptoRandomSeed` function is provided. // the `std.options.cryptoRandomSeed` function is provided.
if (@hasDecl(root, "cryptoRandomSeed")) { std.options.cryptoRandomSeed(&seed);
root.cryptoRandomSeed(&seed);
} else {
fillWithOsEntropy(&seed);
}
const ctx = @ptrCast(*Context, wipe_mem.ptr); const ctx = @ptrCast(*Context, wipe_mem.ptr);
ctx.gimli = std.crypto.core.Gimli.init(seed); ctx.gimli = std.crypto.core.Gimli.init(seed);

View file

@ -1861,10 +1861,9 @@ pub const have_segfault_handling_support = switch (native_os) {
.freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"), .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),
else => false, else => false,
}; };
pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
root.enable_segfault_handler const enable_segfault_handler = std.options.enable_segfault_handler;
else pub const default_enable_segfault_handler = runtime_safety and have_segfault_handling_support;
runtime_safety and have_segfault_handling_support;
pub fn maybeEnableSegfaultHandler() void { pub fn maybeEnableSegfaultHandler() void {
if (enable_segfault_handler) { if (enable_segfault_handler) {

View file

@ -17,7 +17,7 @@ pub fn Batch(
comptime async_behavior: enum { comptime async_behavior: enum {
/// Observe the value of `std.io.is_async` to decide whether `add` /// Observe the value of `std.io.is_async` to decide whether `add`
/// and `wait` will be async functions. Asserts that the jobs do not suspend when /// and `wait` will be async functions. Asserts that the jobs do not suspend when
/// `std.io.mode == .blocking`. This is a generally safe assumption, and the /// `std.options.io_mode == .blocking`. This is a generally safe assumption, and the
/// usual recommended option for this parameter. /// usual recommended option for this parameter.
auto_async, auto_async,

View file

@ -1,6 +1,5 @@
const std = @import("../std.zig"); const std = @import("../std.zig");
const builtin = @import("builtin"); const builtin = @import("builtin");
const root = @import("root");
const assert = std.debug.assert; const assert = std.debug.assert;
const testing = std.testing; const testing = std.testing;
const mem = std.mem; const mem = std.mem;
@ -104,25 +103,29 @@ pub const Loop = struct {
}; };
}; };
const LoopOrVoid = switch (std.io.mode) { pub const Instance = switch (std.options.io_mode) {
.blocking => void, .blocking => @TypeOf(null),
.evented => Loop, .evented => ?*Loop,
}; };
pub const instance = std.options.event_loop;
var global_instance_state: LoopOrVoid = undefined; var global_instance_state: Loop = undefined;
const default_instance: ?*LoopOrVoid = switch (std.io.mode) { pub const default_instance = switch (std.options.io_mode) {
.blocking => null, .blocking => null,
.evented => &global_instance_state, .evented => &global_instance_state,
}; };
pub const instance: ?*LoopOrVoid = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;
pub const Mode = enum {
single_threaded,
multi_threaded,
};
pub const default_mode = .multi_threaded;
/// TODO copy elision / named return values so that the threads referencing *Loop /// TODO copy elision / named return values so that the threads referencing *Loop
/// have the correct pointer value. /// have the correct pointer value.
/// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
pub fn init(self: *Loop) !void { pub fn init(self: *Loop) !void {
if (builtin.single_threaded or if (builtin.single_threaded or std.options.event_loop_mode == .single_threaded) {
(@hasDecl(root, "event_loop_mode") and root.event_loop_mode == .single_threaded))
{
return self.initSingleThreaded(); return self.initSingleThreaded();
} else { } else {
return self.initMultiThreaded(); return self.initMultiThreaded();

View file

@ -2661,17 +2661,17 @@ pub fn cwd() Dir {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
} else if (builtin.os.tag == .wasi) { } else if (builtin.os.tag == .wasi) {
if (@hasDecl(root, "wasi_cwd")) { return std.options.wasiCwd();
return root.wasi_cwd();
} else {
// Expect the first preopen to be current working directory.
return .{ .fd = 3 };
}
} else { } else {
return Dir{ .fd = os.AT.FDCWD }; return Dir{ .fd = os.AT.FDCWD };
} }
} }
pub fn defaultWasiCwd() Dir {
// Expect the first preopen to be current working directory.
return .{ .fd = 3 };
}
/// Opens a directory at the given path. The directory is a system resource that remains /// Opens a directory at the given path. The directory is a system resource that remains
/// open until `close` is called on the result. /// open until `close` is called on the result.
/// See `openDirAbsoluteZ` for a function that accepts a null-terminated path. /// See `openDirAbsoluteZ` for a function that accepts a null-terminated path.

View file

@ -21,7 +21,7 @@ pub const File = struct {
/// blocking. /// blocking.
capable_io_mode: io.ModeOverride = io.default_mode, capable_io_mode: io.ModeOverride = io.default_mode,
/// Furthermore, even when `std.io.mode` is async, it is still sometimes desirable /// Furthermore, even when `std.options.io_mode` is async, it is still sometimes desirable
/// to perform blocking I/O, although not by default. For example, when printing a /// to perform blocking I/O, although not by default. For example, when printing a
/// stack trace to stderr. This field tracks both by acting as an overriding I/O mode. /// stack trace to stderr. This field tracks both by acting as an overriding I/O mode.
/// When not building in async I/O mode, the type only has the `.blocking` tag, making /// When not building in async I/O mode, the type only has the `.blocking` tag, making

View file

@ -19,14 +19,7 @@ pub const Mode = enum {
evented, evented,
}; };
/// The application's chosen I/O mode. This defaults to `Mode.blocking` but can be overridden const mode = std.options.io_mode;
/// by `root.event_loop`.
pub const mode: Mode = if (@hasDecl(root, "io_mode"))
root.io_mode
else if (@hasDecl(root, "event_loop"))
Mode.evented
else
Mode.blocking;
pub const is_async = mode != .blocking; pub const is_async = mode != .blocking;
/// This is an enum value to use for I/O mode at runtime, since it takes up zero bytes at runtime, /// This is an enum value to use for I/O mode at runtime, since it takes up zero bytes at runtime,

View file

@ -1,6 +1,6 @@
//! std.log is a standardized interface for logging which allows for the logging //! std.log is a standardized interface for logging which allows for the logging
//! of programs and libraries using this interface to be formatted and filtered //! of programs and libraries using this interface to be formatted and filtered
//! by the implementer of the root.log function. //! by the implementer of the `std.options.logFn` function.
//! //!
//! Each log message has an associated scope enum, which can be used to give //! Each log message has an associated scope enum, which can be used to give
//! context to the logging. The logging functions in std.log implicitly use a //! context to the logging. The logging functions in std.log implicitly use a
@ -13,16 +13,20 @@
//! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its //! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its
//! log messages. //! log messages.
//! //!
//! An example root.log might look something like this: //! An example `logFn` might look something like this:
//! //!
//! ``` //! ```
//! const std = @import("std"); //! const std = @import("std");
//! //!
//! // Set the log level to info //! pub const std_options = struct {
//! pub const log_level: std.log.Level = .info; //! // Set the log level to info
//! pub const log_level = .info;
//! //!
//! // Define root.log to override the std implementation //! // Define logFn to override the std implementation
//! pub fn log( //! pub const logFn = myLogFn;
//! };
//!
//! pub fn myLogFn(
//! comptime level: std.log.Level, //! comptime level: std.log.Level,
//! comptime scope: @TypeOf(.EnumLiteral), //! comptime scope: @TypeOf(.EnumLiteral),
//! comptime format: []const u8, //! comptime format: []const u8,
@ -70,7 +74,6 @@
const std = @import("std.zig"); const std = @import("std.zig");
const builtin = @import("builtin"); const builtin = @import("builtin");
const root = @import("root");
pub const Level = enum { pub const Level = enum {
/// Error: something has gone wrong. This might be recoverable or might /// Error: something has gone wrong. This might be recoverable or might
@ -102,22 +105,14 @@ pub const default_level: Level = switch (builtin.mode) {
.ReleaseFast, .ReleaseSmall => .err, .ReleaseFast, .ReleaseSmall => .err,
}; };
/// The current log level. This is set to root.log_level if present, otherwise const level = std.options.log_level;
/// log.default_level.
pub const level: Level = if (@hasDecl(root, "log_level"))
root.log_level
else
default_level;
pub const ScopeLevel = struct { pub const ScopeLevel = struct {
scope: @Type(.EnumLiteral), scope: @Type(.EnumLiteral),
level: Level, level: Level,
}; };
const scope_levels = if (@hasDecl(root, "scope_levels")) const scope_levels = std.options.log_scope_levels;
root.scope_levels
else
[0]ScopeLevel{};
fn log( fn log(
comptime message_level: Level, comptime message_level: Level,
@ -127,13 +122,7 @@ fn log(
) void { ) void {
if (comptime !logEnabled(message_level, scope)) return; if (comptime !logEnabled(message_level, scope)) return;
if (@hasDecl(root, "log")) { std.options.logFn(message_level, scope, format, args);
if (@typeInfo(@TypeOf(root.log)) != .Fn)
@compileError("Expected root.log to be a function");
root.log(message_level, scope, format, args);
} else {
defaultLog(message_level, scope, format, args);
}
} }
/// Determine if a specific log message level and scope combination are enabled for logging. /// Determine if a specific log message level and scope combination are enabled for logging.
@ -149,8 +138,8 @@ pub fn defaultLogEnabled(comptime message_level: Level) bool {
return comptime logEnabled(message_level, default_log_scope); return comptime logEnabled(message_level, default_log_scope);
} }
/// The default implementation for root.log. root.log may forward log messages /// The default implementation for the log function, custom log functions may
/// to this function. /// forward log messages to this function.
pub fn defaultLog( pub fn defaultLog(
comptime message_level: Level, comptime message_level: Level,
comptime scope: @Type(.EnumLiteral), comptime scope: @Type(.EnumLiteral),

View file

@ -527,7 +527,7 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret
// and we want fewer call frames in stack traces. // and we want fewer call frames in stack traces.
inline fn initEventLoopAndCallMain() u8 { inline fn initEventLoopAndCallMain() u8 {
if (std.event.Loop.instance) |loop| { if (std.event.Loop.instance) |loop| {
if (!@hasDecl(root, "event_loop")) { if (loop == std.event.Loop.default_instance) {
loop.init() catch |err| { loop.init() catch |err| {
std.log.err("{s}", .{@errorName(err)}); std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {
@ -556,7 +556,7 @@ inline fn initEventLoopAndCallMain() u8 {
// because it is working around stage1 compiler bugs. // because it is working around stage1 compiler bugs.
inline fn initEventLoopAndCallWinMain() std.os.windows.INT { inline fn initEventLoopAndCallWinMain() std.os.windows.INT {
if (std.event.Loop.instance) |loop| { if (std.event.Loop.instance) |loop| {
if (!@hasDecl(root, "event_loop")) { if (loop == std.event.Loop.default_instance) {
loop.init() catch |err| { loop.init() catch |err| {
std.log.err("{s}", .{@errorName(err)}); std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {

View file

@ -94,10 +94,79 @@ pub const wasm = @import("wasm.zig");
pub const zig = @import("zig.zig"); pub const zig = @import("zig.zig");
pub const start = @import("start.zig"); pub const start = @import("start.zig");
const root = @import("root");
const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
pub const options = struct {
pub const enable_segfault_handler: bool = if (@hasDecl(options_override, "enable_segfault_handler"))
options_override.enable_segfault_handler
else
debug.default_enable_segfault_handler;
/// Function used to implement std.fs.cwd for wasi.
pub const wasiCwd: fn () fs.Dir = if (@hasDecl(options_override, "wasiCwd"))
options_override.wasiCwd
else
fs.defaultWasiCwd;
/// The application's chosen I/O mode.
pub const io_mode: io.Mode = if (@hasDecl(options_override, "io_mode"))
options_override.io_mode
else if (@hasDecl(options_override, "event_loop"))
.evented
else
.blocking;
pub const event_loop: event.Loop.Instance = if (@hasDecl(options_override, "event_loop"))
options_override.event_loop
else
event.Loop.default_instance;
pub const event_loop_mode: event.Loop.Mode = if (@hasDecl(options_override, "event_loop_mode"))
options_override.event_loop_mode
else
event.Loop.default_mode;
/// The current log level.
pub const log_level: log.Level = if (@hasDecl(options_override, "log_level"))
options_override.log_level
else
log.default_level;
pub const log_scope_levels: []const log.ScopeLevel = if (@hasDecl(options_override, "log_scope_levels"))
options_override.log_scope_levels
else
&.{};
pub const logFn: fn (
comptime message_level: log.Level,
comptime scope: @TypeOf(.enum_literal),
comptime format: []const u8,
args: anytype,
) void = if (@hasDecl(options_override, "logFn"))
options_override.logFn
else
log.defaultLog;
pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed"))
options_override.cryptoRandomSeed
else
@import("crypto/tlcsprng.zig").defaultRandomSeed;
pub const crypto_always_getrandom: bool = if (@hasDecl(options_override, "crypto_always_getrandom"))
options_override.crypto_always_getrandom
else
false;
};
// This forces the start.zig file to be imported, and the comptime logic inside that // This forces the start.zig file to be imported, and the comptime logic inside that
// file decides whether to export any appropriate start symbols, and call main. // file decides whether to export any appropriate start symbols, and call main.
comptime { comptime {
_ = start; _ = start;
for (@typeInfo(options_override).Struct.decls) |decl| {
if (!@hasDecl(options, decl.name)) @compileError("no option named " ++ decl.name);
}
} }
test { test {

View file

@ -2,7 +2,10 @@ const std = @import("std");
const io = std.io; const io = std.io;
const builtin = @import("builtin"); const builtin = @import("builtin");
pub const io_mode: io.Mode = builtin.test_io_mode; pub const std_options = struct {
pub const io_mode: io.Mode = builtin.test_io_mode;
pub const logFn = log;
};
var log_err_count: usize = 0; var log_err_count: usize = 0;
@ -45,7 +48,7 @@ pub fn main() void {
if (!have_tty) { if (!have_tty) {
std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name }); std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name });
} }
const result = if (test_fn.async_frame_size) |size| switch (io_mode) { const result = if (test_fn.async_frame_size) |size| switch (std.options.io_mode) {
.evented => blk: { .evented => blk: {
if (async_frame_buffer.len < size) { if (async_frame_buffer.len < size) {
std.heap.page_allocator.free(async_frame_buffer); std.heap.page_allocator.free(async_frame_buffer);

View file

@ -13,12 +13,10 @@ const Decl = Module.Decl;
pub const is_enabled = builtin.mode == .Debug; pub const is_enabled = builtin.mode == .Debug;
/// To use these crash report diagnostics, publish these symbols in your main file. /// To use these crash report diagnostics, publish this panic in your main file
/// and add `pub const enable_segfault_handler = false;` to your `std_options`.
/// You will also need to call initialize() on startup, preferably as the very first operation in your program. /// You will also need to call initialize() on startup, preferably as the very first operation in your program.
pub const root_decls = struct { pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic;
pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic;
pub const enable_segfault_handler = false;
};
/// Install signal handlers to identify crashes and report diagnostics. /// Install signal handlers to identify crashes and report diagnostics.
pub fn initialize() void { pub fn initialize() void {

View file

@ -25,11 +25,23 @@ const target_util = @import("target.zig");
const ThreadPool = @import("ThreadPool.zig"); const ThreadPool = @import("ThreadPool.zig");
const crash_report = @import("crash_report.zig"); const crash_report = @import("crash_report.zig");
// Crash report needs to override the panic handler and other root decls pub const std_options = struct {
pub usingnamespace crash_report.root_decls; pub const wasiCwd = wasi_cwd;
pub const logFn = log;
pub const enable_segfault_handler = false;
pub const log_level: std.log.Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe, .ReleaseFast => .info,
.ReleaseSmall => .err,
};
};
// Crash report needs to override the panic handler
pub const panic = crash_report.panic;
var wasi_preopens: fs.wasi.Preopens = undefined; var wasi_preopens: fs.wasi.Preopens = undefined;
pub inline fn wasi_cwd() fs.Dir { pub fn wasi_cwd() fs.Dir {
// Expect the first preopen to be current working directory. // Expect the first preopen to be current working directory.
const cwd_fd: std.os.fd_t = 3; const cwd_fd: std.os.fd_t = 3;
assert(mem.eql(u8, wasi_preopens.names[cwd_fd], ".")); assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
@ -111,12 +123,6 @@ const debug_usage = normal_usage ++
const usage = if (debug_extensions_enabled) debug_usage else normal_usage; const usage = if (debug_extensions_enabled) debug_usage else normal_usage;
pub const log_level: std.log.Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe, .ReleaseFast => .info,
.ReleaseSmall => .err,
};
var log_scopes: std.ArrayListUnmanaged([]const u8) = .{}; var log_scopes: std.ArrayListUnmanaged([]const u8) = .{};
pub fn log( pub fn log(
@ -128,7 +134,7 @@ pub fn log(
// Hide debug messages unless: // Hide debug messages unless:
// * logging enabled with `-Dlog`. // * logging enabled with `-Dlog`.
// * the --debug-log arg for the scope has been provided // * the --debug-log arg for the scope has been provided
if (@enumToInt(level) > @enumToInt(std.log.level) or if (@enumToInt(level) > @enumToInt(std.options.log_level) or
@enumToInt(level) > @enumToInt(std.log.Level.info)) @enumToInt(level) > @enumToInt(std.log.Level.info))
{ {
if (!build_options.enable_logging) return; if (!build_options.enable_logging) return;

View file

@ -509,8 +509,8 @@ test "ptrCast comptime known slice to C pointer" {
test "ptrToInt on a generic function" { test "ptrToInt on a generic function" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag != .linux) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag != .linux) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
const S = struct { const S = struct {
fn generic(i: anytype) @TypeOf(i) { fn generic(i: anytype) @TypeOf(i) {

View file

@ -440,11 +440,14 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
cases.add("std.log per scope log level override", cases.add("std.log per scope log level override",
\\const std = @import("std"); \\const std = @import("std");
\\ \\
\\pub const log_level: std.log.Level = .debug; \\pub const std_options = struct {
\\ \\ pub const log_level: std.log.Level = .debug;
\\pub const scope_levels = [_]std.log.ScopeLevel{ \\
\\ .{ .scope = .a, .level = .warn }, \\ pub const log_scope_levels = &[_]std.log.ScopeLevel{
\\ .{ .scope = .c, .level = .err }, \\ .{ .scope = .a, .level = .warn },
\\ .{ .scope = .c, .level = .err },
\\ };
\\ pub const logFn = log;
\\}; \\};
\\ \\
\\const loga = std.log.scoped(.a); \\const loga = std.log.scoped(.a);
@ -494,7 +497,10 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
cases.add("std.heap.LoggingAllocator logs to std.log", cases.add("std.heap.LoggingAllocator logs to std.log",
\\const std = @import("std"); \\const std = @import("std");
\\ \\
\\pub const log_level: std.log.Level = .debug; \\pub const std_options = struct {
\\ pub const log_level: std.log.Level = .debug;
\\ pub const logFn = log;
\\};
\\ \\
\\pub fn main() !void { \\pub fn main() !void {
\\ var allocator_buf: [10]u8 = undefined; \\ var allocator_buf: [10]u8 = undefined;

View file

@ -1,5 +1,9 @@
const std = @import("std"); const std = @import("std");
pub const std_options = struct {
pub const logFn = log;
};
pub fn log( pub fn log(
comptime message_level: std.log.Level, comptime message_level: std.log.Level,
comptime scope: @Type(.EnumLiteral), comptime scope: @Type(.EnumLiteral),

View file

@ -1,2 +1,4 @@
pub const io_mode = .evented; pub const std_options = struct {
pub const io_mode = .evented;
};
pub fn main() void {} pub fn main() void {}