update main function to accept args and environ

Now `main` takes an `init: std.Init` parameter, and this is the only way
to access command line arguments and environment variables.

After a deprecation period, this will close #4524
This commit is contained in:
Andrew Kelley 2025-02-03 20:53:22 -08:00
parent 8a3aebaee0
commit 85e8cc8abc
3 changed files with 658 additions and 522 deletions

View File

@ -41,14 +41,14 @@ test {
_ = windows;
}
/// See also `getenv`. Populated by startup code before main().
/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
/// https://github.com/ziglang/zig/issues/4524
/// Deprecated in favor of accepting a `std.process.Init` argument to main.
/// To be removed after 0.14.0 is tagged.
/// See https://github.com/ziglang/zig/issues/4524 for more details.
pub var environ: [][*:0]u8 = undefined;
/// Populated by startup code before main().
/// Not available on WASI or Windows without libc. See `std.process.argsAlloc`
/// or `std.process.argsWithAllocator` for a cross-platform alternative.
/// Deprecated in favor of accepting a `std.process.Init` argument to main.
/// To be removed after 0.14.0 is tagged.
/// See https://github.com/ziglang/zig/issues/4524 for more details.
pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (native_os) {
.windows => @compileError("argv isn't supported on Windows: use std.process.argsAlloc instead"),
.wasi => @compileError("argv isn't supported on WASI: use std.process.argsAlloc instead"),

File diff suppressed because it is too large Load Diff

View File

@ -185,17 +185,13 @@ fn _DllMainCRTStartup(
}
fn wasm_freestanding_start() callconv(.c) void {
// This is marked inline because for some reason LLVM in
// release mode fails to inline it, and we want fewer call frames in stack traces.
_ = @call(.always_inline, callMain, .{});
_ = callMain(.void);
}
fn wasi_start() callconv(.c) void {
// The function call is marked inline because for some reason LLVM in
// release mode fails to inline it, and we want fewer call frames in stack traces.
switch (builtin.wasi_exec_model) {
.reactor => _ = @call(.always_inline, callMain, .{}),
.command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{})),
.reactor => _ = callMain(.void),
.command => std.os.wasi.proc_exit(callMain(.void)),
}
}
@ -472,7 +468,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
std.debug.maybeEnableSegfaultHandler();
std.os.windows.ntdll.RtlExitUserProcess(callMain());
std.os.windows.ntdll.RtlExitUserProcess(callMain(.void));
}
fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn {
@ -499,7 +495,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @as([*][*:0]u8, @ptrCast(envp_optional))[0..envp_count];
if (native_os == .linux) {
const aux: std.process.Init.Aux = if (native_os == .linux) aux: {
// Find the beginning of the auxiliary vector
const auxv: [*]elf.Auxv = @ptrCast(@alignCast(envp.ptr + envp_count + 1));
@ -566,9 +562,11 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
const slice = init_array_start[0 .. init_array_end - init_array_start];
for (slice) |func| func();
}
}
std.posix.exit(callMainWithArgs(argc, argv, envp));
break :aux .{ .data = auxv };
} else .{ .data = {} };
std.posix.exit(callMainWithArgs(argc, argv, envp, aux));
}
fn expandStackSize(phdrs: []elf.Phdr) void {
@ -606,14 +604,18 @@ fn expandStackSize(phdrs: []elf.Phdr) void {
}
}
inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
std.os.argv = argv[0..argc];
std.os.environ = envp;
inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8, aux: std.process.Init.Aux) u8 {
std.os.argv = argv[0..argc]; // To be removed after 0.15.0 is tagged.
std.os.environ = envp; // To be removed after 0.15.0 is tagged.
std.debug.maybeEnableSegfaultHandler();
maybeIgnoreSigpipe();
return callMain();
return callMain(.{
.args = .{ .data = if (builtin.os.tag == .windows) {} else argv[0..argc] },
.env = .{ .data = if (builtin.os.tag == .windows) {} else envp },
.aux = aux,
});
}
fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int {
@ -628,21 +630,28 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal
expandStackSize(phdrs);
}
return callMainWithArgs(@as(usize, @intCast(c_argc)), @as([*][*:0]u8, @ptrCast(c_argv)), envp);
return callMainWithArgs(@intCast(c_argc), @ptrCast(c_argv), envp, .{ .data = {} });
}
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..@as(usize, @intCast(c_argc))];
return callMain();
const args = @as([*][*:0]u8, @ptrCast(c_argv))[0..@as(usize, @intCast(c_argc))];
std.os.argv = args; // To be removed after 0.15.0 is tagged.
return callMain(.{
.args = .{ .data = args },
.env = .{ .data = {} },
.aux = .{ .data = {} },
});
}
// General error message for a malformed return type
const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'";
pub inline fn callMain() u8 {
const ReturnType = @typeInfo(@TypeOf(root.main)).@"fn".return_type.?;
pub inline fn callMain(init: std.process.Init) u8 {
const func = @typeInfo(@TypeOf(root.main)).@"fn";
const ReturnType = func.return_type.?;
switch (ReturnType) {
// To be deleted after 0.15.0 is tagged.
if (func.params.len == 0) switch (ReturnType) {
void => {
root.main();
return 0;
@ -665,6 +674,37 @@ pub inline fn callMain() u8 {
return 1;
};
return switch (@TypeOf(result)) {
void => 0,
u8 => result,
else => @compileError(bad_main_ret),
};
},
};
switch (ReturnType) {
void => {
root.main(init);
return 0;
},
noreturn, u8 => {
return root.main(init);
},
else => {
if (@typeInfo(ReturnType) != .error_union) @compileError(bad_main_ret);
const result = root.main(init) catch |err| {
if (builtin.zig_backend == .stage2_riscv64) {
std.debug.print("error: failed with error\n", .{});
return 1;
}
std.log.err("{s}", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
return 1;
};
return switch (@TypeOf(result)) {
void => 0,
u8 => result,