authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-01-03 19:37:11+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-05 02:31:29-07:00
logf83834993e2628e347da71a11ffb07c804fc46c5
tree21fd10b3ba19c4236326549872a202a4ffe29b3f
parentfe2bd9dda8467b775da4fe3bd535aece9e07ee1b

std: collect all options under one namespace


17 files changed, 164 insertions(+), 97 deletions(-)

lib/std/crypto/tlcsprng.zig+8-13
......@@ -5,7 +5,6 @@
55
66const std = @import("std");
77const builtin = @import("builtin");
8const root = @import("root");
98const mem = std.mem;
109const os = std.os;
1110
......@@ -67,8 +66,8 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
6766 // Allow applications to decide they would prefer to have every call to
6867 // std.crypto.random always make an OS syscall, rather than rely on an
6968 // application implementation of a CSPRNG.
70 if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) {
71 return fillWithOsEntropy(buffer);
69 if (std.options.crypto_always_getrandom) {
70 return defaultRandomSeed(buffer);
7271 }
7372
7473 if (wipe_mem.len == 0) {
......@@ -86,7 +85,7 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
8685 ) catch {
8786 // Could not allocate memory for the local state, fall back to
8887 // the OS syscall.
89 return fillWithOsEntropy(buffer);
88 return std.options.cryptoRandomSeed(buffer);
9089 };
9190 // The memory is already zero-initialized.
9291 } else {
......@@ -128,14 +127,14 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
128127 // Since we failed to set up fork safety, we fall back to always
129128 // calling getrandom every time.
130129 ctx.init_state = .failed;
131 return fillWithOsEntropy(buffer);
130 return std.options.cryptoRandomSeed(buffer);
132131 },
133132 .initialized => {
134133 return fillWithCsprng(buffer);
135134 },
136135 .failed => {
137136 if (want_fork_safety) {
138 return fillWithOsEntropy(buffer);
137 return std.options.cryptoRandomSeed(buffer);
139138 } else {
140139 unreachable;
141140 }
......@@ -165,7 +164,7 @@ fn fillWithCsprng(buffer: []u8) void {
165164 mem.set(u8, ctx.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
166165}
167166
168fn fillWithOsEntropy(buffer: []u8) void {
167pub fn defaultRandomSeed(buffer: []u8) void {
169168 os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
170169}
171170
......@@ -174,12 +173,8 @@ fn initAndFill(buffer: []u8) void {
174173 // Because we panic on getrandom() failing, we provide the opportunity
175174 // to override the default seed function. This also makes
176175 // `std.crypto.random` available on freestanding targets, provided that
177 // the `cryptoRandomSeed` function is provided.
178 if (@hasDecl(root, "cryptoRandomSeed")) {
179 root.cryptoRandomSeed(&seed);
180 } else {
181 fillWithOsEntropy(&seed);
182 }
176 // the `std.options.cryptoRandomSeed` function is provided.
177 std.options.cryptoRandomSeed(&seed);
183178
184179 const ctx = @ptrCast(*Context, wipe_mem.ptr);
185180 ctx.gimli = std.crypto.core.Gimli.init(seed);
lib/std/debug.zig+3-4
......@@ -1861,10 +1861,9 @@ pub const have_segfault_handling_support = switch (native_os) {
18611861 .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),
18621862 else => false,
18631863};
1864pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
1865 root.enable_segfault_handler
1866else
1867 runtime_safety and have_segfault_handling_support;
1864
1865const enable_segfault_handler = std.options.enable_segfault_handler;
1866pub const default_enable_segfault_handler = runtime_safety and have_segfault_handling_support;
18681867
18691868pub fn maybeEnableSegfaultHandler() void {
18701869 if (enable_segfault_handler) {
lib/std/event/batch.zig+1-1
......@@ -17,7 +17,7 @@ pub fn Batch(
1717 comptime async_behavior: enum {
1818 /// Observe the value of `std.io.is_async` to decide whether `add`
1919 /// and `wait` will be async functions. Asserts that the jobs do not suspend when
20 /// `std.io.mode == .blocking`. This is a generally safe assumption, and the
20 /// `std.options.io_mode == .blocking`. This is a generally safe assumption, and the
2121 /// usual recommended option for this parameter.
2222 auto_async,
2323
lib/std/event/loop.zig+13-10
......@@ -1,6 +1,5 @@
11const std = @import("../std.zig");
22const builtin = @import("builtin");
3const root = @import("root");
43const assert = std.debug.assert;
54const testing = std.testing;
65const mem = std.mem;
......@@ -104,25 +103,29 @@ pub const Loop = struct {
104103 };
105104 };
106105
107 const LoopOrVoid = switch (std.io.mode) {
108 .blocking => void,
109 .evented => Loop,
106 pub const Instance = switch (std.options.io_mode) {
107 .blocking => @TypeOf(null),
108 .evented => ?*Loop,
110109 };
110 pub const instance = std.options.event_loop;
111111
112 var global_instance_state: LoopOrVoid = undefined;
113 const default_instance: ?*LoopOrVoid = switch (std.io.mode) {
112 var global_instance_state: Loop = undefined;
113 pub const default_instance = switch (std.options.io_mode) {
114114 .blocking => null,
115115 .evented => &global_instance_state,
116116 };
117 pub const instance: ?*LoopOrVoid = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;
117
118 pub const Mode = enum {
119 single_threaded,
120 multi_threaded,
121 };
122 pub const default_mode = .multi_threaded;
118123
119124 /// TODO copy elision / named return values so that the threads referencing *Loop
120125 /// have the correct pointer value.
121126 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
122127 pub fn init(self: *Loop) !void {
123 if (builtin.single_threaded or
124 (@hasDecl(root, "event_loop_mode") and root.event_loop_mode == .single_threaded))
125 {
128 if (builtin.single_threaded or std.options.event_loop_mode == .single_threaded) {
126129 return self.initSingleThreaded();
127130 } else {
128131 return self.initMultiThreaded();
lib/std/fs.zig+6-6
......@@ -2661,17 +2661,17 @@ pub fn cwd() Dir {
26612661 if (builtin.os.tag == .windows) {
26622662 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
26632663 } else if (builtin.os.tag == .wasi) {
2664 if (@hasDecl(root, "wasi_cwd")) {
2665 return root.wasi_cwd();
2666 } else {
2667 // Expect the first preopen to be current working directory.
2668 return .{ .fd = 3 };
2669 }
2664 return std.options.wasiCwd();
26702665 } else {
26712666 return Dir{ .fd = os.AT.FDCWD };
26722667 }
26732668}
26742669
2670pub fn defaultWasiCwd() Dir {
2671 // Expect the first preopen to be current working directory.
2672 return .{ .fd = 3 };
2673}
2674
26752675/// Opens a directory at the given path. The directory is a system resource that remains
26762676/// open until `close` is called on the result.
26772677/// See `openDirAbsoluteZ` for a function that accepts a null-terminated path.
lib/std/fs/file.zig+1-1
......@@ -21,7 +21,7 @@ pub const File = struct {
2121 /// blocking.
2222 capable_io_mode: io.ModeOverride = io.default_mode,
2323
24 /// Furthermore, even when `std.io.mode` is async, it is still sometimes desirable
24 /// Furthermore, even when `std.options.io_mode` is async, it is still sometimes desirable
2525 /// to perform blocking I/O, although not by default. For example, when printing a
2626 /// stack trace to stderr. This field tracks both by acting as an overriding I/O mode.
2727 /// When not building in async I/O mode, the type only has the `.blocking` tag, making
lib/std/io.zig+1-8
......@@ -19,14 +19,7 @@ pub const Mode = enum {
1919 evented,
2020};
2121
22/// The application's chosen I/O mode. This defaults to `Mode.blocking` but can be overridden
23/// by `root.event_loop`.
24pub const mode: Mode = if (@hasDecl(root, "io_mode"))
25 root.io_mode
26else if (@hasDecl(root, "event_loop"))
27 Mode.evented
28else
29 Mode.blocking;
22const mode = std.options.io_mode;
3023pub const is_async = mode != .blocking;
3124
3225/// This is an enum value to use for I/O mode at runtime, since it takes up zero bytes at runtime,
lib/std/log.zig+15-26
......@@ -1,6 +1,6 @@
11//! std.log is a standardized interface for logging which allows for the logging
22//! of programs and libraries using this interface to be formatted and filtered
3//! by the implementer of the root.log function.
3//! by the implementer of the `std.options.logFn` function.
44//!
55//! Each log message has an associated scope enum, which can be used to give
66//! context to the logging. The logging functions in std.log implicitly use a
......@@ -13,16 +13,20 @@
1313//! `const log = std.log.scoped(.libfoo);` to use .libfoo as the scope of its
1414//! log messages.
1515//!
16//! An example root.log might look something like this:
16//! An example `logFn` might look something like this:
1717//!
1818//! ```
1919//! const std = @import("std");
2020//!
21//! // Set the log level to info
22//! pub const log_level: std.log.Level = .info;
21//! pub const std_options = struct {
22//! // Set the log level to info
23//! pub const log_level = .info;
2324//!
24//! // Define root.log to override the std implementation
25//! pub fn log(
25//! // Define logFn to override the std implementation
26//! pub const logFn = myLogFn;
27//! };
28//!
29//! pub fn myLogFn(
2630//! comptime level: std.log.Level,
2731//! comptime scope: @TypeOf(.EnumLiteral),
2832//! comptime format: []const u8,
......@@ -70,7 +74,6 @@
7074
7175const std = @import("std.zig");
7276const builtin = @import("builtin");
73const root = @import("root");
7477
7578pub const Level = enum {
7679 /// Error: something has gone wrong. This might be recoverable or might
......@@ -102,22 +105,14 @@ pub const default_level: Level = switch (builtin.mode) {
102105 .ReleaseFast, .ReleaseSmall => .err,
103106};
104107
105/// The current log level. This is set to root.log_level if present, otherwise
106/// log.default_level.
107pub const level: Level = if (@hasDecl(root, "log_level"))
108 root.log_level
109else
110 default_level;
108const level = std.options.log_level;
111109
112110pub const ScopeLevel = struct {
113111 scope: @Type(.EnumLiteral),
114112 level: Level,
115113};
116114
117const scope_levels = if (@hasDecl(root, "scope_levels"))
118 root.scope_levels
119else
120 [0]ScopeLevel{};
115const scope_levels = std.options.log_scope_levels;
121116
122117fn log(
123118 comptime message_level: Level,
......@@ -127,13 +122,7 @@ fn log(
127122) void {
128123 if (comptime !logEnabled(message_level, scope)) return;
129124
130 if (@hasDecl(root, "log")) {
131 if (@typeInfo(@TypeOf(root.log)) != .Fn)
132 @compileError("Expected root.log to be a function");
133 root.log(message_level, scope, format, args);
134 } else {
135 defaultLog(message_level, scope, format, args);
136 }
125 std.options.logFn(message_level, scope, format, args);
137126}
138127
139128/// 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 {
149138 return comptime logEnabled(message_level, default_log_scope);
150139}
151140
152/// The default implementation for root.log. root.log may forward log messages
153/// to this function.
141/// The default implementation for the log function, custom log functions may
142/// forward log messages to this function.
154143pub fn defaultLog(
155144 comptime message_level: Level,
156145 comptime scope: @Type(.EnumLiteral),
lib/std/start.zig+2-2
......@@ -527,7 +527,7 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret
527527// and we want fewer call frames in stack traces.
528528inline fn initEventLoopAndCallMain() u8 {
529529 if (std.event.Loop.instance) |loop| {
530 if (!@hasDecl(root, "event_loop")) {
530 if (loop == std.event.Loop.default_instance) {
531531 loop.init() catch |err| {
532532 std.log.err("{s}", .{@errorName(err)});
533533 if (@errorReturnTrace()) |trace| {
......@@ -556,7 +556,7 @@ inline fn initEventLoopAndCallMain() u8 {
556556// because it is working around stage1 compiler bugs.
557557inline fn initEventLoopAndCallWinMain() std.os.windows.INT {
558558 if (std.event.Loop.instance) |loop| {
559 if (!@hasDecl(root, "event_loop")) {
559 if (loop == std.event.Loop.default_instance) {
560560 loop.init() catch |err| {
561561 std.log.err("{s}", .{@errorName(err)});
562562 if (@errorReturnTrace()) |trace| {
lib/std/std.zig+69
......@@ -94,10 +94,79 @@ pub const wasm = @import("wasm.zig");
9494pub const zig = @import("zig.zig");
9595pub const start = @import("start.zig");
9696
97const root = @import("root");
98const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
99
100pub const options = struct {
101 pub const enable_segfault_handler: bool = if (@hasDecl(options_override, "enable_segfault_handler"))
102 options_override.enable_segfault_handler
103 else
104 debug.default_enable_segfault_handler;
105
106 /// Function used to implement std.fs.cwd for wasi.
107 pub const wasiCwd: fn () fs.Dir = if (@hasDecl(options_override, "wasiCwd"))
108 options_override.wasiCwd
109 else
110 fs.defaultWasiCwd;
111
112 /// The application's chosen I/O mode.
113 pub const io_mode: io.Mode = if (@hasDecl(options_override, "io_mode"))
114 options_override.io_mode
115 else if (@hasDecl(options_override, "event_loop"))
116 .evented
117 else
118 .blocking;
119
120 pub const event_loop: event.Loop.Instance = if (@hasDecl(options_override, "event_loop"))
121 options_override.event_loop
122 else
123 event.Loop.default_instance;
124
125 pub const event_loop_mode: event.Loop.Mode = if (@hasDecl(options_override, "event_loop_mode"))
126 options_override.event_loop_mode
127 else
128 event.Loop.default_mode;
129
130 /// The current log level.
131 pub const log_level: log.Level = if (@hasDecl(options_override, "log_level"))
132 options_override.log_level
133 else
134 log.default_level;
135
136 pub const log_scope_levels: []const log.ScopeLevel = if (@hasDecl(options_override, "log_scope_levels"))
137 options_override.log_scope_levels
138 else
139 &.{};
140
141 pub const logFn: fn (
142 comptime message_level: log.Level,
143 comptime scope: @TypeOf(.enum_literal),
144 comptime format: []const u8,
145 args: anytype,
146 ) void = if (@hasDecl(options_override, "logFn"))
147 options_override.logFn
148 else
149 log.defaultLog;
150
151 pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed"))
152 options_override.cryptoRandomSeed
153 else
154 @import("crypto/tlcsprng.zig").defaultRandomSeed;
155
156 pub const crypto_always_getrandom: bool = if (@hasDecl(options_override, "crypto_always_getrandom"))
157 options_override.crypto_always_getrandom
158 else
159 false;
160};
161
97162// This forces the start.zig file to be imported, and the comptime logic inside that
98163// file decides whether to export any appropriate start symbols, and call main.
99164comptime {
100165 _ = start;
166
167 for (@typeInfo(options_override).Struct.decls) |decl| {
168 if (!@hasDecl(options, decl.name)) @compileError("no option named " ++ decl.name);
169 }
101170}
102171
103172test {
lib/test_runner.zig+5-2
......@@ -2,7 +2,10 @@ const std = @import("std");
22const io = std.io;
33const builtin = @import("builtin");
44
5pub const io_mode: io.Mode = builtin.test_io_mode;
5pub const std_options = struct {
6 pub const io_mode: io.Mode = builtin.test_io_mode;
7 pub const logFn = log;
8};
69
710var log_err_count: usize = 0;
811
......@@ -45,7 +48,7 @@ pub fn main() void {
4548 if (!have_tty) {
4649 std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name });
4750 }
48 const result = if (test_fn.async_frame_size) |size| switch (io_mode) {
51 const result = if (test_fn.async_frame_size) |size| switch (std.options.io_mode) {
4952 .evented => blk: {
5053 if (async_frame_buffer.len < size) {
5154 std.heap.page_allocator.free(async_frame_buffer);
src/crash_report.zig+3-5
......@@ -13,12 +13,10 @@ const Decl = Module.Decl;
1313
1414pub const is_enabled = builtin.mode == .Debug;
1515
16/// To use these crash report diagnostics, publish these symbols in your main file.
16/// To use these crash report diagnostics, publish this panic in your main file
17/// and add `pub const enable_segfault_handler = false;` to your `std_options`.
1718/// You will also need to call initialize() on startup, preferably as the very first operation in your program.
18pub const root_decls = struct {
19 pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic;
20 pub const enable_segfault_handler = false;
21};
19pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic;
2220
2321/// Install signal handlers to identify crashes and report diagnostics.
2422pub fn initialize() void {
src/main.zig+16-10
......@@ -25,11 +25,23 @@ const target_util = @import("target.zig");
2525const ThreadPool = @import("ThreadPool.zig");
2626const crash_report = @import("crash_report.zig");
2727
28// Crash report needs to override the panic handler and other root decls
29pub usingnamespace crash_report.root_decls;
28pub const std_options = struct {
29 pub const wasiCwd = wasi_cwd;
30 pub const logFn = log;
31 pub const enable_segfault_handler = false;
32
33 pub const log_level: std.log.Level = switch (builtin.mode) {
34 .Debug => .debug,
35 .ReleaseSafe, .ReleaseFast => .info,
36 .ReleaseSmall => .err,
37 };
38};
39
40// Crash report needs to override the panic handler
41pub const panic = crash_report.panic;
3042
3143var wasi_preopens: fs.wasi.Preopens = undefined;
32pub inline fn wasi_cwd() fs.Dir {
44pub fn wasi_cwd() fs.Dir {
3345 // Expect the first preopen to be current working directory.
3446 const cwd_fd: std.os.fd_t = 3;
3547 assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
......@@ -111,12 +123,6 @@ const debug_usage = normal_usage ++
111123
112124const usage = if (debug_extensions_enabled) debug_usage else normal_usage;
113125
114pub const log_level: std.log.Level = switch (builtin.mode) {
115 .Debug => .debug,
116 .ReleaseSafe, .ReleaseFast => .info,
117 .ReleaseSmall => .err,
118};
119
120126var log_scopes: std.ArrayListUnmanaged([]const u8) = .{};
121127
122128pub fn log(
......@@ -128,7 +134,7 @@ pub fn log(
128134 // Hide debug messages unless:
129135 // * logging enabled with `-Dlog`.
130136 // * the --debug-log arg for the scope has been provided
131 if (@enumToInt(level) > @enumToInt(std.log.level) or
137 if (@enumToInt(level) > @enumToInt(std.options.log_level) or
132138 @enumToInt(level) > @enumToInt(std.log.Level.info))
133139 {
134140 if (!build_options.enable_logging) return;
test/behavior/pointers.zig+2-2
......@@ -509,8 +509,8 @@ test "ptrCast comptime known slice to C pointer" {
509509test "ptrToInt on a generic function" {
510510 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
511511 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
512 if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag != .linux) return error.SkipZigTest; // TODO
513 if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag != .linux) return error.SkipZigTest; // TODO
512 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
513 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
514514
515515 const S = struct {
516516 fn generic(i: anytype) @TypeOf(i) {
test/compare_output.zig+12-6
......@@ -440,11 +440,14 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
440440 cases.add("std.log per scope log level override",
441441 \\const std = @import("std");
442442 \\
443 \\pub const log_level: std.log.Level = .debug;
444 \\
445 \\pub const scope_levels = [_]std.log.ScopeLevel{
446 \\ .{ .scope = .a, .level = .warn },
447 \\ .{ .scope = .c, .level = .err },
443 \\pub const std_options = struct {
444 \\ pub const log_level: std.log.Level = .debug;
445 \\
446 \\ pub const log_scope_levels = &[_]std.log.ScopeLevel{
447 \\ .{ .scope = .a, .level = .warn },
448 \\ .{ .scope = .c, .level = .err },
449 \\ };
450 \\ pub const logFn = log;
448451 \\};
449452 \\
450453 \\const loga = std.log.scoped(.a);
......@@ -494,7 +497,10 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
494497 cases.add("std.heap.LoggingAllocator logs to std.log",
495498 \\const std = @import("std");
496499 \\
497 \\pub const log_level: std.log.Level = .debug;
500 \\pub const std_options = struct {
501 \\ pub const log_level: std.log.Level = .debug;
502 \\ pub const logFn = log;
503 \\};
498504 \\
499505 \\pub fn main() !void {
500506 \\ var allocator_buf: [10]u8 = undefined;
test/standalone/issue_7030/main.zig+4
......@@ -1,5 +1,9 @@
11const std = @import("std");
22
3pub const std_options = struct {
4 pub const logFn = log;
5};
6
37pub fn log(
48 comptime message_level: std.log.Level,
59 comptime scope: @Type(.EnumLiteral),
test/standalone/issue_9693/main.zig+3-1
......@@ -1,2 +1,4 @@
1pub const io_mode = .evented;
1pub const std_options = struct {
2 pub const io_mode = .evented;
3};
24pub fn main() void {}