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

View File

@ -17,7 +17,7 @@ pub fn Batch(
comptime async_behavior: enum {
/// 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
/// `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.
auto_async,

View File

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

View File

@ -2661,17 +2661,17 @@ pub fn cwd() Dir {
if (builtin.os.tag == .windows) {
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
} else if (builtin.os.tag == .wasi) {
if (@hasDecl(root, "wasi_cwd")) {
return root.wasi_cwd();
} else {
// Expect the first preopen to be current working directory.
return .{ .fd = 3 };
}
return std.options.wasiCwd();
} else {
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
/// open until `close` is called on the result.
/// See `openDirAbsoluteZ` for a function that accepts a null-terminated path.

View File

@ -21,7 +21,7 @@ pub const File = struct {
/// blocking.
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
/// 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

View File

@ -19,14 +19,7 @@ pub const Mode = enum {
evented,
};
/// The application's chosen I/O mode. This defaults to `Mode.blocking` but can be overridden
/// 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;
const mode = std.options.io_mode;
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,

View File

@ -1,6 +1,6 @@
//! 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
//! 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
//! 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
//! log messages.
//!
//! An example root.log might look something like this:
//! An example `logFn` might look something like this:
//!
//! ```
//! const std = @import("std");
//!
//! // Set the log level to info
//! pub const log_level: std.log.Level = .info;
//! pub const std_options = struct {
//! // Set the log level to info
//! pub const log_level = .info;
//!
//! // Define root.log to override the std implementation
//! pub fn log(
//! // Define logFn to override the std implementation
//! pub const logFn = myLogFn;
//! };
//!
//! pub fn myLogFn(
//! comptime level: std.log.Level,
//! comptime scope: @TypeOf(.EnumLiteral),
//! comptime format: []const u8,
@ -70,7 +74,6 @@
const std = @import("std.zig");
const builtin = @import("builtin");
const root = @import("root");
pub const Level = enum {
/// 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,
};
/// The current log level. This is set to root.log_level if present, otherwise
/// log.default_level.
pub const level: Level = if (@hasDecl(root, "log_level"))
root.log_level
else
default_level;
const level = std.options.log_level;
pub const ScopeLevel = struct {
scope: @Type(.EnumLiteral),
level: Level,
};
const scope_levels = if (@hasDecl(root, "scope_levels"))
root.scope_levels
else
[0]ScopeLevel{};
const scope_levels = std.options.log_scope_levels;
fn log(
comptime message_level: Level,
@ -127,13 +122,7 @@ fn log(
) void {
if (comptime !logEnabled(message_level, scope)) return;
if (@hasDecl(root, "log")) {
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);
}
std.options.logFn(message_level, scope, format, args);
}
/// 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);
}
/// The default implementation for root.log. root.log may forward log messages
/// to this function.
/// The default implementation for the log function, custom log functions may
/// forward log messages to this function.
pub fn defaultLog(
comptime message_level: Level,
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.
inline fn initEventLoopAndCallMain() u8 {
if (std.event.Loop.instance) |loop| {
if (!@hasDecl(root, "event_loop")) {
if (loop == std.event.Loop.default_instance) {
loop.init() catch |err| {
std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
@ -556,7 +556,7 @@ inline fn initEventLoopAndCallMain() u8 {
// because it is working around stage1 compiler bugs.
inline fn initEventLoopAndCallWinMain() std.os.windows.INT {
if (std.event.Loop.instance) |loop| {
if (!@hasDecl(root, "event_loop")) {
if (loop == std.event.Loop.default_instance) {
loop.init() catch |err| {
std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {

View File

@ -94,10 +94,79 @@ pub const wasm = @import("wasm.zig");
pub const zig = @import("zig.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
// file decides whether to export any appropriate start symbols, and call main.
comptime {
_ = start;
for (@typeInfo(options_override).Struct.decls) |decl| {
if (!@hasDecl(options, decl.name)) @compileError("no option named " ++ decl.name);
}
}
test {

View File

@ -2,7 +2,10 @@ const std = @import("std");
const io = std.io;
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;
@ -45,7 +48,7 @@ pub fn main() void {
if (!have_tty) {
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: {
if (async_frame_buffer.len < size) {
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;
/// 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.
pub const root_decls = struct {
pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic;
pub const enable_segfault_handler = false;
};
pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic;
/// Install signal handlers to identify crashes and report diagnostics.
pub fn initialize() void {

View File

@ -25,11 +25,23 @@ const target_util = @import("target.zig");
const ThreadPool = @import("ThreadPool.zig");
const crash_report = @import("crash_report.zig");
// Crash report needs to override the panic handler and other root decls
pub usingnamespace crash_report.root_decls;
pub const std_options = struct {
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;
pub inline fn wasi_cwd() fs.Dir {
pub fn wasi_cwd() fs.Dir {
// Expect the first preopen to be current working directory.
const cwd_fd: std.os.fd_t = 3;
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;
pub const log_level: std.log.Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe, .ReleaseFast => .info,
.ReleaseSmall => .err,
};
var log_scopes: std.ArrayListUnmanaged([]const u8) = .{};
pub fn log(
@ -128,7 +134,7 @@ pub fn log(
// Hide debug messages unless:
// * logging enabled with `-Dlog`.
// * 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))
{
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" {
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_aarch64 and builtin.os.tag != .linux) 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_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
const S = struct {
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",
\\const std = @import("std");
\\
\\pub const log_level: std.log.Level = .debug;
\\
\\pub const scope_levels = [_]std.log.ScopeLevel{
\\ .{ .scope = .a, .level = .warn },
\\ .{ .scope = .c, .level = .err },
\\pub const std_options = struct {
\\ pub const log_level: std.log.Level = .debug;
\\
\\ pub const log_scope_levels = &[_]std.log.ScopeLevel{
\\ .{ .scope = .a, .level = .warn },
\\ .{ .scope = .c, .level = .err },
\\ };
\\ pub const logFn = log;
\\};
\\
\\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",
\\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 {
\\ var allocator_buf: [10]u8 = undefined;

View File

@ -1,5 +1,9 @@
const std = @import("std");
pub const std_options = struct {
pub const logFn = log;
};
pub fn log(
comptime message_level: std.log.Level,
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 {}