diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53f4a2bd1980ecfbefa7f542b9c80b4f1b2d5945..ebd86915f4e55871a0a8d65cb3f42ab5f40dd93b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -513,7 +513,6 @@ set(ZIG_STAGE2_SOURCES
src/Builtin.zig
src/Compilation.zig
src/Compilation/Config.zig
- src/DarwinPosixSpawn.zig
src/InternPool.zig
src/Package.zig
src/Package/Fetch.zig
diff --git a/build.zig b/build.zig
index 338709c7c4193ad3c3c4874104fdcf15205eda45..a52dee1de981e8d374af0aa5e93fd072727f33b0 100644
--- a/build.zig
+++ b/build.zig
@@ -261,7 +261,7 @@ pub fn build(b: *std.Build) !void {
"--git-dir", ".git", // affected by the -C argument
"describe", "--match", "*.*.*", //
"--tags", "--abbrev=9",
- }, &code, .Ignore) catch {
+ }, &code, .ignore) catch {
break :v version_string;
};
const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r");
@@ -470,10 +470,7 @@ pub fn build(b: *std.Build) !void {
.skip_llvm = skip_llvm,
.skip_libc = skip_libc,
.max_rss = switch (b.graph.host.result.os.tag) {
- .freebsd => switch (b.graph.host.result.cpu.arch) {
- .x86_64 => 1_060_217_241,
- else => 1_100_000_000,
- },
+ .freebsd => 2_000_000_000,
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 659_809_075,
.loongarch64 => 598_902_374,
@@ -829,30 +826,7 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu
fn addCompilerStep(b: *std.Build, options: AddCompilerModOptions) *std.Build.Step.Compile {
const exe = b.addExecutable(.{
.name = "zig",
- .max_rss = switch (b.graph.host.result.os.tag) {
- .freebsd => switch (b.graph.host.result.cpu.arch) {
- .x86_64 => 6_044_158_771,
- else => 6_100_000_000,
- },
- .linux => switch (b.graph.host.result.cpu.arch) {
- .aarch64 => 6_240_805_683,
- .loongarch64 => 5_024_158_515,
- .powerpc64le => 5_224_914_534,
- .riscv64 => 6_996_309_196,
- .s390x => 4_997_174_476,
- .x86_64 => 6_664_025_702,
- else => 7_000_000_000,
- },
- .macos => switch (b.graph.host.result.cpu.arch) {
- .aarch64 => 6_639_145_779,
- else => 6_700_000_000,
- },
- .windows => switch (b.graph.host.result.cpu.arch) {
- .x86_64 => 5_770_394_009,
- else => 5_800_000_000,
- },
- else => 7_000_000_000,
- },
+ .max_rss = 7_000_000_000,
.root_module = addCompilerMod(b, options),
});
exe.stack_size = stack_size;
diff --git a/doc/langref.html.in b/doc/langref.html.in
index c0272124caeb9529e670e06c3464fd05ef369ada..c3aa3c9b1b443c588647ecbe4e8d9a1d6724812a 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -7027,8 +7027,7 @@ WebAssembly.instantiate(typedArray, {
The result is 3{#end_shell_samp#}
{#header_close#}
{#header_open|WASI#}
-
Zig's support for WebAssembly System Interface (WASI) is under active development.
- Example of using the standard library and reading command line arguments:
+ Zig standard library has first-class support for WebAssembly System Interface.
{#code|wasi_args.zig#}
{#shell_samp#}$ wasmtime wasi_args.wasm 123 hello
diff --git a/doc/langref/hello.zig b/doc/langref/hello.zig
index 3fc2fb98d58b1e65317102941f5a8a06f740e1a7..0986aaae7d4a4c9d39e2544bf2063dbc9cb8e42d 100644
--- a/doc/langref/hello.zig
+++ b/doc/langref/hello.zig
@@ -1,17 +1,7 @@
const std = @import("std");
-// See https://github.com/ziglang/zig/issues/24510
-// for the plan to simplify this code.
-pub fn main() !void {
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_allocator.deinit();
- const gpa = debug_allocator.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
+pub fn main(init: std.process.Init) !void {
+ try std.Io.File.stdout().writeStreamingAll(init.io, "Hello, World!\n");
}
// exe=succeed
diff --git a/doc/langref/wasi_args.zig b/doc/langref/wasi_args.zig
index 6801e67f0c7ad4fe57af4fe9bca6870524f2b81c..e1c8e532d3e8d6542c08c0b076f84cbed991a8b6 100644
--- a/doc/langref/wasi_args.zig
+++ b/doc/langref/wasi_args.zig
@@ -1,13 +1,9 @@
const std = @import("std");
-pub fn main() !void {
- var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
- const gpa = general_purpose_allocator.allocator();
- const args = try std.process.argsAlloc(gpa);
- defer std.process.argsFree(gpa, args);
-
- for (args, 0..) |arg, i| {
- std.debug.print("{}: {s}\n", .{ i, arg });
+pub fn main(init: std.process.Init) !void {
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
+ for (0.., args) |i, arg| {
+ std.debug.print("{d}: {s}\n", .{ i, arg });
}
}
diff --git a/doc/langref/wasi_preopens.zig b/doc/langref/wasi_preopens.zig
index 5a167bc8dbd96967193f256da71d89e5d5aee37d..99ab36f31483d318a3be9c254a4927427d2e4ca4 100644
--- a/doc/langref/wasi_preopens.zig
+++ b/doc/langref/wasi_preopens.zig
@@ -1,18 +1,10 @@
const std = @import("std");
-const fs = std.fs;
-pub fn main() !void {
- var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
- const gpa = general_purpose_allocator.allocator();
-
- var arena_instance = std.heap.ArenaAllocator.init(gpa);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- const preopens = try fs.wasi.preopensAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const preopens = try std.fs.wasi.preopensAlloc(init.arena.allocator());
for (preopens.names, 0..) |preopen, i| {
- std.debug.print("{}: {s}\n", .{ i, preopen });
+ std.debug.print("{d}: {s}\n", .{ i, preopen });
}
}
diff --git a/lib/compiler/aro/aro/Compilation.zig b/lib/compiler/aro/aro/Compilation.zig
index aec780af02abd7f2c9611e7f08b0db14f8f36756..6f3a1a44da119e3e1b27750a551efb852df21d33 100644
--- a/lib/compiler/aro/aro/Compilation.zig
+++ b/lib/compiler/aro/aro/Compilation.zig
@@ -74,9 +74,7 @@ pub const Environment = struct {
pub const default: @This() = .{ .provided = 0 };
};
- /// Load all of the environment variables using the std.process API. Do not use if using Aro as a shared library on Linux without libc
- /// See https://github.com/ziglang/zig/issues/4524
- pub fn loadAll(allocator: std.mem.Allocator) !Environment {
+ pub fn loadAll(allocator: std.mem.Allocator, environ_map: *const std.process.Environ.Map) !Environment {
var env: Environment = .{};
errdefer env.deinit(allocator);
@@ -85,11 +83,7 @@ pub const Environment = struct {
var env_var_buf: [field.name.len]u8 = undefined;
const env_var_name = std.ascii.upperString(&env_var_buf, field.name);
- const val: ?[]const u8 = std.process.getEnvVarOwned(allocator, env_var_name) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.EnvironmentVariableNotFound => null,
- error.InvalidWtf8 => null,
- };
+ const val: ?[]const u8 = if (environ_map.get(env_var_name)) |v| try allocator.dupe(u8, v) else null;
@field(env, field.name) = val;
}
return env;
@@ -193,13 +187,20 @@ pub fn init(gpa: Allocator, arena: Allocator, io: Io, diagnostics: *Diagnostics,
/// Initialize Compilation with default environment,
/// pragma handlers and emulation mode set to target.
-pub fn initDefault(gpa: Allocator, arena: Allocator, io: Io, diagnostics: *Diagnostics, cwd: Io.Dir) !Compilation {
+pub fn initDefault(
+ gpa: Allocator,
+ arena: Allocator,
+ io: Io,
+ diagnostics: *Diagnostics,
+ cwd: Io.Dir,
+ environ_map: *const std.process.Environ.Map,
+) !Compilation {
var comp: Compilation = .{
.gpa = gpa,
.arena = arena,
.io = io,
.diagnostics = diagnostics,
- .environment = try Environment.loadAll(gpa),
+ .environment = try Environment.loadAll(gpa, environ_map),
.cwd = cwd,
};
errdefer comp.deinit();
diff --git a/lib/compiler/aro/aro/Driver.zig b/lib/compiler/aro/aro/Driver.zig
index 340a35bdde97b4995461bc139866a146246663db..400a1ef6f67bf9980a1c2a56750017af10c85f26 100644
--- a/lib/compiler/aro/aro/Driver.zig
+++ b/lib/compiler/aro/aro/Driver.zig
@@ -1250,21 +1250,25 @@ fn getOutFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u8) !
}
fn invokeAssembler(d: *Driver, tc: *Toolchain, input_path: []const u8, output_path: []const u8) !void {
+ const io = d.comp.io;
var assembler_path_buf: [std.fs.max_path_bytes]u8 = undefined;
const assembler_path = try tc.getAssemblerPath(&assembler_path_buf);
const argv = [_][]const u8{ assembler_path, input_path, "-o", output_path };
- var child = std.process.Child.init(&argv, d.comp.gpa);
- // TODO handle better
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
-
- const term = child.spawnAndWait() catch |er| {
+ var child = std.process.spawn(io, .{
+ .argv = &argv,
+ // TODO handle better
+ .stdin = .inherit,
+ .stdout = .inherit,
+ .stderr = .inherit,
+ }) catch |er| {
return d.fatal("unable to spawn linker: {s}", .{errorDescription(er)});
};
+ const term = child.wait(io) catch |er| {
+ return d.fatal("unable to wait linker: {s}", .{errorDescription(er)});
+ };
switch (term) {
- .Exited => |code| if (code != 0) {
+ .exited => |code| if (code != 0) {
const e = d.fatal("assembler exited with an error code", .{});
return e;
},
@@ -1490,6 +1494,7 @@ fn dumpLinkerArgs(w: *std.Io.Writer, items: []const []const u8) !void {
/// **MAY call `exit` if `fast_exit` is set.**
pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) Compilation.Error!void {
const gpa = d.comp.gpa;
+ const io = d.comp.io;
var argv: std.ArrayList([]const u8) = .empty;
defer argv.deinit(gpa);
@@ -1506,17 +1511,20 @@ pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) Compil
return d.fatal("unable to dump linker args: {s}", .{errorDescription(stdout.err.?)});
};
}
- var child = std.process.Child.init(argv.items, d.comp.gpa);
- // TODO handle better
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
-
- const term = child.spawnAndWait() catch |er| {
+ var child = std.process.spawn(io, .{
+ .argv = argv.items,
+ // TODO handle better
+ .stdin = .inherit,
+ .stdout = .inherit,
+ .stderr = .inherit,
+ }) catch |er| {
return d.fatal("unable to spawn linker: {s}", .{errorDescription(er)});
};
+ const term = child.wait(io) catch |er| {
+ return d.fatal("unable to wait linker: {s}", .{errorDescription(er)});
+ };
switch (term) {
- .Exited => |code| if (code != 0) {
+ .exited => |code| if (code != 0) {
const e = d.fatal("linker exited with an error code", .{});
if (fast_exit) d.exitWithCleanup(code);
return e;
diff --git a/lib/compiler/aro/main.zig b/lib/compiler/aro/main.zig
index ca079c0e5f7e12dcba64de54a07cb807cb72b4ae..dcc07eaca64c9c69ce67ef7121128c026e4c3cda 100644
--- a/lib/compiler/aro/main.zig
+++ b/lib/compiler/aro/main.zig
@@ -18,7 +18,7 @@ var debug_allocator: std.heap.DebugAllocator(.{
.canary = @truncate(0xc647026dc6875134),
}) = .{};
-pub fn main() u8 {
+pub fn main(init: std.process.Init.Minimal) u8 {
const gpa = if (@import("builtin").link_libc)
std.heap.c_allocator
else
@@ -37,7 +37,7 @@ pub fn main() u8 {
const fast_exit = @import("builtin").mode != .Debug;
- const args = process.argsAlloc(arena) catch {
+ const args = init.args.toSlice(arena) catch {
std.debug.print("out of memory\n", .{});
if (fast_exit) process.exit(1);
return 1;
diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig
index f5315d6496213795d6570d3654b9d4db8cc7361e..82fc696618893810090a42a104de7a3e35611560 100644
--- a/lib/compiler/build_runner.zig
+++ b/lib/compiler/build_runner.zig
@@ -24,7 +24,7 @@ pub const std_options: std.Options = .{
.crypto_fork_safety = false,
};
-pub fn main() !void {
+pub fn main(init: process.Init.Minimal) !void {
// The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not
// always the case. So, we do need a true gpa for some things.
var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init;
@@ -37,9 +37,12 @@ pub fn main() !void {
var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ .child_allocator = single_threaded_arena.allocator() };
const arena = thread_safe_arena.allocator();
- const args = try process.argsAlloc(arena);
+ const args = try init.args.toSlice(arena);
- var threaded: std.Io.Threaded = .init(gpa, .{});
+ var threaded: std.Io.Threaded = .init(gpa, .{
+ .environ = init.environ,
+ .argv0 = .init(init.args),
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -81,9 +84,10 @@ pub fn main() !void {
.io = io,
.gpa = arena,
.manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),
+ .cwd = try process.getCwdAlloc(single_threaded_arena.allocator()),
},
.zig_exe = zig_exe,
- .env_map = try process.getEnvMap(arena),
+ .environ_map = try init.environ.createMap(arena),
.global_cache_root = global_cache_directory,
.zig_lib_directory = zig_lib_directory,
.host = .{
@@ -126,13 +130,13 @@ pub fn main() !void {
var debounce_interval_ms: u16 = 50;
var webui_listen: ?Io.net.IpAddress = null;
- if (try std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(arena)) |str| {
+ if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {
if (std.meta.stringToEnum(ErrorStyle, str)) |style| {
error_style = style;
}
}
- if (try std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(arena)) |str| {
+ if (std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| {
if (std.meta.stringToEnum(MultilineErrors, str)) |style| {
multiline_errors = style;
}
@@ -429,8 +433,8 @@ pub fn main() !void {
}
}
- const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet();
- const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet();
+ const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map);
+ const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map);
graph.stderr_mode = switch (color) {
.auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE),
@@ -540,7 +544,7 @@ pub fn main() !void {
var w: Watch = w: {
if (!watch) break :w undefined;
if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});
- break :w try .init();
+ break :w try .init(graph.cache.cwd);
};
const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});
diff --git a/lib/compiler/libc.zig b/lib/compiler/libc.zig
index eb4614c95db3552f2e36342c4ce1ea7161347f31..8ca53fefc905c01527c7157bee4c0d10575e582e 100644
--- a/lib/compiler/libc.zig
+++ b/lib/compiler/libc.zig
@@ -24,23 +24,19 @@ const usage_libc =
var stdout_buffer: [4096]u8 = undefined;
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
- const gpa = arena;
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const gpa = init.gpa;
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
+ const environ_map = init.environ_map;
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
const zig_lib_directory = args[1];
var input_file: ?[]const u8 = null;
var target_arch_os_abi: []const u8 = "native";
var print_includes: bool = false;
- var stdout_writer = Io.File.stdout().writer(&stdout_buffer);
+ var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer);
const stdout = &stdout_writer.interface;
{
var i: usize = 2;
@@ -77,7 +73,7 @@ pub fn main() !void {
const libc_installation: ?*LibCInstallation = libc: {
if (input_file) |libc_file| {
const libc = try arena.create(LibCInstallation);
- libc.* = LibCInstallation.parse(arena, libc_file, &target) catch |err| {
+ libc.* = LibCInstallation.parse(arena, io, libc_file, &target) catch |err| {
fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err });
};
break :libc libc;
@@ -90,11 +86,13 @@ pub fn main() !void {
const libc_dirs = std.zig.LibCDirs.detect(
arena,
+ io,
zig_lib_directory,
&target,
is_native_abi,
true,
libc_installation,
+ environ_map,
) catch |err| {
const zig_target = try target.zigTriple(arena);
fatal("unable to detect libc for target {s}: {t}", .{ zig_target, err });
@@ -114,7 +112,7 @@ pub fn main() !void {
}
if (input_file) |libc_file| {
- var libc = LibCInstallation.parse(gpa, libc_file, &target) catch |err| {
+ var libc = LibCInstallation.parse(gpa, io, libc_file, &target) catch |err| {
fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err });
};
defer libc.deinit(gpa);
@@ -125,6 +123,7 @@ pub fn main() !void {
var libc = LibCInstallation.findNative(gpa, io, .{
.verbose = true,
.target = &target,
+ .environ_map = environ_map,
}) catch |err| {
fatal("unable to detect native libc: {t}", .{err});
};
diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig
index 77b60c9b372efc1616413ddb713741cbb59c0e65..57d019bc95b8887c9bce708d6be08fb3abc9205a 100644
--- a/lib/compiler/objcopy.zig
+++ b/lib/compiler/objcopy.zig
@@ -17,20 +17,10 @@ var stdout_buffer: [1024]u8 = undefined;
var input_buffer: [1024]u8 = undefined;
var output_buffer: [1024]u8 = undefined;
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
- const gpa = general_purpose_allocator.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
- return cmdObjCopy(arena, io, args[1..]);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
+ return cmdObjCopy(arena, init.io, args[1..]);
}
fn cmdObjCopy(arena: Allocator, io: Io, args: []const []const u8) !void {
diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig
index 922031d00873a8a18cbe2d766c1b5f3b35baa98e..abdd9f84362292f50dc1c67d04cbc707d2e30888 100644
--- a/lib/compiler/reduce.zig
+++ b/lib/compiler/reduce.zig
@@ -47,19 +47,11 @@ const Interestingness = enum { interesting, unknown, boring };
// - reduce flags sent to the compiler
// - integrate with the build system?
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
- const gpa = general_purpose_allocator.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const gpa = init.gpa;
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
var opt_checker_path: ?[]const u8 = null;
var opt_root_source_file_path: ?[]const u8 = null;
@@ -73,8 +65,7 @@ pub fn main() !void {
const arg = args[i];
if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
- const stdout = Io.File.stdout();
- try stdout.writeAll(usage);
+ try Io.File.stdout().writeStreamingAll(io, usage);
return std.process.cleanExit(io);
} else if (mem.eql(u8, arg, "--")) {
argv = args[i + 1 ..];
@@ -131,12 +122,10 @@ pub fn main() !void {
if (!skip_smoke_test) {
std.debug.print("smoke testing the interestingness check...\n", .{});
- switch (try runCheck(arena, interestingness_argv.items)) {
+ switch (try runCheck(arena, io, interestingness_argv.items)) {
.interesting => {},
.boring, .unknown => |t| {
- fatal("interestingness check returned {s} for unmodified input\n", .{
- @tagName(t),
- });
+ fatal("interestingness check returned {t} for unmodified input\n", .{t});
},
}
}
@@ -238,7 +227,7 @@ pub fn main() !void {
try Io.Dir.cwd().writeFile(io, .{ .sub_path = root_source_file_path, .data = rendered.written() });
// std.debug.print("trying this code:\n{s}\n", .{rendered.items});
- const interestingness = try runCheck(arena, interestingness_argv.items);
+ const interestingness = try runCheck(arena, io, interestingness_argv.items);
std.debug.print("{d} random transformations: {t}. {d}/{d}\n", .{
subset_size, interestingness, start_index, transformations.items.len,
});
@@ -293,20 +282,24 @@ fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random)
fn termToInteresting(term: std.process.Child.Term) Interestingness {
return switch (term) {
- .Exited => |code| switch (code) {
+ .exited => |code| switch (code) {
0 => .interesting,
1 => .unknown,
else => .boring,
},
- else => b: {
+ .signal => |sig| {
+ std.debug.print("interestingness check terminated with signal {t}\n", .{sig});
+ return .boring;
+ },
+ else => {
std.debug.print("interestingness check aborted unexpectedly\n", .{});
- break :b .boring;
+ return .boring;
},
};
}
fn runCheck(arena: Allocator, io: Io, argv: []const []const u8) !Interestingness {
- const result = try std.process.Child.run(arena, io, .{ .argv = argv });
+ const result = try std.process.run(arena, io, .{ .argv = argv });
if (result.stderr.len != 0)
std.debug.print("{s}", .{result.stderr});
return termToInteresting(result.term);
diff --git a/lib/compiler/resinator/compile.zig b/lib/compiler/resinator/compile.zig
index 4f75fca18a4287ae96003db378f68f8c83f40c7d..a315ea488e6be0a7673f0c48b430b75271939304 100644
--- a/lib/compiler/resinator/compile.zig
+++ b/lib/compiler/resinator/compile.zig
@@ -80,7 +80,7 @@ pub const Dependencies = struct {
}
};
-pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io.Writer, options: CompileOptions) !void {
+pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io.Writer, options: CompileOptions, environ_map: *const std.process.Environ.Map) !void {
var lexer = lex.Lexer.init(source, .{
.default_code_page = options.default_code_page,
.source_mappings = options.source_mappings,
@@ -148,8 +148,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io
try search_dirs.append(allocator, .{ .dir = dir, .path = try allocator.dupe(u8, system_include_path) });
}
if (!options.ignore_include_env_var) {
- const INCLUDE = std.process.getEnvVarOwned(allocator, "INCLUDE") catch "";
- defer allocator.free(INCLUDE);
+ const INCLUDE = environ_map.get("INCLUDE") orelse "";
// The only precedence here is llvm-rc which also uses the platform-specific
// delimiter. There's no precedence set by `rc.exe` since it's Windows-only.
diff --git a/lib/compiler/resinator/main.zig b/lib/compiler/resinator/main.zig
index e286a5f4b9d8047d5a77935567b24d71b9aee69c..d1aaa24264d2d57ed9072390ac44cc0477e6d756 100644
--- a/lib/compiler/resinator/main.zig
+++ b/lib/compiler/resinator/main.zig
@@ -19,12 +19,18 @@ const fmtResourceType = @import("res.zig").NameOrOrdinal.fmtResourceType;
const aro = @import("aro");
const compiler_util = @import("../util.zig");
-pub fn main() !void {
+pub fn main(init: std.process.Init.Minimal) !void {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
defer std.debug.assert(debug_allocator.deinit() == .ok);
const gpa = debug_allocator.allocator();
- var threaded: std.Io.Threaded = .init(gpa, .{});
+ var environ_map = try init.environ.createMap(gpa);
+ defer environ_map.deinit();
+
+ var threaded: std.Io.Threaded = .init(gpa, .{
+ .environ = init.environ,
+ .argv0 = .init(init.args),
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -32,7 +38,7 @@ pub fn main() !void {
defer arena_state.deinit();
const arena = arena_state.allocator();
- const args = try std.process.argsAlloc(arena);
+ const args = try init.args.toSlice(arena);
if (args.len < 2) {
const stderr = try io.lockStderr(&.{}, null);
@@ -145,8 +151,8 @@ pub fn main() !void {
defer argv.deinit(aro_arena);
try argv.append(aro_arena, "arocc"); // dummy command name
- const resolved_include_paths = try include_paths.get(&error_handler);
- try preprocess.appendAroArgs(aro_arena, &argv, options, resolved_include_paths);
+ const resolved_include_paths = try include_paths.get(&error_handler, &environ_map);
+ try preprocess.appendAroArgs(aro_arena, &argv, options, resolved_include_paths, &environ_map);
try argv.append(aro_arena, switch (options.input_source) {
.stdio => "-",
.filename => |filename| filename,
@@ -280,7 +286,7 @@ pub fn main() !void {
.dependencies = maybe_dependencies,
.ignore_include_env_var = options.ignore_include_env_var,
.extra_include_paths = options.extra_include_paths.items,
- .system_include_paths = try include_paths.get(&error_handler),
+ .system_include_paths = try include_paths.get(&error_handler, &environ_map),
.default_language_id = options.default_language_id,
.default_code_page = default_code_page,
.disjoint_code_page = has_disjoint_code_page,
@@ -289,7 +295,7 @@ pub fn main() !void {
.max_string_literal_codepoints = options.max_string_literal_codepoints,
.silent_duplicate_control_ids = options.silent_duplicate_control_ids,
.warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page,
- }) catch |err| switch (err) {
+ }, &environ_map) catch |err| switch (err) {
error.ParseError, error.CompileError => {
try error_handler.emitDiagnostics(gpa, Io.Dir.cwd(), final_input, &diagnostics, mapping_results.mappings);
// Delete the output file on error
@@ -536,13 +542,24 @@ const LazyIncludePaths = struct {
target_machine_type: std.coff.IMAGE.FILE.MACHINE,
resolved_include_paths: ?[]const []const u8 = null,
- pub fn get(self: *LazyIncludePaths, error_handler: *ErrorHandler) ![]const []const u8 {
+ pub fn get(
+ self: *LazyIncludePaths,
+ error_handler: *ErrorHandler,
+ environ_map: *const std.process.Environ.Map,
+ ) ![]const []const u8 {
const io = self.io;
if (self.resolved_include_paths) |include_paths|
return include_paths;
- return getIncludePaths(self.arena, io, self.auto_includes_option, self.zig_lib_dir, self.target_machine_type) catch |err| switch (err) {
+ return getIncludePaths(
+ self.arena,
+ io,
+ self.auto_includes_option,
+ self.zig_lib_dir,
+ self.target_machine_type,
+ environ_map,
+ ) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => |e| {
switch (e) {
@@ -569,6 +586,7 @@ fn getIncludePaths(
auto_includes_option: cli.Options.AutoIncludes,
zig_lib_dir: []const u8,
target_machine_type: std.coff.IMAGE.FILE.MACHINE,
+ environ_map: *const std.process.Environ.Map,
) ![]const []const u8 {
if (auto_includes_option == .none) return &[_][]const u8{};
@@ -615,7 +633,7 @@ fn getIncludePaths(
};
const target = std.zig.resolveTargetQueryOrFatal(io, target_query);
const is_native_abi = target_query.isNativeAbi();
- const detected_libc = std.zig.LibCDirs.detect(arena, io, zig_lib_dir, &target, is_native_abi, true, null) catch {
+ const detected_libc = std.zig.LibCDirs.detect(arena, io, zig_lib_dir, &target, is_native_abi, true, null, environ_map) catch {
if (includes == .any) {
// fall back to mingw
includes = .gnu;
@@ -641,7 +659,16 @@ fn getIncludePaths(
};
const target = std.zig.resolveTargetQueryOrFatal(io, target_query);
const is_native_abi = target_query.isNativeAbi();
- const detected_libc = std.zig.LibCDirs.detect(arena, io, zig_lib_dir, &target, is_native_abi, true, null) catch |err| switch (err) {
+ const detected_libc = std.zig.LibCDirs.detect(
+ arena,
+ io,
+ zig_lib_dir,
+ &target,
+ is_native_abi,
+ true,
+ null,
+ environ_map,
+ ) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => return error.MingwIncludesNotFound,
};
diff --git a/lib/compiler/resinator/preprocess.zig b/lib/compiler/resinator/preprocess.zig
index a67721a7e1c62863ef36f0b725333386d4789847..1d8c038b60eaf6d7aa5e4e31b465e2a7e479816f 100644
--- a/lib/compiler/resinator/preprocess.zig
+++ b/lib/compiler/resinator/preprocess.zig
@@ -86,7 +86,7 @@ fn hasAnyErrors(comp: *aro.Compilation) bool {
/// `arena` is used for temporary -D argument strings and the INCLUDE environment variable.
/// The arena should be kept alive at least as long as `argv`.
-pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options: cli.Options, system_include_paths: []const []const u8) !void {
+pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options: cli.Options, system_include_paths: []const []const u8, environ_map: *const std.process.Environ.Map) !void {
try argv.appendSlice(arena, &.{
"-E",
"--comments",
@@ -109,7 +109,7 @@ pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options
}
if (!options.ignore_include_env_var) {
- const INCLUDE = std.process.getEnvVarOwned(arena, "INCLUDE") catch "";
+ const INCLUDE = environ_map.get("INCLUDE") orelse "";
// The only precedence here is llvm-rc which also uses the platform-specific
// delimiter. There's no precedence set by `rc.exe` since it's Windows-only.
diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig
index e2b58b86683f2018cf7f6d3638a231690f515da6..55f49c44e27d2aef6d0a7d513317454f1a3a44d2 100644
--- a/lib/compiler/std-docs.zig
+++ b/lib/compiler/std-docs.zig
@@ -21,19 +21,12 @@ fn usage(io: Io) noreturn {
std.process.exit(1);
}
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const gpa = init.gpa;
+ const io = init.io;
- var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
- const gpa = general_purpose_allocator.allocator();
-
- var threaded: Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- var argv = try std.process.argsWithAllocator(arena);
+ var argv = try init.minimal.args.iterateAllocator(arena);
defer argv.deinit();
assert(argv.skip());
const zig_lib_directory = argv.next().?;
@@ -72,7 +65,7 @@ pub fn main() !void {
const url_with_newline = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/\n", .{port});
Io.File.stdout().writeStreamingAll(io, url_with_newline) catch {};
if (should_open_browser) {
- openBrowserTab(gpa, io, url_with_newline[0 .. url_with_newline.len - 1 :'\n']) catch |err| {
+ openBrowserTab(io, url_with_newline[0 .. url_with_newline.len - 1 :'\n']) catch |err| {
std.log.err("unable to open browser: {t}", .{err});
};
}
@@ -324,11 +317,12 @@ fn buildWasmBinary(
"--listen=-", //
});
- var child = std.process.Child.init(argv.items, gpa);
- child.stdin_behavior = .Pipe;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
- try child.spawn(io);
+ var child = try std.process.spawn(io, .{
+ .argv = argv.items,
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .pipe,
+ });
var poller = Io.poll(gpa, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
@@ -388,19 +382,26 @@ fn buildWasmBinary(
child.stdin = null;
switch (try child.wait(io)) {
- .Exited => |code| {
+ .exited => |code| {
if (code != 0) {
std.log.err(
"the following command exited with error code {d}:\n{s}",
- .{ code, try std.Build.Step.allocPrintCmd(arena, null, argv.items) },
+ .{ code, try std.Build.Step.allocPrintCmd(arena, null, null, argv.items) },
);
return error.WasmCompilationFailed;
}
},
- .Signal, .Stopped, .Unknown => {
+ .signal => |sig| {
+ std.log.err(
+ "the following command terminated with signal {t}:\n{s}",
+ .{ sig, try std.Build.Step.allocPrintCmd(arena, null, null, argv.items) },
+ );
+ return error.WasmCompilationFailed;
+ },
+ .stopped, .unknown => {
std.log.err(
"the following command terminated unexpectedly:\n{s}",
- .{try std.Build.Step.allocPrintCmd(arena, null, argv.items)},
+ .{try std.Build.Step.allocPrintCmd(arena, null, null, argv.items)},
);
return error.WasmCompilationFailed;
},
@@ -410,14 +411,14 @@ fn buildWasmBinary(
try result_error_bundle.renderToStderr(io, .{}, .auto);
std.log.err("the following command failed with {d} compilation errors:\n{s}", .{
result_error_bundle.errorMessageCount(),
- try std.Build.Step.allocPrintCmd(arena, null, argv.items),
+ try std.Build.Step.allocPrintCmd(arena, null, null, argv.items),
});
return error.WasmCompilationFailed;
}
return result orelse {
std.log.err("child process failed to report result\n{s}", .{
- try std.Build.Step.allocPrintCmd(arena, null, argv.items),
+ try std.Build.Step.allocPrintCmd(arena, null, null, argv.items),
});
return error.WasmCompilationFailed;
};
@@ -434,22 +435,24 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {
};
}
-fn openBrowserTab(gpa: Allocator, io: Io, url: []const u8) !void {
+fn openBrowserTab(io: Io, url: []const u8) !void {
// Until https://github.com/ziglang/zig/issues/19205 is implemented, we
- // spawn a thread for this child process.
- _ = try std.Thread.spawn(.{}, openBrowserTabThread, .{ gpa, io, url });
+ // spawn and then leak a concurrent task for this child process.
+ const future = try io.concurrent(openBrowserTabTask, .{ io, url });
+ _ = future; // leak it
}
-fn openBrowserTabThread(gpa: Allocator, io: Io, url: []const u8) !void {
+fn openBrowserTabTask(io: Io, url: []const u8) !void {
const main_exe = switch (builtin.os.tag) {
.windows => "explorer",
.macos => "open",
else => "xdg-open",
};
- var child = std.process.Child.init(&.{ main_exe, url }, gpa);
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Ignore;
- child.stderr_behavior = .Ignore;
- try child.spawn(io);
+ var child = try std.process.spawn(io, .{
+ .argv = &.{ main_exe, url },
+ .stdin = .ignore,
+ .stdout = .ignore,
+ .stderr = .ignore,
+ });
_ = try child.wait(io);
}
diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig
index cd3c27412b055fb53bcabe3942f4fdff7d0065e7..5885f5cf12655871f0baf154514160d09ec48879 100644
--- a/lib/compiler/test_runner.zig
+++ b/lib/compiler/test_runner.zig
@@ -29,7 +29,7 @@ const need_simple = switch (builtin.zig_backend) {
else => false,
};
-pub fn main() void {
+pub fn main(init: std.process.Init.Minimal) void {
@disableInstrumentation();
if (builtin.cpu.arch.isSpirV()) {
@@ -38,11 +38,10 @@ pub fn main() void {
}
if (need_simple) {
- return mainSimple() catch @panic("test failure\n");
+ return mainSimple() catch @panic("test failure");
}
- const args = std.process.argsAlloc(fba.allocator()) catch
- @panic("unable to parse command line args");
+ const args = init.args.toSlice(fba.allocator()) catch @panic("unable to parse command line args");
var listen = false;
var opt_cache_dir: ?[]const u8 = null;
@@ -66,13 +65,13 @@ pub fn main() void {
}
if (listen) {
- return mainServer(args) catch @panic("internal test runner failure");
+ return mainServer(init) catch @panic("internal test runner failure");
} else {
- return mainTerminal(args);
+ return mainTerminal(init);
}
}
-fn mainServer(args: []const [:0]const u8) !void {
+fn mainServer(init: std.process.Init.Minimal) !void {
@disableInstrumentation();
var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io, &stdin_buffer);
var stdout_writer = Io.File.stdout().writerStreaming(runner_threaded_io, &stdout_buffer);
@@ -132,7 +131,8 @@ fn mainServer(args: []const [:0]const u8) !void {
.run_test => {
testing.allocator_instance = .{};
testing.io_instance = .init(testing.allocator, .{
- .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{},
+ .argv0 = .init(init.args),
+ .environ = init.environ,
});
log_err_count = 0;
const index = try server.receiveBody_u32();
@@ -217,7 +217,7 @@ fn mainServer(args: []const [:0]const u8) !void {
}
}
-fn mainTerminal(args: []const [:0]const u8) void {
+fn mainTerminal(init: std.process.Init.Minimal) void {
@disableInstrumentation();
if (builtin.fuzz) @panic("fuzz test requires server");
@@ -236,7 +236,8 @@ fn mainTerminal(args: []const [:0]const u8) void {
for (test_fn_list, 0..) |test_fn, i| {
testing.allocator_instance = .{};
testing.io_instance = .init(testing.allocator, .{
- .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{},
+ .argv0 = .init(init.args),
+ .environ = init.environ,
});
defer {
testing.io_instance.deinit();
diff --git a/lib/compiler/translate-c/main.zig b/lib/compiler/translate-c/main.zig
index ee50df422adbafc194b85930cb21d2b77485a7b6..ce8f4f55c3b6d4060df197f382e8cfa746fbd3d2 100644
--- a/lib/compiler/translate-c/main.zig
+++ b/lib/compiler/translate-c/main.zig
@@ -9,21 +9,13 @@ const Translator = @import("Translator.zig");
const fast_exit = @import("builtin").mode != .Debug;
-var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
+pub fn main(init: std.process.Init) u8 {
+ const gpa = init.gpa;
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const environ_map = init.environ_map;
-pub fn main() u8 {
- const gpa = general_purpose_allocator.allocator();
- defer _ = general_purpose_allocator.deinit();
-
- var arena_instance = std.heap.ArenaAllocator.init(gpa);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = process.argsAlloc(arena) catch {
+ const args = init.minimal.args.toSlice(arena) catch {
std.debug.print("ran out of memory allocating arguments\n", .{});
if (fast_exit) process.exit(1);
return 1;
@@ -34,8 +26,8 @@ pub fn main() u8 {
zig_integration = true;
}
- const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet();
- const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet();
+ const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(environ_map);
+ const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(environ_map);
var stderr_buf: [1024]u8 = undefined;
var stderr = Io.File.stderr().writer(io, &stderr_buf);
@@ -50,7 +42,7 @@ pub fn main() u8 {
};
defer diagnostics.deinit();
- var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, Io.Dir.cwd()) catch |err| switch (err) {
+ var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, .cwd(), environ_map) catch |err| switch (err) {
error.OutOfMemory => {
std.debug.print("ran out of memory initializing C compilation\n", .{});
if (fast_exit) process.exit(1);
@@ -123,43 +115,41 @@ pub const usage =
\\
;
-fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration: bool) !void {
+fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig_integration: bool) !void {
const gpa = d.comp.gpa;
const io = d.comp.io;
- const aro_args = args: {
- var i: usize = 0;
- for (args) |arg| {
- args[i] = arg;
- if (mem.eql(u8, arg, "--help")) {
- var stdout_buf: [512]u8 = undefined;
- var stdout = Io.File.stdout().writer(io, &stdout_buf);
- try stdout.interface.print(usage, .{args[0]});
- try stdout.interface.flush();
- return;
- } else if (mem.eql(u8, arg, "--version")) {
- var stdout_buf: [512]u8 = undefined;
- var stdout = Io.File.stdout().writer(io, &stdout_buf);
- // TODO add version
- try stdout.interface.writeAll("0.0.0-dev\n");
- try stdout.interface.flush();
- return;
- } else if (mem.eql(u8, arg, "--zig-integration")) {
- if (i != 1 or !zig_integration)
- return d.fatal("--zig-integration must be the first argument", .{});
- } else {
- i += 1;
- }
+ var aro_args: std.ArrayList([:0]const u8) = .empty;
+ defer aro_args.deinit(gpa);
+
+ for (args, 0..) |arg, i| {
+ if (mem.eql(u8, arg, "--help")) {
+ var stdout_buf: [512]u8 = undefined;
+ var stdout = Io.File.stdout().writer(io, &stdout_buf);
+ try stdout.interface.print(usage, .{args[0]});
+ try stdout.interface.flush();
+ return;
+ } else if (mem.eql(u8, arg, "--version")) {
+ var stdout_buf: [512]u8 = undefined;
+ var stdout = Io.File.stdout().writer(io, &stdout_buf);
+ // TODO add version
+ try stdout.interface.writeAll("0.0.0-dev\n");
+ try stdout.interface.flush();
+ return;
+ } else if (mem.eql(u8, arg, "--zig-integration")) {
+ if (i != 1 or !zig_integration)
+ return d.fatal("--zig-integration must be the first argument", .{});
+ } else {
+ try aro_args.append(gpa, arg);
}
- break :args args[0..i];
- };
+ }
const user_macros = macros: {
var macro_buf: std.ArrayList(u8) = .empty;
defer macro_buf.deinit(gpa);
var discard_buf: [256]u8 = undefined;
var discarding: std.Io.Writer.Discarding = .init(&discard_buf);
- assert(!try d.parseArgs(&discarding.writer, ¯o_buf, aro_args));
+ assert(!try d.parseArgs(&discarding.writer, ¯o_buf, aro_args.items));
if (macro_buf.items.len > std.math.maxInt(u32)) {
return d.fatal("user provided macro source exceeded max size", .{});
}
diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig
index 040d2c6c411fa32fa244080edaf3a44424d46503..5a21cf7b34a070b26f19dd51171c8a0efa3daab0 100644
--- a/lib/compiler_rt.zig
+++ b/lib/compiler_rt.zig
@@ -1,7 +1,23 @@
+const std = @import("std");
const builtin = @import("builtin");
const common = @import("compiler_rt/common.zig");
-pub const panic = common.panic;
+/// Avoid dragging in the runtime safety mechanisms into this .o file, unless
+/// we're trying to test compiler-rt.
+pub const panic = if (common.test_safety)
+ std.debug.FullPanic(std.debug.defaultPanic)
+else
+ std.debug.no_panic;
+
+pub const std_options_debug_threaded_io: ?*std.Io.Threaded = if (builtin.is_test)
+ std.Io.Threaded.global_single_threaded
+else
+ null;
+
+pub const std_options_debug_io: std.Io = if (builtin.is_test)
+ std.Io.Threaded.global_single_threaded.ioBasic()
+else
+ unreachable;
comptime {
// Integer routines
diff --git a/lib/compiler_rt/absvdi2.zig b/lib/compiler_rt/absvdi2.zig
index 8b5e5d8a5de6b4781781ab0c029dfffb3f925ce0..add32608e8a389c6a62139410f356b0c24072981 100644
--- a/lib/compiler_rt/absvdi2.zig
+++ b/lib/compiler_rt/absvdi2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const absv = @import("./absv.zig").absv;
-pub const panic = common.panic;
-
comptime {
@export(&__absvdi2, .{ .name = "__absvdi2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/absvsi2.zig b/lib/compiler_rt/absvsi2.zig
index 0030813db83c337d0a5d75b5812d52b6d54b461f..fb527cd997f6a16a001683effd5bb789d0080625 100644
--- a/lib/compiler_rt/absvsi2.zig
+++ b/lib/compiler_rt/absvsi2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const absv = @import("./absv.zig").absv;
-pub const panic = common.panic;
-
comptime {
@export(&__absvsi2, .{ .name = "__absvsi2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/absvti2.zig b/lib/compiler_rt/absvti2.zig
index be232ece2ce0b446a92ef29120f98cbe406a1b3e..1b2ddaaefac6ad7fa139a331fe569bcf11f27cc9 100644
--- a/lib/compiler_rt/absvti2.zig
+++ b/lib/compiler_rt/absvti2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const absv = @import("./absv.zig").absv;
-pub const panic = common.panic;
-
comptime {
@export(&__absvti2, .{ .name = "__absvti2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/adddf3.zig b/lib/compiler_rt/adddf3.zig
index 3a10f555203ac0bc1630a0e4cc34969d5918a0f6..98fba79bbd5116617489d61e32c0d02122f1a579 100644
--- a/lib/compiler_rt/adddf3.zig
+++ b/lib/compiler_rt/adddf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/addhf3.zig b/lib/compiler_rt/addhf3.zig
index 48a6328836a26a684473155d20d92afabf208fca..842c88f5d4268c9b3b2030096ba52bb296106dcd 100644
--- a/lib/compiler_rt/addhf3.zig
+++ b/lib/compiler_rt/addhf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
@export(&__addhf3, .{ .name = "__addhf3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/addsf3.zig b/lib/compiler_rt/addsf3.zig
index c4b7773eb0f0e28801fda6611f42b870cd881111..ef1627ae7ae05d9a83716fa53178b9d50971e040 100644
--- a/lib/compiler_rt/addsf3.zig
+++ b/lib/compiler_rt/addsf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/addtf3.zig b/lib/compiler_rt/addtf3.zig
index 34e5bdf7afd5d1cf72932c949dc9d2faf4513cb4..d4011f923805cce7f58da56beeadf5c7700b9001 100644
--- a/lib/compiler_rt/addtf3.zig
+++ b/lib/compiler_rt/addtf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__addtf3, .{ .name = "__addkf3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/addvdi3.zig b/lib/compiler_rt/addvdi3.zig
index 03aa9b91c717fb791569dda4f75ddc3dfbf20b57..e7346b717ff87faada5d780b02d0b42e1e30d7b6 100644
--- a/lib/compiler_rt/addvdi3.zig
+++ b/lib/compiler_rt/addvdi3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const testing = @import("std").testing;
-pub const panic = common.panic;
-
comptime {
@export(&__addvdi3, .{ .name = "__addvdi3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/addvsi3.zig b/lib/compiler_rt/addvsi3.zig
index e688fdba58441d8147fbbede01015e72f71f607c..468047eda1c5bf9440291928d5648ea216be9816 100644
--- a/lib/compiler_rt/addvsi3.zig
+++ b/lib/compiler_rt/addvsi3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const testing = @import("std").testing;
-pub const panic = common.panic;
-
comptime {
@export(&__addvsi3, .{ .name = "__addvsi3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/addxf3.zig b/lib/compiler_rt/addxf3.zig
index 1aac736750210b626ae8a8e759042a13de71a9a8..e1509f04f5f69f703f243e7a7fc043e2b5dda4c8 100644
--- a/lib/compiler_rt/addxf3.zig
+++ b/lib/compiler_rt/addxf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
@export(&__addxf3, .{ .name = "__addxf3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/arm.zig b/lib/compiler_rt/arm.zig
index 01775ab06fc9f41e81283a5b992a1c74edbe1249..aa31e855dc13e8b2901bb67602a72e67d71625a6 100644
--- a/lib/compiler_rt/arm.zig
+++ b/lib/compiler_rt/arm.zig
@@ -6,8 +6,6 @@ const target = builtin.target;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (!builtin.is_test) {
if (arch.isArm()) {
diff --git a/lib/compiler_rt/atomics.zig b/lib/compiler_rt/atomics.zig
index 09fd1c2c906c513037ec10909c5907d783482e09..b87b7a6315252a277ed7b6d8f6b001f833885ed9 100644
--- a/lib/compiler_rt/atomics.zig
+++ b/lib/compiler_rt/atomics.zig
@@ -5,7 +5,6 @@ const cpu = builtin.cpu;
const arch = cpu.arch;
const linkage = common.linkage;
const visibility = common.visibility;
-pub const panic = common.panic;
// This parameter is true iff the target architecture supports the bare minimum
// to implement the atomic load/store intrinsics.
diff --git a/lib/compiler_rt/aulldiv.zig b/lib/compiler_rt/aulldiv.zig
index 5a202fec00dcda541da5526c7dccb3dfc3ce2687..fbfa18dfd195c295c91514d21d9abce04ba9bea2 100644
--- a/lib/compiler_rt/aulldiv.zig
+++ b/lib/compiler_rt/aulldiv.zig
@@ -5,8 +5,6 @@ const os = builtin.os.tag;
const abi = builtin.abi;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_x86_msvc_abi) {
// Don't let LLVM apply the stdcall name mangling on those MSVC builtins
diff --git a/lib/compiler_rt/aullrem.zig b/lib/compiler_rt/aullrem.zig
index d4365265be254e3c4a6f3a0bc4aad1a87a3180ec..ea12c16a0620c16d27711831bfb753d791403e5d 100644
--- a/lib/compiler_rt/aullrem.zig
+++ b/lib/compiler_rt/aullrem.zig
@@ -5,8 +5,6 @@ const os = builtin.os.tag;
const abi = builtin.abi;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_x86_msvc_abi) {
// Don't let LLVM apply the stdcall name mangling on those MSVC builtins
diff --git a/lib/compiler_rt/bitreverse.zig b/lib/compiler_rt/bitreverse.zig
index fe6cf8988da638d177ca05277a03ed7bb8498f0c..6dbeac77d9e196c8363a4fd8faf5888131c9d858 100644
--- a/lib/compiler_rt/bitreverse.zig
+++ b/lib/compiler_rt/bitreverse.zig
@@ -2,8 +2,6 @@ const std = @import("std");
const builtin = @import("builtin");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__bitreversesi2, .{ .name = "__bitreversesi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__bitreversedi2, .{ .name = "__bitreversedi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/bswap.zig b/lib/compiler_rt/bswap.zig
index 2d6df339dfc22fb7a597e9d8cb4f9dfafe06d478..e6f634c66fb787de4d0671ca622d696c2e930e7f 100644
--- a/lib/compiler_rt/bswap.zig
+++ b/lib/compiler_rt/bswap.zig
@@ -2,8 +2,6 @@ const std = @import("std");
const builtin = @import("builtin");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__bswapsi2, .{ .name = "__bswapsi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__bswapdi2, .{ .name = "__bswapdi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/ceil.zig b/lib/compiler_rt/ceil.zig
index 1900496be97f6eab3f2b20673ffdfbb6ee2549f6..9f797346f6bffa8f0d3b881c0a5369fac7890906 100644
--- a/lib/compiler_rt/ceil.zig
+++ b/lib/compiler_rt/ceil.zig
@@ -13,8 +13,6 @@ const mem = std.mem;
const expect = std.testing.expect;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__ceilh, .{ .name = "__ceilh", .linkage = common.linkage, .visibility = common.visibility });
@export(&ceilf, .{ .name = "ceilf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/clear_cache.zig b/lib/compiler_rt/clear_cache.zig
index 9435f66c84fc442aca5781720cda0ec0dfd1a041..0e3b5d371f93e8b8374ff83537ee8aed10357fa5 100644
--- a/lib/compiler_rt/clear_cache.zig
+++ b/lib/compiler_rt/clear_cache.zig
@@ -3,7 +3,6 @@ const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const os = builtin.os.tag;
const common = @import("common.zig");
-pub const panic = common.panic;
// Ported from llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28
diff --git a/lib/compiler_rt/cmp.zig b/lib/compiler_rt/cmp.zig
index 67cb5b0938fa27c41902bf86111a27ca3a8f436c..cac23a7508f1546d8fe372582bd03e526062868a 100644
--- a/lib/compiler_rt/cmp.zig
+++ b/lib/compiler_rt/cmp.zig
@@ -2,8 +2,6 @@ const std = @import("std");
const builtin = @import("builtin");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__cmpsi2, .{ .name = "__cmpsi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__cmpdi2, .{ .name = "__cmpdi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/cmpdf2.zig b/lib/compiler_rt/cmpdf2.zig
index a0338c7a17920286889650c88b02da7ab021597c..ebec596f270d739e689f3f735f94800a96ed95dc 100644
--- a/lib/compiler_rt/cmpdf2.zig
+++ b/lib/compiler_rt/cmpdf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/cmphf2.zig b/lib/compiler_rt/cmphf2.zig
index 9fcc1e24d53eb7c52425643ee4d8842efb56e085..ea70e71e161fe6131e48632d5a455862455257f7 100644
--- a/lib/compiler_rt/cmphf2.zig
+++ b/lib/compiler_rt/cmphf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__eqhf2, .{ .name = "__eqhf2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__nehf2, .{ .name = "__nehf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/cmpsf2.zig b/lib/compiler_rt/cmpsf2.zig
index 371319b072ab263938c32a9574b2b135582c49fd..4b200198e662fcdab160b1f5ab79b1914b6c4d65 100644
--- a/lib/compiler_rt/cmpsf2.zig
+++ b/lib/compiler_rt/cmpsf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/cmptf2.zig b/lib/compiler_rt/cmptf2.zig
index d5ee7970d4753d24181572330a67631c67793bce..3188ecf90ec5f180fc500f39530a432354135673 100644
--- a/lib/compiler_rt/cmptf2.zig
+++ b/lib/compiler_rt/cmptf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__eqtf2, .{ .name = "__eqkf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/cmpxf2.zig b/lib/compiler_rt/cmpxf2.zig
index dc90f87c340e98ac0f1bafd480ce8cbbfdab6264..f70de9c0b617eacdb8a9d1a751280c980b07f79c 100644
--- a/lib/compiler_rt/cmpxf2.zig
+++ b/lib/compiler_rt/cmpxf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__eqxf2, .{ .name = "__eqxf2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__nexf2, .{ .name = "__nexf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig
index d74eab18d8fb24bfb89146358675920cdfe9f6cb..a9b4c2296c5974415af57e3d2dc1aba2941f96cf 100644
--- a/lib/compiler_rt/common.zig
+++ b/lib/compiler_rt/common.zig
@@ -126,10 +126,6 @@ pub const test_safety = switch (builtin.zig_backend) {
else => builtin.is_test,
};
-// Avoid dragging in the runtime safety mechanisms into this .o file, unless
-// we're trying to test compiler-rt.
-pub const panic = if (test_safety) std.debug.FullPanic(std.debug.defaultPanic) else std.debug.no_panic;
-
/// This seems to mostly correspond to `clang::TargetInfo::HasFloat16`.
pub fn F16T(comptime OtherType: type) type {
return switch (builtin.cpu.arch) {
diff --git a/lib/compiler_rt/cos.zig b/lib/compiler_rt/cos.zig
index 01a5196232aba4a591957e9f0b083064e7479e4f..3384887385f0faeb4ad3c1b0b5f35e7a189ceb31 100644
--- a/lib/compiler_rt/cos.zig
+++ b/lib/compiler_rt/cos.zig
@@ -4,8 +4,6 @@ const mem = std.mem;
const expect = std.testing.expect;
const common = @import("common.zig");
-pub const panic = common.panic;
-
const trig = @import("trig.zig");
const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
diff --git a/lib/compiler_rt/count0bits.zig b/lib/compiler_rt/count0bits.zig
index 874604eb2c60c240e1d4aee1721c7ecbdb62bf0b..3696c899447e5328f1b13b369a0f9e41f1924ebe 100644
--- a/lib/compiler_rt/count0bits.zig
+++ b/lib/compiler_rt/count0bits.zig
@@ -2,8 +2,6 @@ const std = @import("std");
const builtin = @import("builtin");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__clzsi2, .{ .name = "__clzsi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__clzdi2, .{ .name = "__clzdi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/divdf3.zig b/lib/compiler_rt/divdf3.zig
index 7b47cd3a703c0e55e3b630bad6b132383cb812ab..a94544165bdfc11531e877a7c919e6085e60fb39 100644
--- a/lib/compiler_rt/divdf3.zig
+++ b/lib/compiler_rt/divdf3.zig
@@ -10,8 +10,6 @@ const common = @import("common.zig");
const normalize = common.normalize;
const wideMultiply = common.wideMultiply;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/divsf3.zig b/lib/compiler_rt/divsf3.zig
index a74d3f7abdba7e3b9e205b2e6aa56e1bc7433c62..dca1d49ebe0cdd0f8e1c66fd758ba8f91ea10a09 100644
--- a/lib/compiler_rt/divsf3.zig
+++ b/lib/compiler_rt/divsf3.zig
@@ -9,8 +9,6 @@ const arch = builtin.cpu.arch;
const common = @import("common.zig");
const normalize = common.normalize;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/divtf3.zig b/lib/compiler_rt/divtf3.zig
index 0bab96de3996960269b80bc0e5a471c3ebe4cba7..6606d0de3808cfc36917c3dc55348106d851eaa1 100644
--- a/lib/compiler_rt/divtf3.zig
+++ b/lib/compiler_rt/divtf3.zig
@@ -5,8 +5,6 @@ const common = @import("common.zig");
const normalize = common.normalize;
const wideMultiply = common.wideMultiply;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__divtf3, .{ .name = "__divkf3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/divti3.zig b/lib/compiler_rt/divti3.zig
index 5f24d196d721a700ae1c65e3fcea8130f9206573..9eb8dda18741793935b231748f1994b0d19809b0 100644
--- a/lib/compiler_rt/divti3.zig
+++ b/lib/compiler_rt/divti3.zig
@@ -4,8 +4,6 @@ const udivmod = @import("udivmod.zig").udivmod;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/divxf3.zig b/lib/compiler_rt/divxf3.zig
index 1579db226cc6f93c7183ea0b2d650df311e3b110..069510a2b6872f33db80117c6c9ba873d6b0927d 100644
--- a/lib/compiler_rt/divxf3.zig
+++ b/lib/compiler_rt/divxf3.zig
@@ -6,8 +6,6 @@ const common = @import("common.zig");
const normalize = common.normalize;
const wideMultiply = common.wideMultiply;
-pub const panic = common.panic;
-
comptime {
@export(&__divxf3, .{ .name = "__divxf3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/emutls.zig b/lib/compiler_rt/emutls.zig
index d081aa92943e7f60401b79dda53c8810c963e497..c52ce020edddb269e4f0f52e789e6381092384cd 100644
--- a/lib/compiler_rt/emutls.zig
+++ b/lib/compiler_rt/emutls.zig
@@ -15,8 +15,6 @@ const expect = std.testing.expect;
/// typedef unsigned int gcc_word __attribute__((mode(word)));
const gcc_word = usize;
-pub const panic = common.panic;
-
comptime {
if (builtin.link_libc and (builtin.abi.isAndroid() or builtin.abi.isOpenHarmony() or builtin.os.tag == .openbsd)) {
@export(&__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/exp.zig b/lib/compiler_rt/exp.zig
index fa4356a3366f330b8320913aec2c6230815625c8..f5c55369302434b3bfd42f66ec686543d6b27617 100644
--- a/lib/compiler_rt/exp.zig
+++ b/lib/compiler_rt/exp.zig
@@ -13,8 +13,6 @@ const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__exph, .{ .name = "__exph", .linkage = common.linkage, .visibility = common.visibility });
@export(&expf, .{ .name = "expf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/exp2.zig b/lib/compiler_rt/exp2.zig
index ce79dff497481bc03553c6366bfa34eb05c30180..a0a4f757e623b4bbd5d6c5052353d745caf86617 100644
--- a/lib/compiler_rt/exp2.zig
+++ b/lib/compiler_rt/exp2.zig
@@ -13,8 +13,6 @@ const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__exp2h, .{ .name = "__exp2h", .linkage = common.linkage, .visibility = common.visibility });
@export(&exp2f, .{ .name = "exp2f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/extenddftf2.zig b/lib/compiler_rt/extenddftf2.zig
index 457a64522a9a9490a27a2e75ff5c6bdf6d0200f8..9ef552c2c55032bcca12bb99c7f3dbbf3f121e5e 100644
--- a/lib/compiler_rt/extenddftf2.zig
+++ b/lib/compiler_rt/extenddftf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extendf = @import("./extendf.zig").extendf;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__extenddftf2, .{ .name = "__extenddfkf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/extenddfxf2.zig b/lib/compiler_rt/extenddfxf2.zig
index 55a1df56623842f377c37fc1d7a30258fbd98209..1d7bc998d76d2f65c738fa6cc22dcd7f2faf09c4 100644
--- a/lib/compiler_rt/extenddfxf2.zig
+++ b/lib/compiler_rt/extenddfxf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extend_f80 = @import("./extendf.zig").extend_f80;
-pub const panic = common.panic;
-
comptime {
@export(&__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/extendhfdf2.zig b/lib/compiler_rt/extendhfdf2.zig
index 7b1a7929f7edf4a578876f3573befe42f43bafcb..3b3b65ec114ceb3e11d743fa664b794c3bc2b773 100644
--- a/lib/compiler_rt/extendhfdf2.zig
+++ b/lib/compiler_rt/extendhfdf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extendf = @import("./extendf.zig").extendf;
-pub const panic = common.panic;
-
comptime {
@export(&__extendhfdf2, .{ .name = "__extendhfdf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/extendhfsf2.zig b/lib/compiler_rt/extendhfsf2.zig
index 37f3d79c7af55ad1837b44df7a03469ac9110b11..f4f8be4305c846f3f5d9d748c9b7c2b383aa5768 100644
--- a/lib/compiler_rt/extendhfsf2.zig
+++ b/lib/compiler_rt/extendhfsf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extendf = @import("./extendf.zig").extendf;
-pub const panic = common.panic;
-
comptime {
if (common.gnu_f16_abi) {
@export(&__gnu_h2f_ieee, .{ .name = "__gnu_h2f_ieee", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/extendhftf2.zig b/lib/compiler_rt/extendhftf2.zig
index 3b1f257fa9aaaea09633ebfb2dbda751dee21ee7..a94b849a5a2c3416e326bfe9d3e5bd041a468174 100644
--- a/lib/compiler_rt/extendhftf2.zig
+++ b/lib/compiler_rt/extendhftf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extendf = @import("./extendf.zig").extendf;
-pub const panic = common.panic;
-
comptime {
@export(&__extendhftf2, .{ .name = "__extendhftf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/extendhfxf2.zig b/lib/compiler_rt/extendhfxf2.zig
index 74e4fb64cc3c29049684fe4e5234b4a5b5fbc25f..5e205b1f888808ed29d7c8e189c8213ed70817b8 100644
--- a/lib/compiler_rt/extendhfxf2.zig
+++ b/lib/compiler_rt/extendhfxf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extend_f80 = @import("./extendf.zig").extend_f80;
-pub const panic = common.panic;
-
comptime {
@export(&__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/extendsfdf2.zig b/lib/compiler_rt/extendsfdf2.zig
index c87b721cdd4014c85be1f41cb1f284fbf2ac00cd..9cc0a436633d5b3910bc2e8b4c790393395cbd10 100644
--- a/lib/compiler_rt/extendsfdf2.zig
+++ b/lib/compiler_rt/extendsfdf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extendf = @import("./extendf.zig").extendf;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/extendsftf2.zig b/lib/compiler_rt/extendsftf2.zig
index 4e45f85029705509d1d9d1320cbd412eb94c83e5..3690a0733c87db737d1ae6c7508a9c5decf0a0a9 100644
--- a/lib/compiler_rt/extendsftf2.zig
+++ b/lib/compiler_rt/extendsftf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extendf = @import("./extendf.zig").extendf;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__extendsftf2, .{ .name = "__extendsfkf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/extendsfxf2.zig b/lib/compiler_rt/extendsfxf2.zig
index b883034d4eb2477d10e05432d4d51e652a544b81..8665c445f770c60c7c9d2af732c8c46f0a9ddf50 100644
--- a/lib/compiler_rt/extendsfxf2.zig
+++ b/lib/compiler_rt/extendsfxf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const extend_f80 = @import("./extendf.zig").extend_f80;
-pub const panic = common.panic;
-
comptime {
@export(&__extendsfxf2, .{ .name = "__extendsfxf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/extendxftf2.zig b/lib/compiler_rt/extendxftf2.zig
index 39d66d399ca73f0390183b3270df9fea9a3af64d..cec1fa1f7ce9891734177d7baab3776307bd4657 100644
--- a/lib/compiler_rt/extendxftf2.zig
+++ b/lib/compiler_rt/extendxftf2.zig
@@ -1,8 +1,6 @@
const std = @import("std");
const common = @import("./common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__extendxftf2, .{ .name = "__extendxftf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fabs.zig b/lib/compiler_rt/fabs.zig
index 31c6cb97936255c01ed0a004ad14cf4811ffe82d..9a10f4ce617a62e3e0e0c8a3f78f55053855d6de 100644
--- a/lib/compiler_rt/fabs.zig
+++ b/lib/compiler_rt/fabs.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__fabsh, .{ .name = "__fabsh", .linkage = common.linkage, .visibility = common.visibility });
@export(&fabsf, .{ .name = "fabsf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixdfdi.zig b/lib/compiler_rt/fixdfdi.zig
index a92c94504389835f53fa1c0f4ca215bc5103a961..97f3be044e2be76206a67d30083b0f54584c66af 100644
--- a/lib/compiler_rt/fixdfdi.zig
+++ b/lib/compiler_rt/fixdfdi.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixdfei.zig b/lib/compiler_rt/fixdfei.zig
index 31c7994c58c59062bf8c67cbfbe856eddbb7f503..3565cb45ba2bf2d692cd66273af486c70268611a 100644
--- a/lib/compiler_rt/fixdfei.zig
+++ b/lib/compiler_rt/fixdfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixdfei, .{ .name = "__fixdfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixdfsi.zig b/lib/compiler_rt/fixdfsi.zig
index 5076cdc4c07da8f8cb77a3efaa554fab774c1608..b1b26867b9c1899c73efe201c43f04ee95da3a58 100644
--- a/lib/compiler_rt/fixdfsi.zig
+++ b/lib/compiler_rt/fixdfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixdfti.zig b/lib/compiler_rt/fixdfti.zig
index 46b17505fc5fcab49723c0d17b12426067725011..499811c5511a30c9724aca2fab257efe4b470465 100644
--- a/lib/compiler_rt/fixdfti.zig
+++ b/lib/compiler_rt/fixdfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixdfti_windows_x86_64, .{ .name = "__fixdfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixhfdi.zig b/lib/compiler_rt/fixhfdi.zig
index 154bb501ac8a7615bac9638fa65e5c0cf98ee09a..3bcb848972d90eb84012ba83b47e19d4bee78585 100644
--- a/lib/compiler_rt/fixhfdi.zig
+++ b/lib/compiler_rt/fixhfdi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixhfdi, .{ .name = "__fixhfdi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixhfei.zig b/lib/compiler_rt/fixhfei.zig
index c7f30055c561bb319b83341298eb06ecdd654395..1eef56073fc6331fc74c3c19cd69e95290e4070c 100644
--- a/lib/compiler_rt/fixhfei.zig
+++ b/lib/compiler_rt/fixhfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixhfei, .{ .name = "__fixhfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixhfsi.zig b/lib/compiler_rt/fixhfsi.zig
index 253e7b64501bf28dd7da1ab57987284216a647f6..658f13bb71903e92f400bd7a01e2c8b91c3ee703 100644
--- a/lib/compiler_rt/fixhfsi.zig
+++ b/lib/compiler_rt/fixhfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixhfsi, .{ .name = "__fixhfsi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixhfti.zig b/lib/compiler_rt/fixhfti.zig
index 01586578fef9b9f5772304367c01f2c398227c74..1c51ebc056482625c0301cd75d6a552e9702a594 100644
--- a/lib/compiler_rt/fixhfti.zig
+++ b/lib/compiler_rt/fixhfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixhfti_windows_x86_64, .{ .name = "__fixhfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixsfdi.zig b/lib/compiler_rt/fixsfdi.zig
index f0f7bb01e3fe50a39a44fe0233406dc265be6448..894d5a0c1353b6a1890decd86d4ba7d41f2366b8 100644
--- a/lib/compiler_rt/fixsfdi.zig
+++ b/lib/compiler_rt/fixsfdi.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixsfei.zig b/lib/compiler_rt/fixsfei.zig
index c72e002fb1ffb40e8d66435d6d75f96e9f69953c..efbccfa29956506c7111750aa8f2a972f19bf8a3 100644
--- a/lib/compiler_rt/fixsfei.zig
+++ b/lib/compiler_rt/fixsfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixsfei, .{ .name = "__fixsfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixsfsi.zig b/lib/compiler_rt/fixsfsi.zig
index fb83d6922793a831b3db44c1410eb3923c743c74..abb856436985d3cd99d51fcfe02f830dca4ad84b 100644
--- a/lib/compiler_rt/fixsfsi.zig
+++ b/lib/compiler_rt/fixsfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixsfti.zig b/lib/compiler_rt/fixsfti.zig
index e1d4e7188caf8dfc9545afe265533e977cb4e673..82b1db70dc9409cab195e2dc0a19117047033bf8 100644
--- a/lib/compiler_rt/fixsfti.zig
+++ b/lib/compiler_rt/fixsfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixsfti_windows_x86_64, .{ .name = "__fixsfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixtfdi.zig b/lib/compiler_rt/fixtfdi.zig
index ebb7484c625731075ebe83ee409b73cf8d7b4b33..5c41f56595e5bdcddb55fdc63caab5836f0a22f3 100644
--- a/lib/compiler_rt/fixtfdi.zig
+++ b/lib/compiler_rt/fixtfdi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__fixtfdi, .{ .name = "__fixkfdi", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixtfei.zig b/lib/compiler_rt/fixtfei.zig
index 8daf3754f77e991d9253fe2a06a81e14b8f783a8..980db4c614f89bdef90ad03ea892861724e334ea 100644
--- a/lib/compiler_rt/fixtfei.zig
+++ b/lib/compiler_rt/fixtfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixtfei, .{ .name = "__fixtfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixtfsi.zig b/lib/compiler_rt/fixtfsi.zig
index a6d2daf00c189445a53b06b4aa300d82153d6dc0..0d525cf8c4c442175fa7ebf39af2711823f74243 100644
--- a/lib/compiler_rt/fixtfsi.zig
+++ b/lib/compiler_rt/fixtfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__fixtfsi, .{ .name = "__fixkfsi", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixtfti.zig b/lib/compiler_rt/fixtfti.zig
index 44f88e8dd060dd267cc758c37d5451201c3db565..fe71a0f08906d929771ff367b97921b6044a05b8 100644
--- a/lib/compiler_rt/fixtfti.zig
+++ b/lib/compiler_rt/fixtfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixtfti_windows_x86_64, .{ .name = "__fixtfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunsdfdi.zig b/lib/compiler_rt/fixunsdfdi.zig
index 52577361a6889c489a0bab81ab5701fb087a70a9..211963bf3e6f3db00d5db224a7483a044ee44b54 100644
--- a/lib/compiler_rt/fixunsdfdi.zig
+++ b/lib/compiler_rt/fixunsdfdi.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunsdfei.zig b/lib/compiler_rt/fixunsdfei.zig
index 886feab66aa5cc5ac1e46068ec599672950f8de7..be5bbf0a8a7a4c51c72609fe76e93b76ff81012f 100644
--- a/lib/compiler_rt/fixunsdfei.zig
+++ b/lib/compiler_rt/fixunsdfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunsdfei, .{ .name = "__fixunsdfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunsdfsi.zig b/lib/compiler_rt/fixunsdfsi.zig
index 246cbf97eb2441031adc856bdae57c55102a7b22..a8e9197361a79ec78d5d051308155dea204fd60f 100644
--- a/lib/compiler_rt/fixunsdfsi.zig
+++ b/lib/compiler_rt/fixunsdfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunsdfti.zig b/lib/compiler_rt/fixunsdfti.zig
index b4429a9d7622552c64b72795ad713d347ba58c78..28b79a0733b0cb8a881b4c5556d7ce32690d1069 100644
--- a/lib/compiler_rt/fixunsdfti.zig
+++ b/lib/compiler_rt/fixunsdfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixunsdfti_windows_x86_64, .{ .name = "__fixunsdfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunshfdi.zig b/lib/compiler_rt/fixunshfdi.zig
index dca13e17c4e4c4507245f156ce977e279923ff9d..a949ee99045d07e3a007c074bf974fcb95c28fb6 100644
--- a/lib/compiler_rt/fixunshfdi.zig
+++ b/lib/compiler_rt/fixunshfdi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunshfdi, .{ .name = "__fixunshfdi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunshfei.zig b/lib/compiler_rt/fixunshfei.zig
index 788605a6868e56e81b1cd94e3582d2cc02dcada0..4cb7ef2ba8da0c8d9862cd3ea0e9425429834fba 100644
--- a/lib/compiler_rt/fixunshfei.zig
+++ b/lib/compiler_rt/fixunshfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunshfei, .{ .name = "__fixunshfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunshfsi.zig b/lib/compiler_rt/fixunshfsi.zig
index 27db84357fae02da10ddfa6b04409796acb64dcf..fca40d1b0a33f07b577ac1bb40388e55b03dd5dd 100644
--- a/lib/compiler_rt/fixunshfsi.zig
+++ b/lib/compiler_rt/fixunshfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunshfsi, .{ .name = "__fixunshfsi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunshfti.zig b/lib/compiler_rt/fixunshfti.zig
index c92039212d33682903b4d2a597f8b23e0a384cd8..c61be597e772b6abebe139712a00c0f251747c6b 100644
--- a/lib/compiler_rt/fixunshfti.zig
+++ b/lib/compiler_rt/fixunshfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixunshfti_windows_x86_64, .{ .name = "__fixunshfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunssfdi.zig b/lib/compiler_rt/fixunssfdi.zig
index e05d0377e06f963c1c88c61d3a9e5611b6961b02..f12ec1cd5a9d93ca82e4f02e91c7550f90e44fe4 100644
--- a/lib/compiler_rt/fixunssfdi.zig
+++ b/lib/compiler_rt/fixunssfdi.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunssfei.zig b/lib/compiler_rt/fixunssfei.zig
index bb89fc283deabb6debc74987169abfd777b45091..0fb957ed1f79ed31b5f27f5f1b368fec7b4655a6 100644
--- a/lib/compiler_rt/fixunssfei.zig
+++ b/lib/compiler_rt/fixunssfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunssfei, .{ .name = "__fixunssfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunssfsi.zig b/lib/compiler_rt/fixunssfsi.zig
index d0038add1bb440e34a17f8038562a97d7e22f408..4def4b867fea5cb931fa4af64b040a5d6446f31d 100644
--- a/lib/compiler_rt/fixunssfsi.zig
+++ b/lib/compiler_rt/fixunssfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunssfti.zig b/lib/compiler_rt/fixunssfti.zig
index 3137fb3bc616a5f2deaa8fa84cdd3264cfbbcfb7..693442417c77cab0c74bff9135025ab86bcde86f 100644
--- a/lib/compiler_rt/fixunssfti.zig
+++ b/lib/compiler_rt/fixunssfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixunssfti_windows_x86_64, .{ .name = "__fixunssfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunstfdi.zig b/lib/compiler_rt/fixunstfdi.zig
index 87a06bc37b2b1afd1f266fb5c0a5e33478a3aaef..f1e1822a311831e70993b892e3ecf8e417f5628f 100644
--- a/lib/compiler_rt/fixunstfdi.zig
+++ b/lib/compiler_rt/fixunstfdi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunstfei.zig b/lib/compiler_rt/fixunstfei.zig
index 997a100afacc188790560cc5f9aeb4e8ce6ab565..84ac5010c96e9c3113eec67e9ca6f5291787f9e8 100644
--- a/lib/compiler_rt/fixunstfei.zig
+++ b/lib/compiler_rt/fixunstfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunstfei, .{ .name = "__fixunstfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunstfsi.zig b/lib/compiler_rt/fixunstfsi.zig
index 8a57e2dbc1071c33b37b1c2cbb0e0251a515948e..d3a9bfbed85fa07355dd69fceed197ae5ebfb0d9 100644
--- a/lib/compiler_rt/fixunstfsi.zig
+++ b/lib/compiler_rt/fixunstfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunstfti.zig b/lib/compiler_rt/fixunstfti.zig
index 2c182e3a449f44668f24c82290ab9477ba50a018..92fcd569dc6d939305bbc0daf3c3fa6c52978853 100644
--- a/lib/compiler_rt/fixunstfti.zig
+++ b/lib/compiler_rt/fixunstfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixunstfti_windows_x86_64, .{ .name = "__fixunstfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixunsxfdi.zig b/lib/compiler_rt/fixunsxfdi.zig
index 1e10786fc879f918f49ac41c64a4b0423ee400aa..4dffd0253ee533db22eba53b5c5e6334cf0bf593 100644
--- a/lib/compiler_rt/fixunsxfdi.zig
+++ b/lib/compiler_rt/fixunsxfdi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunsxfei.zig b/lib/compiler_rt/fixunsxfei.zig
index 88c7f1824388a68c41dc86cc94cb1adae965da65..dc795e74c691e6cb6c0927ef703163aa5481f5b6 100644
--- a/lib/compiler_rt/fixunsxfei.zig
+++ b/lib/compiler_rt/fixunsxfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunsxfei, .{ .name = "__fixunsxfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunsxfsi.zig b/lib/compiler_rt/fixunsxfsi.zig
index c876ea0d3e07cb1ee454da430e4950c847116354..8f900076c728c85d317746acd237c3003f184543 100644
--- a/lib/compiler_rt/fixunsxfsi.zig
+++ b/lib/compiler_rt/fixunsxfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixunsxfti.zig b/lib/compiler_rt/fixunsxfti.zig
index bfe8a2c6e2a632c492257e560dcbda98a1090d5b..39dde2ca1222f8f776f308fbfa1e4802f90c66ef 100644
--- a/lib/compiler_rt/fixunsxfti.zig
+++ b/lib/compiler_rt/fixunsxfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixunsxfti_windows_x86_64, .{ .name = "__fixunsxfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fixxfdi.zig b/lib/compiler_rt/fixxfdi.zig
index 2477a4bb834d2c5b4fcb69fd9d39226323f7a7b4..155fead1bbe18c813a7a641700c23f3e6e41c431 100644
--- a/lib/compiler_rt/fixxfdi.zig
+++ b/lib/compiler_rt/fixxfdi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixxfdi, .{ .name = "__fixxfdi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixxfei.zig b/lib/compiler_rt/fixxfei.zig
index 6f28153a66342017354df57144f3b301f26df948..75edfad035cbb30fea542e043343de9570a32f67 100644
--- a/lib/compiler_rt/fixxfei.zig
+++ b/lib/compiler_rt/fixxfei.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixxfei, .{ .name = "__fixxfei", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixxfsi.zig b/lib/compiler_rt/fixxfsi.zig
index 846df4d00667a09cb4aef42be0fcb76b19df9cd4..8a645bdae1fbe57c78398827589e4b070d563611 100644
--- a/lib/compiler_rt/fixxfsi.zig
+++ b/lib/compiler_rt/fixxfsi.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
@export(&__fixxfsi, .{ .name = "__fixxfsi", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/fixxfti.zig b/lib/compiler_rt/fixxfti.zig
index e1db47dc338c61fe938bf3c230c958aaf08427fa..292b9688e7fa1b627edf9943915dba087141bdc7 100644
--- a/lib/compiler_rt/fixxfti.zig
+++ b/lib/compiler_rt/fixxfti.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const intFromFloat = @import("./int_from_float.zig").intFromFloat;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__fixxfti_windows_x86_64, .{ .name = "__fixxfti", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatdidf.zig b/lib/compiler_rt/floatdidf.zig
index 38890e35a4e4e7888f59e4005bc65d01a75345db..f614521478da14f27429c1a413d247d1c035c7e5 100644
--- a/lib/compiler_rt/floatdidf.zig
+++ b/lib/compiler_rt/floatdidf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatdihf.zig b/lib/compiler_rt/floatdihf.zig
index 37a03e4ed35523bf98dba187b4c9c8be86c3a34a..fd534e5946202527e9b6ce1cef31e970230c384b 100644
--- a/lib/compiler_rt/floatdihf.zig
+++ b/lib/compiler_rt/floatdihf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatdihf, .{ .name = "__floatdihf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatdisf.zig b/lib/compiler_rt/floatdisf.zig
index a942d7b0b1f6a46865671c570614f28c6931ad5c..1efd9c937ff2b2849db3be096696bff395d55f0a 100644
--- a/lib/compiler_rt/floatdisf.zig
+++ b/lib/compiler_rt/floatdisf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatditf.zig b/lib/compiler_rt/floatditf.zig
index b5cc262fa35468c7f6975875aa3129349684f3c6..864361b7b3d567f959e1e6cde55e20b40a9d487c 100644
--- a/lib/compiler_rt/floatditf.zig
+++ b/lib/compiler_rt/floatditf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__floatditf, .{ .name = "__floatdikf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatdixf.zig b/lib/compiler_rt/floatdixf.zig
index eba666c05394061fa184cd9e1181c376cca0834e..8ac6f06fb5929319ad28e387377b120ae3e95e37 100644
--- a/lib/compiler_rt/floatdixf.zig
+++ b/lib/compiler_rt/floatdixf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatdixf, .{ .name = "__floatdixf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floateidf.zig b/lib/compiler_rt/floateidf.zig
index 96411ae878cc1d5787926a91080795988f51ce4a..6434743939ec3895302925bca84937d42d6a2287 100644
--- a/lib/compiler_rt/floateidf.zig
+++ b/lib/compiler_rt/floateidf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floateidf, .{ .name = "__floateidf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floateihf.zig b/lib/compiler_rt/floateihf.zig
index ac9fd355c7f6094b99d9100646c5301290354fa1..013e6d2d79cb1e444e68c10b8a8e24aa42d8d182 100644
--- a/lib/compiler_rt/floateihf.zig
+++ b/lib/compiler_rt/floateihf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floateihf, .{ .name = "__floateihf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floateisf.zig b/lib/compiler_rt/floateisf.zig
index f53b75643f7325e507e6e34ab114229022e17c92..eb24b6aa43123cd73d5ea61e89777fa1f66ef507 100644
--- a/lib/compiler_rt/floateisf.zig
+++ b/lib/compiler_rt/floateisf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floateisf, .{ .name = "__floateisf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floateitf.zig b/lib/compiler_rt/floateitf.zig
index 3e07c6becbf840d54f71076d91cd4d935e5043e3..e5b50ab7a6b34c44d6d0fe7029037e21114fad70 100644
--- a/lib/compiler_rt/floateitf.zig
+++ b/lib/compiler_rt/floateitf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floateitf, .{ .name = "__floateitf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floateixf.zig b/lib/compiler_rt/floateixf.zig
index 65585e2ed30cd8a598f2126befb0fe581b026fda..ffb8445fd337ae8d7311233675435f7e7a3af1f7 100644
--- a/lib/compiler_rt/floateixf.zig
+++ b/lib/compiler_rt/floateixf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floateixf, .{ .name = "__floateixf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatsidf.zig b/lib/compiler_rt/floatsidf.zig
index c3f249a4558a03905a9534c2eba924ecaf0bac3f..435c8db9a465184dc532cba1947a2dd7a4b8bb12 100644
--- a/lib/compiler_rt/floatsidf.zig
+++ b/lib/compiler_rt/floatsidf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatsihf.zig b/lib/compiler_rt/floatsihf.zig
index a5d5d2ba92e8d92dec6fa73974459feef95fbc23..5f2db12585d900e8cd9e36e3b760c2cabcad927b 100644
--- a/lib/compiler_rt/floatsihf.zig
+++ b/lib/compiler_rt/floatsihf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatsihf, .{ .name = "__floatsihf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatsisf.zig b/lib/compiler_rt/floatsisf.zig
index 133bfaabbd3aee3ea3c52be7350955cb2035674f..9c36387c668c1d4bd7aa3dc371368dab2f47c53a 100644
--- a/lib/compiler_rt/floatsisf.zig
+++ b/lib/compiler_rt/floatsisf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatsitf.zig b/lib/compiler_rt/floatsitf.zig
index 6a5f81ae218ecabe6ddaa766c57836ba45246ec3..750633052dcee4880f17f52684c0b4ee0d36a4dd 100644
--- a/lib/compiler_rt/floatsitf.zig
+++ b/lib/compiler_rt/floatsitf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__floatsitf, .{ .name = "__floatsikf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatsixf.zig b/lib/compiler_rt/floatsixf.zig
index 31791eb9cc89dd963d9c76bcab79cd4a131f832d..59bf7ddb99ffd827ab0fe52540ecf7af99a80477 100644
--- a/lib/compiler_rt/floatsixf.zig
+++ b/lib/compiler_rt/floatsixf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatsixf, .{ .name = "__floatsixf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floattidf.zig b/lib/compiler_rt/floattidf.zig
index 420ef9b201480e3062fd1b5f905bc28994c91836..2ac5726b5166ab233125ae44dcf74a4fe11eed5b 100644
--- a/lib/compiler_rt/floattidf.zig
+++ b/lib/compiler_rt/floattidf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floattidf_windows_x86_64, .{ .name = "__floattidf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floattihf.zig b/lib/compiler_rt/floattihf.zig
index 63f079b3a886eebf84b322c9daa71968608affa7..9ad705870e9eafbd49f2eb9c88d0d83bc7c5315a 100644
--- a/lib/compiler_rt/floattihf.zig
+++ b/lib/compiler_rt/floattihf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floattihf_windows_x86_64, .{ .name = "__floattihf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floattisf.zig b/lib/compiler_rt/floattisf.zig
index 284580c4b2cc683de00921cac6f8c332a2b19f47..953192f33115a103b27f48a56875ab7176c2af15 100644
--- a/lib/compiler_rt/floattisf.zig
+++ b/lib/compiler_rt/floattisf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floattisf_windows_x86_64, .{ .name = "__floattisf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floattitf.zig b/lib/compiler_rt/floattitf.zig
index c8d2090457fd63ee21b6eeef7e9bcc79c21bffc7..3973aa4ab294fea0b9f905194bebc914a9532cd5 100644
--- a/lib/compiler_rt/floattitf.zig
+++ b/lib/compiler_rt/floattitf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floattitf_windows_x86_64, .{ .name = "__floattitf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floattixf.zig b/lib/compiler_rt/floattixf.zig
index ebf83446d32243304c3ddbbacc5554896f036241..ac1df5ba32b359bbe414d6ec134744299d2e7786 100644
--- a/lib/compiler_rt/floattixf.zig
+++ b/lib/compiler_rt/floattixf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floattixf_windows_x86_64, .{ .name = "__floattixf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatundidf.zig b/lib/compiler_rt/floatundidf.zig
index 887ef83215943431098b12a172b071049a42429c..7a9e4afe7af40701c85b9347ddedf36ead437f17 100644
--- a/lib/compiler_rt/floatundidf.zig
+++ b/lib/compiler_rt/floatundidf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatundihf.zig b/lib/compiler_rt/floatundihf.zig
index 5fefedec1d83803e3b365203f36bd569616c81d3..e7a3f865f829ff28e7efaa805790d17a52f7d884 100644
--- a/lib/compiler_rt/floatundihf.zig
+++ b/lib/compiler_rt/floatundihf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatundihf, .{ .name = "__floatundihf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatundisf.zig b/lib/compiler_rt/floatundisf.zig
index 8537779996ebc147a23f04ef92fd385161a14757..89e7fa1f4bf65848bcc6abbdb422e4dc77a737dc 100644
--- a/lib/compiler_rt/floatundisf.zig
+++ b/lib/compiler_rt/floatundisf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatunditf.zig b/lib/compiler_rt/floatunditf.zig
index aadb25132810dbcf6f6e0fc557597fae355dfe0f..d313ff3d12a55290777a6af85ea4881e945eacf1 100644
--- a/lib/compiler_rt/floatunditf.zig
+++ b/lib/compiler_rt/floatunditf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__floatunditf, .{ .name = "__floatundikf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatundixf.zig b/lib/compiler_rt/floatundixf.zig
index 7f801fd6ba0ca8cb3ba89ca0a5f876179b7099f0..b9691e91448866757cca2b6278768b6a6e8e3bd5 100644
--- a/lib/compiler_rt/floatundixf.zig
+++ b/lib/compiler_rt/floatundixf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatundixf, .{ .name = "__floatundixf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatuneidf.zig b/lib/compiler_rt/floatuneidf.zig
index c7b1e032ea718474affc18b53886dd3484b947b9..bb8a6579cec5d9f48125d6c7cfe9bc8b1c7c51c7 100644
--- a/lib/compiler_rt/floatuneidf.zig
+++ b/lib/compiler_rt/floatuneidf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatuneidf, .{ .name = "__floatuneidf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatuneihf.zig b/lib/compiler_rt/floatuneihf.zig
index 00c53a19197cd15c7c97272f8ae0ba2ea34db49e..17b1e1d29059b1ad28302e94ad33b9335275c59b 100644
--- a/lib/compiler_rt/floatuneihf.zig
+++ b/lib/compiler_rt/floatuneihf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatuneihf, .{ .name = "__floatuneihf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatuneisf.zig b/lib/compiler_rt/floatuneisf.zig
index 4acb0fa7b234b8b66074b9ead9ac0fa811f5748d..bb43b6ee652c37bd7a2fbd6712edb636d70bde37 100644
--- a/lib/compiler_rt/floatuneisf.zig
+++ b/lib/compiler_rt/floatuneisf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatuneisf, .{ .name = "__floatuneisf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatuneitf.zig b/lib/compiler_rt/floatuneitf.zig
index 323f44611d23beed2266a90eef2abc6fb725a5c2..d84678ce115f88c3cb21cc5203fb162f6404a653 100644
--- a/lib/compiler_rt/floatuneitf.zig
+++ b/lib/compiler_rt/floatuneitf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatuneitf, .{ .name = "__floatuneitf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatuneixf.zig b/lib/compiler_rt/floatuneixf.zig
index 12157d20a34324791049f343f3ed212ddf113419..ad0b8f24de1db0f665ece481853fb5ba76f1e53a 100644
--- a/lib/compiler_rt/floatuneixf.zig
+++ b/lib/compiler_rt/floatuneixf.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatuneixf, .{ .name = "__floatuneixf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatunsidf.zig b/lib/compiler_rt/floatunsidf.zig
index c712428ce24f9a5fb7cbe0b821d48e35c77a0808..fc50a5ad37db3cda3be420f0479f3aab09461c2a 100644
--- a/lib/compiler_rt/floatunsidf.zig
+++ b/lib/compiler_rt/floatunsidf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatunsihf.zig b/lib/compiler_rt/floatunsihf.zig
index 5c5778a460b18632c57ab2a2a5b0053e5afb38ef..123b76fcde1b1175e74902f26e2ff820a2d6c662 100644
--- a/lib/compiler_rt/floatunsihf.zig
+++ b/lib/compiler_rt/floatunsihf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatunsihf, .{ .name = "__floatunsihf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatunsisf.zig b/lib/compiler_rt/floatunsisf.zig
index 9fa213ed227729bc4b33baa8a69b4e7733a42d80..4bcceba5c8c967a73312765468a98876a5b128c9 100644
--- a/lib/compiler_rt/floatunsisf.zig
+++ b/lib/compiler_rt/floatunsisf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatunsitf.zig b/lib/compiler_rt/floatunsitf.zig
index d09d31d05dc9c10f484c232bb0fd1af3a644bb3a..e491987003f054005b841a316f414412dfb9ecb8 100644
--- a/lib/compiler_rt/floatunsitf.zig
+++ b/lib/compiler_rt/floatunsitf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__floatunsitf, .{ .name = "__floatunsikf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatunsixf.zig b/lib/compiler_rt/floatunsixf.zig
index 678085ad6fcf0cf4bf732ca615bb27dd0ddf929c..875d3476fa99d0a2d96faa89ccb6f7e99c27232f 100644
--- a/lib/compiler_rt/floatunsixf.zig
+++ b/lib/compiler_rt/floatunsixf.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
@export(&__floatunsixf, .{ .name = "__floatunsixf", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/floatuntidf.zig b/lib/compiler_rt/floatuntidf.zig
index cee154ed0bee1912506bc8fb50c9534b9b2279e9..284e50dd367bd6dfed766842fffa0f8fecd43815 100644
--- a/lib/compiler_rt/floatuntidf.zig
+++ b/lib/compiler_rt/floatuntidf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floatuntidf_windows_x86_64, .{ .name = "__floatuntidf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatuntihf.zig b/lib/compiler_rt/floatuntihf.zig
index 63cfdb604facadb2e7e125d71d151819fe09a3d7..41da6f1bfb501f9b78ab65277cb4a49da40f6ad6 100644
--- a/lib/compiler_rt/floatuntihf.zig
+++ b/lib/compiler_rt/floatuntihf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floatuntihf_windows_x86_64, .{ .name = "__floatuntihf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatuntisf.zig b/lib/compiler_rt/floatuntisf.zig
index 1e65576f063e137458fb08e8eea65a0cfa167f31..f79d2e5feda54382074c9ca12cc32696aa1f6228 100644
--- a/lib/compiler_rt/floatuntisf.zig
+++ b/lib/compiler_rt/floatuntisf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floatuntisf_windows_x86_64, .{ .name = "__floatuntisf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatuntitf.zig b/lib/compiler_rt/floatuntitf.zig
index 41c48bee990f48498964b97d0e8fcd56666567e4..77dd22ed5978b6b86019d4c50ed7421003f7041a 100644
--- a/lib/compiler_rt/floatuntitf.zig
+++ b/lib/compiler_rt/floatuntitf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floatuntitf_windows_x86_64, .{ .name = "__floatuntitf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floatuntixf.zig b/lib/compiler_rt/floatuntixf.zig
index 353a3c3c0d15d85bba78c5ee079c0152581fda24..12a561b5732a2d8284ed81325c8e5304781fe1dd 100644
--- a/lib/compiler_rt/floatuntixf.zig
+++ b/lib/compiler_rt/floatuntixf.zig
@@ -2,8 +2,6 @@ const builtin = @import("builtin");
const common = @import("./common.zig");
const floatFromInt = @import("./float_from_int.zig").floatFromInt;
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__floatuntixf_windows_x86_64, .{ .name = "__floatuntixf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/floor.zig b/lib/compiler_rt/floor.zig
index ba1869089aa9877afec47e851a3fe357fd7dc4d3..7510abed663e71fe4d1d601546670546d1cab6b3 100644
--- a/lib/compiler_rt/floor.zig
+++ b/lib/compiler_rt/floor.zig
@@ -13,8 +13,6 @@ const expect = std.testing.expect;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__floorh, .{ .name = "__floorh", .linkage = common.linkage, .visibility = common.visibility });
@export(&floorf, .{ .name = "floorf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fma.zig b/lib/compiler_rt/fma.zig
index 79a120d5a2ccdde4c037297e677420b065a24b04..cce9215f881d1d0aad575e1afa17c9e50a08fe6d 100644
--- a/lib/compiler_rt/fma.zig
+++ b/lib/compiler_rt/fma.zig
@@ -10,8 +10,6 @@ const math = std.math;
const expect = std.testing.expect;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__fmah, .{ .name = "__fmah", .linkage = common.linkage, .visibility = common.visibility });
@export(&fmaf, .{ .name = "fmaf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fmax.zig b/lib/compiler_rt/fmax.zig
index 02e7d1d75bbee6a5e966bebf68dba1b39c88e22b..19a7c3f6f7899021efddc3d458d39a52403c056e 100644
--- a/lib/compiler_rt/fmax.zig
+++ b/lib/compiler_rt/fmax.zig
@@ -4,8 +4,6 @@ const math = std.math;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__fmaxh, .{ .name = "__fmaxh", .linkage = common.linkage, .visibility = common.visibility });
@export(&fmaxf, .{ .name = "fmaxf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fmin.zig b/lib/compiler_rt/fmin.zig
index 71f9ef589a7640ccf0732e255418c609656465b3..aa54ba6e0b081a0bdcedceaf79c674abb1591d3c 100644
--- a/lib/compiler_rt/fmin.zig
+++ b/lib/compiler_rt/fmin.zig
@@ -4,8 +4,6 @@ const math = std.math;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__fminh, .{ .name = "__fminh", .linkage = common.linkage, .visibility = common.visibility });
@export(&fminf, .{ .name = "fminf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/fmod.zig b/lib/compiler_rt/fmod.zig
index 5c055049e3ffb3b63bc9806ebca2d8af7e5acd34..ae5abaae65f68021638dec7948fc729d302bc589 100644
--- a/lib/compiler_rt/fmod.zig
+++ b/lib/compiler_rt/fmod.zig
@@ -6,8 +6,6 @@ const arch = builtin.cpu.arch;
const common = @import("common.zig");
const normalize = common.normalize;
-pub const panic = common.panic;
-
comptime {
@export(&__fmodh, .{ .name = "__fmodh", .linkage = common.linkage, .visibility = common.visibility });
@export(&fmodf, .{ .name = "fmodf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/gedf2.zig b/lib/compiler_rt/gedf2.zig
index 33b4fa992609a3b08164037a1ee463c6db37ef6a..aeb08b4a8b9eae9979b99ba37db0db4047992a92 100644
--- a/lib/compiler_rt/gedf2.zig
+++ b/lib/compiler_rt/gedf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/gehf2.zig b/lib/compiler_rt/gehf2.zig
index 12eccf98d824905f72c9f3babb1f648e31056807..b46d4a1a24d0038abc38ce6e6ca5d5dda8324ef8 100644
--- a/lib/compiler_rt/gehf2.zig
+++ b/lib/compiler_rt/gehf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__gehf2, .{ .name = "__gehf2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__gthf2, .{ .name = "__gthf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/gesf2.zig b/lib/compiler_rt/gesf2.zig
index 5f9aefef47958d31dc45f181d641756cf199b51f..d4b6a23c662db77423c5bf35c664ef0462fb4552 100644
--- a/lib/compiler_rt/gesf2.zig
+++ b/lib/compiler_rt/gesf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/getf2.zig b/lib/compiler_rt/getf2.zig
index 3a17843718c83435fdf89ece9b94951d6c5c6b54..a6014b365e56a2a68bb3bb9fb22bcfbad3f29ed2 100644
--- a/lib/compiler_rt/getf2.zig
+++ b/lib/compiler_rt/getf2.zig
@@ -3,8 +3,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__getf2, .{ .name = "__gekf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/gexf2.zig b/lib/compiler_rt/gexf2.zig
index 7ea0d6eebe5bee6fff210950a105e3d010acd061..365c298cc6161fd39f5360fedbbc0d70e6fa7c11 100644
--- a/lib/compiler_rt/gexf2.zig
+++ b/lib/compiler_rt/gexf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__gexf2, .{ .name = "__gexf2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__gtxf2, .{ .name = "__gtxf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/int.zig b/lib/compiler_rt/int.zig
index 16c504ee66d1eb545d2629a8f54174da37c6a82d..3f38bad973d55ac78551fcda15deb1b2341e2c3c 100644
--- a/lib/compiler_rt/int.zig
+++ b/lib/compiler_rt/int.zig
@@ -10,8 +10,6 @@ const common = @import("common.zig");
const udivmod = @import("udivmod.zig").udivmod;
const __divti3 = @import("divti3.zig").__divti3;
-pub const panic = common.panic;
-
comptime {
@export(&__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility });
@export(&__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/log.zig b/lib/compiler_rt/log.zig
index f8bbf430ace402be8ffe1452264ab1d3ecce15d6..d2644d65dbc3889c20063134b6f2f8103bf79af9 100644
--- a/lib/compiler_rt/log.zig
+++ b/lib/compiler_rt/log.zig
@@ -12,8 +12,6 @@ const expectEqual = std.testing.expectEqual;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__logh, .{ .name = "__logh", .linkage = common.linkage, .visibility = common.visibility });
@export(&logf, .{ .name = "logf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/log10.zig b/lib/compiler_rt/log10.zig
index 1c2ce4bbcabf4f7c6a04646cc8e9b9a914099552..5c55bb3f51a27b3d3b44660673ee2c4aa665a05b 100644
--- a/lib/compiler_rt/log10.zig
+++ b/lib/compiler_rt/log10.zig
@@ -13,8 +13,6 @@ const maxInt = std.math.maxInt;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__log10h, .{ .name = "__log10h", .linkage = common.linkage, .visibility = common.visibility });
@export(&log10f, .{ .name = "log10f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/log2.zig b/lib/compiler_rt/log2.zig
index 4cedcfe0c1d284e7a3c03c293be1ef4a6cdd4231..5773b3b210f8c6bad150af9218951712f4e093b4 100644
--- a/lib/compiler_rt/log2.zig
+++ b/lib/compiler_rt/log2.zig
@@ -13,8 +13,6 @@ const maxInt = std.math.maxInt;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__log2h, .{ .name = "__log2h", .linkage = common.linkage, .visibility = common.visibility });
@export(&log2f, .{ .name = "log2f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/modti3.zig b/lib/compiler_rt/modti3.zig
index b73f3d05fa05c2a44bb7d3af4137e496cafdc26f..74e431a06166be0d1a4048400e8ebe3095345e98 100644
--- a/lib/compiler_rt/modti3.zig
+++ b/lib/compiler_rt/modti3.zig
@@ -7,8 +7,6 @@ const builtin = @import("builtin");
const udivmod = @import("udivmod.zig").udivmod;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/mulXi3.zig b/lib/compiler_rt/mulXi3.zig
index a96ef135c4d3a1dbf3dbebb0200a7589b973b909..198d51c8883875c28134e48eb60e667699e05c49 100644
--- a/lib/compiler_rt/mulXi3.zig
+++ b/lib/compiler_rt/mulXi3.zig
@@ -4,8 +4,6 @@ const testing = std.testing;
const common = @import("common.zig");
const native_endian = builtin.cpu.arch.endian();
-pub const panic = common.panic;
-
comptime {
@export(&__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility });
if (common.want_aeabi) {
diff --git a/lib/compiler_rt/muldc3.zig b/lib/compiler_rt/muldc3.zig
index 1c53b4d07397872fa2170e929f8afa92952f2514..9670c1312887c99a038540ed097bb22daede93f3 100644
--- a/lib/compiler_rt/muldc3.zig
+++ b/lib/compiler_rt/muldc3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulc3 = @import("./mulc3.zig");
-pub const panic = common.panic;
-
comptime {
if (@import("builtin").zig_backend != .stage2_c) {
@export(&__muldc3, .{ .name = "__muldc3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/muldf3.zig b/lib/compiler_rt/muldf3.zig
index 1fa4c9debbd2e5e7aab2fa917425327ffa3a43f5..cf67df0913ab624cb1f5a840f8da139e9d0d38a1 100644
--- a/lib/compiler_rt/muldf3.zig
+++ b/lib/compiler_rt/muldf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulf3 = @import("./mulf3.zig").mulf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/mulhc3.zig b/lib/compiler_rt/mulhc3.zig
index 4d97a0978dc5a6663f3dd46bbd5f923feb089ddb..09bca03a050c1281dedc68c1c94028d0f5e79dea 100644
--- a/lib/compiler_rt/mulhc3.zig
+++ b/lib/compiler_rt/mulhc3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulc3 = @import("./mulc3.zig");
-pub const panic = common.panic;
-
comptime {
if (@import("builtin").zig_backend != .stage2_c) {
@export(&__mulhc3, .{ .name = "__mulhc3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/mulhf3.zig b/lib/compiler_rt/mulhf3.zig
index 2731625a160e8bbd8891603d0b6c70029a2d4b5a..22f7a95627f9c82dbe3f9b818e062c7188cd8a44 100644
--- a/lib/compiler_rt/mulhf3.zig
+++ b/lib/compiler_rt/mulhf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulf3 = @import("./mulf3.zig").mulf3;
-pub const panic = common.panic;
-
comptime {
@export(&__mulhf3, .{ .name = "__mulhf3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/mulo.zig b/lib/compiler_rt/mulo.zig
index 20fbff3b96e4ecc1eb39dec897affafd278badc6..b85623eb7e5107984a9b52b1a47d0f40200f1218 100644
--- a/lib/compiler_rt/mulo.zig
+++ b/lib/compiler_rt/mulo.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const math = std.math;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__mulosi4, .{ .name = "__mulosi4", .linkage = common.linkage, .visibility = common.visibility });
@export(&__mulodi4, .{ .name = "__mulodi4", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/mulsc3.zig b/lib/compiler_rt/mulsc3.zig
index f011d2fe2319778a5a8b9b51b340ddb1dfe295f0..49c1e31aecd43e18961bf2c3853b8855993a917c 100644
--- a/lib/compiler_rt/mulsc3.zig
+++ b/lib/compiler_rt/mulsc3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulc3 = @import("./mulc3.zig");
-pub const panic = common.panic;
-
comptime {
if (@import("builtin").zig_backend != .stage2_c) {
@export(&__mulsc3, .{ .name = "__mulsc3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/mulsf3.zig b/lib/compiler_rt/mulsf3.zig
index 8929b5deba123448372928fe0f7a088bc702e442..a603652262bcfd7612abe196ac80d6a04dc1be35 100644
--- a/lib/compiler_rt/mulsf3.zig
+++ b/lib/compiler_rt/mulsf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulf3 = @import("./mulf3.zig").mulf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/multc3.zig b/lib/compiler_rt/multc3.zig
index cf3621919daee92be2fe27e0a7b256044e079631..1ed373660f81ef101d6b37086b456a11dd546707 100644
--- a/lib/compiler_rt/multc3.zig
+++ b/lib/compiler_rt/multc3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulc3 = @import("./mulc3.zig");
-pub const panic = common.panic;
-
comptime {
if (@import("builtin").zig_backend != .stage2_c) {
if (common.want_ppc_abi)
diff --git a/lib/compiler_rt/multf3.zig b/lib/compiler_rt/multf3.zig
index 0da8c506e8551b4378283fe0eac4653b1bc1ede2..6ef9cc2c2580ece0ab9f86495091c21e03a9275c 100644
--- a/lib/compiler_rt/multf3.zig
+++ b/lib/compiler_rt/multf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulf3 = @import("./mulf3.zig").mulf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__multf3, .{ .name = "__mulkf3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/mulvsi3.zig b/lib/compiler_rt/mulvsi3.zig
index d225552a582a7824639118d983c84fe2be5a9231..e773ea0d9ad756fe0d8a834ed04f4c79834fa171 100644
--- a/lib/compiler_rt/mulvsi3.zig
+++ b/lib/compiler_rt/mulvsi3.zig
@@ -2,8 +2,6 @@ const mulv = @import("mulo.zig");
const common = @import("./common.zig");
const testing = @import("std").testing;
-pub const panic = common.panic;
-
comptime {
@export(&__mulvsi3, .{ .name = "__mulvsi3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/mulxc3.zig b/lib/compiler_rt/mulxc3.zig
index ef6927ce089a5d38480cf41660f2acb5a94b7322..cbf9c640830519ad6058986202fa886a7ee5f15d 100644
--- a/lib/compiler_rt/mulxc3.zig
+++ b/lib/compiler_rt/mulxc3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulc3 = @import("./mulc3.zig");
-pub const panic = common.panic;
-
comptime {
if (@import("builtin").zig_backend != .stage2_c) {
@export(&__mulxc3, .{ .name = "__mulxc3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/mulxf3.zig b/lib/compiler_rt/mulxf3.zig
index 2db80b81b0102d6e032856f564afc2bbc5fe9ce0..1d9f465ebd053a498493f964f6406911384b72c7 100644
--- a/lib/compiler_rt/mulxf3.zig
+++ b/lib/compiler_rt/mulxf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const mulf3 = @import("./mulf3.zig").mulf3;
-pub const panic = common.panic;
-
comptime {
@export(&__mulxf3, .{ .name = "__mulxf3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/negXi2.zig b/lib/compiler_rt/negXi2.zig
index eee5202e71b8d0a53f2e4726fe68466d368fd917..7a57e988c6e5520a91595e89f4f160271638b35f 100644
--- a/lib/compiler_rt/negXi2.zig
+++ b/lib/compiler_rt/negXi2.zig
@@ -10,8 +10,6 @@ const std = @import("std");
const builtin = @import("builtin");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__negsi2, .{ .name = "__negsi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__negdi2, .{ .name = "__negdi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/negdf2.zig b/lib/compiler_rt/negdf2.zig
index 85e7f2bd82afbfcd0ee409bac49e97c44a7d30b8..b0ac26e6c0c5b8f6e946c38c81ccbd15c38da129 100644
--- a/lib/compiler_rt/negdf2.zig
+++ b/lib/compiler_rt/negdf2.zig
@@ -1,7 +1,5 @@
const common = @import("./common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/neghf2.zig b/lib/compiler_rt/neghf2.zig
index 86b28d48021dca0cf5e25132e42a7416564a1de1..59c7e155b72f714d28d7fa24b0b8860ca853e3ac 100644
--- a/lib/compiler_rt/neghf2.zig
+++ b/lib/compiler_rt/neghf2.zig
@@ -1,7 +1,5 @@
const common = @import("./common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__neghf2, .{ .name = "__neghf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/negsf2.zig b/lib/compiler_rt/negsf2.zig
index 08276e7f26bacfb5f5f15b13294c99f7d7a520ef..065dc3acd10607b31f7ae4f210c6d873761cf1bf 100644
--- a/lib/compiler_rt/negsf2.zig
+++ b/lib/compiler_rt/negsf2.zig
@@ -1,7 +1,5 @@
const common = @import("./common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/negtf2.zig b/lib/compiler_rt/negtf2.zig
index 9fb9384446591b8f107a1566e0a952fcdf389591..ecb5cc2c9007256061a7f102029e1d14c75d0494 100644
--- a/lib/compiler_rt/negtf2.zig
+++ b/lib/compiler_rt/negtf2.zig
@@ -1,7 +1,5 @@
const common = @import("./common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi)
@export(&__negtf2, .{ .name = "__negkf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/negv.zig b/lib/compiler_rt/negv.zig
index 6a2e717db1505f7a7b98be11116e6fdade939703..98934b78f424552476ddab4bf819db24bc48b33b 100644
--- a/lib/compiler_rt/negv.zig
+++ b/lib/compiler_rt/negv.zig
@@ -5,8 +5,6 @@ const std = @import("std");
const builtin = @import("builtin");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__negvsi2, .{ .name = "__negvsi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__negvdi2, .{ .name = "__negvdi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/negxf2.zig b/lib/compiler_rt/negxf2.zig
index 9a9c178654989ce9feb27528b92f19e3c3bc8427..ea16c8fef215c883559c16d06bf9766c02106fcb 100644
--- a/lib/compiler_rt/negxf2.zig
+++ b/lib/compiler_rt/negxf2.zig
@@ -1,7 +1,5 @@
const common = @import("./common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__negxf2, .{ .name = "__negxf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/parity.zig b/lib/compiler_rt/parity.zig
index 5a3e5a886d0eb8319900b4a5447b51f21f5afc06..71d50f327b178d01545fb9315e01aee2061970cf 100644
--- a/lib/compiler_rt/parity.zig
+++ b/lib/compiler_rt/parity.zig
@@ -5,8 +5,6 @@ const std = @import("std");
const builtin = @import("builtin");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__paritysi2, .{ .name = "__paritysi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__paritydi2, .{ .name = "__paritydi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/popcount.zig b/lib/compiler_rt/popcount.zig
index f937bd0ce05cf370ed768524844d5142460a6070..12ecda16cc1c2c41119017ccf32c1405f9a434f8 100644
--- a/lib/compiler_rt/popcount.zig
+++ b/lib/compiler_rt/popcount.zig
@@ -10,8 +10,6 @@ const builtin = @import("builtin");
const std = @import("std");
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__popcountsi2, .{ .name = "__popcountsi2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__popcountdi2, .{ .name = "__popcountdi2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/powiXf2.zig b/lib/compiler_rt/powiXf2.zig
index 21e7042a06b2bc5b9e832c1a71c9a34a6c7c7de2..b603998d556949d01c0915c51e2c50f72a509a82 100644
--- a/lib/compiler_rt/powiXf2.zig
+++ b/lib/compiler_rt/powiXf2.zig
@@ -7,8 +7,6 @@ const builtin = @import("builtin");
const common = @import("common.zig");
const std = @import("std");
-pub const panic = common.panic;
-
comptime {
@export(&__powihf2, .{ .name = "__powihf2", .linkage = common.linkage, .visibility = common.visibility });
@export(&__powisf2, .{ .name = "__powisf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/round.zig b/lib/compiler_rt/round.zig
index 9e8ea42d67c338e803dcf16f38832e85f7a39b15..b643bfd89fab0a9e277dfd0cecafc54d706595e1 100644
--- a/lib/compiler_rt/round.zig
+++ b/lib/compiler_rt/round.zig
@@ -12,8 +12,6 @@ const expect = std.testing.expect;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__roundh, .{ .name = "__roundh", .linkage = common.linkage, .visibility = common.visibility });
@export(&roundf, .{ .name = "roundf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/shift.zig b/lib/compiler_rt/shift.zig
index 17860da943a66af4d42a988d1c6e2423447f29ca..9c75bd3475fb2e4dddd31cee61f44cf46514ac15 100644
--- a/lib/compiler_rt/shift.zig
+++ b/lib/compiler_rt/shift.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const Log2Int = std.math.Log2Int;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
// symbol compatibility with libgcc
@export(&__ashlsi3, .{ .name = "__ashlsi3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/sin.zig b/lib/compiler_rt/sin.zig
index e14779d27a2b2cd74e847a6191dc38a8d38749d4..d25c1cd23ae194c3dabed9c497714aca5457e95e 100644
--- a/lib/compiler_rt/sin.zig
+++ b/lib/compiler_rt/sin.zig
@@ -16,8 +16,6 @@ const trig = @import("trig.zig");
const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
-pub const panic = common.panic;
-
comptime {
@export(&__sinh, .{ .name = "__sinh", .linkage = common.linkage, .visibility = common.visibility });
@export(&sinf, .{ .name = "sinf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/sincos.zig b/lib/compiler_rt/sincos.zig
index 72b90a51e70339fc4828a90719dcf770a468afcc..46fec8ff64c37ee972b998793f29742260709286 100644
--- a/lib/compiler_rt/sincos.zig
+++ b/lib/compiler_rt/sincos.zig
@@ -8,8 +8,6 @@ const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__sincosh, .{ .name = "__sincosh", .linkage = common.linkage, .visibility = common.visibility });
@export(&sincosf, .{ .name = "sincosf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/sqrt.zig b/lib/compiler_rt/sqrt.zig
index ed4602120b1efcf66aed14c0a8b84378abe1aaa7..2c321863682a547f405708cf97c52e0acf3f5a17 100644
--- a/lib/compiler_rt/sqrt.zig
+++ b/lib/compiler_rt/sqrt.zig
@@ -11,8 +11,6 @@ const arch = builtin.cpu.arch;
const math = std.math;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__sqrth, .{ .name = "__sqrth", .linkage = common.linkage, .visibility = common.visibility });
@export(&sqrtf, .{ .name = "sqrtf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/stack_probe.zig b/lib/compiler_rt/stack_probe.zig
index 21259ec4354a09e6ef8e0b82fdd9271b48ef6c85..83e08be0153754724f8e3b08609fcf3b33dbb1c5 100644
--- a/lib/compiler_rt/stack_probe.zig
+++ b/lib/compiler_rt/stack_probe.zig
@@ -5,8 +5,6 @@ const os_tag = builtin.os.tag;
const arch = builtin.cpu.arch;
const abi = builtin.abi;
-pub const panic = common.panic;
-
comptime {
if (builtin.os.tag == .windows) {
// Default stack-probe functions emitted by LLVM
diff --git a/lib/compiler_rt/subdf3.zig b/lib/compiler_rt/subdf3.zig
index 24ded58e0c54450f77617f1449dbc2ca7e3614d1..b1d78509028c1a57217e188e23d28e70b04d6227 100644
--- a/lib/compiler_rt/subdf3.zig
+++ b/lib/compiler_rt/subdf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/subhf3.zig b/lib/compiler_rt/subhf3.zig
index cc42eb927022cc80d83575b439dbc338a6809faa..e019005ae478c34125c77f4b698f5c6737c8e26c 100644
--- a/lib/compiler_rt/subhf3.zig
+++ b/lib/compiler_rt/subhf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
@export(&__subhf3, .{ .name = "__subhf3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/subsf3.zig b/lib/compiler_rt/subsf3.zig
index f3bb334bd81e7460b08e1da81b9ca3dfd48eee73..9a1cb559b23af9393a9916d36f67e2987ad74af5 100644
--- a/lib/compiler_rt/subsf3.zig
+++ b/lib/compiler_rt/subsf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/subtf3.zig b/lib/compiler_rt/subtf3.zig
index aff4904adf6e6147a6967b46caf5afb0858a2a07..b187eaf579b3894df819e902051c9fe33058d897 100644
--- a/lib/compiler_rt/subtf3.zig
+++ b/lib/compiler_rt/subtf3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const addf3 = @import("./addf3.zig").addf3;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__subtf3, .{ .name = "__subkf3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/subvdi3.zig b/lib/compiler_rt/subvdi3.zig
index a34deb2da1bfd6c688206d7ee62f70533604a36b..17c893c91141925285dd5eecbd6297523fbb57a2 100644
--- a/lib/compiler_rt/subvdi3.zig
+++ b/lib/compiler_rt/subvdi3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const testing = @import("std").testing;
-pub const panic = common.panic;
-
comptime {
@export(&__subvdi3, .{ .name = "__subvdi3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/subvsi3.zig b/lib/compiler_rt/subvsi3.zig
index c524a3a63499e2408aa579c6c2c89a2bf47103b1..68f551071c8ff1e123aecf6075fd85f7c649ab40 100644
--- a/lib/compiler_rt/subvsi3.zig
+++ b/lib/compiler_rt/subvsi3.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const testing = @import("std").testing;
-pub const panic = common.panic;
-
comptime {
@export(&__subvsi3, .{ .name = "__subvsi3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/subxf3.zig b/lib/compiler_rt/subxf3.zig
index e79dd230b5d8a8da80ce672cea28eb63a754eead..d119f0bd984fc311b2f67a89760c9f147397af9d 100644
--- a/lib/compiler_rt/subxf3.zig
+++ b/lib/compiler_rt/subxf3.zig
@@ -1,8 +1,6 @@
const std = @import("std");
const common = @import("./common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__subxf3, .{ .name = "__subxf3", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/tan.zig b/lib/compiler_rt/tan.zig
index af8d68eb44447af300b2b1af992fc1e1492b6fca..a78d7afb67b491d1d5ebb246689dbc7e4b8d9f73 100644
--- a/lib/compiler_rt/tan.zig
+++ b/lib/compiler_rt/tan.zig
@@ -18,8 +18,6 @@ const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
const arch = builtin.cpu.arch;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__tanh, .{ .name = "__tanh", .linkage = common.linkage, .visibility = common.visibility });
@export(&tanf, .{ .name = "tanf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/trunc.zig b/lib/compiler_rt/trunc.zig
index ff829ea7108992829d365f8f5705a5579529269f..55468c752fa4ff279ab872f5bd15077576fe1186 100644
--- a/lib/compiler_rt/trunc.zig
+++ b/lib/compiler_rt/trunc.zig
@@ -12,8 +12,6 @@ const mem = std.mem;
const expect = std.testing.expect;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__trunch, .{ .name = "__trunch", .linkage = common.linkage, .visibility = common.visibility });
@export(&truncf, .{ .name = "truncf", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/truncdfhf2.zig b/lib/compiler_rt/truncdfhf2.zig
index a24650ddb78f4f256fc46d370fc37fbe76823c95..4db14f0211849bb30d47f429e24b7a510736aab6 100644
--- a/lib/compiler_rt/truncdfhf2.zig
+++ b/lib/compiler_rt/truncdfhf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const truncf = @import("./truncf.zig").truncf;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/truncdfsf2.zig b/lib/compiler_rt/truncdfsf2.zig
index fd42d5d34428d3cf5a53ccd7d9fb3725e598218b..8d6ed5c3206f963401abb558adcdc9e56aae9e22 100644
--- a/lib/compiler_rt/truncdfsf2.zig
+++ b/lib/compiler_rt/truncdfsf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const truncf = @import("./truncf.zig").truncf;
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/truncsfhf2.zig b/lib/compiler_rt/truncsfhf2.zig
index 18d5854c91b36a434a957dd97071357463f23a8b..acd5631da8f71e013e2eb55e5bf87ab3a02299cd 100644
--- a/lib/compiler_rt/truncsfhf2.zig
+++ b/lib/compiler_rt/truncsfhf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const truncf = @import("./truncf.zig").truncf;
-pub const panic = common.panic;
-
comptime {
if (common.gnu_f16_abi) {
@export(&__gnu_f2h_ieee, .{ .name = "__gnu_f2h_ieee", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/trunctfdf2.zig b/lib/compiler_rt/trunctfdf2.zig
index 37b9227e0b975d610ce86d8b7e9de6f47231bcb1..4f569dae675c8e1fa9e394f167388bd79c53d3ff 100644
--- a/lib/compiler_rt/trunctfdf2.zig
+++ b/lib/compiler_rt/trunctfdf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const truncf = @import("./truncf.zig").truncf;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/trunctfhf2.zig b/lib/compiler_rt/trunctfhf2.zig
index daf4776106dbc002dbb65d1f968ae64a70df245e..f5adc918c2d50fc455e08db16e09de88b25088a3 100644
--- a/lib/compiler_rt/trunctfhf2.zig
+++ b/lib/compiler_rt/trunctfhf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const truncf = @import("./truncf.zig").truncf;
-pub const panic = common.panic;
-
comptime {
@export(&__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/trunctfsf2.zig b/lib/compiler_rt/trunctfsf2.zig
index e238e35fb346184d7ee9b4a1a5ad5e0d83c722be..17e7570c9f0ecaa1b607d06deaf400a8ff83abc2 100644
--- a/lib/compiler_rt/trunctfsf2.zig
+++ b/lib/compiler_rt/trunctfsf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const truncf = @import("./truncf.zig").truncf;
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/trunctfxf2.zig b/lib/compiler_rt/trunctfxf2.zig
index ddc977943a09fbcb3343e2d7452fc54f9de8052a..df0db2393a6277e451df262fedc0a5b4bf48fb1c 100644
--- a/lib/compiler_rt/trunctfxf2.zig
+++ b/lib/compiler_rt/trunctfxf2.zig
@@ -2,8 +2,6 @@ const math = @import("std").math;
const common = @import("./common.zig");
const trunc_f80 = @import("./truncf.zig").trunc_f80;
-pub const panic = common.panic;
-
comptime {
@export(&__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/truncxfdf2.zig b/lib/compiler_rt/truncxfdf2.zig
index f45a07962e5c4c35285ccf20a7a207a510bb2bad..1d7e66c278f3c0ecf55fa3e034ed429ad5bae913 100644
--- a/lib/compiler_rt/truncxfdf2.zig
+++ b/lib/compiler_rt/truncxfdf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const trunc_f80 = @import("./truncf.zig").trunc_f80;
-pub const panic = common.panic;
-
comptime {
@export(&__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/truncxfhf2.zig b/lib/compiler_rt/truncxfhf2.zig
index 34ee97c04b944db8f2d67a85740e8c95dad4dffe..b2dd9c7468265eb8ac5c0b9d865327e4e2208493 100644
--- a/lib/compiler_rt/truncxfhf2.zig
+++ b/lib/compiler_rt/truncxfhf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const trunc_f80 = @import("./truncf.zig").trunc_f80;
-pub const panic = common.panic;
-
comptime {
@export(&__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/truncxfsf2.zig b/lib/compiler_rt/truncxfsf2.zig
index 136e941af42543bc9926789f1e3f9d4fb58f405c..9d36f7aa29a407b0e266b74437cfdd0c869eae35 100644
--- a/lib/compiler_rt/truncxfsf2.zig
+++ b/lib/compiler_rt/truncxfsf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const trunc_f80 = @import("./truncf.zig").trunc_f80;
-pub const panic = common.panic;
-
comptime {
@export(&__truncxfsf2, .{ .name = "__truncxfsf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/udivmodti4.zig b/lib/compiler_rt/udivmodti4.zig
index 9e9585c635db05af3260e4c8456db2e3b20e2c0e..a1b89bb9625b5c221fb454c37511f6cb1fc981aa 100644
--- a/lib/compiler_rt/udivmodti4.zig
+++ b/lib/compiler_rt/udivmodti4.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const udivmod = @import("udivmod.zig").udivmod;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/udivti3.zig b/lib/compiler_rt/udivti3.zig
index 8423c1df1876bea7c8237dc1bd38730bf995c49d..a579d81e47dff972a53973e8276df9b28fede533 100644
--- a/lib/compiler_rt/udivti3.zig
+++ b/lib/compiler_rt/udivti3.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const udivmod = @import("udivmod.zig").udivmod;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/umodti3.zig b/lib/compiler_rt/umodti3.zig
index 592bc44390a11b34fc0108c88217fc694741af90..63cfcc2c25367aac11c205181494c2b84ee7a8af 100644
--- a/lib/compiler_rt/umodti3.zig
+++ b/lib/compiler_rt/umodti3.zig
@@ -3,8 +3,6 @@ const builtin = @import("builtin");
const udivmod = @import("udivmod.zig").udivmod;
const common = @import("common.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_windows_v2u64_abi) {
@export(&__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/unorddf2.zig b/lib/compiler_rt/unorddf2.zig
index dcc8762c66d78b0b9ce4a9655c150ab8d0b1bc21..5b17343a5168961aa57f61ee3ac3c9279ecd270e 100644
--- a/lib/compiler_rt/unorddf2.zig
+++ b/lib/compiler_rt/unorddf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/unordhf2.zig b/lib/compiler_rt/unordhf2.zig
index 2c2edad816866c9f3890b662b0e17f908c299d77..4fcffd1a1d1cca24f075b363f62370b938000b70 100644
--- a/lib/compiler_rt/unordhf2.zig
+++ b/lib/compiler_rt/unordhf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__unordhf2, .{ .name = "__unordhf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/compiler_rt/unordsf2.zig b/lib/compiler_rt/unordsf2.zig
index a403fa4fd3d5f0466887234b934d20141162951c..56bfb94028d8e6623b4ca7cd141f7d81b9809f85 100644
--- a/lib/compiler_rt/unordsf2.zig
+++ b/lib/compiler_rt/unordsf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_aeabi) {
@export(&__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/unordtf2.zig b/lib/compiler_rt/unordtf2.zig
index 618f148296fd4e4bddbc73cf295e551e37bc7046..22e90acaaed05f16447edb08287d5122f73749af 100644
--- a/lib/compiler_rt/unordtf2.zig
+++ b/lib/compiler_rt/unordtf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
if (common.want_ppc_abi) {
@export(&__unordtf2, .{ .name = "__unordkf2", .linkage = common.linkage, .visibility = common.visibility });
diff --git a/lib/compiler_rt/unordxf2.zig b/lib/compiler_rt/unordxf2.zig
index 97a4f8d06f7b6231ee13da4121195c6d840d1976..2ebf5f413cc6ccfa26fd4df0a00e5064262b0edc 100644
--- a/lib/compiler_rt/unordxf2.zig
+++ b/lib/compiler_rt/unordxf2.zig
@@ -1,8 +1,6 @@
const common = @import("./common.zig");
const comparef = @import("./comparef.zig");
-pub const panic = common.panic;
-
comptime {
@export(&__unordxf2, .{ .name = "__unordxf2", .linkage = common.linkage, .visibility = common.visibility });
}
diff --git a/lib/init/src/main.zig b/lib/init/src/main.zig
index 865e6c53228c9c69f441eec4427656d671cb9ce3..2c6df25be543819dfd345b0c8b5e69588ed51afe 100644
--- a/lib/init/src/main.zig
+++ b/lib/init/src/main.zig
@@ -3,19 +3,21 @@ const Io = std.Io;
const _NAME = @import(".NAME");
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
// Prints to stderr, unbuffered, ignoring potential errors.
std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
- // In order to allocate memory we must construct an `Allocator` instance.
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_allocator.deinit(); // This checks for leaks.
- const gpa = debug_allocator.allocator();
+ // This is appropriate for anything that lives as long as the process.
+ const arena: std.mem.Allocator = init.arena.allocator();
- // In order to do I/O operations we must construct an `Io` instance.
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
+ // Accessing command line arguments:
+ const args = try init.minimal.args.toSlice(arena);
+ for (args) |arg| {
+ std.log.info("arg: {s}", .{arg});
+ }
+
+ // In order to do I/O operations need an `Io` instance.
+ const io = init.io;
// Stdout is for the actual output of your application, for example if you
// are implementing gzip, then only the compressed bytes should be sent to
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index dc3489bba3104a312568a95eb15bac7d3eec3dd3..1724025beaa37480303d93dfb568da768799fa56 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -12,7 +12,6 @@ const StringHashMap = std.StringHashMap;
const Allocator = std.mem.Allocator;
const Target = std.Target;
const process = std.process;
-const EnvMap = std.process.EnvMap;
const File = std.Io.File;
const Sha256 = std.crypto.hash.sha2.Sha256;
const ArrayList = std.ArrayList;
@@ -118,7 +117,7 @@ pub const Graph = struct {
debug_compiler_runtime_libs: bool = false,
cache: Cache,
zig_exe: [:0]const u8,
- env_map: EnvMap,
+ environ_map: process.Environ.Map,
global_cache_root: Cache.Directory,
zig_lib_directory: Cache.Directory,
needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .empty,
@@ -190,7 +189,7 @@ pub const RunError = error{
ExitCodeFailure,
ProcessTerminated,
ExecNotSupported,
-} || std.process.Child.SpawnError;
+} || std.process.SpawnError;
pub const PkgConfigError = error{
PkgConfigCrashed,
@@ -289,7 +288,7 @@ pub fn create(
.lib_dir = undefined,
.exe_dir = undefined,
.h_dir = undefined,
- .dest_dir = graph.env_map.get("DESTDIR"),
+ .dest_dir = graph.environ_map.get("DESTDIR"),
.install_tls = .{
.step = .init(.{
.id = TopLevelStep.base_id,
@@ -1738,8 +1737,7 @@ pub fn pathFromRoot(b: *Build, sub_path: []const u8) []u8 {
}
fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 {
- const cwd = process.getCwdAlloc(b.allocator) catch @panic("OOM");
- return b.pathResolve(&.{ cwd, sub_path });
+ return b.pathResolve(&.{ b.graph.cache.cwd, sub_path });
}
pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 {
@@ -1755,7 +1753,7 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 {
}
fn supportedWindowsProgramExtension(ext: []const u8) bool {
- inline for (@typeInfo(std.process.Child.WindowsExtension).@"enum".fields) |field| {
+ inline for (@typeInfo(std.process.WindowsExtension).@"enum".fields) |field| {
if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true;
}
return false;
@@ -1773,7 +1771,7 @@ fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 {
}
if (builtin.os.tag == .windows) {
- if (b.graph.env_map.get("PATHEXT")) |PATHEXT| {
+ if (b.graph.environ_map.get("PATHEXT")) |PATHEXT| {
var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter);
while (it.next()) |ext| {
@@ -1804,7 +1802,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const
return tryFindProgram(b, b.pathJoin(&.{ search_prefix, "bin", name })) orelse continue;
}
}
- if (b.graph.env_map.get("PATH")) |PATH| {
+ if (b.graph.environ_map.get("PATH")) |PATH| {
for (names) |name| {
if (fs.path.isAbsolute(name)) {
return name;
@@ -1830,24 +1828,26 @@ pub fn runAllowFail(
b: *Build,
argv: []const []const u8,
out_code: *u8,
- stderr_behavior: std.process.Child.StdIo,
+ stderr_behavior: std.process.SpawnOptions.StdIo,
) RunError![]u8 {
assert(argv.len != 0);
if (!process.can_spawn)
return error.ExecNotSupported;
- const io = b.graph.io;
+ const graph = b.graph;
+ const io = graph.io;
const max_output_size = 400 * 1024;
- var child = std.process.Child.init(argv, b.allocator);
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = stderr_behavior;
- child.env_map = &b.graph.env_map;
+ try Step.handleVerbose2(b, null, &graph.environ_map, argv);
- try Step.handleVerbose2(b, null, child.env_map, argv);
- try child.spawn(io);
+ var child = try std.process.spawn(io, .{
+ .argv = argv,
+ .environ_map = &graph.environ_map,
+ .stdin = .ignore,
+ .stdout = .pipe,
+ .stderr = stderr_behavior,
+ });
var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch {
@@ -1857,14 +1857,18 @@ pub fn runAllowFail(
const term = try child.wait(io);
switch (term) {
- .Exited => |code| {
+ .exited => |code| {
if (code != 0) {
out_code.* = @as(u8, @truncate(code));
return error.ExitCodeFailure;
}
return stdout;
},
- .Signal, .Stopped, .Unknown => |code| {
+ .signal => |sig| {
+ out_code.* = @as(u8, @truncate(@intFromEnum(sig)));
+ return error.ProcessTerminated;
+ },
+ .stopped, .unknown => |code| {
out_code.* = @as(u8, @truncate(code));
return error.ProcessTerminated;
},
@@ -1875,21 +1879,11 @@ pub fn runAllowFail(
/// inside step make() functions. If any errors occur, it fails the build with
/// a helpful message.
pub fn run(b: *Build, argv: []const []const u8) []u8 {
- if (!process.can_spawn) {
- std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{
- try Step.allocPrintCmd(b.allocator, null, argv),
- });
- process.exit(1);
- }
-
var code: u8 = undefined;
- return b.runAllowFail(argv, &code, .Inherit) catch |err| {
- const printed_cmd = Step.allocPrintCmd(b.allocator, null, argv) catch @panic("OOM");
- std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{
- @errorName(err), printed_cmd,
- });
- process.exit(1);
- };
+ return b.runAllowFail(argv, &code, .inherit) catch |err| process.fatal(
+ "the following command failed with {t}:\n{s}",
+ .{ err, Step.allocPrintCmd(b.allocator, null, null, argv) catch @panic("OOM") },
+ );
}
pub fn addSearchPrefix(b: *Build, search_prefix: []const u8) void {
diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig
index b384ab13ff9ae1fc1509b36e60e34bbc91a57177..e35beca617d3d1913571195f17e4cb88795f10f2 100644
--- a/lib/std/Build/Cache.zig
+++ b/lib/std/Build/Cache.zig
@@ -30,6 +30,8 @@ mutex: Io.Mutex = .init,
/// and usefulness of the cache for advanced use cases.
prefixes_buffer: [4]Directory = undefined,
prefixes_len: usize = 0,
+/// Used to identify prefixes. References external memory.
+cwd: []const u8,
pub const Path = @import("Cache/Path.zig");
pub const Directory = @import("Cache/Directory.zig");
@@ -78,11 +80,12 @@ fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath {
/// Takes ownership of `resolved_path` on success.
fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath {
const gpa = cache.gpa;
+ const cwd = cache.cwd;
const prefixes_slice = cache.prefixes();
var i: u8 = 1; // Start at 1 to skip over checking the null prefix.
while (i < prefixes_slice.len) : (i += 1) {
const p = prefixes_slice[i].path.?;
- const sub_path = getPrefixSubpath(gpa, p, resolved_path) catch |err| switch (err) {
+ const sub_path = getPrefixSubpath(gpa, cwd, p, resolved_path) catch |err| switch (err) {
error.NotASubPath => continue,
else => |e| return e,
};
@@ -100,10 +103,10 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath {
};
}
-fn getPrefixSubpath(allocator: Allocator, prefix: []const u8, path: []u8) ![]u8 {
- const relative = try std.fs.path.relative(allocator, prefix, path);
- errdefer allocator.free(relative);
- var component_iterator = std.fs.path.NativeComponentIterator.init(relative);
+fn getPrefixSubpath(gpa: Allocator, cwd: []const u8, prefix: []const u8, path: []u8) ![]u8 {
+ const relative = try std.fs.path.relative(gpa, cwd, null, prefix, path);
+ errdefer gpa.free(relative);
+ var component_iterator: std.fs.path.NativeComponentIterator = .init(relative);
if (component_iterator.root() != null) {
return error.NotASubPath;
}
@@ -1307,11 +1310,14 @@ fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp {
}
test "cache file and then recall it" {
- const io = std.testing.io;
+ const io = testing.io;
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
+ const cwd = try std.process.getCwdAlloc(testing.allocator);
+ defer testing.allocator.free(cwd);
+
const temp_file = "test.txt";
const temp_manifest_dir = "temp_manifest_dir";
@@ -1331,6 +1337,7 @@ test "cache file and then recall it" {
.io = io,
.gpa = testing.allocator,
.manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}),
+ .cwd = cwd,
};
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
defer cache.manifest_dir.close(io);
@@ -1371,11 +1378,14 @@ test "cache file and then recall it" {
}
test "check that changing a file makes cache fail" {
- const io = std.testing.io;
+ const io = testing.io;
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
+ const cwd = try std.process.getCwdAlloc(testing.allocator);
+ defer testing.allocator.free(cwd);
+
const temp_file = "cache_hash_change_file_test.txt";
const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
const original_temp_file_contents = "Hello, world!\n";
@@ -1397,6 +1407,7 @@ test "check that changing a file makes cache fail" {
.io = io,
.gpa = testing.allocator,
.manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}),
+ .cwd = cwd,
};
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
defer cache.manifest_dir.close(io);
@@ -1448,6 +1459,9 @@ test "no file inputs" {
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
+ const cwd = try std.process.getCwdAlloc(testing.allocator);
+ defer testing.allocator.free(cwd);
+
const temp_manifest_dir = "no_file_inputs_manifest_dir";
var digest1: HexDigest = undefined;
@@ -1457,6 +1471,7 @@ test "no file inputs" {
.io = io,
.gpa = testing.allocator,
.manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}),
+ .cwd = cwd,
};
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
defer cache.manifest_dir.close(io);
@@ -1489,11 +1504,14 @@ test "no file inputs" {
}
test "Manifest with files added after initial hash work" {
- const io = std.testing.io;
+ const io = testing.io;
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
+ const cwd = try std.process.getCwdAlloc(testing.allocator);
+ defer testing.allocator.free(cwd);
+
const temp_file1 = "cache_hash_post_file_test1.txt";
const temp_file2 = "cache_hash_post_file_test2.txt";
const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
@@ -1516,6 +1534,7 @@ test "Manifest with files added after initial hash work" {
.io = io,
.gpa = testing.allocator,
.manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}),
+ .cwd = cwd,
};
cache.addPrefix(.{ .path = null, .handle = tmp.dir });
defer cache.manifest_dir.close(io);
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig
index 243dee8604da4eca431012923409fdc1b48e2303..28141cebe536778ee7503521952404e7d25a0e58 100644
--- a/lib/std/Build/Step.zig
+++ b/lib/std/Build/Step.zig
@@ -348,19 +348,21 @@ pub fn captureChildProcess(
gpa: Allocator,
progress_node: std.Progress.Node,
argv: []const []const u8,
-) !std.process.Child.RunResult {
- const arena = s.owner.allocator;
- const io = s.owner.graph.io;
+) !std.process.RunResult {
+ const graph = s.owner.graph;
+ const arena = graph.arena;
+ const io = graph.io;
// If an error occurs, it's happened in this command:
assert(s.result_failed_command == null);
- s.result_failed_command = try allocPrintCmd(gpa, null, argv);
+ s.result_failed_command = try allocPrintCmd(gpa, null, null, argv);
try handleChildProcUnsupported(s);
try handleVerbose(s.owner, null, argv);
- const result = std.process.Child.run(arena, io, .{
+ const result = std.process.run(arena, io, .{
.argv = argv,
+ .environ_map = &graph.environ_map,
.progress_node = progress_node,
}) catch |err| return s.fail("failed to run {s}: {t}", .{ argv[0], err });
@@ -406,7 +408,7 @@ pub fn evalZigProcess(
// If an error occurs, it's happened in this command:
assert(s.result_failed_command == null);
- s.result_failed_command = try allocPrintCmd(gpa, null, argv);
+ s.result_failed_command = try allocPrintCmd(gpa, null, null, argv);
if (s.getZigProcess()) |zp| update: {
assert(watch);
@@ -442,35 +444,34 @@ pub fn evalZigProcess(
return result;
}
assert(argv.len != 0);
- const arena = b.allocator;
try handleChildProcUnsupported(s);
try handleVerbose(s.owner, null, argv);
- var child = std.process.Child.init(argv, arena);
- child.env_map = &b.graph.env_map;
- child.stdin_behavior = .Pipe;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
- child.request_resource_usage_statistics = true;
- child.progress_node = prog_node;
-
- child.spawn(io) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err });
-
const zp = try gpa.create(ZigProcess);
+ defer if (!watch) gpa.destroy(zp);
+
+ zp.child = std.process.spawn(io, .{
+ .argv = argv,
+ .environ_map = &b.graph.environ_map,
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .pipe,
+ .request_resource_usage_statistics = true,
+ .progress_node = prog_node,
+ }) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err });
+ defer if (!watch) zp.child.kill(io);
+
zp.* = .{
- .child = child,
+ .child = zp.child,
.poller = Io.poll(gpa, ZigProcess.StreamEnum, .{
- .stdout = child.stdout.?,
- .stderr = child.stderr.?,
+ .stdout = zp.child.stdout.?,
+ .stderr = zp.child.stderr.?,
}),
- .progress_ipc_fd = if (std.Progress.have_ipc) child.progress_node.getIpcFd() else {},
+ .progress_ipc_fd = if (std.Progress.have_ipc) prog_node.getIpcFd() else {},
};
if (watch) s.setZigProcess(zp);
- defer if (!watch) {
- zp.poller.deinit();
- gpa.destroy(zp);
- };
+ defer if (!watch) zp.poller.deinit();
const result = try zigProcessUpdate(s, zp, watch, web_server, gpa);
@@ -486,7 +487,7 @@ pub fn evalZigProcess(
// Special handling for Compile step that is expecting compile errors.
if (s.cast(Compile)) |compile| switch (term) {
- .Exited => {
+ .exited => {
// Note that the exit code may be 0 in this case due to the
// compiler server protocol.
if (compile.expect_errors != null) {
@@ -692,13 +693,17 @@ pub fn handleVerbose(
pub fn handleVerbose2(
b: *Build,
opt_cwd: ?[]const u8,
- opt_env: ?*const std.process.EnvMap,
+ opt_env: ?*const std.process.Environ.Map,
argv: []const []const u8,
) error{OutOfMemory}!void {
if (b.verbose) {
+ const graph = b.graph;
// Intention of verbose is to print all sub-process command lines to
// stderr before spawning them.
- const text = try allocPrintCmd2(b.allocator, opt_cwd, opt_env, argv);
+ const text = try allocPrintCmd(b.allocator, opt_cwd, if (opt_env) |env| .{
+ .child = env,
+ .parent = &graph.environ_map,
+ } else null, argv);
std.debug.print("{s}\n", .{text});
}
}
@@ -714,12 +719,15 @@ pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFaile
pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void {
assert(s.result_failed_command != null);
switch (term) {
- .Exited => |code| {
+ .exited => |code| {
if (code != 0) {
return s.fail("process exited with error code {d}", .{code});
}
},
- .Signal, .Stopped, .Unknown => {
+ .signal => |sig| {
+ return s.fail("process terminated with signal {t}", .{sig});
+ },
+ .stopped, .unknown => {
return s.fail("process terminated unexpectedly", .{});
},
}
@@ -728,15 +736,10 @@ pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ Mak
pub fn allocPrintCmd(
gpa: Allocator,
opt_cwd: ?[]const u8,
- argv: []const []const u8,
-) Allocator.Error![]u8 {
- return allocPrintCmd2(gpa, opt_cwd, null, argv);
-}
-
-pub fn allocPrintCmd2(
- gpa: Allocator,
- opt_cwd: ?[]const u8,
- opt_env: ?*const std.process.EnvMap,
+ opt_env: ?struct {
+ child: *const std.process.Environ.Map,
+ parent: *const std.process.Environ.Map,
+ },
argv: []const []const u8,
) Allocator.Error![]u8 {
const shell = struct {
@@ -779,13 +782,11 @@ pub fn allocPrintCmd2(
const writer = &aw.writer;
if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory;
if (opt_env) |env| {
- var process_env_map = std.process.getEnvMap(gpa) catch std.process.EnvMap.init(gpa);
- defer process_env_map.deinit();
- var it = env.iterator();
+ var it = env.child.iterator();
while (it.next()) |entry| {
const key = entry.key_ptr.*;
const value = entry.value_ptr.*;
- if (process_env_map.get(key)) |process_value| {
+ if (env.parent.get(key)) |process_value| {
if (std.mem.eql(u8, value, process_value)) continue;
}
writer.print("{s}=", .{key}) catch return error.OutOfMemory;
diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig
index 0454e5b79dbc2582cac7188b9a2593decb37dfa2..2403436c73818693a142a42b82bf67acaa5c69a3 100644
--- a/lib/std/Build/Step/Compile.zig
+++ b/lib/std/Build/Step/Compile.zig
@@ -741,13 +741,13 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {
};
var code: u8 = undefined;
- const pkg_config_exe = b.graph.env_map.get("PKG_CONFIG") orelse "pkg-config";
+ const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config";
const stdout = if (b.runAllowFail(&[_][]const u8{
pkg_config_exe,
pkg_name,
"--cflags",
"--libs",
- }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
+ }, &code, .ignore)) |stdout| stdout else |err| switch (err) {
error.ProcessTerminated => return error.PkgConfigCrashed,
error.ExecNotSupported => return error.PkgConfigFailed,
error.ExitCodeFailure => return error.PkgConfigFailed,
@@ -1846,8 +1846,8 @@ pub fn doAtomicSymLinks(
}
fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg {
- const pkg_config_exe = b.graph.env_map.get("PKG_CONFIG") orelse "pkg-config";
- const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .Ignore);
+ const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config";
+ const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .ignore);
var list = std.array_list.Managed(PkgConfigPkg).init(b.allocator);
errdefer list.deinit();
var line_it = mem.tokenizeAny(u8, stdout, "\r\n");
diff --git a/lib/std/Build/Step/Fmt.zig b/lib/std/Build/Step/Fmt.zig
index ff748af97973a8723cd0a3344d305736de0b7eb2..2da07b63bb422176c8674ff6ccf71a0e00749c4e 100644
--- a/lib/std/Build/Step/Fmt.zig
+++ b/lib/std/Build/Step/Fmt.zig
@@ -69,7 +69,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
const run_result = try step.captureChildProcess(options.gpa, prog_node, argv.items);
if (fmt.check) switch (run_result.term) {
- .Exited => |code| if (code != 0 and run_result.stdout.len != 0) {
+ .exited => |code| if (code != 0 and run_result.stdout.len != 0) {
var it = std.mem.tokenizeScalar(u8, run_result.stdout, '\n');
while (it.next()) |bad_file_name| {
try step.addError("{s}: non-conforming formatting", .{bad_file_name});
diff --git a/lib/std/Build/Step/Options.zig b/lib/std/Build/Step/Options.zig
index 610d417aeabf53cc1881e0d57563224f6b29cda2..9bb91606208fed0af89168ad1d452cb9c84ffe71 100644
--- a/lib/std/Build/Step/Options.zig
+++ b/lib/std/Build/Step/Options.zig
@@ -537,6 +537,9 @@ test Options {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
+ const cwd = try std.process.getCwdAlloc(std.testing.allocator);
+ defer std.testing.allocator.free(cwd);
+
var graph: std.Build.Graph = .{
.io = io,
.arena = arena.allocator(),
@@ -544,9 +547,10 @@ test Options {
.io = io,
.gpa = arena.allocator(),
.manifest_dir = Io.Dir.cwd(),
+ .cwd = cwd,
},
.zig_exe = "test",
- .env_map = std.process.EnvMap.init(arena.allocator()),
+ .environ_map = std.process.Environ.Map.init(arena.allocator()),
.global_cache_root = .{ .path = "test", .handle = Io.Dir.cwd() },
.host = .{
.query = .{},
diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig
index 258fadd06ccc46c6bcadcf86fec737fe57da93b9..1084c7fbfd89478853a7b4c607df1b538c18ae7c 100644
--- a/lib/std/Build/Step/Run.zig
+++ b/lib/std/Build/Step/Run.zig
@@ -8,7 +8,7 @@ const Step = std.Build.Step;
const Dir = std.Io.Dir;
const mem = std.mem;
const process = std.process;
-const EnvMap = std.process.EnvMap;
+const EnvMap = std.process.Environ.Map;
const assert = std.debug.assert;
const Path = std.Build.Cache.Path;
@@ -23,7 +23,7 @@ argv: std.ArrayList(Arg),
cwd: ?Build.LazyPath,
/// Override this field to modify the environment, or use setEnvironmentVariable
-env_map: ?*EnvMap,
+environ_map: ?*EnvMap,
/// Controls the `NO_COLOR` and `CLICOLOR_FORCE` environment variables.
color: Color = .auto,
@@ -149,7 +149,7 @@ pub const StdIo = union(enum) {
expect_stderr_match: []const u8,
expect_stdout_exact: []const u8,
expect_stdout_match: []const u8,
- expect_term: std.process.Child.Term,
+ expect_term: process.Child.Term,
};
};
@@ -215,7 +215,7 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {
}),
.argv = .{},
.cwd = null,
- .env_map = null,
+ .environ_map = null,
.disable_zig_progress = false,
.stdio = .infer_from_args,
.stdin = .none,
@@ -540,12 +540,12 @@ pub fn clearEnvironment(run: *Run) void {
const b = run.step.owner;
const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM");
new_env_map.* = .init(b.allocator);
- run.env_map = new_env_map;
+ run.environ_map = new_env_map;
}
pub fn addPathDir(run: *Run, search_path: []const u8) void {
const b = run.step.owner;
- const env_map = getEnvMapInternal(run);
+ const environ_map = getEnvMapInternal(run);
const use_wine = b.enable_wine and b.graph.host.result.os.tag != .windows and use_wine: switch (run.argv.items[0]) {
.artifact => |p| p.artifact.rootModuleTarget().os.tag == .windows,
@@ -562,7 +562,7 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void {
.output_file, .output_directory => false,
};
const key = if (use_wine) "WINEPATH" else "PATH";
- const prev_path = env_map.get(key);
+ const prev_path = environ_map.get(key);
if (prev_path) |pp| {
const new_path = b.fmt("{s}{c}{s}", .{
@@ -570,9 +570,9 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void {
if (use_wine) Dir.path.delimiter_windows else Dir.path.delimiter,
search_path,
});
- env_map.put(key, new_path) catch @panic("OOM");
+ environ_map.put(key, new_path) catch @panic("OOM");
} else {
- env_map.put(key, b.dupePath(search_path)) catch @panic("OOM");
+ environ_map.put(key, b.dupePath(search_path)) catch @panic("OOM");
}
}
@@ -581,43 +581,51 @@ pub fn getEnvMap(run: *Run) *EnvMap {
}
fn getEnvMapInternal(run: *Run) *EnvMap {
- const arena = run.step.owner.allocator;
- return run.env_map orelse {
- const env_map = arena.create(EnvMap) catch @panic("OOM");
- env_map.* = process.getEnvMap(arena) catch @panic("unhandled error");
- run.env_map = env_map;
- return env_map;
+ const graph = run.step.owner.graph;
+ const arena = graph.arena;
+ return run.environ_map orelse {
+ const cloned_map = arena.create(EnvMap) catch @panic("OOM");
+ cloned_map.* = graph.environ_map.clone(arena) catch @panic("OOM");
+ run.environ_map = cloned_map;
+ return cloned_map;
};
}
pub fn setEnvironmentVariable(run: *Run, key: []const u8, value: []const u8) void {
- const b = run.step.owner;
- const env_map = run.getEnvMap();
- env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error");
+ const environ_map = run.getEnvMap();
+ // This data structure already dupes keys and values.
+ environ_map.put(key, value) catch @panic("OOM");
}
pub fn removeEnvironmentVariable(run: *Run, key: []const u8) void {
- run.getEnvMap().remove(key);
+ _ = run.getEnvMap().swapRemove(key);
}
/// Adds a check for exact stderr match. Does not add any other checks.
pub fn expectStdErrEqual(run: *Run, bytes: []const u8) void {
- const new_check: StdIo.Check = .{ .expect_stderr_exact = run.step.owner.dupe(bytes) };
- run.addCheck(new_check);
+ run.addCheck(.{ .expect_stderr_exact = run.step.owner.dupe(bytes) });
+}
+
+pub fn expectStdErrMatch(run: *Run, bytes: []const u8) void {
+ run.addCheck(.{ .expect_stderr_match = run.step.owner.dupe(bytes) });
}
/// Adds a check for exact stdout match as well as a check for exit code 0, if
/// there is not already an expected termination check.
pub fn expectStdOutEqual(run: *Run, bytes: []const u8) void {
- const new_check: StdIo.Check = .{ .expect_stdout_exact = run.step.owner.dupe(bytes) };
- run.addCheck(new_check);
- if (!run.hasTermCheck()) {
- run.expectExitCode(0);
- }
+ run.addCheck(.{ .expect_stdout_exact = run.step.owner.dupe(bytes) });
+ if (!run.hasTermCheck()) run.expectExitCode(0);
+}
+
+/// Adds a check for stdout match as well as a check for exit code 0, if there
+/// is not already an expected termination check.
+pub fn expectStdOutMatch(run: *Run, bytes: []const u8) void {
+ run.addCheck(.{ .expect_stdout_match = run.step.owner.dupe(bytes) });
+ if (!run.hasTermCheck()) run.expectExitCode(0);
}
pub fn expectExitCode(run: *Run, code: u8) void {
- const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } };
+ const new_check: StdIo.Check = .{ .expect_term = .{ .exited = code } };
run.addCheck(new_check);
}
@@ -749,28 +757,30 @@ fn checksContainStderr(checks: []const StdIo.Check) bool {
/// to make sure the child doesn't see paths relative to a cwd other than its own.
fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 {
const b = run.step.owner;
- const path_str = path.toString(b.graph.arena) catch @panic("OOM");
+ const graph = b.graph;
+ const arena = graph.arena;
+
+ const path_str = path.toString(arena) catch @panic("OOM");
if (Dir.path.isAbsolute(path_str)) {
// Absolute paths don't need changing.
return path_str;
}
const child_cwd_rel: []const u8 = rel: {
const child_lazy_cwd = run.cwd orelse break :rel path_str;
- const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(b.graph.arena) catch @panic("OOM");
+ const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(arena) catch @panic("OOM");
// Convert it from relative to *our* cwd, to relative to the *child's* cwd.
- break :rel Dir.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM");
+ break :rel Dir.path.relative(arena, graph.cache.cwd, &graph.environ_map, child_cwd, path_str) catch @panic("OOM");
};
// Not every path can be made relative, e.g. if the path and the child cwd are on different
// disk designators on Windows. In that case, `relative` will return an absolute path which we can
// just return.
- if (Dir.path.isAbsolute(child_cwd_rel)) {
- return child_cwd_rel;
- }
+ if (Dir.path.isAbsolute(child_cwd_rel)) return child_cwd_rel;
+
// We're not done yet. In some cases this path must be prefixed with './':
// * On POSIX, the executable name cannot be a single component like 'foo'
// * Some executables might treat a leading '-' like a flag, which we must avoid
// There's no harm in it, so just *always* apply this prefix.
- return Dir.path.join(b.graph.arena, &.{ ".", child_cwd_rel }) catch @panic("OOM");
+ return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM");
}
const IndexedOutput = struct {
@@ -791,32 +801,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
var man = b.graph.cache.obtain();
defer man.deinit();
- if (run.env_map) |env_map| {
- const KV = struct { []const u8, []const u8 };
- var kv_pairs = try std.array_list.Managed(KV).initCapacity(arena, env_map.count());
- var iter = env_map.iterator();
- while (iter.next()) |entry| {
- kv_pairs.appendAssumeCapacity(.{ entry.key_ptr.*, entry.value_ptr.* });
- }
-
- std.mem.sortUnstable(KV, kv_pairs.items, {}, struct {
- fn lessThan(_: void, kv1: KV, kv2: KV) bool {
- const k1 = kv1[0];
- const k2 = kv2[0];
-
- if (k1.len != k2.len) return k1.len < k2.len;
-
- for (k1, k2) |c1, c2| {
- if (c1 == c2) continue;
- return c1 < c2;
- }
- unreachable; // two keys cannot be equal
- }
- }.lessThan);
-
- for (kv_pairs.items) |kv| {
- man.hash.addBytes(kv[0]);
- man.hash.addBytes(kv[1]);
+ if (run.environ_map) |environ_map| {
+ for (environ_map.keys(), environ_map.values()) |key, value| {
+ man.hash.addBytes(key);
+ man.hash.addBytes(value);
}
}
@@ -1181,40 +1169,40 @@ fn populateGeneratedPaths(
}
}
-fn formatTerm(term: ?std.process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error!void {
+fn formatTerm(term: ?process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error!void {
if (term) |t| switch (t) {
- .Exited => |code| try w.print("exited with code {d}", .{code}),
- .Signal => |sig| try w.print("terminated with signal {d}", .{sig}),
- .Stopped => |sig| try w.print("stopped with signal {d}", .{sig}),
- .Unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}),
+ .exited => |code| try w.print("exited with code {d}", .{code}),
+ .signal => |sig| try w.print("terminated with signal {t}", .{sig}),
+ .stopped => |sig| try w.print("stopped with signal {d}", .{sig}),
+ .unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}),
} else {
try w.writeAll("exited with any code");
}
}
-fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Alt(?std.process.Child.Term, formatTerm) {
+fn fmtTerm(term: ?process.Child.Term) std.fmt.Alt(?process.Child.Term, formatTerm) {
return .{ .data = term };
}
-fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term) bool {
+fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool {
return if (expected) |e| switch (e) {
- .Exited => |expected_code| switch (actual) {
- .Exited => |actual_code| expected_code == actual_code,
+ .exited => |expected_code| switch (actual) {
+ .exited => |actual_code| expected_code == actual_code,
else => false,
},
- .Signal => |expected_sig| switch (actual) {
- .Signal => |actual_sig| expected_sig == actual_sig,
+ .signal => |expected_sig| switch (actual) {
+ .signal => |actual_sig| expected_sig == actual_sig,
else => false,
},
- .Stopped => |expected_sig| switch (actual) {
- .Stopped => |actual_sig| expected_sig == actual_sig,
+ .stopped => |expected_sig| switch (actual) {
+ .stopped => |actual_sig| expected_sig == actual_sig,
else => false,
},
- .Unknown => |expected_code| switch (actual) {
- .Unknown => |actual_code| expected_code == actual_code,
+ .unknown => |expected_code| switch (actual) {
+ .unknown => |actual_code| expected_code == actual_code,
else => false,
},
} else switch (actual) {
- .Exited => true,
+ .exited => true,
else => false,
};
}
@@ -1241,7 +1229,7 @@ fn runCommand(
const cwd: ?[]const u8 = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, step) else null;
try step.handleChildProcUnsupported();
- try Step.handleVerbose2(step.owner, cwd, run.env_map, argv);
+ try Step.handleVerbose2(step.owner, cwd, run.environ_map, argv);
const allow_skip = switch (run.stdio) {
.check, .zig_test => run.skip_foreign_checks,
@@ -1251,13 +1239,13 @@ fn runCommand(
var interp_argv = std.array_list.Managed([]const u8).init(b.allocator);
defer interp_argv.deinit();
- var env_map: EnvMap = env: {
- const orig = run.env_map orelse &b.graph.env_map;
+ var environ_map: EnvMap = env: {
+ const orig = run.environ_map orelse &b.graph.environ_map;
break :env try orig.clone(gpa);
};
- defer env_map.deinit();
+ defer environ_map.deinit();
- const opt_generic_result = spawnChildAndCollect(run, argv, &env_map, has_side_effects, options, fuzz_context) catch |err| term: {
+ const opt_generic_result = spawnChildAndCollect(run, argv, &environ_map, has_side_effects, options, fuzz_context) catch |err| term: {
// InvalidExe: cpu arch mismatch
// FileNotFound: can happen with a wrong dynamic linker path
if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
@@ -1293,8 +1281,8 @@ fn runCommand(
// Wine's excessive stderr logging is only situationally helpful. Disable it by default, but
// allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired.
- if (env_map.get("WINEDEBUG") == null) {
- try env_map.put("WINEDEBUG", "-all");
+ if (environ_map.get("WINEDEBUG") == null) {
+ try environ_map.put("WINEDEBUG", "-all");
}
} else {
return failForeign(run, "-fwine", argv[0], exe);
@@ -1391,9 +1379,9 @@ fn runCommand(
gpa.free(step.result_failed_command.?);
step.result_failed_command = null;
- try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items);
+ try Step.handleVerbose2(step.owner, cwd, run.environ_map, interp_argv.items);
- break :term spawnChildAndCollect(run, interp_argv.items, &env_map, has_side_effects, options, fuzz_context) catch |e| {
+ break :term spawnChildAndCollect(run, interp_argv.items, &environ_map, has_side_effects, options, fuzz_context) catch |e| {
if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
if (e == error.MakeFailed) return error.MakeFailed; // error already reported
return step.fail("unable to spawn interpreter {s}: {s}", .{
@@ -1525,8 +1513,8 @@ fn runCommand(
else => {
// On failure, report captured stderr like normal standard error output.
const bad_exit = switch (generic_result.term) {
- .Exited => |code| code != 0,
- .Signal, .Stopped, .Unknown => true,
+ .exited => |code| code != 0,
+ .signal, .stopped, .unknown => true,
};
if (bad_exit) {
if (generic_result.stderr) |bytes| {
@@ -1540,7 +1528,7 @@ fn runCommand(
}
const EvalGenericResult = struct {
- term: std.process.Child.Term,
+ term: process.Child.Term,
stdout: ?[]const u8,
stderr: ?[]const u8,
};
@@ -1548,13 +1536,12 @@ const EvalGenericResult = struct {
fn spawnChildAndCollect(
run: *Run,
argv: []const []const u8,
- env_map: *EnvMap,
+ environ_map: *EnvMap,
has_side_effects: bool,
options: Step.MakeOptions,
fuzz_context: ?FuzzContext,
) !?EvalGenericResult {
const b = run.step.owner;
- const arena = b.allocator;
const graph = b.graph;
const io = graph.io;
@@ -1563,75 +1550,76 @@ fn spawnChildAndCollect(
assert(run.stdio == .zig_test);
}
- var child = std.process.Child.init(argv, arena);
- if (run.cwd) |lazy_cwd| {
- child.cwd = lazy_cwd.getPath2(b, &run.step);
- }
- child.env_map = env_map;
- child.request_resource_usage_statistics = true;
-
- child.stdin_behavior = switch (run.stdio) {
- .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
- .inherit => .Inherit,
- .check => .Ignore,
- .zig_test => .Pipe,
- };
- child.stdout_behavior = switch (run.stdio) {
- .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
- .inherit => .Inherit,
- .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore,
- .zig_test => .Pipe,
- };
- child.stderr_behavior = switch (run.stdio) {
- .infer_from_args => if (has_side_effects) .Inherit else .Pipe,
- .inherit => .Inherit,
- .check => .Pipe,
- .zig_test => .Pipe,
- };
- if (run.captured_stdout != null) child.stdout_behavior = .Pipe;
- if (run.captured_stderr != null) child.stderr_behavior = .Pipe;
- if (run.stdin != .none) {
- assert(run.stdio != .inherit);
- child.stdin_behavior = .Pipe;
- }
+ const child_cwd = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, &run.step) else null;
// If an error occurs, it's caused by this command:
assert(run.step.result_failed_command == null);
- run.step.result_failed_command = try Step.allocPrintCmd(options.gpa, child.cwd, argv);
+ run.step.result_failed_command = try Step.allocPrintCmd(options.gpa, child_cwd, .{
+ .child = environ_map,
+ .parent = &graph.environ_map,
+ }, argv);
+
+ var spawn_options: process.SpawnOptions = .{
+ .argv = argv,
+ .cwd = child_cwd,
+ .environ_map = environ_map,
+ .request_resource_usage_statistics = true,
+ .stdin = if (run.stdin != .none) s: {
+ assert(run.stdio != .inherit);
+ break :s .pipe;
+ } else switch (run.stdio) {
+ .infer_from_args => if (has_side_effects) .inherit else .ignore,
+ .inherit => .inherit,
+ .check => .ignore,
+ .zig_test => .pipe,
+ },
+ .stdout = if (run.captured_stdout != null) .pipe else switch (run.stdio) {
+ .infer_from_args => if (has_side_effects) .inherit else .ignore,
+ .inherit => .inherit,
+ .check => |checks| if (checksContainStdout(checks.items)) .pipe else .ignore,
+ .zig_test => .pipe,
+ },
+ .stderr = if (run.captured_stderr != null) .pipe else switch (run.stdio) {
+ .infer_from_args => if (has_side_effects) .inherit else .pipe,
+ .inherit => .inherit,
+ .check => .pipe,
+ .zig_test => .pipe,
+ },
+ };
if (run.stdio == .zig_test) {
var timer = try std.time.Timer.start();
defer run.step.result_duration_ns = timer.read();
- try evalZigTest(run, &child, options, fuzz_context);
+ try evalZigTest(run, spawn_options, options, fuzz_context);
return null;
} else {
- const inherit = child.stdout_behavior == .Inherit or child.stderr_behavior == .Inherit;
+ const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;
if (!run.disable_zig_progress and !inherit) {
- child.progress_node = options.progress_node;
+ spawn_options.progress_node = options.progress_node;
}
const terminal_mode: Io.Terminal.Mode = if (inherit) m: {
const stderr = try io.lockStderr(&.{}, graph.stderr_mode);
break :m stderr.terminal_mode;
} else .no_color;
defer if (inherit) io.unlockStderr();
- try setColorEnvironmentVariables(run, env_map, terminal_mode);
+ try setColorEnvironmentVariables(run, environ_map, terminal_mode);
var timer = try std.time.Timer.start();
- const res = try evalGeneric(run, &child);
+ const res = try evalGeneric(run, spawn_options);
run.step.result_duration_ns = timer.read();
return .{ .term = res.term, .stdout = res.stdout, .stderr = res.stderr };
}
}
-fn setColorEnvironmentVariables(run: *Run, env_map: *EnvMap, terminal_mode: Io.Terminal.Mode) !void {
+fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode: Io.Terminal.Mode) !void {
color: switch (run.color) {
.manual => {},
.enable => {
- try env_map.put("CLICOLOR_FORCE", "1");
- env_map.remove("NO_COLOR");
+ try environ_map.put("CLICOLOR_FORCE", "1");
+ _ = environ_map.swapRemove("NO_COLOR");
},
.disable => {
- try env_map.put("NO_COLOR", "1");
- env_map.remove("CLICOLOR_FORCE");
+ try environ_map.put("NO_COLOR", "1");
+ _ = environ_map.swapRemove("CLICOLOR_FORCE");
},
.inherit => switch (terminal_mode) {
.no_color, .windows_api => continue :color .disable,
@@ -1655,7 +1643,7 @@ const StdioPollEnum = enum { stdout, stderr };
fn evalZigTest(
run: *Run,
- child: *std.process.Child,
+ spawn_options: process.SpawnOptions,
options: Step.MakeOptions,
fuzz_context: ?FuzzContext,
) !void {
@@ -1679,14 +1667,14 @@ fn evalZigTest(
var test_metadata: ?TestMetadata = null;
while (true) {
- try child.spawn(io);
+ var child = try process.spawn(io, spawn_options);
var poller = std.Io.poll(gpa, StdioPollEnum, .{
.stdout = child.stdout.?,
.stderr = child.stderr.?,
});
var child_killed = false;
defer if (!child_killed) {
- _ = child.kill(io) catch {};
+ child.kill(io);
poller.deinit();
run.step.result_peak_rss = @max(
run.step.result_peak_rss,
@@ -1694,11 +1682,9 @@ fn evalZigTest(
);
};
- try child.waitForSpawn();
-
switch (try pollZigTest(
run,
- child,
+ &child,
options,
fuzz_context,
&poller,
@@ -1760,7 +1746,7 @@ fn evalZigTest(
// Report an error if the child terminated uncleanly or if we were still trying to run more tests.
run.step.result_stderr = stderr_owned;
const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32);
- if (!tests_done or !termMatches(.{ .Exited = 0 }, term)) {
+ if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {
// The individual unit test results are irrelevant: the test runner itself broke!
// Fail immediately without populating `s.test_results`.
return run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)});
@@ -1815,7 +1801,7 @@ fn evalZigTest(
/// * `poll` fails, indicating the child closed stdout and stderr
fn pollZigTest(
run: *Run,
- child: *std.process.Child,
+ child: *process.Child,
options: Step.MakeOptions,
fuzz_context: ?FuzzContext,
poller: *std.Io.Poller(StdioPollEnum),
@@ -2173,15 +2159,13 @@ fn sendRunFuzzTestMessage(
};
}
-fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
+fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResult {
const b = run.step.owner;
const io = b.graph.io;
const arena = b.allocator;
- try child.spawn(io);
- errdefer _ = child.kill(io) catch {};
-
- try child.waitForSpawn();
+ var child = try process.spawn(io, spawn_options);
+ defer child.kill(io);
switch (run.stdin) {
.bytes => |bytes| {
@@ -2331,10 +2315,10 @@ fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void {
=> |s| hh.addBytes(s),
.expect_term => |term| {
- hh.add(@as(std.meta.Tag(std.process.Child.Term), term));
+ hh.add(@as(std.meta.Tag(process.Child.Term), term));
switch (term) {
- .Exited => |x| hh.add(x),
- .Signal, .Stopped, .Unknown => |x| hh.add(x),
+ inline .exited, .signal => |x| hh.add(x),
+ .stopped, .unknown => |x| hh.add(x),
}
},
}
diff --git a/lib/std/Build/Watch.zig b/lib/std/Build/Watch.zig
index 9042241b5539935956f79d86c1306764f428c8ad..0a0178a018b535f5d06c11275c87753a4a6aef2d 100644
--- a/lib/std/Build/Watch.zig
+++ b/lib/std/Build/Watch.zig
@@ -99,7 +99,8 @@ const Os = switch (builtin.os.tag) {
};
};
- fn init() !Watch {
+ fn init(cwd_path: []const u8) !Watch {
+ _ = cwd_path;
return .{
.dir_table = .{},
.dir_count = 0,
@@ -427,7 +428,8 @@ const Os = switch (builtin.os.tag) {
}
};
- fn init() !Watch {
+ fn init(cwd_path: []const u8) !Watch {
+ _ = cwd_path;
return .{
.dir_table = .{},
.dir_count = 0,
@@ -658,14 +660,13 @@ const Os = switch (builtin.os.tag) {
const EV = std.c.EV;
const NOTE = std.c.NOTE;
- fn init() !Watch {
- const kq_fd = try posix.kqueue();
- errdefer posix.close(kq_fd);
+ fn init(cwd_path: []const u8) !Watch {
+ _ = cwd_path;
return .{
.dir_table = .{},
.dir_count = 0,
.os = .{
- .kq_fd = kq_fd,
+ .kq_fd = try posix.kqueue(),
.handles = .empty,
},
.generation = 0,
@@ -841,9 +842,9 @@ const Os = switch (builtin.os.tag) {
.macos => struct {
fse: FsEvents,
- fn init() !Watch {
+ fn init(cwd_path: []const u8) !Watch {
return .{
- .os = .{ .fse = try .init() },
+ .os = .{ .fse = try .init(cwd_path) },
.dir_count = 0,
.dir_table = undefined,
.generation = undefined,
@@ -863,8 +864,8 @@ const Os = switch (builtin.os.tag) {
else => void,
};
-pub fn init() !Watch {
- return Os.init();
+pub fn init(cwd_path: []const u8) !Watch {
+ return Os.init(cwd_path);
}
pub const Match = struct {
diff --git a/lib/std/Build/Watch/FsEvents.zig b/lib/std/Build/Watch/FsEvents.zig
index 2a48534b3accd009b42efa9abcb64d2da725e66f..b23805c7ee14a459a69adea27ebe68be3371276f 100644
--- a/lib/std/Build/Watch/FsEvents.zig
+++ b/lib/std/Build/Watch/FsEvents.zig
@@ -43,6 +43,8 @@ dispatch_queue: dispatch_queue_t,
/// of writing. See the comment at the start of `wait` for details.
since_event: FSEventStreamEventId,
+cwd_path: []const u8,
+
/// All of the symbols we pull from the `dlopen`ed CoreServices framework. If any of these symbols
/// is not present, `init` will close the framework and return an error.
const ResolvedSymbols = struct {
@@ -78,7 +80,7 @@ const ResolvedSymbols = struct {
kCFAllocatorUseContext: *const CFAllocatorRef,
};
-pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents {
+pub fn init(cwd_path: []const u8) error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents {
var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch
return error.OpenFrameworkFailed;
errdefer core_services.close();
@@ -99,6 +101,7 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents {
// Not `.since_now`, because this means we can init `FsEvents` *before* we do work in order
// to notice any changes which happened during said work.
.since_event = resolved_symbols.FSEventsGetCurrentEventId(),
+ .cwd_path = cwd_path,
};
}
@@ -120,9 +123,6 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step)
defer fse.paths_arena = paths_arena_instance.state;
const paths_arena = paths_arena_instance.allocator();
- const cwd_path = try std.process.getCwdAlloc(gpa);
- defer gpa.free(cwd_path);
-
var need_dirs: std.StringArrayHashMapUnmanaged(void) = .empty;
defer need_dirs.deinit(gpa);
@@ -131,7 +131,9 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step)
// We take `step` by pointer for a slight memory optimization in a moment.
for (steps) |*step| {
for (step.*.inputs.table.keys(), step.*.inputs.table.values()) |path, *files| {
- const resolved_dir = try std.fs.path.resolvePosix(paths_arena, &.{ cwd_path, path.root_dir.path orelse ".", path.sub_path });
+ const resolved_dir = try std.fs.path.resolvePosix(paths_arena, &.{
+ fse.cwd_path, path.root_dir.path orelse ".", path.sub_path,
+ });
try need_dirs.put(gpa, resolved_dir, {});
for (files.items) |file_name| {
const watch_path = if (std.mem.eql(u8, file_name, "."))
diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig
index a2b35e35229aa716799bfe46a1c53dc2b452f2c7..cb201e991d78a6256426535d0d86cb3517b998bd 100644
--- a/lib/std/Build/WebServer.zig
+++ b/lib/std/Build/WebServer.zig
@@ -482,8 +482,8 @@ pub fn serveFile(
});
}
pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void {
- const gpa = ws.gpa;
- const io = ws.graph.io;
+ const graph = ws.graph;
+ const io = graph.io;
var send_buffer: [0x4000]u8 = undefined;
var response = try request.respondStreaming(&send_buffer, .{
@@ -495,9 +495,6 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons
},
});
- var cached_cwd_path: ?[]const u8 = null;
- defer if (cached_cwd_path) |p| gpa.free(p);
-
var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer };
for (paths) |path| {
@@ -516,10 +513,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons
// resulting in modules named "" and "src". The compiler needs to tell the build system
// about the module graph so that the build system can correctly encode this information in
// the tar file.
- archiver.prefix = path.root_dir.path orelse cwd: {
- if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa);
- break :cwd cached_cwd_path.?;
- };
+ archiver.prefix = path.root_dir.path orelse graph.cache.cwd;
try archiver.writeFile(path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds()));
}
@@ -528,13 +522,13 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons
}
fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path {
- const io = ws.graph.io;
const root_name = "build-web";
const arch_os_abi = "wasm32-freestanding";
const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
const gpa = ws.gpa;
const graph = ws.graph;
+ const io = graph.io;
const main_src_path: Cache.Path = .{
.root_dir = graph.zig_lib_directory,
@@ -572,11 +566,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
"--listen=-",
});
- var child: std.process.Child = .init(argv.items, gpa);
- child.stdin_behavior = .Pipe;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
- try child.spawn(io);
+ var child = try std.process.spawn(io, .{
+ .argv = argv.items,
+ .environ_map = &graph.environ_map,
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .pipe,
+ });
+ defer child.kill(io);
var poller = Io.poll(gpa, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
@@ -636,19 +633,26 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
child.stdin = null;
switch (try child.wait(io)) {
- .Exited => |code| {
+ .exited => |code| {
if (code != 0) {
log.err(
"the following command exited with error code {d}:\n{s}",
- .{ code, try Build.Step.allocPrintCmd(arena, null, argv.items) },
+ .{ code, try Build.Step.allocPrintCmd(arena, null, null, argv.items) },
);
return error.WasmCompilationFailed;
}
},
- .Signal, .Stopped, .Unknown => {
+ .signal => |sig| {
+ log.err(
+ "the following command terminated with signal {t}:\n{s}",
+ .{ sig, try Build.Step.allocPrintCmd(arena, null, null, argv.items) },
+ );
+ return error.WasmCompilationFailed;
+ },
+ .stopped, .unknown => {
log.err(
"the following command terminated unexpectedly:\n{s}",
- .{try Build.Step.allocPrintCmd(arena, null, argv.items)},
+ .{try Build.Step.allocPrintCmd(arena, null, null, argv.items)},
);
return error.WasmCompilationFailed;
},
@@ -658,14 +662,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
try result_error_bundle.renderToStderr(io, .{}, .auto);
log.err("the following command failed with {d} compilation errors:\n{s}", .{
result_error_bundle.errorMessageCount(),
- try Build.Step.allocPrintCmd(arena, null, argv.items),
+ try Build.Step.allocPrintCmd(arena, null, null, argv.items),
});
return error.WasmCompilationFailed;
}
const base_path = result orelse {
log.err("child process failed to report result\n{s}", .{
- try Build.Step.allocPrintCmd(arena, null, argv.items),
+ try Build.Step.allocPrintCmd(arena, null, null, argv.items),
});
return error.WasmCompilationFailed;
};
diff --git a/lib/std/Io.zig b/lib/std/Io.zig
index 21fb71286f8a26b15e3827e4f21f0d54e76b15d4..a1e38135bd4b2b92d842f78025ce888ba8f97484 100644
--- a/lib/std/Io.zig
+++ b/lib/std/Io.zig
@@ -717,6 +717,14 @@ pub const VTable = struct {
tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr,
unlockStderr: *const fn (?*anyopaque) void,
processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void,
+ processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError,
+ processReplacePath: *const fn (?*anyopaque, Dir, std.process.ReplaceOptions) std.process.ReplaceError,
+ processSpawn: *const fn (?*anyopaque, std.process.SpawnOptions) std.process.SpawnError!std.process.Child,
+ processSpawnPath: *const fn (?*anyopaque, Dir, std.process.SpawnOptions) std.process.SpawnError!std.process.Child,
+ childWait: *const fn (?*anyopaque, *std.process.Child) std.process.Child.WaitError!std.process.Child.Term,
+ childKill: *const fn (?*anyopaque, *std.process.Child) void,
+
+ progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File,
now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,
sleep: *const fn (?*anyopaque, Timeout) SleepError!void,
diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig
index 82bf3b927df0cd22a14f640e1b1f58aedc23c373..cc1dec8efe2281702ec35a5de5b44e7eff4e496e 100644
--- a/lib/std/Io/Dir.zig
+++ b/lib/std/Io/Dir.zig
@@ -82,13 +82,14 @@ pub const Entry = struct {
///
/// On POSIX targets, this function is comptime-callable.
///
-/// On WASI, the value this returns is application-configurable.
+/// This function is overridable via `std.Options.cwd`.
pub fn cwd() Dir {
- return switch (native_os) {
+ const cwdFn = std.Options.cwd orelse return switch (native_os) {
.windows => .{ .handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle },
- .wasi => .{ .handle = std.options.wasiCwd() },
+ .wasi => .{ .handle = 3 }, // Expect the first preopen to be current working directory.
else => .{ .handle = std.posix.AT.FDCWD },
};
+ return cwdFn();
}
pub const Reader = struct {
diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig
index e16e70d6a715c799cb65661e471246421f4fdbc2..08217be4becaccfd84d4e195b230a45fd5b979cf 100644
--- a/lib/std/Io/Threaded.zig
+++ b/lib/std/Io/Threaded.zig
@@ -13,6 +13,7 @@ const File = std.Io.File;
const Dir = std.Io.Dir;
const HostName = std.Io.net.HostName;
const IpAddress = std.Io.net.IpAddress;
+const process = std.process;
const Allocator = std.mem.Allocator;
const Alignment = std.mem.Alignment;
const assert = std.debug.assert;
@@ -63,24 +64,41 @@ stderr_writer_initialized: bool = false,
argv0: Argv0,
environ: Environ,
+null_file: NullFile = .{},
+
pub const Argv0 = switch (native_os) {
.openbsd, .haiku => struct {
- value: ?[*:0]const u8 = null,
+ value: ?[*:0]const u8,
+
+ pub const empty: Argv0 = .{ .value = null };
+
+ pub fn init(args: process.Args) Argv0 {
+ return .{ .value = args.value[0] };
+ }
+ },
+ else => struct {
+ pub const empty: Argv0 = .{};
+
+ pub fn init(args: process.Args) Argv0 {
+ _ = args;
+ return .{};
+ }
},
- else => struct {},
};
-pub const Environ = struct {
+const Environ = struct {
/// Unmodified data directly from the OS.
- block: Block = &.{},
+ process_environ: process.Environ = .empty,
/// Protected by `mutex`. Determines whether the other fields have been
- /// memoized based on `block`.
+ /// memoized based on `process_environ`.
initialized: bool = false,
- /// Protected by `mutex`. Memoized based on `block`. Tracks whether the
+ /// Protected by `mutex`. Memoized based on `process_environ`. Tracks whether the
/// environment variables are present, ignoring their value.
exist: Exist = .{},
- /// Protected by `mutex`. Memoized based on `block`.
+ /// Protected by `mutex`. Memoized based on `process_environ`.
string: String = .{},
+ /// ZIG_PROGRESS
+ zig_progress_handle: std.Progress.ParentFileError!u31 = error.EnvironmentVariableMissing,
/// Protected by `mutex`. Tracks the problem, if any, that occurred when
/// trying to scan environment variables.
///
@@ -89,21 +107,50 @@ pub const Environ = struct {
pub const Error = Allocator.Error || Io.UnexpectedError;
- pub const Block = []const [*:0]const u8;
-
pub const Exist = struct {
NO_COLOR: bool = false,
CLICOLOR_FORCE: bool = false,
};
pub const String = switch (native_os) {
- .openbsd, .haiku => struct {
+ .windows, .wasi => struct {},
+ else => struct {
PATH: ?[:0]const u8 = null,
+ DEBUGINFOD_CACHE_PATH: ?[:0]const u8 = null,
+ XDG_CACHE_HOME: ?[:0]const u8 = null,
+ HOME: ?[:0]const u8 = null,
},
- else => struct {},
};
};
+pub const NullFile = switch (native_os) {
+ .windows => struct {
+ handle: ?windows.HANDLE = null,
+
+ fn deinit(this: *@This()) void {
+ if (this.handle) |handle| {
+ windows.CloseHandle(handle);
+ this.handle = null;
+ }
+ }
+ },
+ .wasi, .ios, .tvos, .visionos, .watchos => struct {
+ fn deinit(this: @This()) void {
+ _ = this;
+ }
+ },
+ else => struct {
+ fd: posix.fd_t = -1,
+
+ fn deinit(this: *@This()) void {
+ if (this.fd >= 0) {
+ posix.close(this.fd);
+ this.fd = -1;
+ }
+ }
+ },
+};
+
pub const Pid = if (native_os == .linux) enum(posix.pid_t) {
unknown = 0,
_,
@@ -1105,6 +1152,7 @@ const Syscall = struct {
const max_iovecs_len = 8;
const splat_buffer_size = 64;
+const default_PATH = "/usr/local/bin:/bin/:/usr/bin";
comptime {
if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);
@@ -1137,7 +1185,8 @@ pub const InitOptions = struct {
/// Affects the following operations:
/// * `fileIsTty`
/// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH").
- environ: Environ = .{},
+ /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath`
+ environ: process.Environ,
};
/// Related:
@@ -1167,8 +1216,8 @@ pub fn init(
.old_sig_pipe = undefined,
.have_signal_handler = false,
.argv0 = options.argv0,
- .environ = options.environ,
.worker_threads = .init(null),
+ .environ = .{ .process_environ = options.environ },
};
if (posix.Sigaction != void) {
@@ -1235,6 +1284,7 @@ pub fn deinit(t: *Threaded) void {
if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null);
if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null);
}
+ t.null_file.deinit();
t.* = undefined;
}
@@ -1402,6 +1452,14 @@ pub fn io(t: *Threaded) Io {
.tryLockStderr = tryLockStderr,
.unlockStderr = unlockStderr,
.processSetCurrentDir = processSetCurrentDir,
+ .processReplace = processReplace,
+ .processReplacePath = processReplacePath,
+ .processSpawn = processSpawn,
+ .processSpawnPath = processSpawnPath,
+ .childWait = childWait,
+ .childKill = childKill,
+
+ .progressParentFile = progressParentFile,
.now = now,
.sleep = sleep,
@@ -1540,6 +1598,14 @@ pub fn ioBasic(t: *Threaded) Io {
.tryLockStderr = tryLockStderr,
.unlockStderr = unlockStderr,
.processSetCurrentDir = processSetCurrentDir,
+ .processReplace = processReplace,
+ .processReplacePath = processReplacePath,
+ .processSpawn = processSpawn,
+ .processSpawnPath = processSpawnPath,
+ .childWait = childWait,
+ .childKill = childKill,
+
+ .progressParentFile = progressParentFile,
.now = now,
.sleep = sleep,
@@ -1603,6 +1669,18 @@ const have_fchmod = switch (native_os) {
else => true,
};
+const have_waitid = switch (native_os) {
+ .linux => @hasField(std.os.linux.SYS, "waitid"),
+ else => false,
+};
+
+const have_wait4 = switch (native_os) {
+ .linux => @hasField(std.os.linux.SYS, "wait4"),
+ .dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .serenity, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => true,
+ else => false,
+};
+
+const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open;
const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat;
const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat;
const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat;
@@ -2249,7 +2327,7 @@ fn dirCreateDirPath(
) Dir.CreateDirPathError!Dir.CreatePathStatus {
const t: *Threaded = @ptrCast(@alignCast(userdata));
- var it = std.fs.path.componentIterator(sub_path);
+ var it = Dir.path.componentIterator(sub_path);
var status: Dir.CreatePathStatus = .existed;
var component = it.last() orelse return error.BadPathName;
while (true) {
@@ -2309,9 +2387,9 @@ fn dirCreateDirPathOpenWindows(
_ = permissions; // TODO apply these permissions
- var it = std.fs.path.componentIterator(sub_path);
+ var it = Dir.path.componentIterator(sub_path);
// If there are no components in the path, then create a dummy component with the full path.
- var component: std.fs.path.NativeComponentIterator.Component = it.last() orelse .{
+ var component: Dir.path.NativeComponentIterator.Component = it.last() orelse .{
.name = "",
.path = sub_path,
};
@@ -2349,7 +2427,7 @@ fn dirCreateDirPathOpenWindows(
},
&.{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
- .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
+ .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.Attributes = .{},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
@@ -2988,7 +3066,7 @@ fn dirAccessWindows(
};
var attr: windows.OBJECT_ATTRIBUTES = .{
.Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
- .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
+ .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.Attributes = .{},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
@@ -3537,7 +3615,7 @@ fn dirOpenFileWindows(
_ = t;
const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_array.span();
- const dir_handle = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
+ const dir_handle = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
return dirOpenFileWtf16(dir_handle, sub_path_w, flags);
}
@@ -3620,7 +3698,10 @@ pub fn dirOpenFileWtf16(
// kernel bug with retry attempts.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
- _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
+ try parking_sleep.sleep(.{ .duration = .{
+ .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
+ .clock = .awake,
+ } });
attempt += 1;
syscall = try .start();
continue;
@@ -3642,7 +3723,10 @@ pub fn dirOpenFileWtf16(
// fixed by sleeping and retrying until the error goes away.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
- _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
+ try parking_sleep.sleep(.{ .duration = .{
+ .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
+ .clock = .awake,
+ } });
attempt += 1;
syscall = try .start();
continue;
@@ -3935,7 +4019,7 @@ pub fn dirOpenDirWindows(
},
&.{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
- .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
+ .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.Attributes = .{},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
@@ -5083,7 +5167,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
} },
&.{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
- .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
+ .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.Attributes = .{},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
@@ -5360,7 +5444,7 @@ fn dirRenameWindows(
.POSIX_SEMANTICS = true,
.IGNORE_READONLY_ATTRIBUTE = true,
},
- .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
+ .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
.FileName = new_path_w,
});
var io_status_block: w.IO_STATUS_BLOCK = undefined;
@@ -5389,7 +5473,7 @@ fn dirRenameWindows(
if (need_fallback) {
const rename_info: w.FILE.RENAME_INFORMATION = .init(.{
.Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
- .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
+ .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
.FileName = new_path_w,
});
var io_status_block: w.IO_STATUS_BLOCK = undefined;
@@ -5614,7 +5698,7 @@ fn dirSymLinkWindows(
// Already an NT path, no need to do anything to it
break :target_path target_path_w.span();
} else {
- switch (w.getWin32PathType(u16, target_path_w.span())) {
+ switch (Dir.path.getWin32PathType(u16, target_path_w.span())) {
// Rooted paths need to avoid getting put through wToPrefixedFileW
// (and they are treated as relative in this context)
// Note: It seems that rooted paths in symbolic links are relative to
@@ -5624,13 +5708,13 @@ fn dirSymLinkWindows(
// the C:\ drive.
.rooted => break :target_path target_path_w.span(),
// Keep relative paths relative, but anything else needs to get NT-prefixed.
- else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path_w.span()))
+ else => if (!Dir.path.isAbsoluteWindowsWtf16(target_path_w.span()))
break :target_path target_path_w.span(),
}
}
var prefixed_target_path = try w.wToPrefixedFileW(dir.handle, target_path_w.span());
// We do this after prefixing to ensure that drive-relative paths are treated as absolute
- is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
+ is_target_absolute = Dir.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
break :target_path prefixed_target_path.span();
};
@@ -5638,8 +5722,8 @@ fn dirSymLinkWindows(
var buffer: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
const header_len = @sizeOf(w.ULONG) + @sizeOf(w.USHORT) * 2;
- const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path);
- const symlink_data = SYMLINK_DATA{
+ const target_is_absolute = Dir.path.isAbsoluteWindowsWtf16(final_target_path);
+ const symlink_data: SYMLINK_DATA = .{
.ReparseTag = .SYMLINK,
.ReparseDataLength = @intCast(buf_len - header_len),
.Reserved = 0,
@@ -7892,7 +7976,7 @@ fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {
}
}
-fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.process.OpenExecutableError!File {
+fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.OpenExecutableError!File {
const t: *Threaded = @ptrCast(@alignCast(userdata));
switch (native_os) {
.wasi => return error.OperationUnsupported,
@@ -7933,7 +8017,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.proce
}
}
-fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.ExecutablePathError!usize {
+fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.ExecutablePathError!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
switch (native_os) {
@@ -11693,14 +11777,14 @@ fn netLookupFallible(
fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr {
const t: *Threaded = @ptrCast(@alignCast(userdata));
// Only global mutex since this is Threaded.
- std.process.stderr_thread_mutex.lock();
+ process.stderr_thread_mutex.lock();
return initLockedStderr(t, terminal_mode);
}
fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr {
const t: *Threaded = @ptrCast(@alignCast(userdata));
// Only global mutex since this is Threaded.
- if (!std.process.stderr_thread_mutex.tryLock()) return null;
+ if (!process.stderr_thread_mutex.tryLock()) return null;
return try initLockedStderr(t, terminal_mode);
}
@@ -11731,10 +11815,10 @@ fn unlockStderr(userdata: ?*anyopaque) void {
};
t.stderr_writer.interface.end = 0;
t.stderr_writer.interface.buffer = &.{};
- std.process.stderr_thread_mutex.unlock();
+ process.stderr_thread_mutex.unlock();
}
-fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void {
+fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void {
if (native_os == .wasi) return error.OperationUnsupported;
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
@@ -11769,38 +11853,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentD
};
}
- if (dir.handle == posix.AT.FDCWD) return;
-
- const syscall: Syscall = try .start();
- while (true) {
- switch (posix.errno(posix.system.fchdir(dir.handle))) {
- .SUCCESS => return syscall.finish(),
- .INTR => {
- try syscall.checkCancel();
- continue;
- },
- .ACCES => {
- syscall.finish();
- return error.AccessDenied;
- },
- .BADF => |err| {
- syscall.finish();
- return errnoBug(err);
- },
- .NOTDIR => {
- syscall.finish();
- return error.NotDir;
- },
- .IO => {
- syscall.finish();
- return error.FileSystem;
- },
- else => |err| {
- syscall.finish();
- return posix.unexpectedErrno(err);
- },
- }
- }
+ return fchdir(dir.handle);
}
pub const PosixAddress = extern union {
@@ -12577,6 +12630,45 @@ fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {
fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {}
+const WindowsEnvironStrings = struct {
+ PATH: ?[:0]const u16 = null,
+ PATHEXT: ?[:0]const u16 = null,
+
+ fn scan() WindowsEnvironStrings {
+ const ptr = windows.peb().ProcessParameters.Environment;
+
+ var result: WindowsEnvironStrings = .{};
+ var i: usize = 0;
+ while (ptr[i] != 0) {
+ const key_start = i;
+
+ // There are some special environment variables that start with =,
+ // so we need a special case to not treat = as a key/value separator
+ // if it's the first character.
+ // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
+ if (ptr[key_start] == '=') i += 1;
+
+ while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
+ const key_w = ptr[key_start..i];
+
+ if (ptr[i] == '=') i += 1;
+
+ const value_start = i;
+ while (ptr[i] != 0) : (i += 1) {}
+ const value_w = ptr[value_start..i :0];
+
+ i += 1; // skip over null byte
+
+ inline for (@typeInfo(WindowsEnvironStrings).@"struct".fields) |field| {
+ const field_name_w = comptime std.unicode.wtf8ToWtf16LeStringLiteral(field.name);
+ if (std.mem.eql(u16, key_w, field_name_w)) @field(result, field.name) = value_w;
+ }
+ }
+
+ return result;
+ }
+};
+
fn scanEnviron(t: *Threaded) void {
t.mutex.lock();
defer t.mutex.unlock();
@@ -12585,6 +12677,9 @@ fn scanEnviron(t: *Threaded) void {
t.environ.initialized = true;
if (is_windows) {
+ // This value expires with any call that modifies the environment,
+ // which is outside of this Io implementation's control, so references
+ // must be short-lived.
const ptr = windows.peb().ProcessParameters.Environment;
var i: usize = 0;
@@ -12652,9 +12747,9 @@ fn scanEnviron(t: *Threaded) void {
}
comptime assert(@sizeOf(Environ.String) == 0);
}
- } else if (builtin.link_libc) {
- var ptr = std.c.environ;
- while (ptr[0]) |line| : (ptr += 1) {
+ } else {
+ for (t.environ.process_environ.block) |opt_line| {
+ const line = opt_line.?;
var line_i: usize = 0;
while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
const key = line[0..line_i];
@@ -12667,29 +12762,1989 @@ fn scanEnviron(t: *Threaded) void {
t.environ.exist.NO_COLOR = true;
} else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) {
t.environ.exist.CLICOLOR_FORCE = true;
- } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) {
- t.environ.string.PATH = value;
+ } else if (std.mem.eql(u8, key, "ZIG_PROGRESS")) {
+ t.environ.zig_progress_handle = std.fmt.parseInt(u31, value, 10) catch error.UnrecognizedFormat;
+ } else inline for (@typeInfo(Environ.String).@"struct".fields) |field| {
+ if (std.mem.eql(u8, key, field.name)) @field(t.environ.string, field.name) = value;
+ }
+ }
+ }
+}
+
+fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) process.ReplaceError {
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+
+ if (!process.can_replace) return error.OperationUnsupported;
+
+ t.scanEnviron(); // for PATH
+ const PATH = t.environ.string.PATH orelse default_PATH;
+
+ var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
+ defer arena_allocator.deinit();
+ const arena = arena_allocator.allocator();
+
+ const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null);
+ for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
+
+ const envp: [*:null]const ?[*:0]const u8 = m: {
+ const prog_fd: i32 = -1;
+ if (options.environ_map) |environ_map| {
+ break :m (try environ_map.createBlockPosix(arena, .{
+ .zig_progress_fd = prog_fd,
+ })).ptr;
+ }
+ break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{
+ .zig_progress_fd = prog_fd,
+ })).ptr;
+ };
+
+ return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH);
+}
+
+fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError {
+ if (!process.can_replace) return error.OperationUnsupported;
+ _ = userdata;
+ _ = dir;
+ _ = options;
+ @panic("TODO processReplacePath");
+}
+
+fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child {
+ if (!process.can_spawn) return error.OperationUnsupported;
+ _ = userdata;
+ _ = dir;
+ _ = options;
+ @panic("TODO processSpawnPath");
+}
+
+const processSpawn = switch (native_os) {
+ .wasi, .ios, .tvos, .visionos, .watchos => processSpawnUnsupported,
+ .windows => processSpawnWindows,
+ else => processSpawnPosix,
+};
+
+fn processSpawnUnsupported(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
+ _ = userdata;
+ _ = options;
+ return error.OperationUnsupported;
+}
+
+const Spawned = struct {
+ pid: posix.pid_t,
+ err_fd: posix.fd_t,
+ stdin: ?File,
+ stdout: ?File,
+ stderr: ?File,
+};
+
+fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Spawned {
+ // The child process does need to access (one end of) these pipes. However,
+ // we must initially set CLOEXEC to avoid a race condition. If another thread
+ // is racing to spawn a different child process, we don't want it to inherit
+ // these FDs in any scenario; that would mean that, for instance, calls to
+ // `poll` from the parent would not report the child's stdout as closing when
+ // expected, since the other child may retain a reference to the write end of
+ // the pipe. So, we create the pipes with CLOEXEC initially. After fork, we
+ // need to do something in the new child to make sure we preserve the reference
+ // we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it
+ // turns out, we `dup2` everything anyway, so there's no need!
+ const pipe_flags: posix.O = .{ .CLOEXEC = true };
+
+ const stdin_pipe = if (options.stdin == .pipe) try pipe2(pipe_flags) else undefined;
+ errdefer if (options.stdin == .pipe) {
+ destroyPipe(stdin_pipe);
+ };
+
+ const stdout_pipe = if (options.stdout == .pipe) try pipe2(pipe_flags) else undefined;
+ errdefer if (options.stdout == .pipe) {
+ destroyPipe(stdout_pipe);
+ };
+
+ const stderr_pipe = if (options.stderr == .pipe) try pipe2(pipe_flags) else undefined;
+ errdefer if (options.stderr == .pipe) {
+ destroyPipe(stderr_pipe);
+ };
+
+ const any_ignore = (options.stdin == .ignore or options.stdout == .ignore or options.stderr == .ignore);
+ const dev_null_fd = if (any_ignore) try getDevNullFd(t) else undefined;
+
+ const prog_pipe: [2]posix.fd_t = p: {
+ if (options.progress_node.index == .none) {
+ break :p .{ -1, -1 };
+ } else {
+ // We use CLOEXEC for the same reason as in `pipe_flags`.
+ break :p try pipe2(.{ .NONBLOCK = true, .CLOEXEC = true });
+ }
+ };
+ errdefer destroyPipe(prog_pipe);
+
+ var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
+ defer arena_allocator.deinit();
+ const arena = arena_allocator.allocator();
+
+ // The POSIX standard does not allow malloc() between fork() and execve(),
+ // and this allocator may be a libc allocator.
+ // I have personally observed the child process deadlocking when it tries
+ // to call malloc() due to a heap allocation between fork() and execve(),
+ // in musl v1.1.24.
+ // Additionally, we want to reduce the number of possible ways things
+ // can fail between fork() and execve().
+ // Therefore, we do all the allocation for the execve() before the fork().
+ // This means we must do the null-termination of argv and env vars here.
+ const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null);
+ for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
+
+ const prog_fileno = 3;
+ comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno);
+
+ const envp: [*:null]const ?[*:0]const u8 = m: {
+ const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno;
+ if (options.environ_map) |environ_map| {
+ break :m (try environ_map.createBlockPosix(arena, .{
+ .zig_progress_fd = prog_fd,
+ })).ptr;
+ }
+ break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{
+ .zig_progress_fd = prog_fd,
+ })).ptr;
+ };
+
+ // This pipe communicates to the parent errors in the child between `fork` and `execvpe`.
+ // It is closed by the child (via CLOEXEC) without writing if `execvpe` succeeds.
+ const err_pipe: [2]posix.fd_t = try pipe2(.{ .CLOEXEC = true });
+ errdefer destroyPipe(err_pipe);
+
+ t.scanEnviron(); // for PATH
+ const PATH = t.environ.string.PATH orelse default_PATH;
+
+ const pid_result: posix.pid_t = fork: {
+ const rc = posix.system.fork();
+ switch (posix.errno(rc)) {
+ .SUCCESS => break :fork @intCast(rc),
+ .AGAIN => return error.SystemResources,
+ .NOMEM => return error.SystemResources,
+ .NOSYS => return error.OperationUnsupported,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ };
+
+ if (pid_result == 0) {
+ defer comptime unreachable; // We are the child.
+ if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked;
+ const ep1 = err_pipe[1];
+
+ setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
+ setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
+ setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
+
+ if (options.cwd_dir) |cwd| {
+ fchdir(cwd.handle) catch |err| forkBail(ep1, err);
+ } else if (options.cwd) |cwd| {
+ chdir(cwd) catch |err| forkBail(ep1, err);
+ }
+
+ // Must happen after fchdir above, the cwd file descriptor might be
+ // equal to prog_fileno and be clobbered by this dup2 call.
+ if (prog_pipe[1] != -1) dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(ep1, err);
+
+ if (options.gid) |gid| {
+ switch (posix.errno(posix.system.setregid(gid, gid))) {
+ .SUCCESS => {},
+ .AGAIN => forkBail(ep1, error.ResourceLimitReached),
+ .INVAL => forkBail(ep1, error.InvalidUserId),
+ .PERM => forkBail(ep1, error.PermissionDenied),
+ else => forkBail(ep1, error.Unexpected),
+ }
+ }
+
+ if (options.uid) |uid| {
+ switch (posix.errno(posix.system.setreuid(uid, uid))) {
+ .SUCCESS => {},
+ .AGAIN => forkBail(ep1, error.ResourceLimitReached),
+ .INVAL => forkBail(ep1, error.InvalidUserId),
+ .PERM => forkBail(ep1, error.PermissionDenied),
+ else => forkBail(ep1, error.Unexpected),
}
}
+
+ if (options.pgid) |pid| {
+ switch (posix.errno(posix.system.setpgid(0, pid))) {
+ .SUCCESS => {},
+ .ACCES => forkBail(ep1, error.ProcessAlreadyExec),
+ .INVAL => forkBail(ep1, error.InvalidProcessGroupId),
+ .PERM => forkBail(ep1, error.PermissionDenied),
+ else => forkBail(ep1, error.Unexpected),
+ }
+ }
+
+ if (options.start_suspended) {
+ switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) {
+ .SUCCESS => {},
+ .PERM => forkBail(ep1, error.PermissionDenied),
+ else => forkBail(ep1, error.Unexpected),
+ }
+ }
+
+ const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH);
+ forkBail(ep1, err);
+ }
+
+ const pid: posix.pid_t = @intCast(pid_result); // We are the parent.
+ errdefer comptime unreachable; // The child is forked; we must not error from now on
+
+ posix.close(err_pipe[1]); // make sure only the child holds the write end open
+
+ if (options.stdin == .pipe) posix.close(stdin_pipe[0]);
+ if (options.stdout == .pipe) posix.close(stdout_pipe[1]);
+ if (options.stderr == .pipe) posix.close(stderr_pipe[1]);
+
+ if (prog_pipe[1] != -1) posix.close(prog_pipe[1]);
+
+ options.progress_node.setIpcFd(prog_pipe[0]);
+
+ return .{
+ .pid = pid,
+ .err_fd = err_pipe[0],
+ .stdin = switch (options.stdin) {
+ .pipe => .{ .handle = stdin_pipe[1] },
+ else => null,
+ },
+ .stdout = switch (options.stdout) {
+ .pipe => .{ .handle = stdout_pipe[0] },
+ else => null,
+ },
+ .stderr = switch (options.stderr) {
+ .pipe => .{ .handle = stderr_pipe[0] },
+ else => null,
+ },
+ };
+}
+
+fn getDevNullFd(t: *Threaded) !posix.fd_t {
+ {
+ t.mutex.lock();
+ defer t.mutex.unlock();
+ if (t.null_file.fd != -1) return t.null_file.fd;
+ }
+ const mode: u32 = 0;
+ const syscall: Syscall = try .start();
+ while (true) {
+ const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, mode);
+ switch (posix.errno(rc)) {
+ .SUCCESS => {
+ syscall.finish();
+ const fresh_fd: posix.fd_t = @intCast(rc);
+ t.mutex.lock(); // Another thread might have won the race.
+ defer t.mutex.unlock();
+ if (t.null_file.fd != -1) {
+ posix.close(fresh_fd);
+ return t.null_file.fd;
+ } else {
+ t.null_file.fd = fresh_fd;
+ return fresh_fd;
+ }
+ },
+ .INTR => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .ACCES => return syscall.fail(error.AccessDenied),
+ .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
+ .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
+ .NODEV => return syscall.fail(error.NoDevice),
+ .NOENT => return syscall.fail(error.FileNotFound),
+ .NOMEM => return syscall.fail(error.SystemResources),
+ .PERM => return syscall.fail(error.PermissionDenied),
+ else => |err| return syscall.unexpectedErrno(err),
+ }
+ }
+}
+
+fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+ const spawned = try spawnPosix(t, options);
+ defer posix.close(spawned.err_fd);
+
+ // Wait for the child to report any errors in or before `execvpe`.
+ if (readIntFd(spawned.err_fd)) |child_err_int| {
+ const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int));
+ return child_err;
+ } else |read_err| switch (read_err) {
+ error.EndOfStream => {
+ // Write end closed by CLOEXEC at the time of the `execvpe` call,
+ // indicating success.
+ },
+ else => {
+ // Problem reading the error from the error reporting pipe. We
+ // don't know if the child is alive or dead. Better to assume it is
+ // alive so the resource does not risk being leaked.
+ },
+ }
+
+ return .{
+ .id = spawned.pid,
+ .thread_handle = {},
+ .stdin = spawned.stdin,
+ .stdout = spawned.stdout,
+ .stderr = spawned.stderr,
+ .request_resource_usage_statistics = options.request_resource_usage_statistics,
+ };
+}
+
+fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitError!process.Child.Term {
+ if (native_os == .wasi) unreachable;
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+ _ = t;
+ switch (native_os) {
+ .windows => return childWaitWindows(child),
+ else => return childWaitPosix(child),
+ }
+}
+
+fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
+ if (native_os == .wasi) unreachable;
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+ if (is_windows) {
+ childKillWindows(t, child, 1) catch childCleanupWindows(child);
} else {
- for (t.environ.block) |line| {
- var line_i: usize = 0;
- while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
- const key = line[0..line_i];
+ childKillPosix(child) catch {};
+ childCleanupPosix(child);
+ }
+}
- var end_i: usize = line_i;
- while (line[end_i] != 0) : (end_i += 1) {}
- const value = line[line_i + 1 .. end_i :0];
+fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT) !void {
+ _ = t; // TODO cancelation
+ const handle = child.id.?;
+ if (windows.kernel32.TerminateProcess(handle, exit_code) == 0) {
+ switch (windows.GetLastError()) {
+ .ACCESS_DENIED => {
+ // Usually when TerminateProcess triggers a ACCESS_DENIED error, it
+ // indicates that the process has already exited, but there may be
+ // some rare edge cases where our process handle no longer has the
+ // PROCESS_TERMINATE access right, so let's do another check to make
+ // sure the process is really no longer running:
+ windows.WaitForSingleObjectEx(handle, 0, false) catch return error.AccessDenied;
+ return error.AlreadyTerminated;
+ },
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+ _ = windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE);
+ childCleanupWindows(child);
+}
- if (std.mem.eql(u8, key, "NO_COLOR")) {
- t.environ.exist.NO_COLOR = true;
- } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) {
- t.environ.exist.CLICOLOR_FORCE = true;
- } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) {
- t.environ.string.PATH = value;
+fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {
+ const handle = child.id.?;
+
+ const syscall: Syscall = try .start();
+ while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) {
+ windows.WAIT_OBJECT_0 => break syscall.finish(),
+ windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => {
+ try syscall.checkCancel();
+ continue;
+ },
+ windows.WAIT_FAILED => {
+ syscall.finish();
+ switch (windows.GetLastError()) {
+ else => |err| return windows.unexpectedError(err),
}
+ },
+ else => return syscall.fail(error.Unexpected),
+ };
+
+ const term: process.Child.Term = x: {
+ var exit_code: windows.DWORD = undefined;
+ if (windows.kernel32.GetExitCodeProcess(handle, &exit_code) == 0) {
+ break :x .{ .unknown = 0 };
+ } else {
+ break :x .{ .exited = @as(u8, @truncate(exit_code)) };
}
+ };
+
+ childCleanupWindows(child);
+ return term;
+}
+
+fn childCleanupWindows(child: *process.Child) void {
+ const handle = child.id orelse return;
+
+ if (child.request_resource_usage_statistics)
+ child.resource_usage_statistics.rusage = windows.GetProcessMemoryInfo(handle) catch null;
+
+ windows.CloseHandle(handle);
+ child.id = null;
+
+ windows.CloseHandle(child.thread_handle);
+ child.thread_handle = undefined;
+
+ if (child.stdin) |*stdin| {
+ windows.CloseHandle(stdin.handle);
+ child.stdin = null;
+ }
+ if (child.stdout) |*stdout| {
+ windows.CloseHandle(stdout.handle);
+ child.stdout = null;
+ }
+ if (child.stderr) |*stderr| {
+ windows.CloseHandle(stderr.handle);
+ child.stderr = null;
+ }
+}
+
+fn childWaitPosix(child: *process.Child) process.Child.WaitError!process.Child.Term {
+ defer childCleanupPosix(child);
+
+ const pid = child.id.?;
+
+ var ru: posix.rusage = undefined;
+ const ru_ptr = if (child.request_resource_usage_statistics) &ru else null;
+
+ if (have_wait4) {
+ var status: if (builtin.link_libc) c_int else u32 = undefined;
+ const syscall: Syscall = try .start();
+ while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, ru_ptr))) {
+ .SUCCESS => {
+ syscall.finish();
+ if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
+ return statusToTerm(@bitCast(status));
+ },
+ .INTR => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .CHILD => |err| return syscall.errnoBug(err), // Double-free.
+ else => |err| return syscall.unexpectedErrno(err),
+ };
+ }
+
+ if (have_waitid) {
+ const linux = std.os.linux; // Bypass libc which has the wrong signature.
+ var info: linux.siginfo_t = undefined;
+ const syscall: Syscall = try .start();
+ while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, ru_ptr))) {
+ .SUCCESS => {
+ syscall.finish();
+ if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
+ const status: u32 = @bitCast(info.fields.common.second.sigchld.status);
+ const code: linux.CLD = @enumFromInt(info.code);
+ return switch (code) {
+ .EXITED => .{ .exited = @truncate(status) },
+ .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) },
+ .TRAPPED, .STOPPED => .{ .stopped = status },
+ _, .CONTINUED => .{ .unknown = status },
+ };
+ },
+ .INTR => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .CHILD => |err| return syscall.errnoBug(err), // Double-free.
+ else => |err| return syscall.unexpectedErrno(err),
+ };
+ }
+
+ var status: if (builtin.link_libc) c_int else u32 = undefined;
+ const syscall: Syscall = try .start();
+ while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
+ .SUCCESS => {
+ syscall.finish();
+ return statusToTerm(@bitCast(status));
+ },
+ .INTR => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .CHILD => |err| return syscall.errnoBug(err), // Double-free.
+ else => |err| return syscall.unexpectedErrno(err),
+ };
+}
+
+fn statusToTerm(status: u32) process.Child.Term {
+ return if (posix.W.IFEXITED(status))
+ .{ .exited = posix.W.EXITSTATUS(status) }
+ else if (posix.W.IFSIGNALED(status))
+ .{ .signal = posix.W.TERMSIG(status) }
+ else if (posix.W.IFSTOPPED(status))
+ .{ .stopped = posix.W.STOPSIG(status) }
+ else
+ .{ .unknown = status };
+}
+
+fn childKillPosix(child: *process.Child) !void {
+ // Entire function body is intentionally uncancelable.
+
+ const pid = child.id.?;
+
+ while (true) switch (posix.errno(posix.system.kill(pid, .TERM))) {
+ .SUCCESS => break,
+ .INTR => continue,
+ .PERM => return error.PermissionDenied,
+ .INVAL => |err| return errnoBug(err),
+ .SRCH => |err| return errnoBug(err),
+ else => |err| return posix.unexpectedErrno(err),
+ };
+
+ if (have_wait4) {
+ var status: if (builtin.link_libc) c_int else u32 = undefined;
+ while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, null))) {
+ .SUCCESS => return,
+ .INTR => continue,
+ .CHILD => |err| return errnoBug(err), // Double-free.
+ else => |err| return posix.unexpectedErrno(err),
+ };
+ }
+
+ if (have_waitid) {
+ const linux = std.os.linux; // Bypass libc which has the wrong signature.
+ var info: linux.siginfo_t = undefined;
+ while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, null))) {
+ .SUCCESS => return,
+ .INTR => continue,
+ .CHILD => |err| return errnoBug(err), // Double-free.
+ else => |err| return posix.unexpectedErrno(err),
+ };
+ }
+
+ var status: if (builtin.link_libc) c_int else u32 = undefined;
+ while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
+ .SUCCESS => return,
+ .INTR => continue,
+ .CHILD => |err| return errnoBug(err), // Double-free.
+ else => |err| return posix.unexpectedErrno(err),
+ };
+}
+
+fn childCleanupPosix(child: *process.Child) void {
+ if (child.stdin) |*stdin| {
+ posix.close(stdin.handle);
+ child.stdin = null;
+ }
+ if (child.stdout) |*stdout| {
+ posix.close(stdout.handle);
+ child.stdout = null;
+ }
+ if (child.stderr) |*stderr| {
+ posix.close(stderr.handle);
+ child.stderr = null;
}
+ child.id = null;
+}
+
+/// Errors that can occur between fork() and execv()
+const ForkBailError = process.SpawnError || process.ReplaceError;
+
+/// Child of fork calls this to report an error to the fork parent. Then the
+/// child exits.
+fn forkBail(fd: posix.fd_t, err: ForkBailError) noreturn {
+ writeIntFd(fd, @as(ErrInt, @intFromError(err))) catch {};
+ // If we're linking libc, some naughty applications may have registered atexit handlers
+ // which we really do not want to run in the fork child. I caught LLVM doing this and
+ // it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne,
+ // "Why'd you have to go and make things so complicated?"
+ if (builtin.link_libc) {
+ // The `_exit` function does nothing but make the exit syscall, unlike `exit`.
+ std.c._exit(1);
+ } else if (native_os == .linux and !builtin.single_threaded) {
+ std.os.linux.exit_group(1);
+ } else {
+ posix.system.exit(1);
+ }
+}
+
+fn writeIntFd(fd: posix.fd_t, value: ErrInt) !void {
+ var buffer: [8]u8 = undefined;
+ std.mem.writeInt(u64, &buffer, value, .little);
+ // Skip the cancel mechanism.
+ var i: usize = 0;
+ while (true) {
+ const rc = posix.system.write(fd, buffer[i..].ptr, buffer.len - i);
+ switch (posix.errno(rc)) {
+ .SUCCESS => {
+ const n: usize = @intCast(rc);
+ i += n;
+ if (buffer.len - i == 0) return;
+ },
+ .INTR => continue,
+ else => return error.SystemResources,
+ }
+ }
+}
+
+fn readIntFd(fd: posix.fd_t) !ErrInt {
+ var buffer: [8]u8 = undefined;
+ var i: usize = 0;
+ while (true) {
+ const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i);
+ switch (posix.errno(rc)) {
+ .SUCCESS => {
+ const n: usize = @intCast(rc);
+ if (n == 0) break;
+ i += n;
+ continue;
+ },
+ .INTR => continue,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+ if (buffer.len - i != 0) return error.EndOfStream;
+ return @intCast(std.mem.readInt(u64, &buffer, .little));
+}
+
+const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
+
+fn destroyPipe(pipe: [2]posix.fd_t) void {
+ if (pipe[0] != -1) posix.close(pipe[0]);
+ if (pipe[0] != pipe[1]) posix.close(pipe[1]);
+}
+
+fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
+ switch (stdio) {
+ .pipe => try dup2(pipe_fd, std_fileno),
+ .close => posix.close(std_fileno),
+ .inherit => {},
+ .ignore => try dup2(dev_null_fd, std_fileno),
+ .file => @panic("TODO implement setUpChildIo when file is used"),
+ }
+}
+
+fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+
+ var saAttr: windows.SECURITY_ATTRIBUTES = .{
+ .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
+ .bInheritHandle = windows.TRUE,
+ .lpSecurityDescriptor = null,
+ };
+
+ const any_ignore =
+ options.stdin == .ignore or
+ options.stdout == .ignore or
+ options.stderr == .ignore;
+
+ const nul_handle = if (any_ignore) try getNulHandle(t) else undefined;
+
+ var g_hChildStd_IN_Rd: ?windows.HANDLE = null;
+ var g_hChildStd_IN_Wr: ?windows.HANDLE = null;
+ switch (options.stdin) {
+ .pipe => {
+ try windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr);
+ },
+ .ignore => {
+ g_hChildStd_IN_Rd = nul_handle;
+ },
+ .inherit => {
+ g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null;
+ },
+ .close => {
+ g_hChildStd_IN_Rd = null;
+ },
+ .file => @panic("TODO implement passing file stdio in processSpawnWindows"),
+ }
+ errdefer if (options.stdin == .pipe) {
+ windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr);
+ };
+
+ var g_hChildStd_OUT_Rd: ?windows.HANDLE = null;
+ var g_hChildStd_OUT_Wr: ?windows.HANDLE = null;
+ switch (options.stdout) {
+ .pipe => {
+ try windowsMakeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr);
+ },
+ .ignore => {
+ g_hChildStd_OUT_Wr = nul_handle;
+ },
+ .inherit => {
+ g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null;
+ },
+ .close => {
+ g_hChildStd_OUT_Wr = null;
+ },
+ .file => @panic("TODO implement passing file stdio in processSpawnWindows"),
+ }
+ errdefer if (options.stdout == .pipe) {
+ windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr);
+ };
+
+ var g_hChildStd_ERR_Rd: ?windows.HANDLE = null;
+ var g_hChildStd_ERR_Wr: ?windows.HANDLE = null;
+ switch (options.stderr) {
+ .pipe => {
+ try windowsMakeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr);
+ },
+ .ignore => {
+ g_hChildStd_ERR_Wr = nul_handle;
+ },
+ .inherit => {
+ g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null;
+ },
+ .close => {
+ g_hChildStd_ERR_Wr = null;
+ },
+ .file => @panic("TODO implement passing file stdio in processSpawnWindows"),
+ }
+ errdefer if (options.stderr == .pipe) {
+ windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr);
+ };
+
+ var siStartInfo: windows.STARTUPINFOW = .{
+ .cb = @sizeOf(windows.STARTUPINFOW),
+ .hStdError = g_hChildStd_ERR_Wr,
+ .hStdOutput = g_hChildStd_OUT_Wr,
+ .hStdInput = g_hChildStd_IN_Rd,
+ .dwFlags = windows.STARTF_USESTDHANDLES,
+
+ .lpReserved = null,
+ .lpDesktop = null,
+ .lpTitle = null,
+ .dwX = 0,
+ .dwY = 0,
+ .dwXSize = 0,
+ .dwYSize = 0,
+ .dwXCountChars = 0,
+ .dwYCountChars = 0,
+ .dwFillAttribute = 0,
+ .wShowWindow = 0,
+ .cbReserved2 = 0,
+ .lpReserved2 = null,
+ };
+ var piProcInfo: windows.PROCESS_INFORMATION = undefined;
+
+ var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
+ defer arena_allocator.deinit();
+ const arena = arena_allocator.allocator();
+
+ const cwd_w = if (options.cwd) |cwd| try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd) else null;
+ const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
+
+ const maybe_envp_buf = if (options.environ_map) |environ_map| try environ_map.createBlockWindows(arena) else null;
+ const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null;
+
+ const app_name_wtf8 = options.argv[0];
+ const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8);
+
+ // The cwd provided by options is in effect when choosing the executable
+ // path to match POSIX semantics.
+ var cwd_path_w_needs_free = false;
+ const cwd_path_w = x: {
+ // If the app name is absolute, then we need to use its dirname as the cwd
+ if (app_name_is_absolute) {
+ cwd_path_w_needs_free = true;
+ const dir = Dir.path.dirname(app_name_wtf8).?;
+ break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, dir);
+ } else if (options.cwd) |cwd| {
+ cwd_path_w_needs_free = true;
+ break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd);
+ } else {
+ break :x &[_:0]u16{}; // empty for cwd
+ }
+ };
+
+ // If the app name has more than just a filename, then we need to separate
+ // that into the basename and dirname and use the dirname as an addition to
+ // the cwd path. This is because NtQueryDirectoryFile cannot accept
+ // FileName params with path separators.
+ const app_basename_wtf8 = Dir.path.basename(app_name_wtf8);
+ // If the app name is absolute, then the cwd will already have the app's dirname in it,
+ // so only populate app_dirname if app name is a relative path with > 0 path separators.
+ const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) Dir.path.dirname(app_name_wtf8) else null;
+ const app_dirname_w: ?[:0]u16 = x: {
+ if (maybe_app_dirname_wtf8) |app_dirname_wtf8| {
+ break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_dirname_wtf8);
+ }
+ break :x null;
+ };
+ const app_name_w = try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_basename_wtf8);
+
+ const flags: windows.CreateProcessFlags = .{
+ .create_suspended = options.start_suspended,
+ .create_unicode_environment = true,
+ .create_no_window = options.create_no_window,
+ };
+
+ run: {
+ // We have to scan each time because the PEB environment pointer is not stable.
+ const env_strings: WindowsEnvironStrings = .scan();
+ const PATH = env_strings.PATH orelse &[_:0]u16{};
+ const PATHEXT = env_strings.PATHEXT orelse &[_:0]u16{};
+
+ // In case the command ends up being a .bat/.cmd script, we need to escape things using the cmd.exe rules
+ // and invoke cmd.exe ourselves in order to mitigate arbitrary command execution from maliciously
+ // constructed arguments.
+ //
+ // We'll need to wait until we're actually trying to run the command to know for sure
+ // if the resolved command has the `.bat` or `.cmd` extension, so we defer actually
+ // serializing the command line until we determine how it should be serialized.
+ var cmd_line_cache = WindowsCommandLineCache.init(arena, options.argv);
+
+ var app_buf: std.ArrayList(u16) = .empty;
+ try app_buf.appendSlice(arena, app_name_w);
+
+ var dir_buf: std.ArrayList(u16) = .empty;
+
+ if (cwd_path_w.len > 0) {
+ try dir_buf.appendSlice(arena, cwd_path_w);
+ }
+ if (app_dirname_w) |app_dir| {
+ if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep);
+ try dir_buf.appendSlice(arena, app_dir);
+ }
+
+ windowsCreateProcessPathExt(
+ arena,
+ &dir_buf,
+ &app_buf,
+ PATHEXT,
+ &cmd_line_cache,
+ envp_ptr,
+ cwd_w_ptr,
+ flags,
+ &siStartInfo,
+ &piProcInfo,
+ ) catch |no_path_err| {
+ const original_err = switch (no_path_err) {
+ // argv[0] contains unsupported characters that will never resolve to a valid exe.
+ error.InvalidArg0 => return error.FileNotFound,
+ error.FileNotFound, error.InvalidExe, error.AccessDenied => |e| e,
+ error.UnrecoverableInvalidExe => return error.InvalidExe,
+ else => |e| return e,
+ };
+
+ // If the app name had path separators, that disallows PATH searching,
+ // and there's no need to search the PATH if the app name is absolute.
+ // We still search the path if the cwd is absolute because of the
+ // "cwd provided by options is in effect when choosing the executable path
+ // to match posix semantics" behavior--we don't want to skip searching
+ // the PATH just because we were trying to set the cwd of the child process.
+ if (app_dirname_w != null or app_name_is_absolute) {
+ return original_err;
+ }
+
+ var it = std.mem.tokenizeScalar(u16, PATH, ';');
+ while (it.next()) |search_path| {
+ dir_buf.clearRetainingCapacity();
+ try dir_buf.appendSlice(arena, search_path);
+
+ if (windowsCreateProcessPathExt(
+ arena,
+ &dir_buf,
+ &app_buf,
+ PATHEXT,
+ &cmd_line_cache,
+ envp_ptr,
+ cwd_w_ptr,
+ flags,
+ &siStartInfo,
+ &piProcInfo,
+ )) {
+ break :run;
+ } else |err| switch (err) {
+ // argv[0] contains unsupported characters that will never resolve to a valid exe.
+ error.InvalidArg0 => return error.FileNotFound,
+ error.FileNotFound, error.AccessDenied, error.InvalidExe => continue,
+ error.UnrecoverableInvalidExe => return error.InvalidExe,
+ else => |e| return e,
+ }
+ } else {
+ return original_err;
+ }
+ };
+ }
+
+ if (options.stdin == .pipe) windows.CloseHandle(g_hChildStd_IN_Rd.?);
+ if (options.stderr == .pipe) windows.CloseHandle(g_hChildStd_ERR_Wr.?);
+ if (options.stdout == .pipe) windows.CloseHandle(g_hChildStd_OUT_Wr.?);
+
+ return .{
+ .id = piProcInfo.hProcess,
+ .thread_handle = piProcInfo.hThread,
+ .stdin = if (g_hChildStd_IN_Wr) |h| .{ .handle = h } else null,
+ .stdout = if (g_hChildStd_OUT_Rd) |h| .{ .handle = h } else null,
+ .stderr = if (g_hChildStd_ERR_Rd) |h| .{ .handle = h } else null,
+ .request_resource_usage_statistics = options.request_resource_usage_statistics,
+ };
+}
+
+fn getNulHandle(t: *Threaded) !windows.HANDLE {
+ {
+ t.mutex.lock();
+ defer t.mutex.unlock();
+ if (t.null_file.handle) |handle| return handle;
+ }
+
+ const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' };
+ var nt_name: windows.UNICODE_STRING = .{
+ .Length = device_path.len * 2,
+ .MaximumLength = device_path.len * 2,
+ .Buffer = @constCast(&device_path),
+ };
+ const attr: windows.OBJECT_ATTRIBUTES = .{
+ .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
+ .RootDirectory = null,
+ .Attributes = .{
+ .INHERIT = true,
+ },
+ .ObjectName = &nt_name,
+ .SecurityDescriptor = null,
+ .SecurityQualityOfService = null,
+ };
+ var io_status_block: windows.IO_STATUS_BLOCK = undefined;
+ var fresh_handle: windows.HANDLE = undefined;
+ var syscall: Syscall = try .start();
+ while (true) switch (windows.ntdll.NtCreateFile(
+ &fresh_handle,
+ .{
+ .STANDARD = .{ .SYNCHRONIZE = true },
+ .GENERIC = .{ .WRITE = true, .READ = true },
+ },
+ &attr,
+ &io_status_block,
+ null,
+ .{ .NORMAL = true },
+ .VALID_FLAGS,
+ .OPEN,
+ .{
+ .DIRECTORY_FILE = false,
+ .NON_DIRECTORY_FILE = true,
+ .IO = .SYNCHRONOUS_NONALERT,
+ .OPEN_REPARSE_POINT = false,
+ },
+ null,
+ 0,
+ )) {
+ .SUCCESS => {
+ syscall.finish();
+ t.mutex.lock(); // Another thread might have won the race.
+ defer t.mutex.unlock();
+ if (t.null_file.handle) |prev_handle| {
+ windows.CloseHandle(fresh_handle);
+ return prev_handle;
+ } else {
+ t.null_file.handle = fresh_handle;
+ return fresh_handle;
+ }
+ },
+ .DELETE_PENDING => {
+ // This error means that there *was* a file in this location on
+ // the file system, but it was deleted. However, the OS is not
+ // finished with the deletion operation, and so this CreateFile
+ // call has failed. There is not really a sane way to handle
+ // this other than retrying the creation after the OS finishes
+ // the deletion.
+ syscall.finish();
+ try parking_sleep.sleep(.{ .duration = .{
+ .raw = .fromMilliseconds(1),
+ .clock = .awake,
+ } });
+ syscall = try .start();
+ continue;
+ },
+ .CANCELLED => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
+ .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
+ .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
+ .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
+ .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
+ .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
+ .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
+ .SHARING_VIOLATION => return syscall.fail(error.AccessDenied),
+ .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
+ .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
+ .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
+ .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
+ .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
+ else => |status| return syscall.unexpectedNtstatus(status),
+ };
+}
+
+/// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path.
+/// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path.
+/// Note: `app_buf` should not contain any leading path separators.
+/// Note: If the dir is the cwd, dir_buf should be empty (len = 0).
+fn windowsCreateProcessPathExt(
+ arena: Allocator,
+ dir_buf: *std.ArrayList(u16),
+ app_buf: *std.ArrayList(u16),
+ pathext: [:0]const u16,
+ cmd_line_cache: *WindowsCommandLineCache,
+ envp_ptr: ?[*:0]const u16,
+ cwd_ptr: ?[*:0]u16,
+ flags: windows.CreateProcessFlags,
+ lpStartupInfo: *windows.STARTUPINFOW,
+ lpProcessInformation: *windows.PROCESS_INFORMATION,
+) !void {
+ const app_name_len = app_buf.items.len;
+ const dir_path_len = dir_buf.items.len;
+
+ if (app_name_len == 0) return error.FileNotFound;
+
+ defer app_buf.shrinkRetainingCapacity(app_name_len);
+ defer dir_buf.shrinkRetainingCapacity(dir_path_len);
+
+ // The name of the game here is to avoid CreateProcessW calls at all costs,
+ // and only ever try calling it when we have a real candidate for execution.
+ // Secondarily, we want to minimize the number of syscalls used when checking
+ // for each PATHEXT-appended version of the app name.
+ //
+ // An overview of the technique used:
+ // - Open the search directory for iteration (either cwd or a path from PATH)
+ // - Use NtQueryDirectoryFile with a wildcard filename of `*` to
+ // check if anything that could possibly match either the unappended version
+ // of the app name or any of the versions with a PATHEXT value appended exists.
+ // - If the wildcard NtQueryDirectoryFile call found nothing, we can exit early
+ // without needing to use PATHEXT at all.
+ //
+ // This allows us to use a sequence
+ // for any directory that doesn't contain any possible matches, instead of having
+ // to use a separate look up for each individual filename combination (unappended +
+ // each PATHEXT appended). For directories where the wildcard *does* match something,
+ // we iterate the matches and take note of any that are either the unappended version,
+ // or a version with a supported PATHEXT appended. We then try calling CreateProcessW
+ // with the found versions in the appropriate order.
+ var dir = dir: {
+ // needs to be null-terminated
+ try dir_buf.append(arena, 0);
+ defer dir_buf.shrinkRetainingCapacity(dir_path_len);
+ const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
+ const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z);
+ break :dir dirOpenDirWindows(.cwd(), prefixed_path.span(), .{
+ .iterate = true,
+ }) catch |err| switch (err) {
+ // These errors must not be ignored because they should not be able
+ // to affect which file is chosen to execute. Also `error.Canceled`
+ // must never be swallowed.
+ error.Canceled,
+ error.SystemResources,
+ error.Unexpected,
+ error.ProcessFdQuotaExceeded,
+ error.SystemFdQuotaExceeded,
+ => |e| return e,
+
+ error.AccessDenied,
+ error.PermissionDenied,
+ error.SymLinkLoop,
+ error.FileNotFound,
+ error.NotDir,
+ error.NoDevice,
+ error.NetworkNotFound,
+ error.NameTooLong,
+ error.BadPathName,
+ error.DeviceBusy,
+ => return error.FileNotFound,
+ };
+ };
+ defer windows.CloseHandle(dir.handle);
+
+ // Add wildcard and null-terminator
+ try app_buf.append(arena, '*');
+ try app_buf.append(arena, 0);
+ const app_name_wildcard = app_buf.items[0 .. app_buf.items.len - 1 :0];
+
+ // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries
+ // returned per NtQueryDirectoryFile call.
+ var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined;
+ const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2);
+ if (file_information_buf.len < file_info_maximum_single_entry_size) {
+ @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry");
+ }
+ var io_status: windows.IO_STATUS_BLOCK = undefined;
+
+ const num_supported_pathext = @typeInfo(process.WindowsExtension).@"enum".fields.len;
+ var pathext_seen = [_]bool{false} ** num_supported_pathext;
+ var any_pathext_seen = false;
+ var unappended_exists = false;
+
+ // Fully iterate the wildcard matches via NtQueryDirectoryFile and take note of all versions
+ // of the app_name we should try to spawn.
+ // Note: This is necessary because the order of the files returned is filesystem-dependent:
+ // On NTFS, `blah.exe*` will always return `blah.exe` first if it exists.
+ // On FAT32, it's possible for something like `blah.exe.obj` to be returned first.
+ while (true) {
+ const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong;
+ var app_name_unicode_string = windows.UNICODE_STRING{
+ .Length = app_name_len_bytes,
+ .MaximumLength = app_name_len_bytes,
+ .Buffer = @constCast(app_name_wildcard.ptr),
+ };
+ const rc = windows.ntdll.NtQueryDirectoryFile(
+ dir.handle,
+ null,
+ null,
+ null,
+ &io_status,
+ &file_information_buf,
+ file_information_buf.len,
+ .Directory,
+ windows.FALSE, // single result
+ &app_name_unicode_string,
+ windows.FALSE, // restart iteration
+ );
+
+ // If we get nothing with the wildcard, then we can just bail out
+ // as we know appending PATHEXT will not yield anything.
+ switch (rc) {
+ .SUCCESS => {},
+ .NO_SUCH_FILE => return error.FileNotFound,
+ .NO_MORE_FILES => break,
+ .ACCESS_DENIED => return error.AccessDenied,
+ else => return windows.unexpectedStatus(rc),
+ }
+
+ // According to the docs, this can only happen if there is not enough room in the
+ // buffer to write at least one complete FILE_DIRECTORY_INFORMATION entry.
+ // Therefore, this condition should not be possible to hit with the buffer size we use.
+ std.debug.assert(io_status.Information != 0);
+
+ var it = windows.FileInformationIterator(windows.FILE_DIRECTORY_INFORMATION){ .buf = &file_information_buf };
+ while (it.next()) |info| {
+ // Skip directories
+ if (info.FileAttributes.DIRECTORY) continue;
+ const filename = @as([*]u16, @ptrCast(&info.FileName))[0 .. info.FileNameLength / 2];
+ // Because all results start with the app_name since we're using the wildcard `app_name*`,
+ // if the length is equal to app_name then this is an exact match
+ if (filename.len == app_name_len) {
+ // Note: We can't break early here because it's possible that the unappended version
+ // fails to spawn, in which case we still want to try the PATHEXT appended versions.
+ unappended_exists = true;
+ } else if (windowsCreateProcessSupportsExtension(filename[app_name_len..])) |pathext_ext| {
+ pathext_seen[@intFromEnum(pathext_ext)] = true;
+ any_pathext_seen = true;
+ }
+ }
+ }
+
+ const unappended_err = unappended: {
+ if (unappended_exists) {
+ if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) {
+ '/', '\\' => {},
+ else => try dir_buf.append(arena, Dir.path.sep),
+ };
+ try dir_buf.appendSlice(arena, app_buf.items[0..app_name_len]);
+ try dir_buf.append(arena, 0);
+ const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
+
+ const is_bat_or_cmd = bat_or_cmd: {
+ const app_name = app_buf.items[0..app_name_len];
+ const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :bat_or_cmd false;
+ const ext = app_name[ext_start..];
+ const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse break :bat_or_cmd false;
+ switch (ext_enum) {
+ .cmd, .bat => break :bat_or_cmd true,
+ else => break :bat_or_cmd false,
+ }
+ };
+ const cmd_line_w = if (is_bat_or_cmd)
+ try cmd_line_cache.scriptCommandLine(full_app_name)
+ else
+ try cmd_line_cache.commandLine();
+ const app_name_w = if (is_bat_or_cmd)
+ try cmd_line_cache.cmdExePath()
+ else
+ full_app_name;
+
+ if (windowsCreateProcess(
+ app_name_w.ptr,
+ cmd_line_w.ptr,
+ envp_ptr,
+ cwd_ptr,
+ flags,
+ lpStartupInfo,
+ lpProcessInformation,
+ )) |_| {
+ return;
+ } else |err| switch (err) {
+ error.FileNotFound,
+ error.AccessDenied,
+ => break :unappended err,
+ error.InvalidExe => {
+ // On InvalidExe, if the extension of the app name is .exe then
+ // it's treated as an unrecoverable error. Otherwise, it'll be
+ // skipped as normal.
+ const app_name = app_buf.items[0..app_name_len];
+ const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :unappended err;
+ const ext = app_name[ext_start..];
+ if (windows.eqlIgnoreCaseWtf16(ext, std.unicode.utf8ToUtf16LeStringLiteral(".EXE"))) {
+ return error.UnrecoverableInvalidExe;
+ }
+ break :unappended err;
+ },
+ else => return err,
+ }
+ }
+ break :unappended error.FileNotFound;
+ };
+
+ if (!any_pathext_seen) return unappended_err;
+
+ // Now try any PATHEXT appended versions that we've seen
+ var ext_it = std.mem.tokenizeScalar(u16, pathext, ';');
+ while (ext_it.next()) |ext| {
+ const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse continue;
+ if (!pathext_seen[@intFromEnum(ext_enum)]) continue;
+
+ dir_buf.shrinkRetainingCapacity(dir_path_len);
+ if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) {
+ '/', '\\' => {},
+ else => try dir_buf.append(arena, Dir.path.sep),
+ };
+ try dir_buf.appendSlice(arena, app_buf.items[0..app_name_len]);
+ try dir_buf.appendSlice(arena, ext);
+ try dir_buf.append(arena, 0);
+ const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
+
+ const is_bat_or_cmd = switch (ext_enum) {
+ .cmd, .bat => true,
+ else => false,
+ };
+ const cmd_line_w = if (is_bat_or_cmd)
+ try cmd_line_cache.scriptCommandLine(full_app_name)
+ else
+ try cmd_line_cache.commandLine();
+ const app_name_w = if (is_bat_or_cmd)
+ try cmd_line_cache.cmdExePath()
+ else
+ full_app_name;
+
+ if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| {
+ return;
+ } else |err| switch (err) {
+ error.FileNotFound => continue,
+ error.AccessDenied => continue,
+ error.InvalidExe => {
+ // On InvalidExe, if the extension of the app name is .exe then
+ // it's treated as an unrecoverable error. Otherwise, it'll be
+ // skipped as normal.
+ if (windows.eqlIgnoreCaseWtf16(ext, std.unicode.utf8ToUtf16LeStringLiteral(".EXE"))) {
+ return error.UnrecoverableInvalidExe;
+ }
+ continue;
+ },
+ else => return err,
+ }
+ }
+
+ return unappended_err;
+}
+
+fn windowsCreateProcess(
+ app_name: [*:0]u16,
+ cmd_line: [*:0]u16,
+ env_ptr: ?[*:0]const u16,
+ cwd_ptr: ?[*:0]u16,
+ flags: windows.CreateProcessFlags,
+ lpStartupInfo: *windows.STARTUPINFOW,
+ lpProcessInformation: *windows.PROCESS_INFORMATION,
+) !void {
+ const syscall: Syscall = try .start();
+ while (true) {
+ if (windows.kernel32.CreateProcessW(
+ app_name,
+ cmd_line,
+ null,
+ null,
+ windows.TRUE,
+ flags,
+ env_ptr,
+ cwd_ptr,
+ lpStartupInfo,
+ lpProcessInformation,
+ ) != 0) {
+ return syscall.finish();
+ } else switch (windows.GetLastError()) {
+ .INVALID_PARAMETER => unreachable,
+ .OPERATION_ABORTED => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .FILE_NOT_FOUND => return syscall.fail(error.FileNotFound),
+ .PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
+ .DIRECTORY => return syscall.fail(error.FileNotFound),
+ .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
+ .INVALID_NAME => return syscall.fail(error.InvalidName),
+ .FILENAME_EXCED_RANGE => return syscall.fail(error.NameTooLong),
+ .SHARING_VIOLATION => return syscall.fail(error.FileBusy),
+ .COMMITMENT_LIMIT => return syscall.fail(error.SystemResources),
+
+ // These are all the system errors that are mapped to ENOEXEC by
+ // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
+ // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)
+ // or urt/misc/errno.cpp (newer SDK) in the Windows SDK.
+ .BAD_FORMAT,
+ .INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp
+ .INVALID_STACKSEG,
+ .INVALID_MODULETYPE,
+ .INVALID_EXE_SIGNATURE,
+ .EXE_MARKED_INVALID,
+ .BAD_EXE_FORMAT,
+ .ITERATED_DATA_EXCEEDS_64k,
+ .INVALID_MINALLOCSIZE,
+ .DYNLINK_FROM_INVALID_RING,
+ .IOPL_NOT_ENABLED,
+ .INVALID_SEGDPL,
+ .AUTODATASEG_EXCEEDS_64k,
+ .RING2SEG_MUST_BE_MOVABLE,
+ .RELOC_CHAIN_XEEDS_SEGLIM,
+ .INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp
+ // This one is not mapped to ENOEXEC but it is possible, for example
+ // when calling CreateProcessW on a plain text file with a .exe extension
+ .EXE_MACHINE_TYPE_MISMATCH,
+ => return syscall.fail(error.InvalidExe),
+
+ else => |err| {
+ syscall.finish();
+ return windows.unexpectedError(err);
+ },
+ }
+ }
+}
+
+/// Case-insensitive WTF-16 lookup
+fn windowsCreateProcessSupportsExtension(ext: []const u16) ?process.WindowsExtension {
+ comptime {
+ // Ensures keeping this function in sync with the enum.
+ const fields = @typeInfo(process.WindowsExtension).@"enum".fields;
+ assert(fields.len == 4);
+ assert(@intFromEnum(process.WindowsExtension.bat) == 0);
+ assert(@intFromEnum(process.WindowsExtension.cmd) == 1);
+ assert(@intFromEnum(process.WindowsExtension.com) == 2);
+ assert(@intFromEnum(process.WindowsExtension.exe) == 3);
+ }
+
+ if (ext.len != 4) return null;
+ const State = enum {
+ start,
+ dot,
+ b,
+ ba,
+ c,
+ cm,
+ co,
+ e,
+ ex,
+ };
+ var state: State = .start;
+ for (ext) |c| switch (state) {
+ .start => switch (c) {
+ '.' => state = .dot,
+ else => return null,
+ },
+ .dot => switch (c) {
+ 'b', 'B' => state = .b,
+ 'c', 'C' => state = .c,
+ 'e', 'E' => state = .e,
+ else => return null,
+ },
+ .b => switch (c) {
+ 'a', 'A' => state = .ba,
+ else => return null,
+ },
+ .c => switch (c) {
+ 'm', 'M' => state = .cm,
+ 'o', 'O' => state = .co,
+ else => return null,
+ },
+ .e => switch (c) {
+ 'x', 'X' => state = .ex,
+ else => return null,
+ },
+ .ba => switch (c) {
+ 't', 'T' => return .bat,
+ else => return null,
+ },
+ .cm => switch (c) {
+ 'd', 'D' => return .cmd,
+ else => return null,
+ },
+ .co => switch (c) {
+ 'm', 'M' => return .com,
+ else => return null,
+ },
+ .ex => switch (c) {
+ 'e', 'E' => return .exe,
+ else => return null,
+ },
+ };
+ return null;
+}
+
+test windowsCreateProcessSupportsExtension {
+ try std.testing.expectEqual(process.WindowsExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?);
+ try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null);
+}
+
+/// Serializes argv into a WTF-16 encoded command-line string for use with CreateProcessW.
+///
+/// Serialization is done on-demand and the result is cached in order to allow for:
+/// - Only serializing the particular type of command line needed (`.bat`/`.cmd`
+/// command line serialization is different from `.exe`/etc)
+/// - Reusing the serialized command lines if necessary (i.e. if the execution
+/// of a command fails and the PATH is going to be continued to be searched
+/// for more candidates)
+const WindowsCommandLineCache = struct {
+ cmd_line: ?[:0]u16 = null,
+ script_cmd_line: ?[:0]u16 = null,
+ cmd_exe_path: ?[:0]u16 = null,
+ argv: []const []const u8,
+ allocator: Allocator,
+
+ fn init(allocator: Allocator, argv: []const []const u8) WindowsCommandLineCache {
+ return .{
+ .allocator = allocator,
+ .argv = argv,
+ };
+ }
+
+ fn deinit(self: *WindowsCommandLineCache) void {
+ if (self.cmd_line) |cmd_line| self.allocator.free(cmd_line);
+ if (self.script_cmd_line) |script_cmd_line| self.allocator.free(script_cmd_line);
+ if (self.cmd_exe_path) |cmd_exe_path| self.allocator.free(cmd_exe_path);
+ }
+
+ fn commandLine(self: *WindowsCommandLineCache) ![:0]u16 {
+ if (self.cmd_line == null) {
+ self.cmd_line = try argvToCommandLineWindows(self.allocator, self.argv);
+ }
+ return self.cmd_line.?;
+ }
+
+ /// Not cached, since the path to the batch script will change during PATH searching.
+ /// `script_path` should be as qualified as possible, e.g. if the PATH is being searched,
+ /// then script_path should include both the search path and the script filename
+ /// (this allows avoiding cmd.exe having to search the PATH again).
+ fn scriptCommandLine(self: *WindowsCommandLineCache, script_path: []const u16) ![:0]u16 {
+ if (self.script_cmd_line) |v| self.allocator.free(v);
+ self.script_cmd_line = try argvToScriptCommandLineWindows(
+ self.allocator,
+ script_path,
+ self.argv[1..],
+ );
+ return self.script_cmd_line.?;
+ }
+
+ fn cmdExePath(self: *WindowsCommandLineCache) ![:0]u16 {
+ if (self.cmd_exe_path == null) {
+ self.cmd_exe_path = try windowsCmdExePath(self.allocator);
+ }
+ return self.cmd_exe_path.?;
+ }
+};
+
+/// Returns the absolute path of `cmd.exe` within the Windows system directory.
+/// The caller owns the returned slice.
+fn windowsCmdExePath(allocator: Allocator) error{ OutOfMemory, Unexpected }![:0]u16 {
+ var buf = try std.ArrayList(u16).initCapacity(allocator, 128);
+ errdefer buf.deinit(allocator);
+ while (true) {
+ const unused_slice = buf.unusedCapacitySlice();
+ // TODO: Get the system directory from PEB.ReadOnlyStaticServerData
+ const len = windows.kernel32.GetSystemDirectoryW(@ptrCast(unused_slice), @intCast(unused_slice.len));
+ if (len == 0) {
+ switch (windows.GetLastError()) {
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+ if (len > unused_slice.len) {
+ try buf.ensureUnusedCapacity(allocator, len);
+ } else {
+ buf.items.len = len;
+ break;
+ }
+ }
+ switch (buf.items[buf.items.len - 1]) {
+ '/', '\\' => {},
+ else => try buf.append(allocator, Dir.path.sep),
+ }
+ try buf.appendSlice(allocator, std.unicode.utf8ToUtf16LeStringLiteral("cmd.exe"));
+ return try buf.toOwnedSliceSentinel(allocator, 0);
+}
+
+const ArgvToScriptCommandLineError = error{
+ OutOfMemory,
+ InvalidWtf8,
+ /// NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed
+ /// within arguments when executing a `.bat`/`.cmd` script.
+ /// - NUL/LF signifiies end of arguments, so anything afterwards
+ /// would be lost after execution.
+ /// - CR is stripped by `cmd.exe`, so any CR codepoints
+ /// would be lost after execution.
+ InvalidBatchScriptArg,
+};
+
+/// Serializes `argv` to a Windows command-line string that uses `cmd.exe /c` and `cmd.exe`-specific
+/// escaping rules. The caller owns the returned slice.
+///
+/// Escapes `argv` using the suggested mitigation against arbitrary command execution from:
+/// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
+///
+/// The return of this function will look like
+/// `cmd.exe /d /e:ON /v:OFF /c ""`
+/// and should be used as the `lpCommandLine` of `CreateProcessW`, while the
+/// return of `windowsCmdExePath` should be used as `lpApplicationName`.
+///
+/// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise.
+/// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem.
+fn argvToScriptCommandLineWindows(
+ allocator: Allocator,
+ /// Path to the `.bat`/`.cmd` script. If this path is relative, it is assumed to be relative to the CWD.
+ /// The script must have been verified to exist at this path before calling this function.
+ script_path: []const u16,
+ /// Arguments, not including the script name itself. Expected to be encoded as WTF-8.
+ script_args: []const []const u8,
+) ArgvToScriptCommandLineError![:0]u16 {
+ var buf = try std.array_list.Managed(u8).initCapacity(allocator, 64);
+ defer buf.deinit();
+
+ // `/d` disables execution of AutoRun commands.
+ // `/e:ON` and `/v:OFF` are needed for BatBadBut mitigation:
+ // > If delayed expansion is enabled via the registry value DelayedExpansion,
+ // > it must be disabled by explicitly calling cmd.exe with the /V:OFF option.
+ // > Escaping for % requires the command extension to be enabled.
+ // > If it’s disabled via the registry value EnableExtensions, it must be enabled with the /E:ON option.
+ // https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
+ buf.appendSliceAssumeCapacity("cmd.exe /d /e:ON /v:OFF /c \"");
+
+ // Always quote the path to the script arg
+ buf.appendAssumeCapacity('"');
+ // We always want the path to the batch script to include a path separator in order to
+ // avoid cmd.exe searching the PATH for the script. This is not part of the arbitrary
+ // command execution mitigation, we just know exactly what script we want to execute
+ // at this point, and potentially making cmd.exe re-find it is unnecessary.
+ //
+ // If the script path does not have a path separator, then we know its relative to CWD and
+ // we can just put `.\` in the front.
+ if (std.mem.findAny(u16, script_path, &[_]u16{
+ std.mem.nativeToLittle(u16, '\\'), std.mem.nativeToLittle(u16, '/'),
+ }) == null) {
+ try buf.appendSlice(".\\");
+ }
+ // Note that we don't do any escaping/mitigations for this argument, since the relevant
+ // characters (", %, etc) are illegal in file paths and this function should only be called
+ // with script paths that have been verified to exist.
+ try std.unicode.wtf16LeToWtf8ArrayList(&buf, script_path);
+ buf.appendAssumeCapacity('"');
+
+ for (script_args) |arg| {
+ // Literal carriage returns get stripped when run through cmd.exe
+ // and NUL/newlines act as 'end of command.' Because of this, it's basically
+ // always a mistake to include these characters in argv, so it's
+ // an error condition in order to ensure that the return of this
+ // function can always roundtrip through cmd.exe.
+ if (std.mem.findAny(u8, arg, "\x00\r\n") != null) {
+ return error.InvalidBatchScriptArg;
+ }
+
+ // Separate args with a space.
+ try buf.append(' ');
+
+ // Need to quote if the argument is empty (otherwise the arg would just be lost)
+ // or if the last character is a `\`, since then something like "%~2" in a .bat
+ // script would cause the closing " to be escaped which we don't want.
+ var needs_quotes = arg.len == 0 or arg[arg.len - 1] == '\\';
+ if (!needs_quotes) {
+ for (arg) |c| {
+ switch (c) {
+ // Known good characters that don't need to be quoted
+ 'A'...'Z', 'a'...'z', '0'...'9', '#', '$', '*', '+', '-', '.', '/', ':', '?', '@', '\\', '_' => {},
+ // When in doubt, quote
+ else => {
+ needs_quotes = true;
+ break;
+ },
+ }
+ }
+ }
+ if (needs_quotes) {
+ try buf.append('"');
+ }
+ var backslashes: usize = 0;
+ for (arg) |c| {
+ switch (c) {
+ '\\' => {
+ backslashes += 1;
+ },
+ '"' => {
+ try buf.appendNTimes('\\', backslashes);
+ try buf.append('"');
+ backslashes = 0;
+ },
+ // Replace `%` with `%%cd:~,%`.
+ //
+ // cmd.exe allows extracting a substring from an environment
+ // variable with the syntax: `%foo:~,%`.
+ // Therefore, `%cd:~,%` will always expand to an empty string
+ // since both the start and end index are blank, and it is assumed
+ // that `%cd%` is always available since it is a built-in variable
+ // that corresponds to the current directory.
+ //
+ // This means that replacing `%foo%` with `%%cd:~,%foo%%cd:~,%`
+ // will stop `%foo%` from being expanded and *after* expansion
+ // we'll still be left with `%foo%` (the literal string).
+ '%' => {
+ // the trailing `%` is appended outside the switch
+ try buf.appendSlice("%%cd:~,");
+ backslashes = 0;
+ },
+ else => {
+ backslashes = 0;
+ },
+ }
+ try buf.append(c);
+ }
+ if (needs_quotes) {
+ try buf.appendNTimes('\\', backslashes);
+ try buf.append('"');
+ }
+ }
+
+ try buf.append('"');
+
+ return try std.unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items);
+}
+
+const ArgvToCommandLineError = error{ OutOfMemory, InvalidWtf8, InvalidArg0 };
+
+/// Serializes `argv` to a Windows command-line string suitable for passing to a child process and
+/// parsing by the `CommandLineToArgvW` algorithm. The caller owns the returned slice.
+///
+/// To avoid arbitrary command execution, this function should not be used when spawning `.bat`/`.cmd` scripts.
+/// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
+///
+/// When executing `.bat`/`.cmd` scripts, use `argvToScriptCommandLineWindows` instead.
+fn argvToCommandLineWindows(
+ allocator: Allocator,
+ argv: []const []const u8,
+) ArgvToCommandLineError![:0]u16 {
+ var buf = std.array_list.Managed(u8).init(allocator);
+ defer buf.deinit();
+
+ if (argv.len != 0) {
+ const arg0 = argv[0];
+
+ // The first argument must be quoted if it contains spaces or ASCII control characters
+ // (excluding DEL). It also follows special quoting rules where backslashes have no special
+ // interpretation, which makes it impossible to pass certain first arguments containing
+ // double quotes to a child process without characters from the first argument leaking into
+ // subsequent ones (which could have security implications).
+ //
+ // Empty arguments technically don't need quotes, but we quote them anyway for maximum
+ // compatibility with different implementations of the 'CommandLineToArgvW' algorithm.
+ //
+ // Double quotes are illegal in paths on Windows, so for the sake of simplicity we reject
+ // all first arguments containing double quotes, even ones that we could theoretically
+ // serialize in unquoted form.
+ var needs_quotes = arg0.len == 0;
+ for (arg0) |c| {
+ if (c <= ' ') {
+ needs_quotes = true;
+ } else if (c == '"') {
+ return error.InvalidArg0;
+ }
+ }
+ if (needs_quotes) {
+ try buf.append('"');
+ try buf.appendSlice(arg0);
+ try buf.append('"');
+ } else {
+ try buf.appendSlice(arg0);
+ }
+
+ for (argv[1..]) |arg| {
+ try buf.append(' ');
+
+ // Subsequent arguments must be quoted if they contain spaces, tabs or double quotes,
+ // or if they are empty. For simplicity and for maximum compatibility with different
+ // implementations of the 'CommandLineToArgvW' algorithm, we also quote all ASCII
+ // control characters (again, excluding DEL).
+ needs_quotes = for (arg) |c| {
+ if (c <= ' ' or c == '"') {
+ break true;
+ }
+ } else arg.len == 0;
+ if (!needs_quotes) {
+ try buf.appendSlice(arg);
+ continue;
+ }
+
+ try buf.append('"');
+ var backslash_count: usize = 0;
+ for (arg) |byte| {
+ switch (byte) {
+ '\\' => {
+ backslash_count += 1;
+ },
+ '"' => {
+ try buf.appendNTimes('\\', backslash_count * 2 + 1);
+ try buf.append('"');
+ backslash_count = 0;
+ },
+ else => {
+ try buf.appendNTimes('\\', backslash_count);
+ try buf.append(byte);
+ backslash_count = 0;
+ },
+ }
+ }
+ try buf.appendNTimes('\\', backslash_count * 2);
+ try buf.append('"');
+ }
+ }
+
+ return try std.unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items);
+}
+
+test argvToCommandLineWindows {
+ const t = testArgvToCommandLineWindows;
+
+ try t(&.{
+ \\C:\Program Files\zig\zig.exe
+ ,
+ \\run
+ ,
+ \\.\src\main.zig
+ ,
+ \\-target
+ ,
+ \\x86_64-windows-gnu
+ ,
+ \\-O
+ ,
+ \\ReleaseSafe
+ ,
+ \\--
+ ,
+ \\--emoji=🗿
+ ,
+ \\--eval=new Regex("Dwayne \"The Rock\" Johnson")
+ ,
+ },
+ \\"C:\Program Files\zig\zig.exe" run .\src\main.zig -target x86_64-windows-gnu -O ReleaseSafe -- --emoji=🗿 "--eval=new Regex(\"Dwayne \\\"The Rock\\\" Johnson\")"
+ );
+
+ try t(&.{}, "");
+ try t(&.{""}, "\"\"");
+ try t(&.{" "}, "\" \"");
+ try t(&.{"\t"}, "\"\t\"");
+ try t(&.{"\x07"}, "\"\x07\"");
+ try t(&.{"🦎"}, "🦎");
+
+ try t(
+ &.{ "zig", "aa aa", "bb\tbb", "cc\ncc", "dd\r\ndd", "ee\x7Fee" },
+ "zig \"aa aa\" \"bb\tbb\" \"cc\ncc\" \"dd\r\ndd\" ee\x7Fee",
+ );
+
+ try t(
+ &.{ "\\\\foo bar\\foo bar\\", "\\\\zig zag\\zig zag\\" },
+ "\"\\\\foo bar\\foo bar\\\" \"\\\\zig zag\\zig zag\\\\\"",
+ );
+
+ try std.testing.expectError(
+ error.InvalidArg0,
+ argvToCommandLineWindows(std.testing.allocator, &.{"\"quotes\"quotes\""}),
+ );
+ try std.testing.expectError(
+ error.InvalidArg0,
+ argvToCommandLineWindows(std.testing.allocator, &.{"quotes\"quotes"}),
+ );
+ try std.testing.expectError(
+ error.InvalidArg0,
+ argvToCommandLineWindows(std.testing.allocator, &.{"q u o t e s \" q u o t e s"}),
+ );
+}
+
+fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []const u8) !void {
+ const cmd_line_w = try argvToCommandLineWindows(std.testing.allocator, argv);
+ defer std.testing.allocator.free(cmd_line_w);
+
+ const cmd_line = try std.unicode.wtf16LeToWtf8Alloc(std.testing.allocator, cmd_line_w);
+ defer std.testing.allocator.free(cmd_line);
+
+ try std.testing.expectEqualStrings(expected_cmd_line, cmd_line);
+}
+
+fn posixExecv(
+ arg0_expand: process.ArgExpansion,
+ file: [*:0]const u8,
+ child_argv: [*:null]?[*:0]const u8,
+ envp: [*:null]const ?[*:0]const u8,
+ PATH: []const u8,
+) process.ReplaceError {
+ const file_slice = std.mem.sliceTo(file, 0);
+ if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, envp);
+
+ // Use of PATH_MAX here is valid as the path_buf will be passed
+ // directly to the operating system in posixExecvPath.
+ var path_buf: [posix.PATH_MAX]u8 = undefined;
+ var it = std.mem.tokenizeScalar(u8, PATH, ':');
+ var seen_eacces = false;
+ var err: process.ReplaceError = error.FileNotFound;
+
+ // In case of expanding arg0 we must put it back if we return with an error.
+ const prev_arg0 = child_argv[0];
+ defer switch (arg0_expand) {
+ .expand => child_argv[0] = prev_arg0,
+ .no_expand => {},
+ };
+
+ while (it.next()) |search_path| {
+ const path_len = search_path.len + file_slice.len + 1;
+ if (path_buf.len < path_len + 1) return error.NameTooLong;
+ @memcpy(path_buf[0..search_path.len], search_path);
+ path_buf[search_path.len] = '/';
+ @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice);
+ path_buf[path_len] = 0;
+ const full_path = path_buf[0..path_len :0].ptr;
+ switch (arg0_expand) {
+ .expand => child_argv[0] = full_path,
+ .no_expand => {},
+ }
+ err = posixExecvPath(full_path, child_argv, envp);
+ switch (err) {
+ error.AccessDenied => seen_eacces = true,
+ error.FileNotFound, error.NotDir => {},
+ else => |e| return e,
+ }
+ }
+ if (seen_eacces) return error.AccessDenied;
+ return err;
+}
+
+/// This function ignores PATH environment variable.
+pub fn posixExecvPath(
+ path: [*:0]const u8,
+ child_argv: [*:null]const ?[*:0]const u8,
+ envp: [*:null]const ?[*:0]const u8,
+) process.ReplaceError {
+ try Thread.checkCancel();
+ switch (posix.errno(posix.system.execve(path, child_argv, envp))) {
+ .FAULT => |err| return errnoBug(err), // Bad pointer parameter.
+ .@"2BIG" => return error.SystemResources,
+ .MFILE => return error.ProcessFdQuotaExceeded,
+ .NAMETOOLONG => return error.NameTooLong,
+ .NFILE => return error.SystemFdQuotaExceeded,
+ .NOMEM => return error.SystemResources,
+ .ACCES => return error.AccessDenied,
+ .PERM => return error.PermissionDenied,
+ .INVAL => return error.InvalidExe,
+ .NOEXEC => return error.InvalidExe,
+ .IO => return error.FileSystem,
+ .LOOP => return error.FileSystem,
+ .ISDIR => return error.IsDir,
+ .NOENT => return error.FileNotFound,
+ .NOTDIR => return error.NotDir,
+ .TXTBSY => return error.FileBusy,
+ else => |err| switch (native_os) {
+ .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (err) {
+ .BADEXEC => return error.InvalidExe,
+ .BADARCH => return error.InvalidExe,
+ else => return posix.unexpectedErrno(err),
+ },
+ .linux => switch (err) {
+ .LIBBAD => return error.InvalidExe,
+ else => return posix.unexpectedErrno(err),
+ },
+ else => return posix.unexpectedErrno(err),
+ },
+ }
+}
+
+fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
+ var rd_h: windows.HANDLE = undefined;
+ var wr_h: windows.HANDLE = undefined;
+ try windows.CreatePipe(&rd_h, &wr_h, sattr);
+ errdefer windowsDestroyPipe(rd_h, wr_h);
+ try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0);
+ rd.* = rd_h;
+ wr.* = wr_h;
+}
+
+fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
+ if (rd) |h| posix.close(h);
+ if (wr) |h| posix.close(h);
+}
+
+fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
+ var tmp_bufw: [128]u16 = undefined;
+
+ // Anonymous pipes are built upon Named pipes.
+ // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
+ // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes.
+ // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations
+ const pipe_path = blk: {
+ var tmp_buf: [128]u8 = undefined;
+ // Forge a random path for the pipe.
+ const pipe_path = std.fmt.bufPrintSentinel(
+ &tmp_buf,
+ "\\\\.\\pipe\\zig-childprocess-{d}-{d}",
+ .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) },
+ 0,
+ ) catch unreachable;
+ const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable;
+ tmp_bufw[len] = 0;
+ break :blk tmp_bufw[0..len :0];
+ };
+
+ // Create the read handle that can be used with overlapped IO ops.
+ const read_handle = windows.kernel32.CreateNamedPipeW(
+ pipe_path.ptr,
+ windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED,
+ windows.PIPE_TYPE_BYTE,
+ 1,
+ 4096,
+ 4096,
+ 0,
+ sattr,
+ );
+ if (read_handle == windows.INVALID_HANDLE_VALUE) {
+ switch (windows.GetLastError()) {
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+ errdefer posix.close(read_handle);
+
+ var sattr_copy = sattr.*;
+ const write_handle = windows.kernel32.CreateFileW(
+ pipe_path.ptr,
+ .{ .GENERIC = .{ .WRITE = true } },
+ 0,
+ &sattr_copy,
+ windows.OPEN_EXISTING,
+ @bitCast(windows.FILE.ATTRIBUTE{ .NORMAL = true }),
+ null,
+ );
+ if (write_handle == windows.INVALID_HANDLE_VALUE) {
+ switch (windows.GetLastError()) {
+ else => |err| return windows.unexpectedError(err),
+ }
+ }
+ errdefer posix.close(write_handle);
+
+ try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0);
+
+ rd.* = read_handle;
+ wr.* = write_handle;
+}
+
+var pipe_name_counter = std.atomic.Value(u32).init(1);
+
+fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+
+ t.scanEnviron();
+
+ const int = try t.environ.zig_progress_handle;
+
+ return .{ .handle = switch (@typeInfo(Io.File.Handle)) {
+ .int => int,
+ .pointer => @ptrFromInt(int),
+ else => return error.UnsupportedOperation,
+ } };
+}
+
+pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {
+ t.scanEnviron();
+ return @field(t.environ.string, name);
}
test {
@@ -13151,3 +15206,140 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
else => comptime unreachable,
}
}
+
+pub const PipeError = error{
+ SystemFdQuotaExceeded,
+ ProcessFdQuotaExceeded,
+} || Io.UnexpectedError;
+
+pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t {
+ var fds: [2]posix.fd_t = undefined;
+
+ if (@TypeOf(posix.system.pipe2) != void) {
+ switch (posix.errno(posix.system.pipe2(&fds, flags))) {
+ .SUCCESS => return fds,
+ .INVAL => |err| return errnoBug(err), // Invalid flags
+ .NFILE => return error.SystemFdQuotaExceeded,
+ .MFILE => return error.ProcessFdQuotaExceeded,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+
+ switch (posix.errno(posix.system.pipe(&fds))) {
+ .SUCCESS => {},
+ .NFILE => return error.SystemFdQuotaExceeded,
+ .MFILE => return error.ProcessFdQuotaExceeded,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ errdefer {
+ posix.close(fds[0]);
+ posix.close(fds[1]);
+ }
+
+ // https://github.com/ziglang/zig/issues/18882
+ if (@as(u32, @bitCast(flags)) == 0) return fds;
+
+ // CLOEXEC is special, it's a file descriptor flag and must be set using
+ // F.SETFD.
+ if (flags.CLOEXEC) for (fds) |fd| {
+ switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(u32, posix.FD_CLOEXEC)))) {
+ .SUCCESS => {},
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ };
+
+ const new_flags: u32 = f: {
+ var new_flags = flags;
+ new_flags.CLOEXEC = false;
+ break :f @bitCast(new_flags);
+ };
+
+ // Set every other flag affecting the file status using F.SETFL.
+ if (new_flags != 0) for (fds) |fd| {
+ switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, new_flags))) {
+ .SUCCESS => {},
+ .INVAL => |err| return errnoBug(err),
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ };
+
+ return fds;
+}
+
+pub const DupError = error{
+ ProcessFdQuotaExceeded,
+ SystemResources,
+} || Io.UnexpectedError || Io.Cancelable;
+
+pub fn dup2(old_fd: posix.fd_t, new_fd: posix.fd_t) DupError!void {
+ const syscall: Syscall = try .start();
+ while (true) switch (posix.errno(posix.system.dup2(old_fd, new_fd))) {
+ .SUCCESS => return syscall.finish(),
+ .BUSY, .INTR => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .INVAL => |err| return syscall.errnoBug(err), // invalid parameters
+ .BADF => |err| return syscall.errnoBug(err), // use after free
+ .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
+ .NOMEM => return syscall.fail(error.SystemResources),
+ else => |err| return syscall.unexpectedErrno(err),
+ };
+}
+
+pub const FchdirError = error{
+ AccessDenied,
+ NotDir,
+ FileSystem,
+} || Io.Cancelable || Io.UnexpectedError;
+
+pub fn fchdir(fd: posix.fd_t) FchdirError!void {
+ if (fd == posix.AT.FDCWD) return;
+ const syscall: Syscall = try .start();
+ while (true) switch (posix.errno(posix.system.fchdir(fd))) {
+ .SUCCESS => return syscall.finish(),
+ .INTR => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .ACCES => return syscall.fail(error.AccessDenied),
+ .NOTDIR => return syscall.fail(error.NotDir),
+ .IO => return syscall.fail(error.FileSystem),
+ .BADF => |err| return syscall.errnoBug(err),
+ else => |err| return syscall.unexpectedErrno(err),
+ };
+}
+
+pub const ChdirError = error{
+ AccessDenied,
+ FileSystem,
+ SymLinkLoop,
+ NameTooLong,
+ FileNotFound,
+ SystemResources,
+ NotDir,
+ BadPathName,
+} || Io.Cancelable || Io.UnexpectedError;
+
+pub fn chdir(dir_path: []const u8) ChdirError!void {
+ var path_buffer: [posix.PATH_MAX]u8 = undefined;
+ const dir_path_posix = try pathToPosix(dir_path, &path_buffer);
+ const syscall: Syscall = try .start();
+ while (true) switch (posix.errno(posix.system.chdir(dir_path_posix))) {
+ .SUCCESS => return syscall.finish(),
+ .INTR => {
+ try syscall.checkCancel();
+ continue;
+ },
+ .ACCES => return syscall.fail(error.AccessDenied),
+ .IO => return syscall.fail(error.FileSystem),
+ .LOOP => return syscall.fail(error.SymLinkLoop),
+ .NAMETOOLONG => return syscall.fail(error.NameTooLong),
+ .NOENT => return syscall.fail(error.FileNotFound),
+ .NOMEM => return syscall.fail(error.SystemResources),
+ .NOTDIR => return syscall.fail(error.NotDir),
+ .ILSEQ => return syscall.fail(error.BadPathName),
+ .FAULT => |err| return syscall.errnoBug(err),
+ else => |err| return syscall.unexpectedErrno(err),
+ };
+}
diff --git a/lib/std/Io/Threaded/test.zig b/lib/std/Io/Threaded/test.zig
index 74bba51b90a01a2ab8e55d0bda2775dfec1cf9ef..34aa4a8c689d67ceef1d1ca8e985b5487da9e2f1 100644
--- a/lib/std/Io/Threaded/test.zig
+++ b/lib/std/Io/Threaded/test.zig
@@ -13,7 +13,10 @@ test "concurrent vs main prevents deadlock via oversubscription" {
return error.SkipZigTest;
}
- var threaded: Io.Threaded = .init(std.testing.allocator, .{});
+ var threaded: Io.Threaded = .init(std.testing.allocator, .{
+ .argv0 = .empty,
+ .environ = .empty,
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -46,7 +49,10 @@ test "concurrent vs concurrent prevents deadlock via oversubscription" {
return error.SkipZigTest;
}
- var threaded: Io.Threaded = .init(std.testing.allocator, .{});
+ var threaded: Io.Threaded = .init(std.testing.allocator, .{
+ .argv0 = .empty,
+ .environ = .empty,
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -80,7 +86,10 @@ test "async/concurrent context and result alignment" {
var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined;
var fba: std.heap.FixedBufferAllocator = .init(&buffer);
- var threaded: std.Io.Threaded = .init(fba.allocator(), .{});
+ var threaded: std.Io.Threaded = .init(fba.allocator(), .{
+ .argv0 = .empty,
+ .environ = .empty,
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -113,7 +122,10 @@ test "Group.async context alignment" {
var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined;
var fba: std.heap.FixedBufferAllocator = .init(&buffer);
- var threaded: std.Io.Threaded = .init(fba.allocator(), .{});
+ var threaded: std.Io.Threaded = .init(fba.allocator(), .{
+ .argv0 = .empty,
+ .environ = .empty,
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -133,7 +145,10 @@ fn returnArray() [32]u8 {
}
test "async with array return type" {
- var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
+ var threaded: std.Io.Threaded = .init(std.testing.allocator, .{
+ .argv0 = .empty,
+ .environ = .empty,
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -155,7 +170,10 @@ test "cancel blocked read from pipe" {
}
};
- var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
+ var threaded: std.Io.Threaded = .init(std.testing.allocator, .{
+ .argv0 = .empty,
+ .environ = .empty,
+ });
defer threaded.deinit();
const io = threaded.io();
@@ -169,7 +187,7 @@ test "cancel blocked read from pipe" {
.bInheritHandle = std.os.windows.FALSE,
}),
else => {
- const pipe = try std.posix.pipe();
+ const pipe = try std.Io.Threaded.pipe2(.{});
read_end = .{ .handle = pipe[0] };
write_end = .{ .handle = pipe[1] };
},
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index f0baca2784f0ae912488d1ed4a0a1dad28bf1406..75ef13ca91f16307ca028977f5db6e0144996e77 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -422,7 +422,7 @@ pub const StartFailure = union(enum) {
unstarted,
spawn_ipc_worker: error{ConcurrencyUnavailable},
spawn_update_worker: error{ConcurrencyUnavailable},
- parse_env_var: error{ InvalidCharacter, Overflow },
+ parent_ipc: error{ UnsupportedOperation, UnrecognizedFormat },
};
const node_storage_buffer_len = 83;
@@ -446,6 +446,12 @@ const noop_impl = builtin.single_threaded or switch (builtin.os.tag) {
else => false,
};
+pub const ParentFileError = error{
+ UnsupportedOperation,
+ EnvironmentVariableMissing,
+ UnrecognizedFormat,
+};
+
/// Initializes a global Progress instance.
///
/// Asserts there is only one global Progress instance.
@@ -476,20 +482,13 @@ pub fn start(io: Io, options: Options) Node {
global_progress.io = io;
- if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| {
- global_progress.update_worker = io.concurrent(ipcThreadRun, .{
- io,
- @as(Io.File, .{ .handle = switch (@typeInfo(Io.File.Handle)) {
- .int => ipc_fd,
- .pointer => @ptrFromInt(ipc_fd),
- else => @compileError("unsupported fd_t of " ++ @typeName(Io.File.Handle)),
- } }),
- }) catch |err| {
+ if (io.vtable.progressParentFile(io.userdata)) |ipc_file| {
+ global_progress.update_worker = io.concurrent(ipcThreadRun, .{ io, ipc_file }) catch |err| {
global_progress.start_failure = .{ .spawn_ipc_worker = err };
return Node.none;
};
} else |env_err| switch (env_err) {
- error.EnvironmentVariableNotFound => {
+ error.EnvironmentVariableMissing => {
if (options.disable_printing) {
return Node.none;
}
@@ -535,7 +534,7 @@ pub fn start(io: Io, options: Options) Node {
}
},
else => |e| {
- global_progress.start_failure = .{ .parse_env_var = e };
+ global_progress.start_failure = .{ .parent_ipc = e };
return Node.none;
},
}
diff --git a/lib/std/Random/benchmark.zig b/lib/std/Random/benchmark.zig
index 97afe23b95ef45a6468e84b9f72ce00031da6380..a545cda6d5dbfdccb1d6122d10d71354b5ed2654 100644
--- a/lib/std/Random/benchmark.zig
+++ b/lib/std/Random/benchmark.zig
@@ -123,14 +123,15 @@ fn mode(comptime x: comptime_int) comptime_int {
return if (builtin.mode == .Debug) x / 64 else x;
}
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const arena = init.arena.allocator();
+
var stdout_buffer: [0x100]u8 = undefined;
- var stdout_writer = Io.File.stdout().writer(&stdout_buffer);
+ var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer);
const stdout = &stdout_writer.interface;
- var buffer: [1024]u8 = undefined;
- var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
- const args = try std.process.argsAlloc(fixed.allocator());
+ const args = try init.minimal.args.toSlice(arena);
var filter: ?[]u8 = "";
var count: usize = mode(128 * MiB);
diff --git a/lib/std/c.zig b/lib/std/c.zig
index 9bf4bef5d240a311ffd301aa32ece126d271e12e..41c1cd52c0cd03a45586be2bc58e1a29d39b38ff 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -3764,8 +3764,8 @@ pub const W = switch (native_os) {
pub fn EXITSTATUS(x: u32) u8 {
return @as(u8, @intCast(x >> 8));
}
- pub fn TERMSIG(x: u32) u32 {
- return status(x);
+ pub fn TERMSIG(x: u32) SIG {
+ return @enumFromInt(status(x));
}
pub fn STOPSIG(x: u32) u32 {
return x >> 8;
@@ -3797,14 +3797,14 @@ pub const W = switch (native_os) {
pub fn EXITSTATUS(s: u32) u8 {
return @as(u8, @intCast((s & 0xff00) >> 8));
}
- pub fn TERMSIG(s: u32) u32 {
- return s & 0x7f;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn STOPSIG(s: u32) u32 {
return EXITSTATUS(s);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFSTOPPED(s: u32) bool {
return @as(u16, @truncate((((s & 0xffff) *% 0x10001) >> 8))) > 0x7f00;
@@ -3825,14 +3825,14 @@ pub const W = switch (native_os) {
pub fn EXITSTATUS(s: u32) u8 {
return @as(u8, @intCast((s >> 8) & 0xff));
}
- pub fn TERMSIG(s: u32) u32 {
- return s & 0x7f;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn STOPSIG(s: u32) u32 {
return EXITSTATUS(s);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFCONTINUED(s: u32) bool {
@@ -3859,14 +3859,14 @@ pub const W = switch (native_os) {
pub fn EXITSTATUS(s: u32) u8 {
return @as(u8, @intCast((s >> 8) & 0xff));
}
- pub fn TERMSIG(s: u32) u32 {
- return s & 0x7f;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn STOPSIG(s: u32) u32 {
return EXITSTATUS(s);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFCONTINUED(s: u32) bool {
@@ -3893,14 +3893,14 @@ pub const W = switch (native_os) {
pub fn EXITSTATUS(s: u32) u8 {
return @as(u8, @intCast((s & 0xff00) >> 8));
}
- pub fn TERMSIG(s: u32) u32 {
- return s & 0x7f;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn STOPSIG(s: u32) u32 {
return EXITSTATUS(s);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFSTOPPED(s: u32) bool {
return @as(u16, @truncate((((s & 0xffff) *% 0x10001) >> 8))) > 0x7f00;
@@ -3921,8 +3921,8 @@ pub const W = switch (native_os) {
return @as(u8, @intCast(s & 0xff));
}
- pub fn TERMSIG(s: u32) u32 {
- return (s >> 8) & 0xff;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt((s >> 8) & 0xff);
}
pub fn STOPSIG(s: u32) u32 {
@@ -3949,14 +3949,14 @@ pub const W = switch (native_os) {
pub fn EXITSTATUS(s: u32) u8 {
return @as(u8, @intCast((s >> 8) & 0xff));
}
- pub fn TERMSIG(s: u32) u32 {
- return (s & 0x7f);
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn STOPSIG(s: u32) u32 {
return EXITSTATUS(s);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFCONTINUED(s: u32) bool {
@@ -3988,12 +3988,12 @@ pub const W = switch (native_os) {
return EXITSTATUS(s);
}
- pub fn TERMSIG(s: u32) u32 {
- return s & 0x7f;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFSTOPPED(s: u32) bool {
diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig
index 23154324bc280d8b54d45c032de56c60670ac5ba..22f2b6fd106cf0e8963fec9c0d4b5e3025b0f3c0 100644
--- a/lib/std/crypto/benchmark.zig
+++ b/lib/std/crypto/benchmark.zig
@@ -503,16 +503,16 @@ fn mode(comptime x: comptime_int) comptime_int {
return if (builtin.mode == .Debug) x / 64 else x;
}
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const arena = init.arena.allocator();
+
// Size of buffer is about size of printed message.
var stdout_buffer: [0x100]u8 = undefined;
- var stdout_writer = Io.File.stdout().writer(&stdout_buffer);
+ var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer);
const stdout = &stdout_writer.interface;
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena.deinit();
- const arena_allocator = arena.allocator();
- const args = try std.process.argsAlloc(arena_allocator);
+ const args = try init.minimal.args.toSlice(arena);
var filter: ?[]u8 = "";
@@ -556,13 +556,9 @@ pub fn main() !void {
}
}
- var io_threaded = std.Io.Threaded.init(arena_allocator, .{});
- defer io_threaded.deinit();
- const io = io_threaded.io();
-
inline for (parallel_hashes) |H| {
if (filter == null or std.mem.find(u8, H.name, filter.?) != null) {
- const throughput = try benchmarkHashParallel(H.ty, mode(128 * MiB), arena_allocator, io);
+ const throughput = try benchmarkHashParallel(H.ty, mode(128 * MiB), arena, io);
try stdout.print("{s:>17}: {:10} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
try stdout.flush();
}
@@ -634,7 +630,7 @@ pub fn main() !void {
inline for (pwhashes) |H| {
if (filter == null or std.mem.find(u8, H.name, filter.?) != null) {
- const throughput = try benchmarkPwhash(arena_allocator, H.ty, H.params, mode(64), io);
+ const throughput = try benchmarkPwhash(arena, H.ty, H.params, mode(64), io);
try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });
try stdout.flush();
}
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index d000bda62e99011ec9a5258411bf35aafbd6e0ee..0f151447fc56b4d7afdde22709b2bc0fe414d554 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -40,7 +40,7 @@ pub const cpu_context = @import("debug/cpu_context.zig");
/// pub fn deinit(si: *SelfInfo, gpa: Allocator) void;
///
/// /// Returns the symbol and source location of the instruction at `address`.
-/// pub fn getSymbol(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError!Symbol;
+/// pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) SelfInfoError!Symbol;
/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
/// pub fn getModuleName(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError![]const u8;
///
diff --git a/lib/std/debug/ElfFile.zig b/lib/std/debug/ElfFile.zig
index a101309d22c52f4d324fbb43452bef23cd0e958a..007e815e374d61d860544e531d0a9e663157447d 100644
--- a/lib/std/debug/ElfFile.zig
+++ b/lib/std/debug/ElfFile.zig
@@ -67,15 +67,16 @@ pub const DebugInfoSearchPaths = struct {
};
pub fn native(exe_path: []const u8) DebugInfoSearchPaths {
- return .{
+ if (std.Options.elf_debug_info_search_paths) |f| return f(exe_path);
+ if (std.Options.debug_threaded_io) |t| return .{
.debuginfod_client = p: {
- if (std.posix.getenv("DEBUGINFOD_CACHE_PATH")) |p| {
+ if (t.environString("DEBUGINFOD_CACHE_PATH")) |p| {
break :p .{ p, "" };
}
- if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| {
+ if (t.environString("XDG_CACHE_HOME")) |cache_path| {
break :p .{ cache_path, "/debuginfod_client" };
}
- if (std.posix.getenv("HOME")) |home_path| {
+ if (t.environString("HOME")) |home_path| {
break :p .{ home_path, "/.cache/debuginfod_client" };
}
break :p null;
@@ -85,6 +86,7 @@ pub const DebugInfoSearchPaths = struct {
},
.exe_dir = std.fs.path.dirname(exe_path) orelse ".",
};
+ @compileError("std.Options.elf_debug_info_search_paths must be provided");
}
};
diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig
index be76f3a8c24fa92e256588359350d65767b031aa..ffcb4dfd26fdb9b1a80d030221d83b87283ef499 100644
--- a/lib/std/debug/SelfInfo/Elf.zig
+++ b/lib/std/debug/SelfInfo/Elf.zig
@@ -322,6 +322,7 @@ const Module = struct {
if (mod.loaded_elf == null) mod.loaded_elf = loadElf(mod, gpa, io);
return if (mod.loaded_elf.?) |*elf| elf else |err| err;
}
+
fn loadElf(mod: *Module, gpa: Allocator, io: Io) Error!LoadedElf {
const load_result = if (mod.name.len > 0) res: {
var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo;
diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig
index ccd60ecd97f12dea8912ecb69dd51e2c1a396975..05a2562e29b0312ec9089b7b1bf2349a5d671a08 100644
--- a/lib/std/dynamic_library.zig
+++ b/lib/std/dynamic_library.zig
@@ -31,12 +31,20 @@ pub const DynLib = struct {
/// Trusts the file. Malicious file will be able to execute arbitrary code.
pub fn open(path: []const u8) Error!DynLib {
- return .{ .inner = try InnerType.open(path) };
+ if (InnerType == ElfDynLib) {
+ return .{ .inner = try InnerType.open(path, null) };
+ } else {
+ return .{ .inner = try InnerType.open(path) };
+ }
}
/// Trusts the file. Malicious file will be able to execute arbitrary code.
pub fn openZ(path_c: [*:0]const u8) Error!DynLib {
- return .{ .inner = try InnerType.openZ(path_c) };
+ if (InnerType == ElfDynLib) {
+ return .{ .inner = try InnerType.openZ(path_c, null) };
+ } else {
+ return .{ .inner = try InnerType.openZ(path_c) };
+ }
}
/// Trusts the file.
@@ -197,7 +205,7 @@ pub const ElfDynLib = struct {
// - DT_RPATH of the calling binary is not used as a search path
// - DT_RUNPATH of the calling binary is not used as a search path
// - /etc/ld.so.cache is not read
- fn resolveFromName(io: Io, path_or_name: []const u8) !posix.fd_t {
+ fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !posix.fd_t {
// If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname
if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| {
return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
@@ -207,7 +215,7 @@ pub const ElfDynLib = struct {
if (std.os.linux.geteuid() == std.os.linux.getuid() and
std.os.linux.getegid() == std.os.linux.getgid())
{
- if (posix.getenvZ("LD_LIBRARY_PATH")) |ld_library_path| {
+ if (LD_LIBRARY_PATH) |ld_library_path| {
if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| {
return fd;
}
@@ -221,10 +229,10 @@ pub const ElfDynLib = struct {
}
/// Trusts the file. Malicious file will be able to execute arbitrary code.
- pub fn open(path: []const u8) Error!ElfDynLib {
+ pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib {
const io = std.Options.debug_io;
- const fd = try resolveFromName(io, path);
+ const fd = try resolveFromName(io, path, LD_LIBRARY_PATH);
defer posix.close(fd);
const file: Io.File = .{ .handle = fd };
@@ -371,8 +379,8 @@ pub const ElfDynLib = struct {
}
/// Trusts the file. Malicious file will be able to execute arbitrary code.
- pub fn openZ(path_c: [*:0]const u8) Error!ElfDynLib {
- return open(mem.sliceTo(path_c, 0));
+ pub fn openZ(path_c: [*:0]const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib {
+ return open(mem.sliceTo(path_c, 0), LD_LIBRARY_PATH);
}
/// Trusts the file
@@ -554,8 +562,8 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str
test "ElfDynLib" {
if (native_os != .linux) return error.SkipZigTest;
- try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so"));
- try testing.expectError(error.FileNotFound, ElfDynLib.openZ("invalid_so.so"));
+ try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so", null));
+ try testing.expectError(error.FileNotFound, ElfDynLib.openZ("invalid_so.so", null));
}
/// Separated to avoid referencing `WindowsDynLib`, because its field types may not
diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig
index a8465f52d4148d10aea014b10a330b3d0578f371..cd189cc12246df7437c9a8d21d386afadebc980f 100644
--- a/lib/std/fs/path.zig
+++ b/lib/std/fs/path.zig
@@ -13,16 +13,15 @@
//! https://github.com/WebAssembly/wasi-filesystem/issues/17#issuecomment-1430639353
const builtin = @import("builtin");
+const native_os = builtin.target.os.tag;
+
const std = @import("../std.zig");
-const debug = std.debug;
-const assert = debug.assert;
+const assert = std.debug.assert;
const testing = std.testing;
const mem = std.mem;
-const ascii = std.ascii;
-const Allocator = mem.Allocator;
-const windows = std.os.windows;
-const process = std.process;
-const native_os = builtin.target.os.tag;
+const Allocator = std.mem.Allocator;
+const eqlIgnoreCaseWtf8 = std.os.windows.eqlIgnoreCaseWtf8;
+const eqlIgnoreCaseWtf16 = std.os.windows.eqlIgnoreCaseWtf16;
pub const sep_windows: u8 = '\\';
pub const sep_posix: u8 = '/';
@@ -281,7 +280,7 @@ pub fn isAbsolute(path: []const u8) bool {
}
fn isAbsoluteWindowsImpl(comptime T: type, path: []const T) bool {
- return switch (windows.getWin32PathType(T, path)) {
+ return switch (getWin32PathType(T, path)) {
// Unambiguously absolute
.drive_absolute, .unc_absolute, .local_device, .root_local_device => true,
// Unambiguously relative
@@ -515,13 +514,13 @@ test parsePathPosix {
pub fn WindowsPath2(comptime T: type) type {
return struct {
- kind: windows.Win32PathType,
+ kind: Win32PathType,
root: []const T,
};
}
pub fn parsePathWindows(comptime T: type, path: []const T) WindowsPath2(T) {
- const kind = windows.getWin32PathType(T, path);
+ const kind = getWin32PathType(T, path);
const root = root: switch (kind) {
.drive_absolute, .drive_relative => {
const drive_letter_len = getDriveLetter(T, path).len;
@@ -731,7 +730,7 @@ fn parseUNC(comptime T: type, path: []const T) WindowsUNC(T) {
// For the share, there can be any number of path separators between the server
// and the share, so we want to skip over all of them instead of just looking for
// the first one.
- var it = std.mem.tokenizeAny(T, path[server_end + 1 ..], any_sep);
+ var it = mem.tokenizeAny(T, path[server_end + 1 ..], any_sep);
const share = it.next() orelse return .{
.server = path[2..server_end],
.sep_after_server = true,
@@ -803,8 +802,8 @@ const DiskDesignatorKind = enum { drive, unc };
/// `p1` and `p2` are both assumed to be the `kind` provided.
fn compareDiskDesignators(comptime T: type, kind: DiskDesignatorKind, p1: []const T, p2: []const T) bool {
const eql = switch (T) {
- u8 => windows.eqlIgnoreCaseWtf8,
- u16 => windows.eqlIgnoreCaseWtf16,
+ u8 => eqlIgnoreCaseWtf8,
+ u16 => eqlIgnoreCaseWtf16,
else => @compileError("only u8 (WTF-8) and u16 (WTF-16LE) is supported"),
};
switch (kind) {
@@ -1094,10 +1093,14 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) Allocator
}
/// This function is like a series of `cd` statements executed one after another.
+///
/// It resolves "." and ".." to the best of its ability, but will not convert relative paths to
/// an absolute path, use Io.Dir.realpath instead.
+///
/// ".." components may persist in the resolved path if the resolved path is relative.
+///
/// The result does not have a trailing path separator.
+///
/// This function does not perform any syscalls. Executing this series of path
/// lookups on the actual filesystem may produce different results due to
/// symlinks.
@@ -1494,25 +1497,54 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) !void {
try testing.expectEqualSlices(u8, expected_output, basenameWindows(input));
}
-pub const RelativeError = std.process.GetCwdAllocError;
-
-/// Returns the relative path from `from` to `to`. If `from` and `to` each
-/// resolve to the same path (after calling `resolve` on each), a zero-length
-/// string is returned.
-/// On Windows, the result is not guaranteed to be relative, as the paths may be
-/// on different volumes. In that case, the result will be the canonicalized absolute
-/// path of `to`.
-pub fn relative(allocator: Allocator, from: []const u8, to: []const u8) RelativeError![]u8 {
+/// Returns the non-absolute path from `from` to `to`.
+///
+/// Other than memory allocation, this is a pure function; the result solely
+/// depends on the input parameters.
+///
+/// If `from` and `to` each resolve to the same path (after calling `resolve`
+/// on each), a zero-length string is returned.
+///
+/// See `relativePosix` and `relativeWindows` for operating system specific
+/// details and for how `environ_map` is used.
+pub fn relative(
+ gpa: Allocator,
+ cwd: []const u8,
+ environ_map: ?*const std.process.Environ.Map,
+ from: []const u8,
+ to: []const u8,
+) Allocator.Error![]u8 {
if (native_os == .windows) {
- return relativeWindows(allocator, from, to);
+ return relativeWindows(gpa, cwd, environ_map, from, to);
} else {
- return relativePosix(allocator, from, to);
+ return relativePosix(gpa, cwd, from, to);
}
}
-pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ![]u8 {
- if (native_os != .windows) @compileError("this function relies on Windows-specific semantics");
-
+/// Returns the non-absolute path from `from` to `to` according to Windows rules.
+///
+/// Other than memory allocation, this is a pure function; the result solely
+/// depends on the input parameters.
+///
+/// If `from` and `to` each resolve to the same path (after calling `resolve`
+/// on each), a zero-length string is returned.
+///
+/// The result is not guaranteed to be relative, as the paths may be on
+/// different volumes. In that case, the result will be the canonicalized
+/// absolute path of `to`.
+///
+/// Per-drive CWDs are stored in special semi-hidden environment variables of
+/// the format `=:`, e.g. `=C:`. This type of CWD is purely a
+/// shell concept, so there's no guarantee that it'll be set or that it'll even
+/// be accurate. This is the only reason for the `environ_map` parameter. `null` is
+/// treated equivalent to the environment variable missing.
+pub fn relativeWindows(
+ gpa: Allocator,
+ cwd: []const u8,
+ environ_map: ?*const std.process.Environ.Map,
+ from: []const u8,
+ to: []const u8,
+) Allocator.Error![]u8 {
const parsed_from = parsePathWindows(u8, from);
const parsed_to = parsePathWindows(u8, to);
@@ -1533,14 +1565,14 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
};
if (result_is_always_to) {
- return windowsResolveAgainstCwd(allocator, to, parsed_to);
+ return windowsResolveAgainstCwd(gpa, cwd, environ_map, to, parsed_to);
}
- const resolved_from = try windowsResolveAgainstCwd(allocator, from, parsed_from);
- defer allocator.free(resolved_from);
+ const resolved_from = try windowsResolveAgainstCwd(gpa, cwd, environ_map, from, parsed_from);
+ defer gpa.free(resolved_from);
var clean_up_resolved_to = true;
- const resolved_to = try windowsResolveAgainstCwd(allocator, to, parsed_to);
- defer if (clean_up_resolved_to) allocator.free(resolved_to);
+ const resolved_to = try windowsResolveAgainstCwd(gpa, cwd, environ_map, to, parsed_to);
+ defer if (clean_up_resolved_to) gpa.free(resolved_to);
const parsed_resolved_from = parsePathWindows(u8, resolved_from);
const parsed_resolved_to = parsePathWindows(u8, resolved_to);
@@ -1569,18 +1601,18 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
var from_it = mem.tokenizeAny(u8, resolved_from[parsed_resolved_from.root.len..], "/\\");
var to_it = mem.tokenizeAny(u8, resolved_to[parsed_resolved_to.root.len..], "/\\");
while (true) {
- const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());
+ const from_component = from_it.next() orelse return gpa.dupe(u8, to_it.rest());
const to_rest = to_it.rest();
if (to_it.next()) |to_component| {
- if (windows.eqlIgnoreCaseWtf8(from_component, to_component))
+ if (eqlIgnoreCaseWtf8(from_component, to_component))
continue;
}
var up_index_end = "..".len;
while (from_it.next()) |_| {
up_index_end += "\\..".len;
}
- const result = try allocator.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len);
- errdefer allocator.free(result);
+ const result = try gpa.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len);
+ errdefer gpa.free(result);
result[0..2].* = "..".*;
var result_index: usize = 2;
@@ -1597,85 +1629,60 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
result_index += to_component.len;
}
- return allocator.realloc(result, result_index);
+ return gpa.realloc(result, result_index);
}
return [_]u8{};
}
-fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: WindowsPath2(u8)) ![]u8 {
+fn windowsResolveAgainstCwd(
+ gpa: Allocator,
+ cwd: []const u8,
+ environ_map: ?*const std.process.Environ.Map,
+ path: []const u8,
+ parsed: WindowsPath2(u8),
+) ![]u8 {
// Space for 256 WTF-16 code units; potentially 3 WTF-8 bytes per WTF-16 code unit
- var temp_allocator_state = std.heap.stackFallback(256 * 3, allocator);
+ var temp_allocator_state = std.heap.stackFallback(256 * 3, gpa);
return switch (parsed.kind) {
.drive_absolute,
.unc_absolute,
.root_local_device,
.local_device,
- => try resolveWindows(allocator, &.{path}),
- .relative => blk: {
- const temp_allocator = temp_allocator_state.get();
+ => try resolveWindows(gpa, &.{path}),
- const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath;
- const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2];
+ .relative => try resolveWindows(gpa, &.{ cwd, path }),
- const wtf8_len = std.unicode.calcWtf8Len(cwd_w);
- const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len);
- defer temp_allocator.free(wtf8_buf);
- assert(std.unicode.wtf16LeToWtf8(wtf8_buf, cwd_w) == wtf8_len);
-
- break :blk try resolveWindows(allocator, &.{ wtf8_buf, path });
- },
.rooted => blk: {
- const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath;
- const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2];
- const parsed_cwd = parsePathWindows(u16, cwd_w);
+ const parsed_cwd = parsePathWindows(u8, cwd);
switch (parsed_cwd.kind) {
.drive_absolute => {
var drive_buf = "_:\\".*;
- drive_buf[0] = @truncate(cwd_w[0]);
- break :blk try resolveWindows(allocator, &.{ &drive_buf, path });
+ drive_buf[0] = cwd[0];
+ break :blk try resolveWindows(gpa, &.{ &drive_buf, path });
},
.unc_absolute => {
- const temp_allocator = temp_allocator_state.get();
- var root_buf = try temp_allocator.alloc(u8, parsed_cwd.root.len * 3);
- defer temp_allocator.free(root_buf);
-
- const wtf8_len = std.unicode.wtf16LeToWtf8(root_buf, parsed_cwd.root);
- const root = root_buf[0..wtf8_len];
- break :blk try resolveWindows(allocator, &.{ root, path });
+ break :blk try resolveWindows(gpa, &.{ parsed_cwd.root, path });
},
// Effectively a malformed CWD, give up and just return a normalized path
- else => break :blk try resolveWindows(allocator, &.{path}),
+ else => break :blk try resolveWindows(gpa, &.{path}),
}
},
.drive_relative => blk: {
const temp_allocator = temp_allocator_state.get();
const drive_cwd = drive_cwd: {
- const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath;
- const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2];
- const parsed_cwd = parsePathWindows(u16, cwd_w);
+ const parsed_cwd = parsePathWindows(u8, cwd);
if (parsed_cwd.kind == .drive_absolute) {
const drive_letter_w = parsed_cwd.root[0];
const drive_letters_match = drive_letter_w <= 0x7F and
- ascii.toUpper(@intCast(drive_letter_w)) == ascii.toUpper(parsed.root[0]);
- if (drive_letters_match) {
- const wtf8_len = std.unicode.calcWtf8Len(cwd_w);
- const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len);
- assert(std.unicode.wtf16LeToWtf8(wtf8_buf, cwd_w) == wtf8_len);
- break :drive_cwd wtf8_buf[0..];
- }
+ std.ascii.toUpper(@intCast(drive_letter_w)) == std.ascii.toUpper(parsed.root[0]);
+ if (drive_letters_match)
+ break :drive_cwd cwd;
- // Per-drive CWD's are stored in special semi-hidden environment variables
- // of the format `=:`, e.g. `=C:`. This type of CWD is
- // purely a shell concept, so there's no guarantee that it'll be set
- // or that it'll even be accurate.
- var key_buf = std.unicode.wtf8ToWtf16LeStringLiteral("=_:").*;
- key_buf[1] = parsed.root[0];
- if (std.process.getenvW(&key_buf)) |drive_cwd_w| {
- const wtf8_len = std.unicode.calcWtf8Len(drive_cwd_w);
- const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len);
- assert(std.unicode.wtf16LeToWtf8(wtf8_buf, drive_cwd_w) == wtf8_len);
- break :drive_cwd wtf8_buf[0..];
+ if (environ_map) |m| {
+ if (m.get(&.{ '=', parsed.root[0], ':' })) |v| {
+ break :drive_cwd try temp_allocator.dupe(u8, v);
+ }
}
}
@@ -1686,16 +1693,20 @@ fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: Wind
break :drive_cwd drive_buf;
};
defer temp_allocator.free(drive_cwd);
- break :blk try resolveWindows(allocator, &.{ drive_cwd, path });
+ break :blk try resolveWindows(gpa, &.{ drive_cwd, path });
},
};
}
-pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]u8 {
- if (native_os == .windows) @compileError("this function relies on semantics that do not apply to Windows");
-
- const cwd = try process.getCwdAlloc(allocator);
- defer allocator.free(cwd);
+/// Returns the non-absolute path from `from` to `to` according to Windows rules.
+///
+/// Other than memory allocation, this is a pure function; the result solely
+/// depends on the input parameters.
+///
+/// If `from` and `to` each resolve to the same path (after calling `resolve`
+/// on each), a zero-length string is returned.
+///
+pub fn relativePosix(allocator: Allocator, cwd: []const u8, from: []const u8, to: []const u8) Allocator.Error![]u8 {
const resolved_from = try resolvePosix(allocator, &[_][]const u8{ cwd, from });
defer allocator.free(resolved_from);
const resolved_to = try resolvePosix(allocator, &[_][]const u8{ cwd, to });
@@ -1736,69 +1747,67 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]
}
test relative {
- if (native_os == .windows) {
- try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
- try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
- try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
- try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", "");
- try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc");
- try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc");
- try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb");
- try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\");
- try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", "");
- try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc");
- try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\..");
- try testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json");
- try testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz");
- try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux");
- try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz");
- try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", "..");
- try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz");
- try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz");
- try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux");
- try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz");
- try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux");
- try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz");
- try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux");
- try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz");
- try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz");
+ try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
+ try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
+ try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
+ try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", "");
+ try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc");
+ try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc");
+ try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb");
+ try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\");
+ try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", "");
+ try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc");
+ try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\..");
+ try testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json");
+ try testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz");
+ try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux");
+ try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz");
+ try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", "..");
+ try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz");
+ try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz");
+ try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux");
+ try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz");
+ try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux");
+ try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz");
+ try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux");
+ try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz");
+ try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz");
- try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo");
- try testRelativeWindows("c:foo", "c:foo\\bar", "bar");
- try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo");
- try testRelativeWindows("\\foo", "\\foo\\bar", "bar");
+ try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo");
+ try testRelativeWindows("c:foo", "c:foo\\bar", "bar");
+ try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo");
+ try testRelativeWindows("\\foo", "\\foo\\bar", "bar");
- try testRelativeWindows("a/b/c", "a\\b", "..");
- try testRelativeWindows("a/b/c", "a", "..\\..");
- try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d");
+ try testRelativeWindows("a/b/c", "a\\b", "..");
+ try testRelativeWindows("a/b/c", "a", "..\\..");
+ try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d");
- try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", "");
- // Unicode-aware case-insensitive path comparison
- try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", "");
- } else {
- try testRelativePosix("/var/lib", "/var", "..");
- try testRelativePosix("/var/lib", "/bin", "../../bin");
- try testRelativePosix("/var/lib", "/var/lib", "");
- try testRelativePosix("/var/lib", "/var/apache", "../apache");
- try testRelativePosix("/var/", "/var/lib", "lib");
- try testRelativePosix("/", "/var/lib", "var/lib");
- try testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json");
- try testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../..");
- try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz");
- try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux");
- try testRelativePosix("/baz-quux", "/baz", "../baz");
- try testRelativePosix("/baz", "/baz-quux", "../baz-quux");
- }
+ try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", "");
+ // Unicode-aware case-insensitive path comparison
+ try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", "");
+
+ try testRelativePosix("/var/lib", "/var", "..");
+ try testRelativePosix("/var/lib", "/bin", "../../bin");
+ try testRelativePosix("/var/lib", "/var/lib", "");
+ try testRelativePosix("/var/lib", "/var/apache", "../apache");
+ try testRelativePosix("/var/", "/var/lib", "lib");
+ try testRelativePosix("/", "/var/lib", "var/lib");
+ try testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json");
+ try testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../..");
+ try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz");
+ try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux");
+ try testRelativePosix("/baz-quux", "/baz", "../baz");
+ try testRelativePosix("/baz", "/baz-quux", "../baz-quux");
}
fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void {
- const result = try relativePosix(testing.allocator, from, to);
+ const result = try relativePosix(testing.allocator, ".", from, to);
defer testing.allocator.free(result);
try testing.expectEqualStrings(expected_output, result);
}
fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void {
- const result = try relativeWindows(testing.allocator, from, to);
+ const result = try relativeWindows(testing.allocator, ".", null, from, to);
defer testing.allocator.free(result);
try testing.expectEqualStrings(expected_output, result);
}
@@ -2554,3 +2563,124 @@ pub const fmtAsUtf8Lossy = std.unicode.fmtUtf8;
/// a lossy conversion if the path contains any unpaired surrogates.
/// Unpaired surrogates are replaced by the replacement character (U+FFFD).
pub const fmtWtf16LeAsUtf8Lossy = std.unicode.fmtUtf16Le;
+
+/// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type.
+pub const Win32PathType = enum {
+ /// `\\server\share\foo`
+ unc_absolute,
+ /// `C:\foo`
+ drive_absolute,
+ /// `C:foo`
+ drive_relative,
+ /// `\foo`
+ rooted,
+ /// `foo`
+ relative,
+ /// `\\.\foo`, `\\?\foo`
+ local_device,
+ /// `\\.`, `\\?`
+ root_local_device,
+};
+
+/// Get the path type of a Win32 namespace path.
+/// Similar to `RtlDetermineDosPathNameType_U`.
+/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
+pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
+ if (path.len < 1) return .relative;
+
+ const windows_path = std.fs.path.PathType.windows;
+ if (windows_path.isSep(T, path[0])) {
+ // \x
+ if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted;
+ // \\. or \\?
+ if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) {
+ // exactly \\. or \\? with nothing trailing
+ if (path.len == 3) return .root_local_device;
+ // \\.\x or \\?\x
+ if (windows_path.isSep(T, path[3])) return .local_device;
+ }
+ // \\x
+ return .unc_absolute;
+ } else {
+ // Some choice has to be made about how non-ASCII code points as drive-letters are handled, since
+ // path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification
+ // for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code
+ // units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so
+ // checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16.
+ //
+ // `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers
+ // `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get
+ // classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded
+ // in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path).
+ //
+ // The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both
+ // WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI,
+ // drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you
+ // to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will
+ // allow you to set any WTF-16 code unit as a drive letter.
+ //
+ // Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g.
+ // `cd /D €:\` will work, filesystem functions still work, etc.
+ //
+ // The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't
+ // just check path[0], path[1], path[2].
+ const colon_i: usize = switch (T) {
+ u8 => i: {
+ const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative;
+ // Conveniently, 4-byte sequences in WTF-8 have the same starting code point
+ // as 2-code-unit sequences in WTF-16.
+ if (code_point_len > 3) return .relative;
+ break :i code_point_len;
+ },
+ u16 => 1,
+ else => @compileError("unsupported type: " ++ @typeName(T)),
+ };
+ // x
+ if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative;
+ // x:\
+ if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute;
+ // x:
+ return .drive_relative;
+ }
+}
+
+test getWin32PathType {
+ try std.testing.expectEqual(.relative, getWin32PathType(u8, ""));
+ try std.testing.expectEqual(.relative, getWin32PathType(u8, "x"));
+ try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\"));
+
+ try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//."));
+ try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?"));
+ try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?"));
+
+ try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x"));
+ try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x"));
+ try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x"));
+ // local device paths require a path separator after the root, otherwise it is considered a UNC path
+ try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x"));
+ try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x"));
+
+ try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//"));
+ try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x"));
+ try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x"));
+
+ try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x"));
+ try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/"));
+
+ try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:"));
+ try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc"));
+ try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c"));
+
+ try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\"));
+ try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc"));
+ try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c"));
+
+ // Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter
+ try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\"));
+ try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\")));
+ try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:"));
+ try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:")));
+ // But code points that are encoded as two WTF-16 code units are not
+ try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\"));
+ try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\")));
+}
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index 94cf3055e562c0cc077b19c0951578f5bdf05c66..7158eb734e1ce6f86f02c58cec52fb5007fea7b1 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -79,7 +79,7 @@ const PathType = enum {
// using '127.0.0.1' as the server name and '$' as the share name.
var fd_path_buf: [Dir.max_path_bytes]u8 = undefined;
const dir_path = fd_path_buf[0..try dir.realPath(io, &fd_path_buf)];
- const windows_path_type = windows.getWin32PathType(u8, dir_path);
+ const windows_path_type = Dir.path.getWin32PathType(u8, dir_path);
switch (windows_path_type) {
.unc_absolute => return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }),
.drive_absolute => {
diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig
index 6744b87facaed5a65d6e1a987766af3e638931e7..76538be4a8fb116d9f36e0816770cc647038848c 100644
--- a/lib/std/hash/benchmark.zig
+++ b/lib/std/hash/benchmark.zig
@@ -353,14 +353,15 @@ fn mode(comptime x: comptime_int) comptime_int {
return if (builtin.mode == .Debug) x / 64 else x;
}
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const arena = init.arena.allocator();
+
var stdout_buffer: [0x100]u8 = undefined;
- var stdout_writer = Io.File.stdout().writer(&stdout_buffer);
+ var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer);
const stdout = &stdout_writer.interface;
- var buffer: [1024]u8 = undefined;
- var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
- const args = try std.process.argsAlloc(fixed.allocator());
+ const args = try init.minimal.args.toSlice(arena);
var filter: ?[]u8 = "";
var count: usize = mode(128 * MiB);
diff --git a/lib/std/heap/debug_allocator.zig b/lib/std/heap/debug_allocator.zig
index 24f1554544c635dac055b465e38f873530afe430..0b57d934fc770fed35869169f6280fcb4d17ec74 100644
--- a/lib/std/heap/debug_allocator.zig
+++ b/lib/std/heap/debug_allocator.zig
@@ -1272,9 +1272,12 @@ test "shrink large object to large object" {
}
test "shrink large object to large object with larger alignment" {
- if (!builtin.link_libc and builtin.os.tag == .wasi) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/22731
+ if (builtin.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/22731
+ return error.SkipZigTest;
+ }
- var gpa = DebugAllocator(test_config){};
+ var gpa: DebugAllocator(test_config) = .{};
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator();
diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig
index 7f8c9827ef03ab3ba70477a0ec0d226c260df9f1..3979a18029e9b4ddb01635a247691fc4af11b3a2 100644
--- a/lib/std/http/Client.zig
+++ b/lib/std/http/Client.zig
@@ -1307,7 +1307,7 @@ pub fn deinit(client: *Client) void {
/// Asserts the client has no active connections.
/// Uses `arena` for a few small allocations that must outlive the client, or
/// at least until those fields are set to different values.
-pub fn initDefaultProxies(client: *Client, arena: Allocator) !void {
+pub fn initDefaultProxies(client: *Client, arena: Allocator, environ_map: *std.process.Environ.Map) !void {
// Prevent any new connections from being created.
client.connection_pool.mutex.lock();
defer client.connection_pool.mutex.unlock();
@@ -1315,27 +1315,26 @@ pub fn initDefaultProxies(client: *Client, arena: Allocator) !void {
assert(client.connection_pool.used.first == null); // There are active requests.
if (client.http_proxy == null) {
- client.http_proxy = try createProxyFromEnvVar(arena, &.{
+ client.http_proxy = try createProxyFromEnvVar(arena, environ_map, &.{
"http_proxy", "HTTP_PROXY", "all_proxy", "ALL_PROXY",
});
}
if (client.https_proxy == null) {
- client.https_proxy = try createProxyFromEnvVar(arena, &.{
+ client.https_proxy = try createProxyFromEnvVar(arena, environ_map, &.{
"https_proxy", "HTTPS_PROXY", "all_proxy", "ALL_PROXY",
});
}
}
-fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !?*Proxy {
+fn createProxyFromEnvVar(
+ arena: Allocator,
+ environ_map: *std.process.Environ.Map,
+ env_var_names: []const []const u8,
+) !?*Proxy {
const content = for (env_var_names) |name| {
- const content = std.process.getEnvVarOwned(arena, name) catch |err| switch (err) {
- error.EnvironmentVariableNotFound => continue,
- else => |e| return e,
- };
-
+ const content = environ_map.get(name) orelse continue;
if (content.len == 0) continue;
-
break content;
} else return null;
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 667d743f3d89abdd6ec1cac7d9877bd319530db5..4f6643c3af33381ec38b6490600e4b64fe879109 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -1,27 +1,4 @@
-//! This file contains thin wrappers around OS-specific APIs, with these
-//! specific goals in mind:
-//! * Convert "errno"-style error codes into Zig errors.
-//! * When null-terminated byte buffers are required, provide APIs which accept
-//! slices as well as APIs which accept null-terminated byte buffers. Same goes
-//! for WTF-16LE encoding.
-//! * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
-//! cross platform abstracting.
-//! * When there exists a corresponding libc function and linking libc, the libc
-//! implementation is used. Exceptions are made for known buggy areas of libc.
-//! On Linux libc can be side-stepped by using `std.os.linux` directly.
-//! * For Windows, this file represents the API that libc would provide for
-//! Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
-
-const root = @import("root");
-const std = @import("std.zig");
const builtin = @import("builtin");
-const assert = std.debug.assert;
-const math = std.math;
-const mem = std.mem;
-const elf = std.elf;
-const fs = std.fs;
-const dl = @import("dynamic_library.zig");
-const posix = std.posix;
const native_os = builtin.os.tag;
pub const linux = @import("os/linux.zig");
@@ -33,47 +10,7 @@ pub const windows = @import("os/windows.zig");
test {
_ = linux;
- if (native_os == .uefi) {
- _ = uefi;
- }
+ if (native_os == .uefi) _ = uefi;
_ = wasi;
_ = 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
-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.
-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"),
- else => undefined,
-};
-
-pub const FstatError = error{
- SystemResources,
- AccessDenied,
- Unexpected,
-};
-
-pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t {
- var stat: wasi.filestat_t = undefined;
- switch (wasi.fd_filestat_get(fd, &stat)) {
- .SUCCESS => return stat,
- .INVAL => unreachable,
- .BADF => unreachable, // Always a race condition.
- .NOMEM => return error.SystemResources,
- .ACCES => return error.AccessDenied,
- .NOTCAPABLE => return error.AccessDenied,
- else => |err| return posix.unexpectedErrno(err),
- }
-}
-
-pub fn defaultWasiCwd() std.os.wasi.fd_t {
- // Expect the first preopen to be current working directory.
- return 3;
-}
diff --git a/lib/std/os/emscripten.zig b/lib/std/os/emscripten.zig
index cb444b360dd00e0375a6e02e0d95936fce90b2b5..a7fb141ed41b8353e72dd3032fd6486ce9e22ead 100644
--- a/lib/std/os/emscripten.zig
+++ b/lib/std/os/emscripten.zig
@@ -224,14 +224,14 @@ pub const W = struct {
pub fn EXITSTATUS(s: u32) u8 {
return @as(u8, @intCast((s & 0xff00) >> 8));
}
- pub fn TERMSIG(s: u32) u32 {
- return s & 0x7f;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn STOPSIG(s: u32) u32 {
return EXITSTATUS(s);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFSTOPPED(s: u32) bool {
return @as(u16, @truncate(((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00;
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index 174ef64d95f8f2e79c27aebba1055ac4457e74a0..a1626113ace96ba6df88f0f861e731371d041822 100644
--- a/lib/std/os/linux.zig
+++ b/lib/std/os/linux.zig
@@ -1598,8 +1598,15 @@ pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize {
);
}
-pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize {
- return syscall5(.waitid, @intFromEnum(id_type), @as(usize, @bitCast(@as(isize, id))), @intFromPtr(infop), flags, 0);
+pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32, usage: ?*rusage) usize {
+ return syscall5(
+ .waitid,
+ @intFromEnum(id_type),
+ @as(usize, @bitCast(@as(isize, id))),
+ @intFromPtr(infop),
+ flags,
+ @intFromPtr(usage),
+ );
}
pub const F = struct {
@@ -3616,14 +3623,14 @@ pub const W = struct {
pub fn EXITSTATUS(s: u32) u8 {
return @as(u8, @intCast((s & 0xff00) >> 8));
}
- pub fn TERMSIG(s: u32) u32 {
- return s & 0x7f;
+ pub fn TERMSIG(s: u32) SIG {
+ return @enumFromInt(s & 0x7f);
}
pub fn STOPSIG(s: u32) u32 {
return EXITSTATUS(s);
}
pub fn IFEXITED(s: u32) bool {
- return TERMSIG(s) == 0;
+ return (s & 0x7f) == 0;
}
pub fn IFSTOPPED(s: u32) bool {
return @as(u16, @truncate(((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00;
@@ -6205,6 +6212,16 @@ const siginfo_fields_union = extern union {
},
};
+pub const CLD = enum(i32) {
+ EXITED = 1,
+ KILLED = 2,
+ DUMPED = 3,
+ TRAPPED = 4,
+ STOPPED = 5,
+ CONTINUED = 6,
+ _,
+};
+
pub const siginfo_t = if (is_mips)
extern struct {
signo: SIG,
diff --git a/lib/std/os/linux/IoUring/test.zig b/lib/std/os/linux/IoUring/test.zig
index 251ba14a8b1a04875b39614bd687f46b81f30f29..899a6dae6402f0e38598d85a6d38868c05d4ba83 100644
--- a/lib/std/os/linux/IoUring/test.zig
+++ b/lib/std/os/linux/IoUring/test.zig
@@ -280,7 +280,7 @@ test "splice/read" {
var buffer_read = [_]u8{98} ** 20;
try file_src.writeStreamingAll(io, &buffer_write);
- const fds = try posix.pipe();
+ const fds = try std.Io.Threaded.pipe2(.{});
const pipe_offset: u64 = std.math.maxInt(u64);
const sqe_splice_to_pipe = try ring.splice(0x11111111, fd_src, 0, fds[1], pipe_offset, buffer_write.len);
diff --git a/lib/std/os/linux/syscalls.zig b/lib/std/os/linux/syscalls.zig
index 0eec4dafee8fa9f0dda9ebb06033481c6a5f7cff..c111f518b6d617d4874f847bc883715b4a7a8344 100644
--- a/lib/std/os/linux/syscalls.zig
+++ b/lib/std/os/linux/syscalls.zig
@@ -1,5 +1,4 @@
-// This file is automatically generated, DO NOT edit it manually.
-// See tools/generate_linux_syscalls.zig for more info.
+// This file is automatically generated by tools/generate_linux_syscalls.zig
// This list current as of kernel: 6.18.2
pub const X86 = enum(usize) {
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index b47533a3ec9b24738d421146e79ecb238a91dfd7..c041293e2f8f7ad1d3233aa71f7b52067223e63d 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -2927,7 +2927,7 @@ pub fn CreateSymbolicLink(
// Already an NT path, no need to do anything to it
break :target_path target_path;
} else {
- switch (getWin32PathType(u16, target_path)) {
+ switch (std.fs.path.getWin32PathType(u16, target_path)) {
// Rooted paths need to avoid getting put through wToPrefixedFileW
// (and they are treated as relative in this context)
// Note: It seems that rooted paths in symbolic links are relative to
@@ -3756,17 +3756,6 @@ pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) Ge
return buf_ptr[0..rc :0];
}
-pub const TerminateProcessError = error{ AccessDenied, Unexpected };
-
-pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void {
- if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) {
- switch (GetLastError()) {
- Win32Error.ACCESS_DENIED => return error.AccessDenied,
- else => |err| return unexpectedError(err),
- }
- }
-}
-
pub const NtAllocateVirtualMemoryError = error{
AccessDenied,
InvalidParameter,
@@ -3919,7 +3908,7 @@ pub fn CreateProcessW(
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: CreateProcessFlags,
- lpEnvironment: ?*anyopaque,
+ lpEnvironment: ?[*:0]u16,
lpCurrentDirectory: ?LPCWSTR,
lpStartupInfo: *STARTUPINFOW,
lpProcessInformation: *PROCESS_INFORMATION,
@@ -4235,7 +4224,7 @@ pub const RemoveDotDirsError = error{TooManyParentDirs};
/// 2) all repeating back slashes have been collapsed
/// 3) the path is a relative one (does not start with a back slash)
pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize {
- std.debug.assert(path.len == 0 or path[0] != '\\');
+ assert(path.len == 0 or path[0] != '\\');
var write_idx: usize = 0;
var read_idx: usize = 0;
@@ -4251,7 +4240,7 @@ pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!us
}
if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) {
if (write_idx == 0) return error.TooManyParentDirs;
- std.debug.assert(write_idx >= 2);
+ assert(write_idx >= 2);
write_idx -= 1;
while (true) {
write_idx -= 1;
@@ -4353,7 +4342,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE
path_space.data[path_space.len] = 0;
return path_space;
} else {
- const path_type = getWin32PathType(u16, path);
+ const path_type = std.fs.path.getWin32PathType(u16, path);
var path_space: PathSpace = undefined;
if (path_type == .local_device) {
switch (getLocalDevicePathType(u16, path)) {
@@ -4491,8 +4480,8 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE
if (path_type == .unc_absolute) {
// Now add in the UNC, the `C` should overwrite the first `\` of the
// FullPathName, ultimately resulting in `\??\UNC\`
- std.debug.assert(path_space.data[path_buf_offset] == '\\');
- std.debug.assert(path_space.data[path_buf_offset + 1] == '\\');
+ assert(path_space.data[path_buf_offset] == '\\');
+ assert(path_space.data[path_buf_offset + 1] == '\\');
const unc = [_]u16{ 'U', 'N', 'C' };
path_space.data[nt_prefix.len..][0..unc.len].* = unc;
}
@@ -4500,127 +4489,6 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE
}
}
-/// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type.
-pub const Win32PathType = enum {
- /// `\\server\share\foo`
- unc_absolute,
- /// `C:\foo`
- drive_absolute,
- /// `C:foo`
- drive_relative,
- /// `\foo`
- rooted,
- /// `foo`
- relative,
- /// `\\.\foo`, `\\?\foo`
- local_device,
- /// `\\.`, `\\?`
- root_local_device,
-};
-
-/// Get the path type of a Win32 namespace path.
-/// Similar to `RtlDetermineDosPathNameType_U`.
-/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
-pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
- if (path.len < 1) return .relative;
-
- const windows_path = std.fs.path.PathType.windows;
- if (windows_path.isSep(T, path[0])) {
- // \x
- if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted;
- // \\. or \\?
- if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) {
- // exactly \\. or \\? with nothing trailing
- if (path.len == 3) return .root_local_device;
- // \\.\x or \\?\x
- if (windows_path.isSep(T, path[3])) return .local_device;
- }
- // \\x
- return .unc_absolute;
- } else {
- // Some choice has to be made about how non-ASCII code points as drive-letters are handled, since
- // path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification
- // for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code
- // units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so
- // checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16.
- //
- // `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers
- // `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get
- // classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded
- // in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path).
- //
- // The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both
- // WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI,
- // drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you
- // to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will
- // allow you to set any WTF-16 code unit as a drive letter.
- //
- // Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g.
- // `cd /D €:\` will work, filesystem functions still work, etc.
- //
- // The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't
- // just check path[0], path[1], path[2].
- const colon_i: usize = switch (T) {
- u8 => i: {
- const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative;
- // Conveniently, 4-byte sequences in WTF-8 have the same starting code point
- // as 2-code-unit sequences in WTF-16.
- if (code_point_len > 3) return .relative;
- break :i code_point_len;
- },
- u16 => 1,
- else => @compileError("unsupported type: " ++ @typeName(T)),
- };
- // x
- if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative;
- // x:\
- if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute;
- // x:
- return .drive_relative;
- }
-}
-
-test getWin32PathType {
- try std.testing.expectEqual(.relative, getWin32PathType(u8, ""));
- try std.testing.expectEqual(.relative, getWin32PathType(u8, "x"));
- try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\"));
-
- try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//."));
- try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?"));
- try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?"));
-
- try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x"));
- try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x"));
- try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x"));
- // local device paths require a path separator after the root, otherwise it is considered a UNC path
- try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x"));
- try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x"));
-
- try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//"));
- try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x"));
- try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x"));
-
- try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x"));
- try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/"));
-
- try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:"));
- try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc"));
- try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c"));
-
- try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\"));
- try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc"));
- try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c"));
-
- // Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter
- try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\"));
- try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\")));
- try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:"));
- try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:")));
- // But code points that are encoded as two WTF-16 code units are not
- try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\"));
- try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\")));
-}
-
/// Returns true if the path starts with `\??\`, which is indicative of an NT path
/// but is not enough to fully distinguish between NT paths and Win32 paths, as
/// `\??\` is not actually a distinct prefix but rather the path to a special virtual
@@ -4660,10 +4528,10 @@ const LocalDevicePathType = enum {
};
/// Only relevant for Win32 -> NT path conversion.
-/// Asserts `path` is of type `Win32PathType.local_device`.
+/// Asserts `path` is of type `std.fs.path.Win32PathType.local_device`.
fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType {
if (std.debug.runtime_safety) {
- std.debug.assert(getWin32PathType(T, path) == .local_device);
+ assert(std.fs.path.getWin32PathType(T, path) == .local_device);
}
const backslash = mem.nativeToLittle(T, '\\');
diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig
index 81cae8fac897aafb2ca9bdf97bd00f87088a97d7..1ef3d0e55ec6985da80d57881f0a48feeb2dd0c3 100644
--- a/lib/std/os/windows/kernel32.zig
+++ b/lib/std/os/windows/kernel32.zig
@@ -265,7 +265,7 @@ pub extern "kernel32" fn CreateProcessW(
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: windows.CreateProcessFlags,
- lpEnvironment: ?LPVOID,
+ lpEnvironment: ?[*:0]const u16,
lpCurrentDirectory: ?LPCWSTR,
lpStartupInfo: *STARTUPINFOW,
lpProcessInformation: *PROCESS_INFORMATION,
diff --git a/lib/std/os/windows/test.zig b/lib/std/os/windows/test.zig
index 0245583f5ba753d1885223e1c93ecc80f4982019..58eb4e0b97645bf59699d0b3939a4deb1bda0d73 100644
--- a/lib/std/os/windows/test.zig
+++ b/lib/std/os/windows/test.zig
@@ -274,8 +274,8 @@ test "getWin32PathType vs RtlDetermineDosPathNameType_U" {
std.debug.assert(std.unicode.wtf16LeToWtf8(wtf8_buf.items, path) == wtf8_len);
const windows_type = RtlDetermineDosPathNameType_U(path);
- const wtf16_type = windows.getWin32PathType(u16, path);
- const wtf8_type = windows.getWin32PathType(u8, wtf8_buf.items);
+ const wtf16_type = std.fs.path.getWin32PathType(u16, path);
+ const wtf8_type = std.fs.path.getWin32PathType(u8, wtf8_buf.items);
checkPathType(windows_type, wtf16_type) catch |err| {
std.debug.print("expected type {}, got {} for path: {f}\n", .{ windows_type, wtf16_type, std.unicode.fmtUtf16Le(path) });
@@ -295,7 +295,7 @@ test "getWin32PathType vs RtlDetermineDosPathNameType_U" {
}
}
-fn checkPathType(windows_type: RTL_PATH_TYPE, zig_type: windows.Win32PathType) !void {
+fn checkPathType(windows_type: RTL_PATH_TYPE, zig_type: std.fs.path.Win32PathType) !void {
const expected_windows_type: RTL_PATH_TYPE = switch (zig_type) {
.unc_absolute => .UncAbsolute,
.drive_absolute => .DriveAbsolute,
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
index 8bcf7e82ee13d140d322d4667b8d743a40d39524..736dcf824f50a4b89ab63d15079702725828f828 100644
--- a/lib/std/posix.zig
+++ b/lib/std/posix.zig
@@ -772,214 +772,10 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O
}
}
-pub fn dup(old_fd: fd_t) !fd_t {
- const rc = system.dup(old_fd);
- return switch (errno(rc)) {
- .SUCCESS => return @intCast(rc),
- .MFILE => error.ProcessFdQuotaExceeded,
- .BADF => unreachable, // invalid file descriptor
- else => |err| return unexpectedErrno(err),
- };
-}
-
-pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
- while (true) {
- switch (errno(system.dup2(old_fd, new_fd))) {
- .SUCCESS => return,
- .BUSY, .INTR => continue,
- .MFILE => return error.ProcessFdQuotaExceeded,
- .INVAL => unreachable, // invalid parameters passed to dup2
- .BADF => unreachable, // invalid file descriptor
- else => |err| return unexpectedErrno(err),
- }
- }
-}
-
-pub fn getpid() pid_t {
- return system.getpid();
-}
-
pub fn getppid() pid_t {
return system.getppid();
}
-pub const ExecveError = error{
- SystemResources,
- AccessDenied,
- PermissionDenied,
- InvalidExe,
- FileSystem,
- IsDir,
- FileNotFound,
- NotDir,
- FileBusy,
- ProcessFdQuotaExceeded,
- SystemFdQuotaExceeded,
- NameTooLong,
-} || UnexpectedError;
-
-/// This function ignores PATH environment variable. See `execvpeZ` for that.
-pub fn execveZ(
- path: [*:0]const u8,
- child_argv: [*:null]const ?[*:0]const u8,
- envp: [*:null]const ?[*:0]const u8,
-) ExecveError {
- switch (errno(system.execve(path, child_argv, envp))) {
- .SUCCESS => unreachable,
- .FAULT => unreachable,
- .@"2BIG" => return error.SystemResources,
- .MFILE => return error.ProcessFdQuotaExceeded,
- .NAMETOOLONG => return error.NameTooLong,
- .NFILE => return error.SystemFdQuotaExceeded,
- .NOMEM => return error.SystemResources,
- .ACCES => return error.AccessDenied,
- .PERM => return error.PermissionDenied,
- .INVAL => return error.InvalidExe,
- .NOEXEC => return error.InvalidExe,
- .IO => return error.FileSystem,
- .LOOP => return error.FileSystem,
- .ISDIR => return error.IsDir,
- .NOENT => return error.FileNotFound,
- .NOTDIR => return error.NotDir,
- .TXTBSY => return error.FileBusy,
- else => |err| switch (native_os) {
- .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (err) {
- .BADEXEC => return error.InvalidExe,
- .BADARCH => return error.InvalidExe,
- else => return unexpectedErrno(err),
- },
- .linux => switch (err) {
- .LIBBAD => return error.InvalidExe,
- else => return unexpectedErrno(err),
- },
- else => return unexpectedErrno(err),
- },
- }
-}
-
-pub const Arg0Expand = enum {
- expand,
- no_expand,
-};
-
-/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
-/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
-/// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in.
-pub fn execvpeZ_expandArg0(
- comptime arg0_expand: Arg0Expand,
- file: [*:0]const u8,
- child_argv: switch (arg0_expand) {
- .expand => [*:null]?[*:0]const u8,
- .no_expand => [*:null]const ?[*:0]const u8,
- },
- envp: [*:null]const ?[*:0]const u8,
-) ExecveError {
- const file_slice = mem.sliceTo(file, 0);
- if (mem.findScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
-
- const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
- // Use of PATH_MAX here is valid as the path_buf will be passed
- // directly to the operating system in execveZ.
- var path_buf: [PATH_MAX]u8 = undefined;
- var it = mem.tokenizeScalar(u8, PATH, ':');
- var seen_eacces = false;
- var err: ExecveError = error.FileNotFound;
-
- // In case of expanding arg0 we must put it back if we return with an error.
- const prev_arg0 = child_argv[0];
- defer switch (arg0_expand) {
- .expand => child_argv[0] = prev_arg0,
- .no_expand => {},
- };
-
- while (it.next()) |search_path| {
- const path_len = search_path.len + file_slice.len + 1;
- if (path_buf.len < path_len + 1) return error.NameTooLong;
- @memcpy(path_buf[0..search_path.len], search_path);
- path_buf[search_path.len] = '/';
- @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice);
- path_buf[path_len] = 0;
- const full_path = path_buf[0..path_len :0].ptr;
- switch (arg0_expand) {
- .expand => child_argv[0] = full_path,
- .no_expand => {},
- }
- err = execveZ(full_path, child_argv, envp);
- switch (err) {
- error.AccessDenied => seen_eacces = true,
- error.FileNotFound, error.NotDir => {},
- else => |e| return e,
- }
- }
- if (seen_eacces) return error.AccessDenied;
- return err;
-}
-
-/// This function also uses the PATH environment variable to get the full path to the executable.
-/// If `file` is an absolute path, this is the same as `execveZ`.
-pub fn execvpeZ(
- file: [*:0]const u8,
- argv_ptr: [*:null]const ?[*:0]const u8,
- envp: [*:null]const ?[*:0]const u8,
-) ExecveError {
- return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp);
-}
-
-/// Get an environment variable.
-/// See also `getenvZ`.
-pub fn getenv(key: []const u8) ?[:0]const u8 {
- if (native_os == .windows) {
- @compileError("std.posix.getenv is unavailable for Windows because environment strings are in WTF-16 format. See std.process.getEnvVarOwned for a cross-platform API or std.process.getenvW for a Windows-specific API.");
- }
- if (mem.findScalar(u8, key, '=') != null) {
- return null;
- }
- if (builtin.link_libc) {
- var ptr = std.c.environ;
- while (ptr[0]) |line| : (ptr += 1) {
- var line_i: usize = 0;
- while (line[line_i] != 0) : (line_i += 1) {
- if (line_i == key.len) break;
- if (line[line_i] != key[line_i]) break;
- }
- if ((line_i != key.len) or (line[line_i] != '=')) continue;
-
- return mem.sliceTo(line + line_i + 1, 0);
- }
- return null;
- }
- if (native_os == .wasi) {
- @compileError("std.posix.getenv is unavailable for WASI. See std.process.getEnvMap or std.process.getEnvVarOwned for a cross-platform API.");
- }
- // The simplified start logic doesn't populate environ.
- if (std.start.simplified_logic) return null;
- // TODO see https://github.com/ziglang/zig/issues/4524
- for (std.os.environ) |ptr| {
- var line_i: usize = 0;
- while (ptr[line_i] != 0) : (line_i += 1) {
- if (line_i == key.len) break;
- if (ptr[line_i] != key[line_i]) break;
- }
- if ((line_i != key.len) or (ptr[line_i] != '=')) continue;
-
- return mem.sliceTo(ptr + line_i + 1, 0);
- }
- return null;
-}
-
-/// Get an environment variable with a null-terminated name.
-/// See also `getenv`.
-pub fn getenvZ(key: [*:0]const u8) ?[:0]const u8 {
- if (builtin.link_libc) {
- const value = system.getenv(key) orelse return null;
- return mem.sliceTo(value, 0);
- }
- if (native_os == .windows) {
- @compileError("std.posix.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.process.getenvW for Windows-specific API.");
- }
- return getenv(mem.sliceTo(key, 0));
-}
-
pub const GetCwdError = error{
NameTooLong,
CurrentWorkingDirectoryUnlinked,
@@ -1013,198 +809,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
}
}
-/// On Windows, `sub_dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `sub_dir_path` should be encoded as valid UTF-8.
-/// On other platforms, `sub_dir_path` is an opaque sequence of bytes with no particular encoding.
-pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirError!void {
- if (native_os == .windows) {
- @compileError("use std.Io instead");
- } else if (native_os == .wasi and !builtin.link_libc) {
- @compileError("use std.Io instead");
- } else {
- const sub_dir_path_c = try toPosixPath(sub_dir_path);
- return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
- }
-}
-
-/// Same as `mkdirat` except the parameters are null-terminated.
-pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void {
- if (native_os == .windows) {
- @compileError("use std.Io instead");
- } else if (native_os == .wasi and !builtin.link_libc) {
- @compileError("use std.Io instead");
- }
- switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
- .SUCCESS => return,
- .ACCES => return error.AccessDenied,
- .BADF => unreachable,
- .PERM => return error.PermissionDenied,
- .DQUOT => return error.DiskQuota,
- .EXIST => return error.PathAlreadyExists,
- .FAULT => unreachable,
- .LOOP => return error.SymLinkLoop,
- .MLINK => return error.LinkQuotaExceeded,
- .NAMETOOLONG => return error.NameTooLong,
- .NOENT => return error.FileNotFound,
- .NOMEM => return error.SystemResources,
- .NOSPC => return error.NoSpaceLeft,
- .NOTDIR => return error.NotDir,
- .ROFS => return error.ReadOnlyFileSystem,
- // dragonfly: when dir_fd is unlinked from filesystem
- .NOTCONN => return error.FileNotFound,
- .ILSEQ => return error.BadPathName,
- else => |err| return unexpectedErrno(err),
- }
-}
-
-pub const MakeDirError = std.Io.Dir.CreateDirError;
-
-/// Create a directory.
-/// `mode` is ignored on Windows and WASI.
-/// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `dir_path` should be encoded as valid UTF-8.
-/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
-pub fn mkdir(dir_path: []const u8, mode: mode_t) MakeDirError!void {
- if (native_os == .wasi and !builtin.link_libc) {
- return mkdirat(AT.FDCWD, dir_path, mode);
- } else if (native_os == .windows) {
- const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
- return mkdirW(dir_path_w.span(), mode);
- } else {
- const dir_path_c = try toPosixPath(dir_path);
- return mkdirZ(&dir_path_c, mode);
- }
-}
-
-/// Same as `mkdir` but the parameter is null-terminated.
-/// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `dir_path` should be encoded as valid UTF-8.
-/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
-pub fn mkdirZ(dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void {
- if (native_os == .windows) {
- const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
- return mkdirW(dir_path_w.span(), mode);
- } else if (native_os == .wasi and !builtin.link_libc) {
- return mkdir(mem.sliceTo(dir_path, 0), mode);
- }
- switch (errno(system.mkdir(dir_path, mode))) {
- .SUCCESS => return,
- .ACCES => return error.AccessDenied,
- .PERM => return error.PermissionDenied,
- .DQUOT => return error.DiskQuota,
- .EXIST => return error.PathAlreadyExists,
- .FAULT => unreachable,
- .LOOP => return error.SymLinkLoop,
- .MLINK => return error.LinkQuotaExceeded,
- .NAMETOOLONG => return error.NameTooLong,
- .NOENT => return error.FileNotFound,
- .NOMEM => return error.SystemResources,
- .NOSPC => return error.NoSpaceLeft,
- .NOTDIR => return error.NotDir,
- .ROFS => return error.ReadOnlyFileSystem,
- .ILSEQ => return error.BadPathName,
- else => |err| return unexpectedErrno(err),
- }
-}
-
-/// Windows-only. Same as `mkdir` but the parameters is WTF16LE encoded.
-pub fn mkdirW(dir_path_w: []const u16, mode: mode_t) MakeDirError!void {
- _ = mode;
- const sub_dir_handle = windows.OpenFile(dir_path_w, .{
- .dir = Io.Dir.cwd().handle,
- .access_mask = .{
- .STANDARD = .{ .SYNCHRONIZE = true },
- .GENERIC = .{ .READ = true },
- },
- .creation = .CREATE,
- .filter = .dir_only,
- }) catch |err| switch (err) {
- error.IsDir => return error.Unexpected,
- error.PipeBusy => return error.Unexpected,
- error.NoDevice => return error.Unexpected,
- error.WouldBlock => return error.Unexpected,
- error.AntivirusInterference => return error.Unexpected,
- error.OperationCanceled => return error.Unexpected,
- else => |e| return e,
- };
- windows.CloseHandle(sub_dir_handle);
-}
-
-pub const ChangeCurDirError = error{
- AccessDenied,
- FileSystem,
- SymLinkLoop,
- NameTooLong,
- FileNotFound,
- SystemResources,
- NotDir,
- /// WASI: file paths must be valid UTF-8.
- /// Windows: file paths provided by the user must be valid WTF-8.
- /// https://wtf-8.codeberg.page/
- BadPathName,
-} || UnexpectedError;
-
-/// Changes the current working directory of the calling process.
-/// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `dir_path` should be encoded as valid UTF-8.
-/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
-pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
- if (native_os == .wasi and !builtin.link_libc) {
- @compileError("unsupported OS");
- } else if (native_os == .windows) {
- @compileError("unsupported OS");
- } else {
- const dir_path_c = try toPosixPath(dir_path);
- return chdirZ(&dir_path_c);
- }
-}
-
-/// Same as `chdir` except the parameter is null-terminated.
-/// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `dir_path` should be encoded as valid UTF-8.
-/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
-pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
- if (native_os == .windows) {
- @compileError("unsupported OS");
- } else if (native_os == .wasi and !builtin.link_libc) {
- @compileError("unsupported OS");
- }
- switch (errno(system.chdir(dir_path))) {
- .SUCCESS => return,
- .ACCES => return error.AccessDenied,
- .FAULT => unreachable,
- .IO => return error.FileSystem,
- .LOOP => return error.SymLinkLoop,
- .NAMETOOLONG => return error.NameTooLong,
- .NOENT => return error.FileNotFound,
- .NOMEM => return error.SystemResources,
- .NOTDIR => return error.NotDir,
- .ILSEQ => return error.BadPathName,
- else => |err| return unexpectedErrno(err),
- }
-}
-
-pub const FchdirError = error{
- AccessDenied,
- NotDir,
- FileSystem,
-} || UnexpectedError;
-
-pub fn fchdir(dirfd: fd_t) FchdirError!void {
- if (dirfd == AT.FDCWD) return;
- while (true) {
- switch (errno(system.fchdir(dirfd))) {
- .SUCCESS => return,
- .ACCES => return error.AccessDenied,
- .BADF => unreachable,
- .NOTDIR => return error.NotDir,
- .INTR => continue,
- .IO => return error.FileSystem,
- else => |err| return unexpectedErrno(err),
- }
- }
-}
-
pub const SetEidError = error{
InvalidUserId,
PermissionDenied,
@@ -1231,16 +835,6 @@ pub fn seteuid(uid: uid_t) SetEidError!void {
}
}
-pub fn setreuid(ruid: uid_t, euid: uid_t) SetIdError!void {
- switch (errno(system.setreuid(ruid, euid))) {
- .SUCCESS => return,
- .AGAIN => return error.ResourceLimitReached,
- .INVAL => return error.InvalidUserId,
- .PERM => return error.PermissionDenied,
- else => |err| return unexpectedErrno(err),
- }
-}
-
pub fn setgid(gid: gid_t) SetIdError!void {
switch (errno(system.setgid(gid))) {
.SUCCESS => return,
@@ -1260,34 +854,6 @@ pub fn setegid(uid: uid_t) SetEidError!void {
}
}
-pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void {
- switch (errno(system.setregid(rgid, egid))) {
- .SUCCESS => return,
- .AGAIN => return error.ResourceLimitReached,
- .INVAL => return error.InvalidUserId,
- .PERM => return error.PermissionDenied,
- else => |err| return unexpectedErrno(err),
- }
-}
-
-pub const SetPgidError = error{
- ProcessAlreadyExec,
- InvalidProcessGroupId,
- PermissionDenied,
- ProcessNotFound,
-} || UnexpectedError;
-
-pub fn setpgid(pid: pid_t, pgid: pid_t) SetPgidError!void {
- switch (errno(system.setpgid(pid, pgid))) {
- .SUCCESS => return,
- .ACCES => return error.ProcessAlreadyExec,
- .INVAL => return error.InvalidProcessGroupId,
- .PERM => return error.PermissionDenied,
- .SRCH => return error.ProcessNotFound,
- else => |err| return unexpectedErrno(err),
- }
-}
-
pub fn getuid() uid_t {
return system.getuid();
}
@@ -1899,53 +1465,12 @@ pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
}
}
-pub const WaitPidResult = struct {
- pid: pid_t,
- status: u32,
-};
-
-/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit
-/// `fork` and `execve` method.
-pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
- var status: if (builtin.link_libc) c_int else u32 = undefined;
- while (true) {
- const rc = system.waitpid(pid, &status, @intCast(flags));
- switch (errno(rc)) {
- .SUCCESS => return .{
- .pid = @intCast(rc),
- .status = @bitCast(status),
- },
- .INTR => continue,
- .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
- .INVAL => unreachable, // Invalid flags.
- else => unreachable,
- }
- }
-}
-
-pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
- var status: if (builtin.link_libc) c_int else u32 = undefined;
- while (true) {
- const rc = system.wait4(pid, &status, @intCast(flags), ru);
- switch (errno(rc)) {
- .SUCCESS => return .{
- .pid = @intCast(rc),
- .status = @bitCast(status),
- },
- .INTR => continue,
- .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
- .INVAL => unreachable, // Invalid flags.
- else => unreachable,
- }
- }
-}
-
pub const FStatError = std.Io.File.StatError;
/// Return information about a file descriptor.
pub fn fstat(fd: fd_t) FStatError!Stat {
if (native_os == .wasi and !builtin.link_libc) {
- return Stat.fromFilestat(try std.os.fstat_wasi(fd));
+ @compileError("unsupported OS");
}
var stat = mem.zeroes(Stat);
@@ -2468,80 +1993,6 @@ pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void {
}
}
-pub const PipeError = error{
- SystemFdQuotaExceeded,
- ProcessFdQuotaExceeded,
-} || UnexpectedError;
-
-/// Creates a unidirectional data channel that can be used for interprocess communication.
-pub fn pipe() PipeError![2]fd_t {
- var fds: [2]fd_t = undefined;
- switch (errno(system.pipe(&fds))) {
- .SUCCESS => return fds,
- .INVAL => unreachable, // Invalid parameters to pipe()
- .FAULT => unreachable, // Invalid fds pointer
- .NFILE => return error.SystemFdQuotaExceeded,
- .MFILE => return error.ProcessFdQuotaExceeded,
- else => |err| return unexpectedErrno(err),
- }
-}
-
-pub fn pipe2(flags: O) PipeError![2]fd_t {
- if (@TypeOf(system.pipe2) != void) {
- var fds: [2]fd_t = undefined;
- switch (errno(system.pipe2(&fds, flags))) {
- .SUCCESS => return fds,
- .INVAL => unreachable, // Invalid flags
- .FAULT => unreachable, // Invalid fds pointer
- .NFILE => return error.SystemFdQuotaExceeded,
- .MFILE => return error.ProcessFdQuotaExceeded,
- else => |err| return unexpectedErrno(err),
- }
- }
-
- const fds: [2]fd_t = try pipe();
- errdefer {
- close(fds[0]);
- close(fds[1]);
- }
-
- // https://github.com/ziglang/zig/issues/18882
- if (@as(u32, @bitCast(flags)) == 0)
- return fds;
-
- // CLOEXEC is special, it's a file descriptor flag and must be set using
- // F.SETFD.
- if (flags.CLOEXEC) {
- for (fds) |fd| {
- switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) {
- .SUCCESS => {},
- .INVAL => unreachable, // Invalid flags
- .BADF => unreachable, // Always a race condition
- else => |err| return unexpectedErrno(err),
- }
- }
- }
-
- const new_flags: u32 = f: {
- var new_flags = flags;
- new_flags.CLOEXEC = false;
- break :f @bitCast(new_flags);
- };
- // Set every other flag affecting the file status using F.SETFL.
- if (new_flags != 0) {
- for (fds) |fd| {
- switch (errno(system.fcntl(fd, F.SETFL, new_flags))) {
- .SUCCESS => {},
- .INVAL => unreachable, // Invalid flags
- .BADF => unreachable, // Always a race condition
- else => |err| return unexpectedErrno(err),
- }
- }
- }
-
- return fds;
-}
-
pub const SysCtlError = error{
PermissionDenied,
SystemResources,
diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig
index 6fdb980b1bb93ab2db7d1a404cb996c7cf3c24ac..905b3438e4636898fb9b6e4db795346d27901d04 100644
--- a/lib/std/posix/test.zig
+++ b/lib/std/posix/test.zig
@@ -22,10 +22,10 @@ const tmpDir = std.testing.tmpDir;
test "check WASI CWD" {
if (native_os == .wasi) {
- if (std.options.wasiCwd() != 3) {
+ const cwd: Dir = .cwd();
+ if (cwd.handle != 3) {
@panic("WASI code that uses cwd (like this test) needs a preopen for cwd (add '--dir=.' to wasmtime)");
}
-
if (!builtin.link_libc) {
// WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls
try expectEqual(3, posix.AT.FDCWD);
@@ -131,7 +131,7 @@ test "pipe" {
if (native_os == .windows or native_os == .wasi)
return error.SkipZigTest;
- const fds = try posix.pipe();
+ const fds = try std.Io.Threaded.pipe2(.{});
try expect((try posix.write(fds[1], "hello")) == 5);
var buf: [16]u8 = undefined;
try expect((try posix.read(fds[0], buf[0..])) == 5);
@@ -140,11 +140,6 @@ test "pipe" {
posix.close(fds[0]);
}
-test "argsAlloc" {
- const args = try std.process.argsAlloc(std.testing.allocator);
- std.process.argsFree(std.testing.allocator, args);
-}
-
test "memfd_create" {
const io = testing.io;
@@ -438,42 +433,11 @@ test "sigset add/del" {
}
}
-test "dup & dup2" {
- switch (native_os) {
- .linux, .illumos => {},
- else => return error.SkipZigTest,
- }
-
- const io = testing.io;
-
- var tmp = tmpDir(.{});
- defer tmp.cleanup();
-
- {
- var file = try tmp.dir.createFile(io, "os_dup_test", .{});
- defer file.close(io);
-
- var duped = Io.File{ .handle = try posix.dup(file.handle) };
- defer duped.close(io);
- try duped.writeStreamingAll(io, "dup");
-
- // Tests aren't run in parallel so using the next fd shouldn't be an issue.
- const new_fd = duped.handle + 1;
- try posix.dup2(file.handle, new_fd);
- var dup2ed = Io.File{ .handle = new_fd };
- defer dup2ed.close(io);
- try dup2ed.writeStreamingAll(io, "dup2");
- }
-
- var buffer: [8]u8 = undefined;
- try expectEqualStrings("dupdup2", try tmp.dir.readFile(io, "os_dup_test", &buffer));
-}
-
test "getpid" {
if (native_os == .wasi) return error.SkipZigTest;
if (native_os == .windows) return error.SkipZigTest;
- try expect(posix.getpid() != 0);
+ try expect(posix.system.getpid() != 0);
}
test "getppid" {
@@ -532,7 +496,7 @@ test "rename smoke test" {
// Create some directory
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
defer gpa.free(file_path);
- try posix.mkdir(file_path, mode);
+ try Io.Dir.createDirAbsolute(io, file_path, .fromMode(mode));
// Rename the directory
const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
diff --git a/lib/std/process.zig b/lib/std/process.zig
index 865376d907a71a9ff0ce2e1cb0ab1d91c4ce8121..d8db82cc0582821c4c2b9a6243ca32149905491a 100644
--- a/lib/std/process.zig
+++ b/lib/std/process.zig
@@ -16,10 +16,8 @@ const unicode = std.unicode;
const max_path_bytes = std.fs.max_path_bytes;
pub const Child = @import("process/Child.zig");
-pub const changeCurDir = posix.chdir;
-pub const changeCurDirZ = posix.chdirZ;
-
-pub const GetCwdError = posix.GetCwdError;
+pub const Args = @import("process/Args.zig");
+pub const Environ = @import("process/Environ.zig");
/// This is the global, process-wide protection to coordinate stderr writes.
///
@@ -28,6 +26,40 @@ pub const GetCwdError = posix.GetCwdError;
/// information.
pub var stderr_thread_mutex: std.Thread.Mutex.Recursive = .init;
+/// A standard set of pre-initialized useful APIs for programs to take
+/// advantage of. This is the type of the first parameter of the main function.
+/// Applications wanting more flexibility can accept `Init.Minimal` instead.
+///
+/// Completion of https://github.com/ziglang/zig/issues/24510 will also allow
+/// the second parameter of the main function to be a custom struct that
+/// contain auto-parsed CLI arguments.
+pub const Init = struct {
+ /// `Init` is a superset of `Minimal`; the latter is included here.
+ minimal: Minimal,
+ /// Permanent storage for the entire process, cleaned automatically on
+ /// exit. Not threadsafe.
+ arena: *std.heap.ArenaAllocator,
+ /// A default-selected general purpose allocator for temporary heap
+ /// allocations. Debug mode will set up leak checking if possible.
+ /// Threadsafe.
+ gpa: Allocator,
+ /// An appropriate default Io implementation based on the target
+ /// configuration. Debug mode will set up leak checking if possible.
+ io: Io,
+ /// Environment variables, initialized with `gpa`. Not threadsafe.
+ environ_map: *Environ.Map,
+
+ /// Alternative to `Init` as the first parameter of the main function.
+ pub const Minimal = struct {
+ /// Environment variables.
+ environ: Environ,
+ /// Command line arguments.
+ args: Args,
+ };
+};
+
+pub const GetCwdError = posix.GetCwdError;
+
/// The result is a slice of `out_buffer`, from index `0`.
/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
@@ -73,1484 +105,6 @@ test getCwdAlloc {
testing.allocator.free(cwd);
}
-pub const EnvMap = struct {
- hash_map: HashMap,
-
- const HashMap = std.HashMap(
- []const u8,
- []const u8,
- EnvNameHashContext,
- std.hash_map.default_max_load_percentage,
- );
-
- pub const Size = HashMap.Size;
-
- pub const EnvNameHashContext = struct {
- fn upcase(c: u21) u21 {
- if (c <= std.math.maxInt(u16))
- return windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c)));
- return c;
- }
-
- pub fn hash(self: @This(), s: []const u8) u64 {
- _ = self;
- if (native_os == .windows) {
- var h = std.hash.Wyhash.init(0);
- var it = unicode.Wtf8View.initUnchecked(s).iterator();
- while (it.nextCodepoint()) |cp| {
- const cp_upper = upcase(cp);
- h.update(&[_]u8{
- @as(u8, @intCast((cp_upper >> 16) & 0xff)),
- @as(u8, @intCast((cp_upper >> 8) & 0xff)),
- @as(u8, @intCast((cp_upper >> 0) & 0xff)),
- });
- }
- return h.final();
- }
- return std.hash_map.hashString(s);
- }
-
- pub fn eql(self: @This(), a: []const u8, b: []const u8) bool {
- _ = self;
- if (native_os == .windows) {
- var it_a = unicode.Wtf8View.initUnchecked(a).iterator();
- var it_b = unicode.Wtf8View.initUnchecked(b).iterator();
- while (true) {
- const c_a = it_a.nextCodepoint() orelse break;
- const c_b = it_b.nextCodepoint() orelse return false;
- if (upcase(c_a) != upcase(c_b))
- return false;
- }
- return if (it_b.nextCodepoint()) |_| false else true;
- }
- return std.hash_map.eqlString(a, b);
- }
- };
-
- /// Create a EnvMap backed by a specific allocator.
- /// That allocator will be used for both backing allocations
- /// and string deduplication.
- pub fn init(allocator: Allocator) EnvMap {
- return EnvMap{ .hash_map = HashMap.init(allocator) };
- }
-
- /// Free the backing storage of the map, as well as all
- /// of the stored keys and values.
- pub fn deinit(self: *EnvMap) void {
- var it = self.hash_map.iterator();
- while (it.next()) |entry| {
- self.free(entry.key_ptr.*);
- self.free(entry.value_ptr.*);
- }
-
- self.hash_map.deinit();
- }
-
- /// Same as `put` but the key and value become owned by the EnvMap rather
- /// than being copied.
- /// If `putMove` fails, the ownership of key and value does not transfer.
- /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
- pub fn putMove(self: *EnvMap, key: []u8, value: []u8) !void {
- assert(unicode.wtf8ValidateSlice(key));
- const get_or_put = try self.hash_map.getOrPut(key);
- if (get_or_put.found_existing) {
- self.free(get_or_put.key_ptr.*);
- self.free(get_or_put.value_ptr.*);
- get_or_put.key_ptr.* = key;
- }
- get_or_put.value_ptr.* = value;
- }
-
- /// `key` and `value` are copied into the EnvMap.
- /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
- pub fn put(self: *EnvMap, key: []const u8, value: []const u8) !void {
- assert(unicode.wtf8ValidateSlice(key));
- const value_copy = try self.copy(value);
- errdefer self.free(value_copy);
- const get_or_put = try self.hash_map.getOrPut(key);
- if (get_or_put.found_existing) {
- self.free(get_or_put.value_ptr.*);
- } else {
- get_or_put.key_ptr.* = self.copy(key) catch |err| {
- _ = self.hash_map.remove(key);
- return err;
- };
- }
- get_or_put.value_ptr.* = value_copy;
- }
-
- /// Find the address of the value associated with a key.
- /// The returned pointer is invalidated if the map resizes.
- /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
- pub fn getPtr(self: EnvMap, key: []const u8) ?*[]const u8 {
- assert(unicode.wtf8ValidateSlice(key));
- return self.hash_map.getPtr(key);
- }
-
- /// Return the map's copy of the value associated with
- /// a key. The returned string is invalidated if this
- /// key is removed from the map.
- /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
- pub fn get(self: EnvMap, key: []const u8) ?[]const u8 {
- assert(unicode.wtf8ValidateSlice(key));
- return self.hash_map.get(key);
- }
-
- /// Removes the item from the map and frees its value.
- /// This invalidates the value returned by get() for this key.
- /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
- pub fn remove(self: *EnvMap, key: []const u8) void {
- assert(unicode.wtf8ValidateSlice(key));
- const kv = self.hash_map.fetchRemove(key) orelse return;
- self.free(kv.key);
- self.free(kv.value);
- }
-
- /// Returns the number of KV pairs stored in the map.
- pub fn count(self: EnvMap) HashMap.Size {
- return self.hash_map.count();
- }
-
- /// Returns an iterator over entries in the map.
- pub fn iterator(self: *const EnvMap) HashMap.Iterator {
- return self.hash_map.iterator();
- }
-
- /// Returns a full copy of `em` allocated with `gpa`, which is not necessarily
- /// the same allocator used to allocate `em`.
- pub fn clone(em: *const EnvMap, gpa: Allocator) Allocator.Error!EnvMap {
- var new: EnvMap = .init(gpa);
- errdefer new.deinit();
- // Since we need to dupe the keys and values, the only way for error handling to not be a
- // nightmare is to add keys to an empty map one-by-one. This could be avoided if this
- // abstraction were a bit less... OOP-esque.
- try new.hash_map.ensureUnusedCapacity(em.hash_map.count());
- var it = em.hash_map.iterator();
- while (it.next()) |entry| {
- try new.put(entry.key_ptr.*, entry.value_ptr.*);
- }
- return new;
- }
-
- fn free(self: EnvMap, value: []const u8) void {
- self.hash_map.allocator.free(value);
- }
-
- fn copy(self: EnvMap, value: []const u8) ![]u8 {
- return self.hash_map.allocator.dupe(u8, value);
- }
-};
-
-test EnvMap {
- var env = EnvMap.init(testing.allocator);
- defer env.deinit();
-
- try env.put("SOMETHING_NEW", "hello");
- try testing.expectEqualStrings("hello", env.get("SOMETHING_NEW").?);
- try testing.expectEqual(@as(EnvMap.Size, 1), env.count());
-
- // overwrite
- try env.put("SOMETHING_NEW", "something");
- try testing.expectEqualStrings("something", env.get("SOMETHING_NEW").?);
- try testing.expectEqual(@as(EnvMap.Size, 1), env.count());
-
- // a new longer name to test the Windows-specific conversion buffer
- try env.put("SOMETHING_NEW_AND_LONGER", "1");
- try testing.expectEqualStrings("1", env.get("SOMETHING_NEW_AND_LONGER").?);
- try testing.expectEqual(@as(EnvMap.Size, 2), env.count());
-
- // case insensitivity on Windows only
- if (native_os == .windows) {
- try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?);
- } else {
- try testing.expect(null == env.get("something_New_aNd_LONGER"));
- }
-
- var it = env.iterator();
- var count: EnvMap.Size = 0;
- while (it.next()) |entry| {
- const is_an_expected_name = std.mem.eql(u8, "SOMETHING_NEW", entry.key_ptr.*) or std.mem.eql(u8, "SOMETHING_NEW_AND_LONGER", entry.key_ptr.*);
- try testing.expect(is_an_expected_name);
- count += 1;
- }
- try testing.expectEqual(@as(EnvMap.Size, 2), count);
-
- env.remove("SOMETHING_NEW");
- try testing.expect(env.get("SOMETHING_NEW") == null);
-
- try testing.expectEqual(@as(EnvMap.Size, 1), env.count());
-
- if (native_os == .windows) {
- // test Unicode case-insensitivity on Windows
- try env.put("КИРиллИЦА", "something else");
- try testing.expectEqualStrings("something else", env.get("кириллица").?);
-
- // and WTF-8 that's not valid UTF-8
- const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{
- std.mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate
- });
- defer testing.allocator.free(wtf8_with_surrogate_pair);
-
- try env.put(wtf8_with_surrogate_pair, wtf8_with_surrogate_pair);
- try testing.expectEqualSlices(u8, wtf8_with_surrogate_pair, env.get(wtf8_with_surrogate_pair).?);
- }
-}
-
-pub const GetEnvMapError = error{
- OutOfMemory,
- /// WASI-only. `environ_sizes_get` or `environ_get`
- /// failed for an unexpected reason.
- Unexpected,
-};
-
-/// Returns a snapshot of the environment variables of the current process.
-/// Any modifications to the resulting EnvMap will not be reflected in the environment, and
-/// likewise, any future modifications to the environment will not be reflected in the EnvMap.
-/// Caller owns resulting `EnvMap` and should call its `deinit` fn when done.
-pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
- var result = EnvMap.init(allocator);
- errdefer result.deinit();
-
- if (native_os == .windows) {
- const ptr = windows.peb().ProcessParameters.Environment;
-
- var i: usize = 0;
- while (ptr[i] != 0) {
- const key_start = i;
-
- // There are some special environment variables that start with =,
- // so we need a special case to not treat = as a key/value separator
- // if it's the first character.
- // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
- if (ptr[key_start] == '=') i += 1;
-
- while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
- const key_w = ptr[key_start..i];
- const key = try unicode.wtf16LeToWtf8Alloc(allocator, key_w);
- errdefer allocator.free(key);
-
- if (ptr[i] == '=') i += 1;
-
- const value_start = i;
- while (ptr[i] != 0) : (i += 1) {}
- const value_w = ptr[value_start..i];
- const value = try unicode.wtf16LeToWtf8Alloc(allocator, value_w);
- errdefer allocator.free(value);
-
- i += 1; // skip over null byte
-
- try result.putMove(key, value);
- }
- return result;
- } else if (native_os == .wasi and !builtin.link_libc) {
- var environ_count: usize = undefined;
- var environ_buf_size: usize = undefined;
-
- const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
- if (environ_sizes_get_ret != .SUCCESS) {
- return posix.unexpectedErrno(environ_sizes_get_ret);
- }
-
- if (environ_count == 0) {
- return result;
- }
-
- const environ = try allocator.alloc([*:0]u8, environ_count);
- defer allocator.free(environ);
- const environ_buf = try allocator.alloc(u8, environ_buf_size);
- defer allocator.free(environ_buf);
-
- const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr);
- if (environ_get_ret != .SUCCESS) {
- return posix.unexpectedErrno(environ_get_ret);
- }
-
- for (environ) |env| {
- const pair = mem.sliceTo(env, 0);
- var parts = mem.splitScalar(u8, pair, '=');
- const key = parts.first();
- const value = parts.rest();
- try result.put(key, value);
- }
- return result;
- } else if (builtin.link_libc) {
- var ptr = std.c.environ;
- while (ptr[0]) |line| : (ptr += 1) {
- var line_i: usize = 0;
- while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
- const key = line[0..line_i];
-
- var end_i: usize = line_i;
- while (line[end_i] != 0) : (end_i += 1) {}
- const value = line[line_i + 1 .. end_i];
-
- try result.put(key, value);
- }
- return result;
- } else {
- for (std.os.environ) |line| {
- var line_i: usize = 0;
- while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
- const key = line[0..line_i];
-
- var end_i: usize = line_i;
- while (line[end_i] != 0) : (end_i += 1) {}
- const value = line[line_i + 1 .. end_i];
-
- try result.put(key, value);
- }
- return result;
- }
-}
-
-test getEnvMap {
- var env = try getEnvMap(testing.allocator);
- defer env.deinit();
-}
-
-pub const GetEnvVarOwnedError = error{
- OutOfMemory,
- EnvironmentVariableNotFound,
-
- /// On Windows, environment variable keys provided by the user must be valid WTF-8.
- /// https://wtf-8.codeberg.page/
- InvalidWtf8,
-};
-
-/// Caller must free returned memory.
-/// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/),
-/// then `error.InvalidWtf8` is returned.
-/// On Windows, the value is encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On other platforms, the value is an opaque sequence of bytes with no particular encoding.
-pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
- if (native_os == .windows) {
- const result_w = blk: {
- var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
- const stack_allocator = stack_alloc.get();
- const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
- defer stack_allocator.free(key_w);
-
- break :blk getenvW(key_w) orelse return error.EnvironmentVariableNotFound;
- };
- // wtf16LeToWtf8Alloc can only fail with OutOfMemory
- return unicode.wtf16LeToWtf8Alloc(allocator, result_w);
- } else if (native_os == .wasi and !builtin.link_libc) {
- var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
- defer envmap.deinit();
- const val = envmap.get(key) orelse return error.EnvironmentVariableNotFound;
- return allocator.dupe(u8, val);
- } else {
- const result = posix.getenv(key) orelse return error.EnvironmentVariableNotFound;
- return allocator.dupe(u8, result);
- }
-}
-
-/// On Windows, `key` must be valid WTF-8.
-pub inline fn hasEnvVarConstant(comptime key: []const u8) bool {
- if (native_os == .windows) {
- const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key);
- return getenvW(key_w) != null;
- } else if (native_os == .wasi and !builtin.link_libc) {
- return false;
- } else {
- return posix.getenv(key) != null;
- }
-}
-
-/// On Windows, `key` must be valid WTF-8.
-pub inline fn hasNonEmptyEnvVarConstant(comptime key: []const u8) bool {
- if (native_os == .windows) {
- const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key);
- const value = getenvW(key_w) orelse return false;
- return value.len != 0;
- } else if (native_os == .wasi and !builtin.link_libc) {
- return false;
- } else {
- const value = posix.getenv(key) orelse return false;
- return value.len != 0;
- }
-}
-
-pub const ParseEnvVarIntError = std.fmt.ParseIntError || error{EnvironmentVariableNotFound};
-
-/// Parses an environment variable as an integer.
-///
-/// Since the key is comptime-known, no allocation is needed.
-///
-/// On Windows, `key` must be valid WTF-8.
-pub fn parseEnvVarInt(comptime key: []const u8, comptime I: type, base: u8) ParseEnvVarIntError!I {
- if (native_os == .windows) {
- const key_w = comptime std.unicode.wtf8ToWtf16LeStringLiteral(key);
- const text = getenvW(key_w) orelse return error.EnvironmentVariableNotFound;
- return std.fmt.parseIntWithGenericCharacter(I, u16, text, base);
- } else if (native_os == .wasi and !builtin.link_libc) {
- @compileError("parseEnvVarInt is not supported for WASI without libc");
- } else {
- const text = posix.getenv(key) orelse return error.EnvironmentVariableNotFound;
- return std.fmt.parseInt(I, text, base);
- }
-}
-
-pub const HasEnvVarError = error{
- OutOfMemory,
-
- /// On Windows, environment variable keys provided by the user must be valid WTF-8.
- /// https://wtf-8.codeberg.page/
- InvalidWtf8,
-};
-
-/// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/),
-/// then `error.InvalidWtf8` is returned.
-pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool {
- if (native_os == .windows) {
- var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
- const stack_allocator = stack_alloc.get();
- const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
- defer stack_allocator.free(key_w);
- return getenvW(key_w) != null;
- } else if (native_os == .wasi and !builtin.link_libc) {
- var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
- defer envmap.deinit();
- return envmap.getPtr(key) != null;
- } else {
- return posix.getenv(key) != null;
- }
-}
-
-/// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/),
-/// then `error.InvalidWtf8` is returned.
-pub fn hasNonEmptyEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool {
- if (native_os == .windows) {
- var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
- const stack_allocator = stack_alloc.get();
- const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
- defer stack_allocator.free(key_w);
- const value = getenvW(key_w) orelse return false;
- return value.len != 0;
- } else if (native_os == .wasi and !builtin.link_libc) {
- var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
- defer envmap.deinit();
- const value = envmap.getPtr(key) orelse return false;
- return value.len != 0;
- } else {
- const value = posix.getenv(key) orelse return false;
- return value.len != 0;
- }
-}
-
-/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
-/// The returned slice points to memory in the PEB.
-///
-/// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString.
-///
-/// See also:
-/// * `std.posix.getenv`
-/// * `getEnvMap`
-/// * `getEnvVarOwned`
-/// * `hasEnvVarConstant`
-/// * `hasEnvVar`
-pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
- if (native_os != .windows) {
- @compileError("Windows-only");
- }
- const key_slice = mem.sliceTo(key, 0);
- // '=' anywhere but the start makes this an invalid environment variable name
- if (key_slice.len > 0 and std.mem.findScalar(u16, key_slice[1..], '=') != null) {
- return null;
- }
- const ptr = windows.peb().ProcessParameters.Environment;
- var i: usize = 0;
- while (ptr[i] != 0) {
- const key_value = mem.sliceTo(ptr[i..], 0);
-
- // There are some special environment variables that start with =,
- // so we need a special case to not treat = as a key/value separator
- // if it's the first character.
- // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
- const equal_search_start: usize = if (key_value[0] == '=') 1 else 0;
- const equal_index = std.mem.findScalarPos(u16, key_value, equal_search_start, '=') orelse {
- // This is enforced by CreateProcess.
- // If violated, CreateProcess will fail with INVALID_PARAMETER.
- unreachable; // must contain a =
- };
-
- const this_key = key_value[0..equal_index];
- if (windows.eqlIgnoreCaseWtf16(key_slice, this_key)) {
- return key_value[equal_index + 1 ..];
- }
-
- // skip past the NUL terminator
- i += key_value.len + 1;
- }
- return null;
-}
-
-test getEnvVarOwned {
- try testing.expectError(
- error.EnvironmentVariableNotFound,
- getEnvVarOwned(std.testing.allocator, "BADENV"),
- );
-}
-
-test hasEnvVarConstant {
- if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest;
-
- try testing.expect(!hasEnvVarConstant("BADENV"));
-}
-
-test hasEnvVar {
- const has_env = try hasEnvVar(std.testing.allocator, "BADENV");
- try testing.expect(!has_env);
-}
-
-pub const ArgIteratorPosix = struct {
- index: usize,
- count: usize,
-
- pub const InitError = error{};
-
- pub fn init() ArgIteratorPosix {
- return ArgIteratorPosix{
- .index = 0,
- .count = std.os.argv.len,
- };
- }
-
- pub fn next(self: *ArgIteratorPosix) ?[:0]const u8 {
- if (self.index == self.count) return null;
-
- const s = std.os.argv[self.index];
- self.index += 1;
- return mem.sliceTo(s, 0);
- }
-
- pub fn skip(self: *ArgIteratorPosix) bool {
- if (self.index == self.count) return false;
-
- self.index += 1;
- return true;
- }
-};
-
-pub const ArgIteratorWasi = struct {
- allocator: Allocator,
- index: usize,
- args: [][:0]u8,
-
- pub const InitError = error{OutOfMemory} || posix.UnexpectedError;
-
- /// You must call deinit to free the internal buffer of the
- /// iterator after you are done.
- pub fn init(allocator: Allocator) InitError!ArgIteratorWasi {
- const fetched_args = try ArgIteratorWasi.internalInit(allocator);
- return ArgIteratorWasi{
- .allocator = allocator,
- .index = 0,
- .args = fetched_args,
- };
- }
-
- fn internalInit(allocator: Allocator) InitError![][:0]u8 {
- var count: usize = undefined;
- var buf_size: usize = undefined;
-
- switch (std.os.wasi.args_sizes_get(&count, &buf_size)) {
- .SUCCESS => {},
- else => |err| return posix.unexpectedErrno(err),
- }
-
- if (count == 0) {
- return &[_][:0]u8{};
- }
-
- const argv = try allocator.alloc([*:0]u8, count);
- defer allocator.free(argv);
-
- const argv_buf = try allocator.alloc(u8, buf_size);
-
- switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) {
- .SUCCESS => {},
- else => |err| return posix.unexpectedErrno(err),
- }
-
- var result_args = try allocator.alloc([:0]u8, count);
- var i: usize = 0;
- while (i < count) : (i += 1) {
- result_args[i] = mem.sliceTo(argv[i], 0);
- }
-
- return result_args;
- }
-
- pub fn next(self: *ArgIteratorWasi) ?[:0]const u8 {
- if (self.index == self.args.len) return null;
-
- const arg = self.args[self.index];
- self.index += 1;
- return arg;
- }
-
- pub fn skip(self: *ArgIteratorWasi) bool {
- if (self.index == self.args.len) return false;
-
- self.index += 1;
- return true;
- }
-
- /// Call to free the internal buffer of the iterator.
- pub fn deinit(self: *ArgIteratorWasi) void {
- // Nothing is allocated when there are no args
- if (self.args.len == 0) return;
-
- const last_item = self.args[self.args.len - 1];
- const last_byte_addr = @intFromPtr(last_item.ptr) + last_item.len + 1; // null terminated
- const first_item_ptr = self.args[0].ptr;
- const len = last_byte_addr - @intFromPtr(first_item_ptr);
- self.allocator.free(first_item_ptr[0..len]);
- self.allocator.free(self.args);
- }
-};
-
-/// Iterator that implements the Windows command-line parsing algorithm.
-/// The implementation is intended to be compatible with the post-2008 C runtime,
-/// but is *not* intended to be compatible with `CommandLineToArgvW` since
-/// `CommandLineToArgvW` uses the pre-2008 parsing rules.
-///
-/// This iterator faithfully implements the parsing behavior observed from the C runtime with
-/// one exception: if the command-line string is empty, the iterator will immediately complete
-/// without returning any arguments (whereas the C runtime will return a single argument
-/// representing the name of the current executable).
-///
-/// The essential parts of the algorithm are described in Microsoft's documentation:
-///
-/// - https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170#parsing-c-command-line-arguments
-///
-/// David Deley explains some additional undocumented quirks in great detail:
-///
-/// - https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES
-pub const ArgIteratorWindows = struct {
- allocator: Allocator,
- /// Encoded as WTF-16 LE.
- cmd_line: []const u16,
- index: usize = 0,
- /// Owned by the iterator. Long enough to hold contiguous NUL-terminated slices
- /// of each argument encoded as WTF-8.
- buffer: []u8,
- start: usize = 0,
- end: usize = 0,
-
- pub const InitError = error{OutOfMemory};
-
- /// `cmd_line_w` *must* be a WTF16-LE-encoded string.
- ///
- /// The iterator stores and uses `cmd_line_w`, so its memory must be valid for
- /// at least as long as the returned ArgIteratorWindows.
- pub fn init(allocator: Allocator, cmd_line_w: []const u16) InitError!ArgIteratorWindows {
- const wtf8_len = unicode.calcWtf8Len(cmd_line_w);
-
- // This buffer must be large enough to contain contiguous NUL-terminated slices
- // of each argument.
- // - During parsing, the length of a parsed argument will always be equal to
- // to less than its unparsed length
- // - The first argument needs one extra byte of space allocated for its NUL
- // terminator, but for each subsequent argument the necessary whitespace
- // between arguments guarantees room for their NUL terminator(s).
- const buffer = try allocator.alloc(u8, wtf8_len + 1);
- errdefer allocator.free(buffer);
-
- return .{
- .allocator = allocator,
- .cmd_line = cmd_line_w,
- .buffer = buffer,
- };
- }
-
- /// Returns the next argument and advances the iterator. Returns `null` if at the end of the
- /// command-line string. The iterator owns the returned slice.
- /// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
- pub fn next(self: *ArgIteratorWindows) ?[:0]const u8 {
- return self.nextWithStrategy(next_strategy);
- }
-
- /// Skips the next argument and advances the iterator. Returns `true` if an argument was
- /// skipped, `false` if at the end of the command-line string.
- pub fn skip(self: *ArgIteratorWindows) bool {
- return self.nextWithStrategy(skip_strategy);
- }
-
- const next_strategy = struct {
- const T = ?[:0]const u8;
-
- const eof = null;
-
- /// Returns '\' if any backslashes are emitted, otherwise returns `last_emitted_code_unit`.
- fn emitBackslashes(self: *ArgIteratorWindows, count: usize, last_emitted_code_unit: ?u16) ?u16 {
- for (0..count) |_| {
- self.buffer[self.end] = '\\';
- self.end += 1;
- }
- return if (count != 0) '\\' else last_emitted_code_unit;
- }
-
- /// If `last_emitted_code_unit` and `code_unit` form a surrogate pair, then
- /// the previously emitted high surrogate is overwritten by the codepoint encoded
- /// by the surrogate pair, and `null` is returned.
- /// Otherwise, `code_unit` is emitted and returned.
- fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16, last_emitted_code_unit: ?u16) ?u16 {
- // Because we are emitting WTF-8, we need to
- // check to see if we've emitted two consecutive surrogate
- // codepoints that form a valid surrogate pair in order
- // to ensure that we're always emitting well-formed WTF-8
- // (https://wtf-8.codeberg.page/#concatenating).
- //
- // If we do have a valid surrogate pair, we need to emit
- // the UTF-8 sequence for the codepoint that they encode
- // instead of the WTF-8 encoding for the two surrogate pairs
- // separately.
- //
- // This is relevant when dealing with a WTF-16 encoded
- // command line like this:
- // "<0xD801>"<0xDC37>
- // which would get parsed and converted to WTF-8 as:
- // <0xED><0xA0><0x81><0xED><0xB0><0xB7>
- // but instead, we need to recognize the surrogate pair
- // and emit the codepoint it encodes, which in this
- // example is U+10437 (𐐷), which is encoded in UTF-8 as:
- // <0xF0><0x90><0x90><0xB7>
- if (last_emitted_code_unit != null and
- std.unicode.utf16IsLowSurrogate(code_unit) and
- std.unicode.utf16IsHighSurrogate(last_emitted_code_unit.?))
- {
- const codepoint = std.unicode.utf16DecodeSurrogatePair(&.{ last_emitted_code_unit.?, code_unit }) catch unreachable;
-
- // Unpaired surrogate is 3 bytes long
- const dest = self.buffer[self.end - 3 ..];
- const len = unicode.utf8Encode(codepoint, dest) catch unreachable;
- // All codepoints that require a surrogate pair (> U+FFFF) are encoded as 4 bytes
- assert(len == 4);
- self.end += 1;
- return null;
- }
-
- const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable;
- self.end += wtf8_len;
- return code_unit;
- }
-
- fn yieldArg(self: *ArgIteratorWindows) [:0]const u8 {
- self.buffer[self.end] = 0;
- const arg = self.buffer[self.start..self.end :0];
- self.end += 1;
- self.start = self.end;
- return arg;
- }
- };
-
- const skip_strategy = struct {
- const T = bool;
-
- const eof = false;
-
- fn emitBackslashes(_: *ArgIteratorWindows, _: usize, last_emitted_code_unit: ?u16) ?u16 {
- return last_emitted_code_unit;
- }
-
- fn emitCharacter(_: *ArgIteratorWindows, _: u16, last_emitted_code_unit: ?u16) ?u16 {
- return last_emitted_code_unit;
- }
-
- fn yieldArg(_: *ArgIteratorWindows) bool {
- return true;
- }
- };
-
- fn nextWithStrategy(self: *ArgIteratorWindows, comptime strategy: type) strategy.T {
- var last_emitted_code_unit: ?u16 = null;
- // The first argument (the executable name) uses different parsing rules.
- if (self.index == 0) {
- if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) {
- // Immediately complete the iterator.
- // The C runtime would return the name of the current executable here.
- return strategy.eof;
- }
-
- var inside_quotes = false;
- while (true) : (self.index += 1) {
- const char = if (self.index != self.cmd_line.len)
- mem.littleToNative(u16, self.cmd_line[self.index])
- else
- 0;
- switch (char) {
- 0 => {
- return strategy.yieldArg(self);
- },
- '"' => {
- inside_quotes = !inside_quotes;
- },
- ' ', '\t' => {
- if (inside_quotes) {
- last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
- } else {
- self.index += 1;
- return strategy.yieldArg(self);
- }
- },
- else => {
- last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
- },
- }
- }
- }
-
- // Skip spaces and tabs. The iterator completes if we reach the end of the string here.
- while (true) : (self.index += 1) {
- const char = if (self.index != self.cmd_line.len)
- mem.littleToNative(u16, self.cmd_line[self.index])
- else
- 0;
- switch (char) {
- 0 => return strategy.eof,
- ' ', '\t' => continue,
- else => break,
- }
- }
-
- // Parsing rules for subsequent arguments:
- //
- // - The end of the string always terminates the current argument.
- // - When not in 'inside_quotes' mode, a space or tab terminates the current argument.
- // - 2n backslashes followed by a quote emit n backslashes (note: n can be zero).
- // If in 'inside_quotes' and the quote is immediately followed by a second quote,
- // one quote is emitted and the other is skipped, otherwise, the quote is skipped
- // and 'inside_quotes' is toggled.
- // - 2n + 1 backslashes followed by a quote emit n backslashes followed by a quote.
- // - n backslashes not followed by a quote emit n backslashes.
- var backslash_count: usize = 0;
- var inside_quotes = false;
- while (true) : (self.index += 1) {
- const char = if (self.index != self.cmd_line.len)
- mem.littleToNative(u16, self.cmd_line[self.index])
- else
- 0;
- switch (char) {
- 0 => {
- last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
- return strategy.yieldArg(self);
- },
- ' ', '\t' => {
- last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
- backslash_count = 0;
- if (inside_quotes) {
- last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
- } else return strategy.yieldArg(self);
- },
- '"' => {
- const char_is_escaped_quote = backslash_count % 2 != 0;
- last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count / 2, last_emitted_code_unit);
- backslash_count = 0;
- if (char_is_escaped_quote) {
- last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit);
- } else {
- if (inside_quotes and
- self.index + 1 != self.cmd_line.len and
- mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"')
- {
- last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit);
- self.index += 1;
- } else {
- inside_quotes = !inside_quotes;
- }
- }
- },
- '\\' => {
- backslash_count += 1;
- },
- else => {
- last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
- backslash_count = 0;
- last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
- },
- }
- }
- }
-
- /// Frees the iterator's copy of the command-line string and all previously returned
- /// argument slices.
- pub fn deinit(self: *ArgIteratorWindows) void {
- self.allocator.free(self.buffer);
- }
-};
-
-/// Optional parameters for `ArgIteratorGeneral`
-pub const ArgIteratorGeneralOptions = struct {
- comments: bool = false,
- single_quotes: bool = false,
-};
-
-/// A general Iterator to parse a string into a set of arguments
-pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type {
- return struct {
- allocator: Allocator,
- index: usize = 0,
- cmd_line: []const u8,
-
- /// Should the cmd_line field be free'd (using the allocator) on deinit()?
- free_cmd_line_on_deinit: bool,
-
- /// buffer MUST be long enough to hold the cmd_line plus a null terminator.
- /// buffer will we free'd (using the allocator) on deinit()
- buffer: []u8,
- start: usize = 0,
- end: usize = 0,
-
- pub const Self = @This();
-
- pub const InitError = error{OutOfMemory};
-
- /// cmd_line_utf8 MUST remain valid and constant while using this instance
- pub fn init(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self {
- const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1);
- errdefer allocator.free(buffer);
-
- return Self{
- .allocator = allocator,
- .cmd_line = cmd_line_utf8,
- .free_cmd_line_on_deinit = false,
- .buffer = buffer,
- };
- }
-
- /// cmd_line_utf8 will be free'd (with the allocator) on deinit()
- pub fn initTakeOwnership(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self {
- const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1);
- errdefer allocator.free(buffer);
-
- return Self{
- .allocator = allocator,
- .cmd_line = cmd_line_utf8,
- .free_cmd_line_on_deinit = true,
- .buffer = buffer,
- };
- }
-
- // Skips over whitespace in the cmd_line.
- // Returns false if the terminating sentinel is reached, true otherwise.
- // Also skips over comments (if supported).
- fn skipWhitespace(self: *Self) bool {
- while (true) : (self.index += 1) {
- const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0;
- switch (character) {
- 0 => return false,
- ' ', '\t', '\r', '\n' => continue,
- '#' => {
- if (options.comments) {
- while (true) : (self.index += 1) {
- switch (self.cmd_line[self.index]) {
- '\n' => break,
- 0 => return false,
- else => continue,
- }
- }
- continue;
- } else {
- break;
- }
- },
- else => break,
- }
- }
- return true;
- }
-
- pub fn skip(self: *Self) bool {
- if (!self.skipWhitespace()) {
- return false;
- }
-
- var backslash_count: usize = 0;
- var in_quote = false;
- while (true) : (self.index += 1) {
- const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0;
- switch (character) {
- 0 => return true,
- '"', '\'' => {
- if (!options.single_quotes and character == '\'') {
- backslash_count = 0;
- continue;
- }
- const quote_is_real = backslash_count % 2 == 0;
- if (quote_is_real) {
- in_quote = !in_quote;
- }
- },
- '\\' => {
- backslash_count += 1;
- },
- ' ', '\t', '\r', '\n' => {
- if (!in_quote) {
- return true;
- }
- backslash_count = 0;
- },
- else => {
- backslash_count = 0;
- continue;
- },
- }
- }
- }
-
- /// Returns a slice of the internal buffer that contains the next argument.
- /// Returns null when it reaches the end.
- pub fn next(self: *Self) ?[:0]const u8 {
- if (!self.skipWhitespace()) {
- return null;
- }
-
- var backslash_count: usize = 0;
- var in_quote = false;
- while (true) : (self.index += 1) {
- const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0;
- switch (character) {
- 0 => {
- self.emitBackslashes(backslash_count);
- self.buffer[self.end] = 0;
- const token = self.buffer[self.start..self.end :0];
- self.end += 1;
- self.start = self.end;
- return token;
- },
- '"', '\'' => {
- if (!options.single_quotes and character == '\'') {
- self.emitBackslashes(backslash_count);
- backslash_count = 0;
- self.emitCharacter(character);
- continue;
- }
- const quote_is_real = backslash_count % 2 == 0;
- self.emitBackslashes(backslash_count / 2);
- backslash_count = 0;
-
- if (quote_is_real) {
- in_quote = !in_quote;
- } else {
- self.emitCharacter('"');
- }
- },
- '\\' => {
- backslash_count += 1;
- },
- ' ', '\t', '\r', '\n' => {
- self.emitBackslashes(backslash_count);
- backslash_count = 0;
- if (in_quote) {
- self.emitCharacter(character);
- } else {
- self.buffer[self.end] = 0;
- const token = self.buffer[self.start..self.end :0];
- self.end += 1;
- self.start = self.end;
- return token;
- }
- },
- else => {
- self.emitBackslashes(backslash_count);
- backslash_count = 0;
- self.emitCharacter(character);
- },
- }
- }
- }
-
- fn emitBackslashes(self: *Self, emit_count: usize) void {
- var i: usize = 0;
- while (i < emit_count) : (i += 1) {
- self.emitCharacter('\\');
- }
- }
-
- fn emitCharacter(self: *Self, char: u8) void {
- self.buffer[self.end] = char;
- self.end += 1;
- }
-
- /// Call to free the internal buffer of the iterator.
- pub fn deinit(self: *Self) void {
- self.allocator.free(self.buffer);
-
- if (self.free_cmd_line_on_deinit) {
- self.allocator.free(self.cmd_line);
- }
- }
- };
-}
-
-/// Cross-platform command line argument iterator.
-pub const ArgIterator = struct {
- const InnerType = switch (native_os) {
- .windows => ArgIteratorWindows,
- .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi,
- else => ArgIteratorPosix,
- };
-
- inner: InnerType,
-
- /// Initialize the args iterator. Consider using initWithAllocator() instead
- /// for cross-platform compatibility.
- pub fn init() ArgIterator {
- if (native_os == .wasi) {
- @compileError("In WASI, use initWithAllocator instead.");
- }
- if (native_os == .windows) {
- @compileError("In Windows, use initWithAllocator instead.");
- }
-
- return ArgIterator{ .inner = InnerType.init() };
- }
-
- pub const InitError = InnerType.InitError;
-
- /// You must deinitialize iterator's internal buffers by calling `deinit` when done.
- pub fn initWithAllocator(allocator: Allocator) InitError!ArgIterator {
- if (native_os == .wasi and !builtin.link_libc) {
- return ArgIterator{ .inner = try InnerType.init(allocator) };
- }
- if (native_os == .windows) {
- const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
- const cmd_line_w = cmd_line.Buffer.?[0 .. cmd_line.Length / 2];
- return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) };
- }
-
- return ArgIterator{ .inner = InnerType.init() };
- }
-
- /// Get the next argument. Returns 'null' if we are at the end.
- /// Returned slice is pointing to the iterator's internal buffer.
- /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
- /// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
- pub fn next(self: *ArgIterator) ?([:0]const u8) {
- return self.inner.next();
- }
-
- /// Parse past 1 argument without capturing it.
- /// Returns `true` if skipped an arg, `false` if we are at the end.
- pub fn skip(self: *ArgIterator) bool {
- return self.inner.skip();
- }
-
- /// Call this to free the iterator's internal buffer if the iterator
- /// was created with `initWithAllocator` function.
- pub fn deinit(self: *ArgIterator) void {
- // Unless we're targeting WASI or Windows, this is a no-op.
- if (native_os == .wasi and !builtin.link_libc) {
- self.inner.deinit();
- }
-
- if (native_os == .windows) {
- self.inner.deinit();
- }
- }
-};
-
-/// Holds the command-line arguments, with the program name as the first entry.
-/// Use argsWithAllocator() for cross-platform code.
-pub fn args() ArgIterator {
- return ArgIterator.init();
-}
-
-/// You must deinitialize iterator's internal buffers by calling `deinit` when done.
-pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator {
- return ArgIterator.initWithAllocator(allocator);
-}
-
-/// Caller must call argsFree on result.
-/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
-pub fn argsAlloc(allocator: Allocator) ![][:0]u8 {
- // TODO refactor to only make 1 allocation.
- var it = try argsWithAllocator(allocator);
- defer it.deinit();
-
- var contents = std.array_list.Managed(u8).init(allocator);
- defer contents.deinit();
-
- var slice_list = std.array_list.Managed(usize).init(allocator);
- defer slice_list.deinit();
-
- while (it.next()) |arg| {
- try contents.appendSlice(arg[0 .. arg.len + 1]);
- try slice_list.append(arg.len);
- }
-
- const contents_slice = contents.items;
- const slice_sizes = slice_list.items;
- const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len);
- const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len);
- const buf = try allocator.alignedAlloc(u8, .of([]u8), total_bytes);
- errdefer allocator.free(buf);
-
- const result_slice_list = mem.bytesAsSlice([:0]u8, buf[0..slice_list_bytes]);
- const result_contents = buf[slice_list_bytes..];
- @memcpy(result_contents[0..contents_slice.len], contents_slice);
-
- var contents_index: usize = 0;
- for (slice_sizes, 0..) |len, i| {
- const new_index = contents_index + len;
- result_slice_list[i] = result_contents[contents_index..new_index :0];
- contents_index = new_index + 1;
- }
-
- return result_slice_list;
-}
-
-pub fn argsFree(allocator: Allocator, args_alloc: []const [:0]u8) void {
- var total_bytes: usize = 0;
- for (args_alloc) |arg| {
- total_bytes += @sizeOf([]u8) + arg.len + 1;
- }
- const unaligned_allocated_buf = @as([*]const u8, @ptrCast(args_alloc.ptr))[0..total_bytes];
- const aligned_allocated_buf: []align(@alignOf([]u8)) const u8 = @alignCast(unaligned_allocated_buf);
- return allocator.free(aligned_allocated_buf);
-}
-
-test ArgIteratorWindows {
- const t = testArgIteratorWindows;
-
- try t(
- \\"C:\Program Files\zig\zig.exe" run .\src\main.zig -target x86_64-windows-gnu -O ReleaseSafe -- --emoji=🗿 --eval="new Regex(\"Dwayne \\\"The Rock\\\" Johnson\")"
- , &.{
- \\C:\Program Files\zig\zig.exe
- ,
- \\run
- ,
- \\.\src\main.zig
- ,
- \\-target
- ,
- \\x86_64-windows-gnu
- ,
- \\-O
- ,
- \\ReleaseSafe
- ,
- \\--
- ,
- \\--emoji=🗿
- ,
- \\--eval=new Regex("Dwayne \"The Rock\" Johnson")
- ,
- });
-
- // Empty
- try t("", &.{});
-
- // Separators
- try t("aa bb cc", &.{ "aa", "bb", "cc" });
- try t("aa\tbb\tcc", &.{ "aa", "bb", "cc" });
- try t("aa\nbb\ncc", &.{"aa\nbb\ncc"});
- try t("aa\r\nbb\r\ncc", &.{"aa\r\nbb\r\ncc"});
- try t("aa\rbb\rcc", &.{"aa\rbb\rcc"});
- try t("aa\x07bb\x07cc", &.{"aa\x07bb\x07cc"});
- try t("aa\x7Fbb\x7Fcc", &.{"aa\x7Fbb\x7Fcc"});
- try t("aa🦎bb🦎cc", &.{"aa🦎bb🦎cc"});
-
- // Leading/trailing whitespace
- try t(" ", &.{""});
- try t(" aa bb ", &.{ "", "aa", "bb" });
- try t("\t\t", &.{""});
- try t("\t\taa\t\tbb\t\t", &.{ "", "aa", "bb" });
- try t("\n\n", &.{"\n\n"});
- try t("\n\naa\n\nbb\n\n", &.{"\n\naa\n\nbb\n\n"});
-
- // Executable name with quotes/backslashes
- try t("\"aa bb\tcc\ndd\"", &.{"aa bb\tcc\ndd"});
- try t("\"", &.{""});
- try t("\"\"", &.{""});
- try t("\"\"\"", &.{""});
- try t("\"\"\"\"", &.{""});
- try t("\"\"\"\"\"", &.{""});
- try t("aa\"bb\"cc\"dd", &.{"aabbccdd"});
- try t("aa\"bb cc\"dd", &.{"aabb ccdd"});
- try t("\"aa\\\"bb\"", &.{"aa\\bb"});
- try t("\"aa\\\\\"", &.{"aa\\\\"});
- try t("aa\\\"bb", &.{"aa\\bb"});
- try t("aa\\\\\"bb", &.{"aa\\\\bb"});
-
- // Arguments with quotes/backslashes
- try t(". \"aa bb\tcc\ndd\"", &.{ ".", "aa bb\tcc\ndd" });
- try t(". aa\" \"bb\"\t\"cc\"\n\"dd\"", &.{ ".", "aa bb\tcc\ndd" });
- try t(". ", &.{"."});
- try t(". \"", &.{ ".", "" });
- try t(". \"\"", &.{ ".", "" });
- try t(". \"\"\"", &.{ ".", "\"" });
- try t(". \"\"\"\"", &.{ ".", "\"" });
- try t(". \"\"\"\"\"", &.{ ".", "\"\"" });
- try t(". \"\"\"\"\"\"", &.{ ".", "\"\"" });
- try t(". \" \"", &.{ ".", " " });
- try t(". \" \"\"", &.{ ".", " \"" });
- try t(". \" \"\"\"", &.{ ".", " \"" });
- try t(". \" \"\"\"\"", &.{ ".", " \"\"" });
- try t(". \" \"\"\"\"\"", &.{ ".", " \"\"" });
- try t(". \" \"\"\"\"\"\"", &.{ ".", " \"\"\"" });
- try t(". \\\"", &.{ ".", "\"" });
- try t(". \\\"\"", &.{ ".", "\"" });
- try t(". \\\"\"\"", &.{ ".", "\"" });
- try t(". \\\"\"\"\"", &.{ ".", "\"\"" });
- try t(". \\\"\"\"\"\"", &.{ ".", "\"\"" });
- try t(". \\\"\"\"\"\"\"", &.{ ".", "\"\"\"" });
- try t(". \" \\\"", &.{ ".", " \"" });
- try t(". \" \\\"\"", &.{ ".", " \"" });
- try t(". \" \\\"\"\"", &.{ ".", " \"\"" });
- try t(". \" \\\"\"\"\"", &.{ ".", " \"\"" });
- try t(". \" \\\"\"\"\"\"", &.{ ".", " \"\"\"" });
- try t(". \" \\\"\"\"\"\"\"", &.{ ".", " \"\"\"" });
- try t(". aa\\bb\\\\cc\\\\\\dd", &.{ ".", "aa\\bb\\\\cc\\\\\\dd" });
- try t(". \\\\\\\"aa bb\"", &.{ ".", "\\\"aa", "bb" });
- try t(". \\\\\\\\\"aa bb\"", &.{ ".", "\\\\aa bb" });
-
- // From https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#results-of-parsing-command-lines
- try t(
- \\foo.exe "abc" d e
- , &.{ "foo.exe", "abc", "d", "e" });
- try t(
- \\foo.exe a\\b d"e f"g h
- , &.{ "foo.exe", "a\\\\b", "de fg", "h" });
- try t(
- \\foo.exe a\\\"b c d
- , &.{ "foo.exe", "a\\\"b", "c", "d" });
- try t(
- \\foo.exe a\\\\"b c" d e
- , &.{ "foo.exe", "a\\\\b c", "d", "e" });
- try t(
- \\foo.exe a"b"" c d
- , &.{ "foo.exe", "ab\" c d" });
-
- // From https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX
- try t("foo.exe CallMeIshmael", &.{ "foo.exe", "CallMeIshmael" });
- try t("foo.exe \"Call Me Ishmael\"", &.{ "foo.exe", "Call Me Ishmael" });
- try t("foo.exe Cal\"l Me I\"shmael", &.{ "foo.exe", "Call Me Ishmael" });
- try t("foo.exe CallMe\\\"Ishmael", &.{ "foo.exe", "CallMe\"Ishmael" });
- try t("foo.exe \"CallMe\\\"Ishmael\"", &.{ "foo.exe", "CallMe\"Ishmael" });
- try t("foo.exe \"Call Me Ishmael\\\\\"", &.{ "foo.exe", "Call Me Ishmael\\" });
- try t("foo.exe \"CallMe\\\\\\\"Ishmael\"", &.{ "foo.exe", "CallMe\\\"Ishmael" });
- try t("foo.exe a\\\\\\b", &.{ "foo.exe", "a\\\\\\b" });
- try t("foo.exe \"a\\\\\\b\"", &.{ "foo.exe", "a\\\\\\b" });
-
- // Surrogate pair encoding of 𐐷 separated by quotes.
- // Encoded as WTF-16:
- // "<0xD801>"<0xDC37>
- // Encoded as WTF-8:
- // "<0xED><0xA0><0x81>"<0xED><0xB0><0xB7>
- // During parsing, the quotes drop out and the surrogate pair
- // should end up encoded as its normal UTF-8 representation.
- try t("foo.exe \"\xed\xa0\x81\"\xed\xb0\xb7", &.{ "foo.exe", "𐐷" });
-}
-
-fn testArgIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void {
- const cmd_line_w = try unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line);
- defer testing.allocator.free(cmd_line_w);
-
- // next
- {
- var it = try ArgIteratorWindows.init(testing.allocator, cmd_line_w);
- defer it.deinit();
-
- for (expected_args) |expected| {
- if (it.next()) |actual| {
- try testing.expectEqualStrings(expected, actual);
- } else {
- return error.TestUnexpectedResult;
- }
- }
- try testing.expect(it.next() == null);
- }
-
- // skip
- {
- var it = try ArgIteratorWindows.init(testing.allocator, cmd_line_w);
- defer it.deinit();
-
- for (0..expected_args.len) |_| {
- try testing.expect(it.skip());
- }
- try testing.expect(!it.skip());
- }
-}
-
-test "general arg parsing" {
- try testGeneralCmdLine("a b\tc d", &.{ "a", "b", "c", "d" });
- try testGeneralCmdLine("\"abc\" d e", &.{ "abc", "d", "e" });
- try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &.{ "a\\\\\\b", "de fg", "h" });
- try testGeneralCmdLine("a\\\\\\\"b c d", &.{ "a\\\"b", "c", "d" });
- try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &.{ "a\\\\b c", "d", "e" });
- try testGeneralCmdLine("a b\tc \"d f", &.{ "a", "b", "c", "d f" });
- try testGeneralCmdLine("j k l\\", &.{ "j", "k", "l\\" });
- try testGeneralCmdLine("\"\" x y z\\\\", &.{ "", "x", "y", "z\\\\" });
-
- try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &.{
- ".\\..\\zig-cache\\build",
- "bin\\zig.exe",
- ".\\..",
- ".\\..\\zig-cache",
- "--help",
- });
-
- try testGeneralCmdLine(
- \\ 'foo' "bar"
- , &.{ "'foo'", "bar" });
-}
-
-fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void {
- var it = try ArgIteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line);
- defer it.deinit();
- for (expected_args) |expected_arg| {
- const arg = it.next().?;
- try testing.expectEqualStrings(expected_arg, arg);
- }
- try testing.expect(it.next() == null);
-}
-
-test "response file arg parsing" {
- try testResponseFileCmdLine(
- \\a b
- \\c d\
- , &.{ "a", "b", "c", "d\\" });
- try testResponseFileCmdLine("a b c d\\", &.{ "a", "b", "c", "d\\" });
-
- try testResponseFileCmdLine(
- \\j
- \\ k l # this is a comment \\ \\\ \\\\ "none" "\\" "\\\"
- \\ "m" #another comment
- \\
- , &.{ "j", "k", "l", "m" });
-
- try testResponseFileCmdLine(
- \\ "" q ""
- \\ "r s # t" "u\" v" #another comment
- \\
- , &.{ "", "q", "", "r s # t", "u\" v" });
-
- try testResponseFileCmdLine(
- \\ -l"advapi32" a# b#c d#
- \\e\\\
- , &.{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" });
-
- try testResponseFileCmdLine(
- \\ 'foo' "bar"
- , &.{ "foo", "bar" });
-}
-
-fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void {
- var it = try ArgIteratorGeneral(.{ .comments = true, .single_quotes = true })
- .init(std.testing.allocator, input_cmd_line);
- defer it.deinit();
- for (expected_args) |expected_arg| {
- const arg = it.next().?;
- try testing.expectEqualStrings(expected_arg, arg);
- }
- try testing.expect(it.next() == null);
-}
-
pub const UserInfo = struct {
uid: posix.uid_t,
gid: posix.gid_t,
@@ -1706,71 +260,278 @@ pub fn getBaseAddress() usize {
}
}
-/// Tells whether calling the `execv` or `execve` functions will be a compile error.
-pub const can_execv = switch (native_os) {
+/// Tells whether the target operating system supports replacing the current
+/// process image. If this is `false` then calling `replace` or `replaceFile`
+/// functions will return `error.OperationUnsupported`.
+pub const can_replace = switch (native_os) {
.windows, .haiku, .wasi => false,
else => true,
};
-/// Tells whether spawning child processes is supported (e.g. via Child)
+/// Tells whether spawning child processes is supported.
pub const can_spawn = switch (native_os) {
.wasi, .ios, .tvos, .visionos, .watchos => false,
else => true,
};
-pub const ExecvError = std.posix.ExecveError || error{OutOfMemory};
-
-/// Replaces the current process image with the executed process.
-/// This function must allocate memory to add a null terminating bytes on path and each arg.
-/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
-/// pointers after the args and after the environment variables.
-/// `argv[0]` is the executable path.
-/// This function also uses the PATH environment variable to get the full path to the executable.
-/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
-/// For that use case, use the `std.posix` functions directly.
-pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError {
- return execve(allocator, argv, null);
+pub const ReplaceError = error{
+ /// The target operating system cannot replace the process image with a new
+ /// one.
+ OperationUnsupported,
+ SystemResources,
+ AccessDenied,
+ PermissionDenied,
+ InvalidExe,
+ FileSystem,
+ IsDir,
+ FileNotFound,
+ NotDir,
+ FileBusy,
+ ProcessFdQuotaExceeded,
+ SystemFdQuotaExceeded,
+} || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
+
+pub const ReplaceOptions = struct {
+ argv: []const []const u8,
+ expand_arg0: ArgExpansion = .no_expand,
+ /// Replaces the environment when provided. The PATH value from here is
+ /// never used to resolve `argv[0]`.
+ environ_map: ?*const Environ.Map = null,
+};
+
+/// Replaces the current process image with the executed process. If this
+/// function succeeds, it does not return.
+///
+/// `argv[0]` is the name of the process to replace the current one with. If it
+/// is not already a file path (i.e. it contains '/'), it is resolved into a
+/// file path based on PATH from the parent environment.
+///
+/// It is illegal to call this function in a fork() child.
+pub fn replace(io: Io, options: ReplaceOptions) ReplaceError {
+ return io.vtable.processReplace(io.userdata, options);
}
-/// Replaces the current process image with the executed process.
-/// This function must allocate memory to add a null terminating bytes on path and each arg.
-/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
-/// pointers after the args and after the environment variables.
-/// `argv[0]` is the executable path.
-/// This function also uses the PATH environment variable to get the full path to the executable.
-/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
-/// For that use case, use the `std.posix` functions directly.
-pub fn execve(
- allocator: Allocator,
+/// Replaces the current process image with the executed process. If this
+/// function succeeds, it does not return.
+///
+/// `argv[0]` is the file path of the process to replace the current one with,
+/// relative to `dir`. It is *always* treated as a file path, even if it does
+/// not contain '/'.
+///
+/// It is illegal to call this function in a fork() child.
+pub fn replacePath(io: Io, dir: Io.Dir, options: ReplaceOptions) ReplaceError {
+ return io.vtable.processReplacePath(io.userdata, dir, options);
+}
+
+pub const ArgExpansion = enum { expand, no_expand };
+
+/// File name extensions supported natively by `CreateProcess()` on Windows.
+pub const WindowsExtension = enum { bat, cmd, com, exe };
+
+pub const SpawnError = error{
+ /// The operating system does not support creating child processes.
+ OperationUnsupported,
+ OutOfMemory,
+ /// POSIX-only. `StdIo.ignore` was selected and opening `/dev/null` returned ENODEV.
+ NoDevice,
+ /// Windows-only. `cwd` or `argv` was provided and it was invalid WTF-8.
+ /// https://wtf-8.codeberg.page/
+ InvalidWtf8,
+ /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process.
+ CurrentWorkingDirectoryUnlinked,
+ /// Windows-only. NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed
+ /// within arguments when executing a `.bat`/`.cmd` script.
+ /// - NUL/LF signifiies end of arguments, so anything afterwards
+ /// would be lost after execution.
+ /// - CR is stripped by `cmd.exe`, so any CR codepoints
+ /// would be lost after execution.
+ InvalidBatchScriptArg,
+ SystemResources,
+ AccessDenied,
+ PermissionDenied,
+ InvalidExe,
+ FileSystem,
+ IsDir,
+ FileNotFound,
+ NotDir,
+ FileBusy,
+ ProcessFdQuotaExceeded,
+ SystemFdQuotaExceeded,
+ ResourceLimitReached,
+ InvalidUserId,
+ InvalidProcessGroupId,
+ SymLinkLoop,
+ InvalidName,
+ /// An attempt was made to change the process group ID of one of the
+ /// children of the calling process and the child had already performed an
+ /// image replacement.
+ ProcessAlreadyExec,
+} || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
+
+pub const SpawnOptions = struct {
argv: []const []const u8,
- env_map: ?*const EnvMap,
-) ExecvError {
- if (!can_execv) @compileError("The target OS does not support execv");
-
- var arena_allocator = std.heap.ArenaAllocator.init(allocator);
- defer arena_allocator.deinit();
- const arena = arena_allocator.allocator();
-
- const argv_buf = try arena.allocSentinel(?[*:0]const u8, argv.len, null);
- for (argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
-
- const envp = m: {
- if (env_map) |m| {
- const envp_buf = try createNullDelimitedEnvMap(arena, m);
- break :m envp_buf.ptr;
- } else if (builtin.link_libc) {
- break :m std.c.environ;
- } else if (builtin.output_mode == .Exe) {
- // Then we have Zig start code and this works.
- // TODO type-safety for null-termination of `os.environ`.
- break :m @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr));
- } else {
- // TODO come up with a solution for this.
- @compileError("missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process");
- }
+
+ /// Set to change the current working directory when spawning the child process.
+ cwd: ?[]const u8 = null,
+ /// Set to change the current working directory when spawning the child process.
+ /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190
+ /// Once that is done, `cwd` will be deprecated in favor of this field.
+ cwd_dir: ?Io.Dir = null,
+ /// Replaces the child environment when provided. The PATH value from here
+ /// is not used to resolve `argv[0]`; that resolution always uses parent
+ /// environment.
+ environ_map: ?*const Environ.Map = null,
+ expand_arg0: ArgExpansion = .no_expand,
+ /// When populated, a pipe will be created for the child process to
+ /// communicate progress back to the parent. The file descriptor of the
+ /// write end of the pipe will be specified in the `ZIG_PROGRESS`
+ /// environment variable inside the child process. The progress reported by
+ /// the child will be attached to this progress node in the parent process.
+ ///
+ /// The child's progress tree will be grafted into the parent's progress tree,
+ /// by substituting this node with the child's root node.
+ progress_node: std.Progress.Node = std.Progress.Node.none,
+
+ stdin: StdIo = .inherit,
+ stdout: StdIo = .inherit,
+ stderr: StdIo = .inherit,
+
+ /// Set to true to obtain rusage information for the child process.
+ /// Depending on the target platform and implementation status, the
+ /// requested statistics may or may not be available. If they are
+ /// available, then the `resource_usage_statistics` field will be populated
+ /// after calling `wait`.
+ /// On Linux and Darwin, this obtains rusage statistics from wait4().
+ request_resource_usage_statistics: bool = false,
+
+ /// Set to change the user id when spawning the child process.
+ uid: ?posix.uid_t = null,
+ /// Set to change the group id when spawning the child process.
+ gid: ?posix.gid_t = null,
+ /// Set to change the process group id when spawning the child process.
+ pgid: ?posix.pid_t = null,
+
+ /// Start child process in suspended state.
+ /// For Posix systems it's started as if SIGSTOP was sent.
+ start_suspended: bool = false,
+ /// Windows-only. Sets the CREATE_NO_WINDOW flag in CreateProcess.
+ create_no_window: bool = false,
+ /// Darwin-only. Disable ASLR for the child process.
+ disable_aslr: bool = false,
+
+ /// Behavior of the child process's standard input, output, and error streams.
+ pub const StdIo = union(enum) {
+ /// Inherit the corresponding stream from the parent process.
+ inherit,
+ /// Pass an already open file from the parent to the child.
+ file: File,
+ /// Pass a null stream to the child process by opening "/dev/null" on POSIX
+ /// and "NUL" on Windows.
+ ignore,
+ /// Create a new pipe for the stream.
+ ///
+ /// The corresponding field (`stdout`, `stderr`, or `stdin`) will be
+ /// assigned a `File` object that can be used to read from or write to the
+ /// pipe.
+ pipe,
+ /// Spawn the child process with the corresponding stream missing. This
+ /// will likely result in the child encountering EBADF if it tries to use
+ /// stdin, stdout, or stderr, or if only one stream is closed, it will
+ /// result in them getting mixed up. Generally, this option is for advanced
+ /// use cases only.
+ close,
};
+};
- return posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp);
+/// Creates a child process.
+///
+/// `argv[0]` is the name of the program to execute. If it is not already a
+/// file path (i.e. it contains '/'), it is resolved into a file path based on
+/// PATH from the parent environment.
+pub fn spawn(io: Io, options: SpawnOptions) SpawnError!Child {
+ return io.vtable.processSpawn(io.userdata, options);
+}
+
+/// Creates a child process.
+///
+/// `argv[0]` is the file path of the program to execute, relative to `dir`. It
+/// is *always* treated as a file path, even if it does not contain '/'.
+pub fn spawnPath(io: Io, dir: Io.Dir, options: SpawnOptions) SpawnError!Child {
+ return io.vtable.processSpawnPath(io.userdata, dir, options);
+}
+
+pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{
+ StdoutStreamTooLong,
+ StderrStreamTooLong,
+};
+
+pub const RunOptions = struct {
+ argv: []const []const u8,
+ max_output_bytes: usize = 50 * 1024,
+
+ /// Set to change the current working directory when spawning the child process.
+ cwd: ?[]const u8 = null,
+ /// Set to change the current working directory when spawning the child process.
+ /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190
+ /// Once that is done, `cwd` will be deprecated in favor of this field.
+ cwd_dir: ?Io.Dir = null,
+ /// Replaces the child environment when provided. The PATH value from here
+ /// is not used to resolve `argv[0]`; that resolution always uses parent
+ /// environment.
+ environ_map: ?*const Environ.Map = null,
+ expand_arg0: ArgExpansion = .no_expand,
+ /// When populated, a pipe will be created for the child process to
+ /// communicate progress back to the parent. The file descriptor of the
+ /// write end of the pipe will be specified in the `ZIG_PROGRESS`
+ /// environment variable inside the child process. The progress reported by
+ /// the child will be attached to this progress node in the parent process.
+ ///
+ /// The child's progress tree will be grafted into the parent's progress tree,
+ /// by substituting this node with the child's root node.
+ progress_node: std.Progress.Node = std.Progress.Node.none,
+ /// Windows-only. Sets the CREATE_NO_WINDOW flag in CreateProcess.
+ create_no_window: bool = true,
+ /// Darwin-only. Disable ASLR for the child process.
+ disable_aslr: bool = false,
+};
+
+pub const RunResult = struct {
+ term: Child.Term,
+ stdout: []u8,
+ stderr: []u8,
+};
+
+/// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
+/// If it succeeds, the caller owns result.stdout and result.stderr memory.
+pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult {
+ var child = try spawn(io, .{
+ .argv = options.argv,
+ .cwd = options.cwd,
+ .cwd_dir = options.cwd_dir,
+ .environ_map = options.environ_map,
+ .expand_arg0 = options.expand_arg0,
+ .progress_node = options.progress_node,
+ .create_no_window = options.create_no_window,
+ .disable_aslr = options.disable_aslr,
+
+ .stdin = .ignore,
+ .stdout = .pipe,
+ .stderr = .pipe,
+ });
+ defer child.kill(io);
+
+ var stdout: std.ArrayList(u8) = .empty;
+ defer stdout.deinit(gpa);
+ var stderr: std.ArrayList(u8) = .empty;
+ defer stderr.deinit(gpa);
+
+ try child.collectOutput(gpa, &stdout, &stderr, options.max_output_bytes);
+
+ return .{
+ .stdout = try stdout.toOwnedSlice(gpa),
+ .stderr = try stderr.toOwnedSlice(gpa),
+ .term = try child.wait(io),
+ };
}
pub const TotalSystemMemoryError = error{
@@ -1903,215 +664,6 @@ test raiseFileDescriptorLimit {
raiseFileDescriptorLimit();
}
-pub const CreateEnvironOptions = struct {
- /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified.
- /// If non-null, negative means to remove the environment variable, and >= 0
- /// means to provide it with the given integer.
- zig_progress_fd: ?i32 = null,
-};
-
-/// Creates a null-delimited environment variable block in the format
-/// expected by POSIX, from a hash map plus options.
-pub fn createEnvironFromMap(
- arena: Allocator,
- map: *const EnvMap,
- options: CreateEnvironOptions,
-) Allocator.Error![:null]?[*:0]u8 {
- const ZigProgressAction = enum { nothing, edit, delete, add };
- const zig_progress_action: ZigProgressAction = a: {
- const fd = options.zig_progress_fd orelse break :a .nothing;
- const contains = map.get("ZIG_PROGRESS") != null;
- if (fd >= 0) {
- break :a if (contains) .edit else .add;
- } else {
- if (contains) break :a .delete;
- }
- break :a .nothing;
- };
-
- const envp_count: usize = c: {
- var count: usize = map.count();
- switch (zig_progress_action) {
- .add => count += 1,
- .delete => count -= 1,
- .nothing, .edit => {},
- }
- break :c count;
- };
-
- const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);
- var i: usize = 0;
-
- if (zig_progress_action == .add) {
- envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
- i += 1;
- }
-
- {
- var it = map.iterator();
- while (it.next()) |pair| {
- if (mem.eql(u8, pair.key_ptr.*, "ZIG_PROGRESS")) switch (zig_progress_action) {
- .add => unreachable,
- .delete => continue,
- .edit => {
- envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{
- pair.key_ptr.*, options.zig_progress_fd.?,
- }, 0);
- i += 1;
- continue;
- },
- .nothing => {},
- };
-
- envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0);
- i += 1;
- }
- }
-
- assert(i == envp_count);
- return envp_buf;
-}
-
-/// Creates a null-delimited environment variable block in the format
-/// expected by POSIX, from a hash map plus options.
-pub fn createEnvironFromExisting(
- arena: Allocator,
- existing: [*:null]const ?[*:0]const u8,
- options: CreateEnvironOptions,
-) Allocator.Error![:null]?[*:0]u8 {
- const existing_count, const contains_zig_progress = c: {
- var count: usize = 0;
- var contains = false;
- while (existing[count]) |line| : (count += 1) {
- contains = contains or mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS");
- }
- break :c .{ count, contains };
- };
- const ZigProgressAction = enum { nothing, edit, delete, add };
- const zig_progress_action: ZigProgressAction = a: {
- const fd = options.zig_progress_fd orelse break :a .nothing;
- if (fd >= 0) {
- break :a if (contains_zig_progress) .edit else .add;
- } else {
- if (contains_zig_progress) break :a .delete;
- }
- break :a .nothing;
- };
-
- const envp_count: usize = c: {
- var count: usize = existing_count;
- switch (zig_progress_action) {
- .add => count += 1,
- .delete => count -= 1,
- .nothing, .edit => {},
- }
- break :c count;
- };
-
- const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);
- var i: usize = 0;
- var existing_index: usize = 0;
-
- if (zig_progress_action == .add) {
- envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
- i += 1;
- }
-
- while (existing[existing_index]) |line| : (existing_index += 1) {
- if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) {
- .add => unreachable,
- .delete => continue,
- .edit => {
- envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
- i += 1;
- continue;
- },
- .nothing => {},
- };
- envp_buf[i] = try arena.dupeZ(u8, mem.span(line));
- i += 1;
- }
-
- assert(i == envp_count);
- return envp_buf;
-}
-
-pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) Allocator.Error![:null]?[*:0]u8 {
- return createEnvironFromMap(arena, env_map, .{});
-}
-
-test createNullDelimitedEnvMap {
- const allocator = testing.allocator;
- var envmap = EnvMap.init(allocator);
- defer envmap.deinit();
-
- try envmap.put("HOME", "/home/ifreund");
- try envmap.put("WAYLAND_DISPLAY", "wayland-1");
- try envmap.put("DISPLAY", ":1");
- try envmap.put("DEBUGINFOD_URLS", " ");
- try envmap.put("XCURSOR_SIZE", "24");
-
- var arena = std.heap.ArenaAllocator.init(allocator);
- defer arena.deinit();
- const environ = try createNullDelimitedEnvMap(arena.allocator(), &envmap);
-
- try testing.expectEqual(@as(usize, 5), environ.len);
-
- inline for (.{
- "HOME=/home/ifreund",
- "WAYLAND_DISPLAY=wayland-1",
- "DISPLAY=:1",
- "DEBUGINFOD_URLS= ",
- "XCURSOR_SIZE=24",
- }) |target| {
- for (environ) |variable| {
- if (mem.eql(u8, mem.span(variable orelse continue), target)) break;
- } else {
- try testing.expect(false); // Environment variable not found
- }
- }
-}
-
-/// Caller must free result.
-pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ![]u16 {
- // count bytes needed
- const max_chars_needed = x: {
- // Only need 2 trailing NUL code units for an empty environment
- var max_chars_needed: usize = if (env_map.count() == 0) 2 else 1;
- var it = env_map.iterator();
- while (it.next()) |pair| {
- // +1 for '='
- // +1 for null byte
- max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2;
- }
- break :x max_chars_needed;
- };
- const result = try allocator.alloc(u16, max_chars_needed);
- errdefer allocator.free(result);
-
- var it = env_map.iterator();
- var i: usize = 0;
- while (it.next()) |pair| {
- i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*);
- result[i] = '=';
- i += 1;
- i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*);
- result[i] = 0;
- i += 1;
- }
- result[i] = 0;
- i += 1;
- // An empty environment is a special case that requires a redundant
- // NUL terminator. CreateProcess will read the second code unit even
- // though theoretically the first should be enough to recognize that the
- // environment is empty (see https://nullprogram.com/blog/2023/08/23/)
- if (env_map.count() == 0) {
- result[i] = 0;
- i += 1;
- }
- return try allocator.realloc(result, i);
-}
-
/// Logs an error and then terminates the process with exit code 1.
pub fn fatal(comptime format: []const u8, format_arguments: anytype) noreturn {
std.log.err(format, format_arguments);
diff --git a/lib/std/process/Args.zig b/lib/std/process/Args.zig
new file mode 100644
index 0000000000000000000000000000000000000000..8e6caf2d651d407a2baaab7b276f0f187381f068
--- /dev/null
+++ b/lib/std/process/Args.zig
@@ -0,0 +1,976 @@
+const Args = @This();
+
+const builtin = @import("builtin");
+const native_os = builtin.os.tag;
+
+const std = @import("../std.zig");
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const testing = std.testing;
+
+vector: Vector,
+
+/// On WASI without libc, this is `void` because the environment has to be
+/// queried and heap-allocated at runtime.
+pub const Vector = switch (native_os) {
+ .windows => []const u16, // WTF-16 encoded
+ .wasi => switch (builtin.link_libc) {
+ false => void,
+ true => []const [*:0]const u8,
+ },
+ .freestanding, .other => void,
+ else => []const [*:0]const u8,
+};
+
+/// Cross-platform access to command line one argument at a time.
+pub const Iterator = struct {
+ const Inner = switch (native_os) {
+ .windows => Windows,
+ .wasi => if (builtin.link_libc) Posix else Wasi,
+ else => Posix,
+ };
+
+ inner: Inner,
+
+ /// Initialize the args iterator. Consider using `initAllocator` instead
+ /// for cross-platform compatibility.
+ pub fn init(a: Args) Iterator {
+ if (native_os == .wasi) @compileError("In WASI, use initAllocator instead.");
+ if (native_os == .windows) @compileError("In Windows, use initAllocator instead.");
+ return .{ .inner = .init(a) };
+ }
+
+ pub const InitError = Inner.InitError;
+
+ /// You must deinitialize iterator's internal buffers by calling `deinit` when done.
+ pub fn initAllocator(a: Args, gpa: Allocator) InitError!Iterator {
+ if (native_os == .wasi and !builtin.link_libc) {
+ return .{ .inner = try .init(gpa) };
+ }
+ if (native_os == .windows) {
+ return .{ .inner = try .init(gpa, a.vector) };
+ }
+
+ return .{ .inner = .init(a) };
+ }
+
+ /// Return subsequent argument, or `null` if no more remaining.
+ ///
+ /// Returned slice is pointing to the iterator's internal buffer.
+ /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
+ /// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
+ pub fn next(it: *Iterator) ?[:0]const u8 {
+ return it.inner.next();
+ }
+
+ /// Parse past 1 argument without capturing it.
+ /// Returns `true` if skipped an arg, `false` if we are at the end.
+ pub fn skip(it: *Iterator) bool {
+ return it.inner.skip();
+ }
+
+ /// Required to release resources if the iterator was initialized with
+ /// `initAllocator` function.
+ pub fn deinit(it: *Iterator) void {
+ // Unless we're targeting WASI or Windows, this is a no-op.
+ if (native_os == .wasi and !builtin.link_libc) it.inner.deinit();
+ if (native_os == .windows) it.inner.deinit();
+ }
+
+ /// Iterator that implements the Windows command-line parsing algorithm.
+ ///
+ /// The implementation is intended to be compatible with the post-2008 C runtime,
+ /// but is *not* intended to be compatible with `CommandLineToArgvW` since
+ /// `CommandLineToArgvW` uses the pre-2008 parsing rules.
+ ///
+ /// This iterator faithfully implements the parsing behavior observed from the C runtime with
+ /// one exception: if the command-line string is empty, the iterator will immediately complete
+ /// without returning any arguments (whereas the C runtime will return a single argument
+ /// representing the name of the current executable).
+ ///
+ /// The essential parts of the algorithm are described in Microsoft's documentation:
+ ///
+ /// - https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170#parsing-c-command-line-arguments
+ ///
+ /// David Deley explains some additional undocumented quirks in great detail:
+ ///
+ /// - https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES
+ pub const Windows = struct {
+ allocator: Allocator,
+ /// Encoded as WTF-16 LE.
+ cmd_line: []const u16,
+ index: usize = 0,
+ /// Owned by the iterator. Long enough to hold contiguous NUL-terminated slices
+ /// of each argument encoded as WTF-8.
+ buffer: []u8,
+ start: usize = 0,
+ end: usize = 0,
+
+ pub const InitError = error{OutOfMemory};
+
+ /// `cmd_line_w` *must* be a WTF16-LE-encoded string.
+ ///
+ /// The iterator stores and uses `cmd_line_w`, so its memory must be valid for
+ /// at least as long as the returned Windows.
+ pub fn init(gpa: Allocator, cmd_line_w: []const u16) Windows.InitError!Windows {
+ const wtf8_len = std.unicode.calcWtf8Len(cmd_line_w);
+
+ // This buffer must be large enough to contain contiguous NUL-terminated slices
+ // of each argument.
+ // - During parsing, the length of a parsed argument will always be equal to
+ // to less than its unparsed length
+ // - The first argument needs one extra byte of space allocated for its NUL
+ // terminator, but for each subsequent argument the necessary whitespace
+ // between arguments guarantees room for their NUL terminator(s).
+ const buffer = try gpa.alloc(u8, wtf8_len + 1);
+ errdefer gpa.free(buffer);
+
+ return .{
+ .allocator = gpa,
+ .cmd_line = cmd_line_w,
+ .buffer = buffer,
+ };
+ }
+
+ /// Returns the next argument and advances the iterator. Returns `null` if at the end of the
+ /// command-line string. The iterator owns the returned slice.
+ /// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
+ pub fn next(self: *Windows) ?[:0]const u8 {
+ return self.nextWithStrategy(next_strategy);
+ }
+
+ /// Skips the next argument and advances the iterator. Returns `true` if an argument was
+ /// skipped, `false` if at the end of the command-line string.
+ pub fn skip(self: *Windows) bool {
+ return self.nextWithStrategy(skip_strategy);
+ }
+
+ const next_strategy = struct {
+ const T = ?[:0]const u8;
+
+ const eof = null;
+
+ /// Returns '\' if any backslashes are emitted, otherwise returns `last_emitted_code_unit`.
+ fn emitBackslashes(self: *Windows, count: usize, last_emitted_code_unit: ?u16) ?u16 {
+ for (0..count) |_| {
+ self.buffer[self.end] = '\\';
+ self.end += 1;
+ }
+ return if (count != 0) '\\' else last_emitted_code_unit;
+ }
+
+ /// If `last_emitted_code_unit` and `code_unit` form a surrogate pair, then
+ /// the previously emitted high surrogate is overwritten by the codepoint encoded
+ /// by the surrogate pair, and `null` is returned.
+ /// Otherwise, `code_unit` is emitted and returned.
+ fn emitCharacter(self: *Windows, code_unit: u16, last_emitted_code_unit: ?u16) ?u16 {
+ // Because we are emitting WTF-8, we need to
+ // check to see if we've emitted two consecutive surrogate
+ // codepoints that form a valid surrogate pair in order
+ // to ensure that we're always emitting well-formed WTF-8
+ // (https://wtf-8.codeberg.page/#concatenating).
+ //
+ // If we do have a valid surrogate pair, we need to emit
+ // the UTF-8 sequence for the codepoint that they encode
+ // instead of the WTF-8 encoding for the two surrogate pairs
+ // separately.
+ //
+ // This is relevant when dealing with a WTF-16 encoded
+ // command line like this:
+ // "<0xD801>"<0xDC37>
+ // which would get parsed and converted to WTF-8 as:
+ // <0xED><0xA0><0x81><0xED><0xB0><0xB7>
+ // but instead, we need to recognize the surrogate pair
+ // and emit the codepoint it encodes, which in this
+ // example is U+10437 (𐐷), which is encoded in UTF-8 as:
+ // <0xF0><0x90><0x90><0xB7>
+ if (last_emitted_code_unit != null and
+ std.unicode.utf16IsLowSurrogate(code_unit) and
+ std.unicode.utf16IsHighSurrogate(last_emitted_code_unit.?))
+ {
+ const codepoint = std.unicode.utf16DecodeSurrogatePair(&.{ last_emitted_code_unit.?, code_unit }) catch unreachable;
+
+ // Unpaired surrogate is 3 bytes long
+ const dest = self.buffer[self.end - 3 ..];
+ const len = std.unicode.utf8Encode(codepoint, dest) catch unreachable;
+ // All codepoints that require a surrogate pair (> U+FFFF) are encoded as 4 bytes
+ assert(len == 4);
+ self.end += 1;
+ return null;
+ }
+
+ const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable;
+ self.end += wtf8_len;
+ return code_unit;
+ }
+
+ fn yieldArg(self: *Windows) [:0]const u8 {
+ self.buffer[self.end] = 0;
+ const arg = self.buffer[self.start..self.end :0];
+ self.end += 1;
+ self.start = self.end;
+ return arg;
+ }
+ };
+
+ const skip_strategy = struct {
+ const T = bool;
+
+ const eof = false;
+
+ fn emitBackslashes(_: *Windows, _: usize, last_emitted_code_unit: ?u16) ?u16 {
+ return last_emitted_code_unit;
+ }
+
+ fn emitCharacter(_: *Windows, _: u16, last_emitted_code_unit: ?u16) ?u16 {
+ return last_emitted_code_unit;
+ }
+
+ fn yieldArg(_: *Windows) bool {
+ return true;
+ }
+ };
+
+ fn nextWithStrategy(self: *Windows, comptime strategy: type) strategy.T {
+ var last_emitted_code_unit: ?u16 = null;
+ // The first argument (the executable name) uses different parsing rules.
+ if (self.index == 0) {
+ if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) {
+ // Immediately complete the iterator.
+ // The C runtime would return the name of the current executable here.
+ return strategy.eof;
+ }
+
+ var inside_quotes = false;
+ while (true) : (self.index += 1) {
+ const char = if (self.index != self.cmd_line.len)
+ std.mem.littleToNative(u16, self.cmd_line[self.index])
+ else
+ 0;
+ switch (char) {
+ 0 => {
+ return strategy.yieldArg(self);
+ },
+ '"' => {
+ inside_quotes = !inside_quotes;
+ },
+ ' ', '\t' => {
+ if (inside_quotes) {
+ last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
+ } else {
+ self.index += 1;
+ return strategy.yieldArg(self);
+ }
+ },
+ else => {
+ last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
+ },
+ }
+ }
+ }
+
+ // Skip spaces and tabs. The iterator completes if we reach the end of the string here.
+ while (true) : (self.index += 1) {
+ const char = if (self.index != self.cmd_line.len)
+ std.mem.littleToNative(u16, self.cmd_line[self.index])
+ else
+ 0;
+ switch (char) {
+ 0 => return strategy.eof,
+ ' ', '\t' => continue,
+ else => break,
+ }
+ }
+
+ // Parsing rules for subsequent arguments:
+ //
+ // - The end of the string always terminates the current argument.
+ // - When not in 'inside_quotes' mode, a space or tab terminates the current argument.
+ // - 2n backslashes followed by a quote emit n backslashes (note: n can be zero).
+ // If in 'inside_quotes' and the quote is immediately followed by a second quote,
+ // one quote is emitted and the other is skipped, otherwise, the quote is skipped
+ // and 'inside_quotes' is toggled.
+ // - 2n + 1 backslashes followed by a quote emit n backslashes followed by a quote.
+ // - n backslashes not followed by a quote emit n backslashes.
+ var backslash_count: usize = 0;
+ var inside_quotes = false;
+ while (true) : (self.index += 1) {
+ const char = if (self.index != self.cmd_line.len)
+ std.mem.littleToNative(u16, self.cmd_line[self.index])
+ else
+ 0;
+ switch (char) {
+ 0 => {
+ last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
+ return strategy.yieldArg(self);
+ },
+ ' ', '\t' => {
+ last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
+ backslash_count = 0;
+ if (inside_quotes) {
+ last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
+ } else return strategy.yieldArg(self);
+ },
+ '"' => {
+ const char_is_escaped_quote = backslash_count % 2 != 0;
+ last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count / 2, last_emitted_code_unit);
+ backslash_count = 0;
+ if (char_is_escaped_quote) {
+ last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit);
+ } else {
+ if (inside_quotes and
+ self.index + 1 != self.cmd_line.len and
+ std.mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"')
+ {
+ last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit);
+ self.index += 1;
+ } else {
+ inside_quotes = !inside_quotes;
+ }
+ }
+ },
+ '\\' => {
+ backslash_count += 1;
+ },
+ else => {
+ last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit);
+ backslash_count = 0;
+ last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit);
+ },
+ }
+ }
+ }
+
+ /// Frees the iterator's copy of the command-line string and all previously returned
+ /// argument slices.
+ pub fn deinit(self: *Windows) void {
+ self.allocator.free(self.buffer);
+ }
+ };
+
+ pub const Posix = struct {
+ remaining: Vector,
+
+ pub const InitError = error{};
+
+ pub fn init(a: Args) Posix {
+ return .{ .remaining = a.vector };
+ }
+
+ pub fn next(it: *Posix) ?[:0]const u8 {
+ if (it.remaining.len == 0) return null;
+ const arg = it.remaining[0];
+ it.remaining = it.remaining[1..];
+ return std.mem.sliceTo(arg, 0);
+ }
+
+ pub fn skip(it: *Posix) bool {
+ if (it.remaining.len == 0) return false;
+ it.remaining = it.remaining[1..];
+ return true;
+ }
+ };
+
+ pub const Wasi = struct {
+ allocator: Allocator,
+ index: usize,
+ args: [][:0]u8,
+
+ pub const InitError = error{OutOfMemory} || std.posix.UnexpectedError;
+
+ /// You must call deinit to free the internal buffer of the
+ /// iterator after you are done.
+ pub fn init(allocator: Allocator) Wasi.InitError!Wasi {
+ const fetched_args = try Wasi.internalInit(allocator);
+ return Wasi{
+ .allocator = allocator,
+ .index = 0,
+ .args = fetched_args,
+ };
+ }
+
+ fn internalInit(allocator: Allocator) Wasi.InitError![][:0]u8 {
+ var count: usize = undefined;
+ var buf_size: usize = undefined;
+
+ switch (std.os.wasi.args_sizes_get(&count, &buf_size)) {
+ .SUCCESS => {},
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+
+ if (count == 0) {
+ return &[_][:0]u8{};
+ }
+
+ const argv = try allocator.alloc([*:0]u8, count);
+ defer allocator.free(argv);
+
+ const argv_buf = try allocator.alloc(u8, buf_size);
+
+ switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) {
+ .SUCCESS => {},
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+
+ var result_args = try allocator.alloc([:0]u8, count);
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ result_args[i] = std.mem.sliceTo(argv[i], 0);
+ }
+
+ return result_args;
+ }
+
+ pub fn next(self: *Wasi) ?[:0]const u8 {
+ if (self.index == self.args.len) return null;
+
+ const arg = self.args[self.index];
+ self.index += 1;
+ return arg;
+ }
+
+ pub fn skip(self: *Wasi) bool {
+ if (self.index == self.args.len) return false;
+
+ self.index += 1;
+ return true;
+ }
+
+ /// Call to free the internal buffer of the iterator.
+ pub fn deinit(self: *Wasi) void {
+ // Nothing is allocated when there are no args
+ if (self.args.len == 0) return;
+
+ const last_item = self.args[self.args.len - 1];
+ const last_byte_addr = @intFromPtr(last_item.ptr) + last_item.len + 1; // null terminated
+ const first_item_ptr = self.args[0].ptr;
+ const len = last_byte_addr - @intFromPtr(first_item_ptr);
+ self.allocator.free(first_item_ptr[0..len]);
+ self.allocator.free(self.args);
+ }
+ };
+};
+
+/// Holds the command-line arguments, with the program name as the first entry.
+/// Use `iterateAllocator` for cross-platform code.
+pub fn iterate(a: Args) Iterator {
+ return .init(a);
+}
+
+/// You must deinitialize iterator's internal buffers by calling `deinit` when
+/// done.
+pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator {
+ return .initAllocator(a, gpa);
+}
+
+pub const ToSliceError = Iterator.Windows.InitError || Iterator.Wasi.InitError;
+
+/// Returned value may reference several allocations and may point into `a`.
+/// Thefore, an arena-style allocator must be used.
+///
+/// * On Windows, the result is encoded as
+/// [WTF-8](https://wtf-8.codeberg.page/).
+/// * On other platforms, the result is an opaque sequence of bytes with no
+/// particular encoding.
+///
+/// See also:
+/// * `iterate`
+/// * `iterateAllocator`
+pub fn toSlice(a: Args, arena: Allocator) ToSliceError![]const [:0]const u8 {
+ if (native_os == .windows) {
+ var it = try a.iterateAllocator(arena);
+ var contents: std.ArrayList(u8) = .empty;
+ var slice_list: std.ArrayList(usize) = .empty;
+ while (it.next()) |arg| {
+ try contents.appendSlice(arena, arg[0 .. arg.len + 1]);
+ try slice_list.append(arena, arg.len);
+ }
+ const contents_slice = contents.items;
+ const slice_sizes = slice_list.items;
+ const slice_list_bytes = std.math.mul(usize, @sizeOf([]u8), slice_sizes.len) catch return error.OutOfMemory;
+ const total_bytes = std.math.add(usize, slice_list_bytes, contents_slice.len) catch return error.OutOfMemory;
+ const buf = try arena.alignedAlloc(u8, .of([]u8), total_bytes);
+ errdefer arena.free(buf);
+
+ const result_slice_list = std.mem.bytesAsSlice([:0]u8, buf[0..slice_list_bytes]);
+ const result_contents = buf[slice_list_bytes..];
+ @memcpy(result_contents[0..contents_slice.len], contents_slice);
+
+ var contents_index: usize = 0;
+ for (slice_sizes, 0..) |len, i| {
+ const new_index = contents_index + len;
+ result_slice_list[i] = result_contents[contents_index..new_index :0];
+ contents_index = new_index + 1;
+ }
+
+ return result_slice_list;
+ } else if (native_os == .wasi and !builtin.link_libc) {
+ var count: usize = undefined;
+ var buf_size: usize = undefined;
+
+ switch (std.os.wasi.args_sizes_get(&count, &buf_size)) {
+ .SUCCESS => {},
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+
+ if (count == 0) return &.{};
+
+ const argv = try arena.alloc([*:0]u8, count);
+ const argv_buf = try arena.alloc(u8, buf_size);
+
+ switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) {
+ .SUCCESS => {},
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+
+ const args = try arena.alloc([:0]const u8, count);
+ for (args, argv) |*dst, src| dst.* = std.mem.sliceTo(src, 0);
+ return args;
+ } else {
+ const args = try arena.alloc([:0]const u8, a.vector.len);
+ for (args, a.vector) |*dst, src| dst.* = std.mem.sliceTo(src, 0);
+ return args;
+ }
+}
+
+test "Iterator.Windows" {
+ const t = testIteratorWindows;
+
+ try t(
+ \\"C:\Program Files\zig\zig.exe" run .\src\main.zig -target x86_64-windows-gnu -O ReleaseSafe -- --emoji=🗿 --eval="new Regex(\"Dwayne \\\"The Rock\\\" Johnson\")"
+ , &.{
+ \\C:\Program Files\zig\zig.exe
+ ,
+ \\run
+ ,
+ \\.\src\main.zig
+ ,
+ \\-target
+ ,
+ \\x86_64-windows-gnu
+ ,
+ \\-O
+ ,
+ \\ReleaseSafe
+ ,
+ \\--
+ ,
+ \\--emoji=🗿
+ ,
+ \\--eval=new Regex("Dwayne \"The Rock\" Johnson")
+ ,
+ });
+
+ // Empty
+ try t("", &.{});
+
+ // Separators
+ try t("aa bb cc", &.{ "aa", "bb", "cc" });
+ try t("aa\tbb\tcc", &.{ "aa", "bb", "cc" });
+ try t("aa\nbb\ncc", &.{"aa\nbb\ncc"});
+ try t("aa\r\nbb\r\ncc", &.{"aa\r\nbb\r\ncc"});
+ try t("aa\rbb\rcc", &.{"aa\rbb\rcc"});
+ try t("aa\x07bb\x07cc", &.{"aa\x07bb\x07cc"});
+ try t("aa\x7Fbb\x7Fcc", &.{"aa\x7Fbb\x7Fcc"});
+ try t("aa🦎bb🦎cc", &.{"aa🦎bb🦎cc"});
+
+ // Leading/trailing whitespace
+ try t(" ", &.{""});
+ try t(" aa bb ", &.{ "", "aa", "bb" });
+ try t("\t\t", &.{""});
+ try t("\t\taa\t\tbb\t\t", &.{ "", "aa", "bb" });
+ try t("\n\n", &.{"\n\n"});
+ try t("\n\naa\n\nbb\n\n", &.{"\n\naa\n\nbb\n\n"});
+
+ // Executable name with quotes/backslashes
+ try t("\"aa bb\tcc\ndd\"", &.{"aa bb\tcc\ndd"});
+ try t("\"", &.{""});
+ try t("\"\"", &.{""});
+ try t("\"\"\"", &.{""});
+ try t("\"\"\"\"", &.{""});
+ try t("\"\"\"\"\"", &.{""});
+ try t("aa\"bb\"cc\"dd", &.{"aabbccdd"});
+ try t("aa\"bb cc\"dd", &.{"aabb ccdd"});
+ try t("\"aa\\\"bb\"", &.{"aa\\bb"});
+ try t("\"aa\\\\\"", &.{"aa\\\\"});
+ try t("aa\\\"bb", &.{"aa\\bb"});
+ try t("aa\\\\\"bb", &.{"aa\\\\bb"});
+
+ // Arguments with quotes/backslashes
+ try t(". \"aa bb\tcc\ndd\"", &.{ ".", "aa bb\tcc\ndd" });
+ try t(". aa\" \"bb\"\t\"cc\"\n\"dd\"", &.{ ".", "aa bb\tcc\ndd" });
+ try t(". ", &.{"."});
+ try t(". \"", &.{ ".", "" });
+ try t(". \"\"", &.{ ".", "" });
+ try t(". \"\"\"", &.{ ".", "\"" });
+ try t(". \"\"\"\"", &.{ ".", "\"" });
+ try t(". \"\"\"\"\"", &.{ ".", "\"\"" });
+ try t(". \"\"\"\"\"\"", &.{ ".", "\"\"" });
+ try t(". \" \"", &.{ ".", " " });
+ try t(". \" \"\"", &.{ ".", " \"" });
+ try t(". \" \"\"\"", &.{ ".", " \"" });
+ try t(". \" \"\"\"\"", &.{ ".", " \"\"" });
+ try t(". \" \"\"\"\"\"", &.{ ".", " \"\"" });
+ try t(". \" \"\"\"\"\"\"", &.{ ".", " \"\"\"" });
+ try t(". \\\"", &.{ ".", "\"" });
+ try t(". \\\"\"", &.{ ".", "\"" });
+ try t(". \\\"\"\"", &.{ ".", "\"" });
+ try t(". \\\"\"\"\"", &.{ ".", "\"\"" });
+ try t(". \\\"\"\"\"\"", &.{ ".", "\"\"" });
+ try t(". \\\"\"\"\"\"\"", &.{ ".", "\"\"\"" });
+ try t(". \" \\\"", &.{ ".", " \"" });
+ try t(". \" \\\"\"", &.{ ".", " \"" });
+ try t(". \" \\\"\"\"", &.{ ".", " \"\"" });
+ try t(". \" \\\"\"\"\"", &.{ ".", " \"\"" });
+ try t(". \" \\\"\"\"\"\"", &.{ ".", " \"\"\"" });
+ try t(". \" \\\"\"\"\"\"\"", &.{ ".", " \"\"\"" });
+ try t(". aa\\bb\\\\cc\\\\\\dd", &.{ ".", "aa\\bb\\\\cc\\\\\\dd" });
+ try t(". \\\\\\\"aa bb\"", &.{ ".", "\\\"aa", "bb" });
+ try t(". \\\\\\\\\"aa bb\"", &.{ ".", "\\\\aa bb" });
+
+ // From https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#results-of-parsing-command-lines
+ try t(
+ \\foo.exe "abc" d e
+ , &.{ "foo.exe", "abc", "d", "e" });
+ try t(
+ \\foo.exe a\\b d"e f"g h
+ , &.{ "foo.exe", "a\\\\b", "de fg", "h" });
+ try t(
+ \\foo.exe a\\\"b c d
+ , &.{ "foo.exe", "a\\\"b", "c", "d" });
+ try t(
+ \\foo.exe a\\\\"b c" d e
+ , &.{ "foo.exe", "a\\\\b c", "d", "e" });
+ try t(
+ \\foo.exe a"b"" c d
+ , &.{ "foo.exe", "ab\" c d" });
+
+ // From https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX
+ try t("foo.exe CallMeIshmael", &.{ "foo.exe", "CallMeIshmael" });
+ try t("foo.exe \"Call Me Ishmael\"", &.{ "foo.exe", "Call Me Ishmael" });
+ try t("foo.exe Cal\"l Me I\"shmael", &.{ "foo.exe", "Call Me Ishmael" });
+ try t("foo.exe CallMe\\\"Ishmael", &.{ "foo.exe", "CallMe\"Ishmael" });
+ try t("foo.exe \"CallMe\\\"Ishmael\"", &.{ "foo.exe", "CallMe\"Ishmael" });
+ try t("foo.exe \"Call Me Ishmael\\\\\"", &.{ "foo.exe", "Call Me Ishmael\\" });
+ try t("foo.exe \"CallMe\\\\\\\"Ishmael\"", &.{ "foo.exe", "CallMe\\\"Ishmael" });
+ try t("foo.exe a\\\\\\b", &.{ "foo.exe", "a\\\\\\b" });
+ try t("foo.exe \"a\\\\\\b\"", &.{ "foo.exe", "a\\\\\\b" });
+
+ // Surrogate pair encoding of 𐐷 separated by quotes.
+ // Encoded as WTF-16:
+ // "<0xD801>"<0xDC37>
+ // Encoded as WTF-8:
+ // "<0xED><0xA0><0x81>"<0xED><0xB0><0xB7>
+ // During parsing, the quotes drop out and the surrogate pair
+ // should end up encoded as its normal UTF-8 representation.
+ try t("foo.exe \"\xed\xa0\x81\"\xed\xb0\xb7", &.{ "foo.exe", "𐐷" });
+}
+
+fn testIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void {
+ const cmd_line_w = try std.unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line);
+ defer testing.allocator.free(cmd_line_w);
+
+ // next
+ {
+ var it = try Iterator.Windows.init(testing.allocator, cmd_line_w);
+ defer it.deinit();
+
+ for (expected_args) |expected| {
+ if (it.next()) |actual| {
+ try testing.expectEqualStrings(expected, actual);
+ } else {
+ return error.TestUnexpectedResult;
+ }
+ }
+ try testing.expect(it.next() == null);
+ }
+
+ // skip
+ {
+ var it = try Iterator.Windows.init(testing.allocator, cmd_line_w);
+ defer it.deinit();
+
+ for (0..expected_args.len) |_| {
+ try testing.expect(it.skip());
+ }
+ try testing.expect(!it.skip());
+ }
+}
+
+test "general parsing" {
+ try testGeneralCmdLine("a b\tc d", &.{ "a", "b", "c", "d" });
+ try testGeneralCmdLine("\"abc\" d e", &.{ "abc", "d", "e" });
+ try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &.{ "a\\\\\\b", "de fg", "h" });
+ try testGeneralCmdLine("a\\\\\\\"b c d", &.{ "a\\\"b", "c", "d" });
+ try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &.{ "a\\\\b c", "d", "e" });
+ try testGeneralCmdLine("a b\tc \"d f", &.{ "a", "b", "c", "d f" });
+ try testGeneralCmdLine("j k l\\", &.{ "j", "k", "l\\" });
+ try testGeneralCmdLine("\"\" x y z\\\\", &.{ "", "x", "y", "z\\\\" });
+
+ try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &.{
+ ".\\..\\zig-cache\\build",
+ "bin\\zig.exe",
+ ".\\..",
+ ".\\..\\zig-cache",
+ "--help",
+ });
+
+ try testGeneralCmdLine(
+ \\ 'foo' "bar"
+ , &.{ "'foo'", "bar" });
+}
+
+fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void {
+ var it = try IteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line);
+ defer it.deinit();
+ for (expected_args) |expected_arg| {
+ const arg = it.next().?;
+ try testing.expectEqualStrings(expected_arg, arg);
+ }
+ try testing.expect(it.next() == null);
+}
+
+/// Optional parameters for `IteratorGeneral`
+pub const IteratorGeneralOptions = struct {
+ comments: bool = false,
+ single_quotes: bool = false,
+};
+
+/// A general Iterator to parse a string into a set of arguments
+pub fn IteratorGeneral(comptime options: IteratorGeneralOptions) type {
+ return struct {
+ allocator: Allocator,
+ index: usize = 0,
+ cmd_line: []const u8,
+
+ /// Should the cmd_line field be free'd (using the allocator) on deinit()?
+ free_cmd_line_on_deinit: bool,
+
+ /// buffer MUST be long enough to hold the cmd_line plus a null terminator.
+ /// buffer will we free'd (using the allocator) on deinit()
+ buffer: []u8,
+ start: usize = 0,
+ end: usize = 0,
+
+ pub const Self = @This();
+
+ pub const InitError = error{OutOfMemory};
+
+ /// cmd_line_utf8 MUST remain valid and constant while using this instance
+ pub fn init(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self {
+ const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1);
+ errdefer allocator.free(buffer);
+
+ return Self{
+ .allocator = allocator,
+ .cmd_line = cmd_line_utf8,
+ .free_cmd_line_on_deinit = false,
+ .buffer = buffer,
+ };
+ }
+
+ /// cmd_line_utf8 will be free'd (with the allocator) on deinit()
+ pub fn initTakeOwnership(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self {
+ const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1);
+ errdefer allocator.free(buffer);
+
+ return Self{
+ .allocator = allocator,
+ .cmd_line = cmd_line_utf8,
+ .free_cmd_line_on_deinit = true,
+ .buffer = buffer,
+ };
+ }
+
+ // Skips over whitespace in the cmd_line.
+ // Returns false if the terminating sentinel is reached, true otherwise.
+ // Also skips over comments (if supported).
+ fn skipWhitespace(self: *Self) bool {
+ while (true) : (self.index += 1) {
+ const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0;
+ switch (character) {
+ 0 => return false,
+ ' ', '\t', '\r', '\n' => continue,
+ '#' => {
+ if (options.comments) {
+ while (true) : (self.index += 1) {
+ switch (self.cmd_line[self.index]) {
+ '\n' => break,
+ 0 => return false,
+ else => continue,
+ }
+ }
+ continue;
+ } else {
+ break;
+ }
+ },
+ else => break,
+ }
+ }
+ return true;
+ }
+
+ pub fn skip(self: *Self) bool {
+ if (!self.skipWhitespace()) {
+ return false;
+ }
+
+ var backslash_count: usize = 0;
+ var in_quote = false;
+ while (true) : (self.index += 1) {
+ const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0;
+ switch (character) {
+ 0 => return true,
+ '"', '\'' => {
+ if (!options.single_quotes and character == '\'') {
+ backslash_count = 0;
+ continue;
+ }
+ const quote_is_real = backslash_count % 2 == 0;
+ if (quote_is_real) {
+ in_quote = !in_quote;
+ }
+ },
+ '\\' => {
+ backslash_count += 1;
+ },
+ ' ', '\t', '\r', '\n' => {
+ if (!in_quote) {
+ return true;
+ }
+ backslash_count = 0;
+ },
+ else => {
+ backslash_count = 0;
+ continue;
+ },
+ }
+ }
+ }
+
+ /// Returns a slice of the internal buffer that contains the next argument.
+ /// Returns null when it reaches the end.
+ pub fn next(self: *Self) ?[:0]const u8 {
+ if (!self.skipWhitespace()) {
+ return null;
+ }
+
+ var backslash_count: usize = 0;
+ var in_quote = false;
+ while (true) : (self.index += 1) {
+ const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0;
+ switch (character) {
+ 0 => {
+ self.emitBackslashes(backslash_count);
+ self.buffer[self.end] = 0;
+ const token = self.buffer[self.start..self.end :0];
+ self.end += 1;
+ self.start = self.end;
+ return token;
+ },
+ '"', '\'' => {
+ if (!options.single_quotes and character == '\'') {
+ self.emitBackslashes(backslash_count);
+ backslash_count = 0;
+ self.emitCharacter(character);
+ continue;
+ }
+ const quote_is_real = backslash_count % 2 == 0;
+ self.emitBackslashes(backslash_count / 2);
+ backslash_count = 0;
+
+ if (quote_is_real) {
+ in_quote = !in_quote;
+ } else {
+ self.emitCharacter('"');
+ }
+ },
+ '\\' => {
+ backslash_count += 1;
+ },
+ ' ', '\t', '\r', '\n' => {
+ self.emitBackslashes(backslash_count);
+ backslash_count = 0;
+ if (in_quote) {
+ self.emitCharacter(character);
+ } else {
+ self.buffer[self.end] = 0;
+ const token = self.buffer[self.start..self.end :0];
+ self.end += 1;
+ self.start = self.end;
+ return token;
+ }
+ },
+ else => {
+ self.emitBackslashes(backslash_count);
+ backslash_count = 0;
+ self.emitCharacter(character);
+ },
+ }
+ }
+ }
+
+ fn emitBackslashes(self: *Self, emit_count: usize) void {
+ var i: usize = 0;
+ while (i < emit_count) : (i += 1) {
+ self.emitCharacter('\\');
+ }
+ }
+
+ fn emitCharacter(self: *Self, char: u8) void {
+ self.buffer[self.end] = char;
+ self.end += 1;
+ }
+
+ /// Call to free the internal buffer of the iterator.
+ pub fn deinit(self: *Self) void {
+ self.allocator.free(self.buffer);
+
+ if (self.free_cmd_line_on_deinit) {
+ self.allocator.free(self.cmd_line);
+ }
+ }
+ };
+}
+
+test "response file arg parsing" {
+ try testResponseFileCmdLine(
+ \\a b
+ \\c d\
+ , &.{ "a", "b", "c", "d\\" });
+ try testResponseFileCmdLine("a b c d\\", &.{ "a", "b", "c", "d\\" });
+
+ try testResponseFileCmdLine(
+ \\j
+ \\ k l # this is a comment \\ \\\ \\\\ "none" "\\" "\\\"
+ \\ "m" #another comment
+ \\
+ , &.{ "j", "k", "l", "m" });
+
+ try testResponseFileCmdLine(
+ \\ "" q ""
+ \\ "r s # t" "u\" v" #another comment
+ \\
+ , &.{ "", "q", "", "r s # t", "u\" v" });
+
+ try testResponseFileCmdLine(
+ \\ -l"advapi32" a# b#c d#
+ \\e\\\
+ , &.{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" });
+
+ try testResponseFileCmdLine(
+ \\ 'foo' "bar"
+ , &.{ "foo", "bar" });
+}
+
+fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void {
+ var it = try IteratorGeneral(.{ .comments = true, .single_quotes = true })
+ .init(std.testing.allocator, input_cmd_line);
+ defer it.deinit();
+ for (expected_args) |expected_arg| {
+ const arg = it.next().?;
+ try testing.expectEqualStrings(expected_arg, arg);
+ }
+ try testing.expect(it.next() == null);
+}
diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig
index 7c737c318b32d6f511aa435c95f79a7c08e9a0a8..17e15f208da5fbb7155ada08fe2a9006127be258 100644
--- a/lib/std/process/Child.zig
+++ b/lib/std/process/Child.zig
@@ -5,116 +5,38 @@ const native_os = builtin.os.tag;
const std = @import("../std.zig");
const Io = std.Io;
-const unicode = std.unicode;
-const fs = std.fs;
const process = std.process;
const File = std.Io.File;
-const windows = std.os.windows;
-const linux = std.os.linux;
-const posix = std.posix;
-const mem = std.mem;
-const EnvMap = std.process.EnvMap;
-const maxInt = std.math.maxInt;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
pub const Id = switch (native_os) {
- .windows => windows.HANDLE,
+ .windows => std.os.windows.HANDLE,
.wasi => void,
- else => posix.pid_t,
+ else => std.posix.pid_t,
};
-/// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.
+/// After `wait` or `kill` is called, this becomes `null`.
/// On Windows this is the hProcess.
/// On POSIX this is the pid.
-id: Id,
-thread_handle: if (native_os == .windows) windows.HANDLE else void,
-
-allocator: Allocator,
-
+id: ?Id,
+thread_handle: if (native_os == .windows) std.os.windows.HANDLE else void,
/// The writing end of the child process's standard input pipe.
-/// Usage requires `stdin_behavior == StdIo.Pipe`.
-/// Available after calling `spawn()`.
+/// Usage requires `process.SpawnOptions.StdIo.pipe`.
stdin: ?File,
-
/// The reading end of the child process's standard output pipe.
-/// Usage requires `stdout_behavior == StdIo.Pipe`.
-/// Available after calling `spawn()`.
+/// Usage requires `process.SpawnOptions.StdIo.pipe`.
stdout: ?File,
-
/// The reading end of the child process's standard error pipe.
-/// Usage requires `stderr_behavior == StdIo.Pipe`.
-/// Available after calling `spawn()`.
+/// Usage requires `process.SpawnOptions.StdIo.pipe`.
stderr: ?File,
-
-/// Terminated state of the child process.
-/// Available after calling `wait()`.
-term: ?(SpawnError!Term),
-
-argv: []const []const u8,
-
-/// Leave as null to use the current env map using the supplied allocator.
-/// Required if unable to access the current env map (e.g. building a library on
-/// some platforms).
-env_map: ?*const EnvMap,
-
-stdin_behavior: StdIo,
-stdout_behavior: StdIo,
-stderr_behavior: StdIo,
-
-/// Set to change the user id when spawning the child process.
-uid: if (native_os == .windows or native_os == .wasi) void else ?posix.uid_t,
-
-/// Set to change the group id when spawning the child process.
-gid: if (native_os == .windows or native_os == .wasi) void else ?posix.gid_t,
-
-/// Set to change the process group id when spawning the child process.
-pgid: if (native_os == .windows or native_os == .wasi) void else ?posix.pid_t,
-
-/// Set to change the current working directory when spawning the child process.
-cwd: ?[]const u8,
-/// Set to change the current working directory when spawning the child process.
-/// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190
-/// Once that is done, `cwd` will be deprecated in favor of this field.
-cwd_dir: ?Io.Dir = null,
-
-err_pipe: if (native_os == .windows) void else ?posix.fd_t,
-
-expand_arg0: Arg0Expand,
-
-/// Darwin-only. Disable ASLR for the child process.
-disable_aslr: bool = false,
-
-/// Start child process in suspended state.
-/// For Posix systems it's started as if SIGSTOP was sent.
-start_suspended: bool = false,
-
-/// Windows-only. Sets the CREATE_NO_WINDOW flag in CreateProcess.
-create_no_window: bool = false,
-
-/// Set to true to obtain rusage information for the child process.
-/// Depending on the target platform and implementation status, the
-/// requested statistics may or may not be available. If they are
-/// available, then the `resource_usage_statistics` field will be populated
-/// after calling `wait`.
-/// On Linux and Darwin, this obtains rusage statistics from wait4().
-request_resource_usage_statistics: bool = false,
-
/// This is available after calling wait if
/// `request_resource_usage_statistics` was set to `true` before calling
/// `spawn`.
+/// TODO move this data into `Term`
resource_usage_statistics: ResourceUsageStatistics = .{},
-
-/// When populated, a pipe will be created for the child process to
-/// communicate progress back to the parent. The file descriptor of the
-/// write end of the pipe will be specified in the `ZIG_PROGRESS`
-/// environment variable inside the child process. The progress reported by
-/// the child will be attached to this progress node in the parent process.
-///
-/// The child's progress tree will be grafted into the parent's progress tree,
-/// by substituting this node with the child's root node.
-progress_node: std.Progress.Node = std.Progress.Node.none,
+request_resource_usage_statistics: bool,
pub const ResourceUsageStatistics = struct {
rusage: @TypeOf(rusage_init) = rusage_init,
@@ -164,233 +86,60 @@ pub const ResourceUsageStatistics = struct {
.tvos,
.visionos,
.watchos,
- => @as(?posix.rusage, null),
- .windows => @as(?windows.VM_COUNTERS, null),
+ => @as(?std.posix.rusage, null),
+ .windows => @as(?std.os.windows.VM_COUNTERS, null),
else => {},
};
};
-pub const Arg0Expand = posix.Arg0Expand;
-
-pub const SpawnError = error{
- OutOfMemory,
-
- /// POSIX-only. `StdIo.Ignore` was selected and opening `/dev/null` returned ENODEV.
- NoDevice,
-
- /// Windows-only. `cwd` or `argv` was provided and it was invalid WTF-8.
- /// https://wtf-8.codeberg.page/
- InvalidWtf8,
-
- /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process.
- CurrentWorkingDirectoryUnlinked,
-
- /// Windows-only. NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed
- /// within arguments when executing a `.bat`/`.cmd` script.
- /// - NUL/LF signifiies end of arguments, so anything afterwards
- /// would be lost after execution.
- /// - CR is stripped by `cmd.exe`, so any CR codepoints
- /// would be lost after execution.
- InvalidBatchScriptArg,
-} ||
- posix.ExecveError ||
- posix.SetIdError ||
- posix.SetPgidError ||
- posix.ChangeCurDirError ||
- windows.CreateProcessError ||
- windows.GetProcessMemoryInfoError ||
- windows.WaitForSingleObjectError;
-
pub const Term = union(enum) {
- Exited: u8,
- Signal: u32,
- Stopped: u32,
- Unknown: u32,
+ exited: u8,
+ signal: std.posix.SIG,
+ stopped: u32,
+ unknown: u32,
};
-/// Behavior of the child process's standard input, output, and error
-/// streams.
-pub const StdIo = enum {
- /// Inherit the stream from the parent process.
- Inherit,
-
- /// Pass a null stream to the child process.
- /// This is /dev/null on POSIX and NUL on Windows.
- Ignore,
-
- /// Create a pipe for the stream.
- /// The corresponding field (`stdout`, `stderr`, or `stdin`)
- /// will be assigned a `File` object that can be used
- /// to read from or write to the pipe.
- Pipe,
-
- /// Close the stream after the child process spawns.
- Close,
-};
-
-/// First argument in argv is the executable.
-pub fn init(argv: []const []const u8, allocator: Allocator) Child {
- return .{
- .allocator = allocator,
- .argv = argv,
- .id = undefined,
- .thread_handle = undefined,
- .err_pipe = if (native_os == .windows) {} else null,
- .term = null,
- .env_map = null,
- .cwd = null,
- .uid = if (native_os == .windows or native_os == .wasi) {} else null,
- .gid = if (native_os == .windows or native_os == .wasi) {} else null,
- .pgid = if (native_os == .windows or native_os == .wasi) {} else null,
- .stdin = null,
- .stdout = null,
- .stderr = null,
- .stdin_behavior = .Inherit,
- .stdout_behavior = .Inherit,
- .stderr_behavior = .Inherit,
- .expand_arg0 = .no_expand,
- };
-}
-
-pub fn setUserName(self: *Child, name: []const u8) !void {
- const user_info = try process.getUserInfo(name);
- self.uid = user_info.uid;
- self.gid = user_info.gid;
-}
-
-/// On success must call `kill` or `wait`.
-/// After spawning the `id` is available.
-pub fn spawn(self: *Child, io: Io) SpawnError!void {
- if (!process.can_spawn) {
- @compileError("the target operating system cannot spawn processes");
- }
-
- if (native_os == .windows) {
- return self.spawnWindows(io);
- } else {
- return self.spawnPosix(io);
- }
-}
-
-pub fn spawnAndWait(child: *Child, io: Io) SpawnError!Term {
- try child.spawn(io);
- return child.wait(io);
-}
-
-/// Forcibly terminates child process and then cleans up all resources.
-pub fn kill(self: *Child, io: Io) !Term {
- if (native_os == .windows) {
- return self.killWindows(io, 1);
- } else {
- return self.killPosix(io);
- }
-}
-
-pub fn killWindows(self: *Child, io: Io, exit_code: windows.UINT) !Term {
- if (self.term) |term| {
- self.cleanupStreams(io);
- return term;
- }
-
- windows.TerminateProcess(self.id, exit_code) catch |err| switch (err) {
- error.AccessDenied => {
- // Usually when TerminateProcess triggers a ACCESS_DENIED error, it
- // indicates that the process has already exited, but there may be
- // some rare edge cases where our process handle no longer has the
- // PROCESS_TERMINATE access right, so let's do another check to make
- // sure the process is really no longer running:
- windows.WaitForSingleObjectEx(self.id, 0, false) catch return err;
- return error.AlreadyTerminated;
- },
- else => return err,
- };
- try self.waitUnwrappedWindows(io);
- return self.term.?;
-}
-
-pub fn killPosix(self: *Child, io: Io) !Term {
- if (self.term) |term| {
- self.cleanupStreams(io);
- return term;
- }
- posix.kill(self.id, posix.SIG.TERM) catch |err| switch (err) {
- error.ProcessNotFound => return error.AlreadyTerminated,
- else => return err,
- };
- self.waitUnwrappedPosix(io);
- return self.term.?;
-}
-
-pub const WaitError = SpawnError || std.os.windows.GetProcessMemoryInfoError;
-
-/// On some targets, `spawn` may not report all spawn errors, such as `error.InvalidExe`.
-/// This function will block until any spawn errors can be reported, and return them.
-pub fn waitForSpawn(self: *Child) SpawnError!void {
- if (native_os == .windows) return; // `spawn` reports everything
- if (self.term) |term| {
- _ = term catch |spawn_err| return spawn_err;
+/// Requests for the operating system to forcibly terminate the child process,
+/// then blocks until it terminates, then cleans up all resources.
+///
+/// Idempotent and does nothing after `wait` returns.
+///
+/// Uncancelable. Ignores unexpected errors from the operating system.
+pub fn kill(child: *Child, io: Io) void {
+ if (child.id == null) {
+ assert(child.stdin == null);
+ assert(child.stdout == null);
+ assert(child.stderr == null);
return;
}
-
- const err_pipe = self.err_pipe orelse return;
- self.err_pipe = null;
- // Wait for the child to report any errors in or before `execvpe`.
- const report = readIntFd(err_pipe);
- posix.close(err_pipe);
- if (report) |child_err_int| {
- const child_err: SpawnError = @errorCast(@errorFromInt(child_err_int));
- self.term = child_err;
- return child_err;
- } else |read_err| switch (read_err) {
- error.EndOfStream => {
- // Write end closed by CLOEXEC at the time of the `execvpe` call,
- // indicating success.
- },
- else => {
- // Problem reading the error from the error reporting pipe. We
- // don't know if the child is alive or dead. Better to assume it is
- // alive so the resource does not risk being leaked.
- },
- }
+ io.vtable.childKill(io.userdata, child);
+ assert(child.id == null);
}
+pub const WaitError = error{
+ AccessDenied,
+} || Io.Cancelable || Io.UnexpectedError;
+
/// Blocks until child process terminates and then cleans up all resources.
-pub fn wait(self: *Child, io: Io) WaitError!Term {
- try self.waitForSpawn(); // report spawn errors
- if (self.term) |term| {
- self.cleanupStreams(io);
- return term;
- }
- switch (native_os) {
- .windows => try self.waitUnwrappedWindows(io),
- else => self.waitUnwrappedPosix(io),
- }
- self.id = undefined;
- return self.term.?;
+pub fn wait(child: *Child, io: Io) WaitError!Term {
+ assert(child.id != null);
+ return io.vtable.childWait(io.userdata, child);
}
-pub const RunResult = struct {
- term: Term,
- stdout: []u8,
- stderr: []u8,
-};
-
/// Collect the output from the process's stdout and stderr. Will return once all output
/// has been collected. This does not mean that the process has ended. `wait` should still
/// be called to wait for and clean up the process.
///
-/// The process must be started with stdout_behavior and stderr_behavior == .Pipe
+/// The process must have been started with stdout and stderr set to
+/// `process.SpawnOptions.StdIo.pipe`.
pub fn collectOutput(
- child: Child,
+ child: *const Child,
/// Used for `stdout` and `stderr`.
allocator: Allocator,
stdout: *ArrayList(u8),
stderr: *ArrayList(u8),
max_output_bytes: usize,
) !void {
- assert(child.stdout_behavior == .Pipe);
- assert(child.stderr_behavior == .Pipe);
-
var poller = std.Io.poll(allocator, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
.stderr = child.stderr.?,
@@ -427,1473 +176,3 @@ pub fn collectOutput(
return error.StderrStreamTooLong;
}
}
-
-pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{
- StdoutStreamTooLong,
- StderrStreamTooLong,
-};
-
-/// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
-/// If it succeeds, the caller owns result.stdout and result.stderr memory.
-pub fn run(allocator: Allocator, io: Io, args: struct {
- argv: []const []const u8,
- cwd: ?[]const u8 = null,
- cwd_dir: ?Io.Dir = null,
- /// Required if unable to access the current env map (e.g. building a
- /// library on some platforms).
- env_map: ?*const EnvMap = null,
- max_output_bytes: usize = 50 * 1024,
- expand_arg0: Arg0Expand = .no_expand,
- progress_node: std.Progress.Node = std.Progress.Node.none,
-}) RunError!RunResult {
- var child = Child.init(args.argv, allocator);
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
- child.cwd = args.cwd;
- child.cwd_dir = args.cwd_dir;
- child.env_map = args.env_map;
- child.expand_arg0 = args.expand_arg0;
- child.progress_node = args.progress_node;
-
- var stdout: ArrayList(u8) = .empty;
- defer stdout.deinit(allocator);
- var stderr: ArrayList(u8) = .empty;
- defer stderr.deinit(allocator);
-
- try child.spawn(io);
- errdefer {
- _ = child.kill(io) catch {};
- }
- try child.collectOutput(allocator, &stdout, &stderr, args.max_output_bytes);
-
- return .{
- .stdout = try stdout.toOwnedSlice(allocator),
- .stderr = try stderr.toOwnedSlice(allocator),
- .term = try child.wait(io),
- };
-}
-
-fn waitUnwrappedWindows(self: *Child, io: Io) WaitError!void {
- const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false);
-
- self.term = @as(SpawnError!Term, x: {
- var exit_code: windows.DWORD = undefined;
- if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) {
- break :x Term{ .Unknown = 0 };
- } else {
- break :x Term{ .Exited = @as(u8, @truncate(exit_code)) };
- }
- });
-
- if (self.request_resource_usage_statistics) {
- self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id);
- }
-
- posix.close(self.id);
- posix.close(self.thread_handle);
- self.cleanupStreams(io);
- return result;
-}
-
-fn waitUnwrappedPosix(self: *Child, io: Io) void {
- const res: posix.WaitPidResult = res: {
- if (self.request_resource_usage_statistics) {
- switch (native_os) {
- .dragonfly,
- .freebsd,
- .netbsd,
- .openbsd,
- .illumos,
- .linux,
- .serenity,
- .driverkit,
- .ios,
- .maccatalyst,
- .macos,
- .tvos,
- .visionos,
- .watchos,
- => {
- var ru: posix.rusage = undefined;
- const res = posix.wait4(self.id, 0, &ru);
- self.resource_usage_statistics.rusage = ru;
- break :res res;
- },
- else => {},
- }
- }
-
- break :res posix.waitpid(self.id, 0);
- };
- const status = res.status;
- self.cleanupStreams(io);
- self.handleWaitResult(status);
-}
-
-fn handleWaitResult(self: *Child, status: u32) void {
- self.term = statusToTerm(status);
-}
-
-fn cleanupStreams(self: *Child, io: Io) void {
- if (self.stdin) |*stdin| {
- stdin.close(io);
- self.stdin = null;
- }
- if (self.stdout) |*stdout| {
- stdout.close(io);
- self.stdout = null;
- }
- if (self.stderr) |*stderr| {
- stderr.close(io);
- self.stderr = null;
- }
-}
-
-fn statusToTerm(status: u32) Term {
- return if (posix.W.IFEXITED(status))
- Term{ .Exited = posix.W.EXITSTATUS(status) }
- else if (posix.W.IFSIGNALED(status))
- Term{ .Signal = posix.W.TERMSIG(status) }
- else if (posix.W.IFSTOPPED(status))
- Term{ .Stopped = posix.W.STOPSIG(status) }
- else
- Term{ .Unknown = status };
-}
-
-fn spawnPosix(self: *Child, io: Io) SpawnError!void {
- // The child process does need to access (one end of) these pipes. However,
- // we must initially set CLOEXEC to avoid a race condition. If another thread
- // is racing to spawn a different child process, we don't want it to inherit
- // these FDs in any scenario; that would mean that, for instance, calls to
- // `poll` from the parent would not report the child's stdout as closing when
- // expected, since the other child may retain a reference to the write end of
- // the pipe. So, we create the pipes with CLOEXEC initially. After fork, we
- // need to do something in the new child to make sure we preserve the reference
- // we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it
- // turns out, we `dup2` everything anyway, so there's no need!
- const pipe_flags: posix.O = .{ .CLOEXEC = true };
-
- const stdin_pipe = if (self.stdin_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined;
- errdefer if (self.stdin_behavior == .Pipe) {
- destroyPipe(stdin_pipe);
- };
-
- const stdout_pipe = if (self.stdout_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined;
- errdefer if (self.stdout_behavior == .Pipe) {
- destroyPipe(stdout_pipe);
- };
-
- const stderr_pipe = if (self.stderr_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined;
- errdefer if (self.stderr_behavior == .Pipe) {
- destroyPipe(stderr_pipe);
- };
-
- const any_ignore = (self.stdin_behavior == .Ignore or self.stdout_behavior == .Ignore or self.stderr_behavior == .Ignore);
- const dev_null_fd = if (any_ignore)
- posix.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) {
- error.PathAlreadyExists => unreachable,
- error.NoSpaceLeft => unreachable,
- error.FileTooBig => unreachable,
- error.DeviceBusy => unreachable,
- error.FileLocksUnsupported => unreachable,
- error.BadPathName => unreachable, // Windows-only
- error.WouldBlock => unreachable,
- error.NetworkNotFound => unreachable, // Windows-only
- error.Canceled => unreachable, // temporarily in the posix error set
- error.SharingViolation => unreachable, // Windows-only
- error.PipeBusy => unreachable, // not a pipe
- error.AntivirusInterference => unreachable, // Windows-only
- else => |e| return e,
- }
- else
- undefined;
- defer {
- if (any_ignore) posix.close(dev_null_fd);
- }
-
- const prog_pipe: [2]posix.fd_t = p: {
- if (self.progress_node.index == .none) {
- break :p .{ -1, -1 };
- } else {
- // We use CLOEXEC for the same reason as in `pipe_flags`.
- break :p try posix.pipe2(.{ .NONBLOCK = true, .CLOEXEC = true });
- }
- };
- errdefer destroyPipe(prog_pipe);
-
- var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
- defer arena_allocator.deinit();
- const arena = arena_allocator.allocator();
-
- // The POSIX standard does not allow malloc() between fork() and execve(),
- // and `self.allocator` may be a libc allocator.
- // I have personally observed the child process deadlocking when it tries
- // to call malloc() due to a heap allocation between fork() and execve(),
- // in musl v1.1.24.
- // Additionally, we want to reduce the number of possible ways things
- // can fail between fork() and execve().
- // Therefore, we do all the allocation for the execve() before the fork().
- // This means we must do the null-termination of argv and env vars here.
- const argv_buf = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null);
- for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
-
- const prog_fileno = 3;
- comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno);
-
- const envp: [*:null]const ?[*:0]const u8 = m: {
- const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno;
- if (self.env_map) |env_map| {
- break :m (try process.createEnvironFromMap(arena, env_map, .{
- .zig_progress_fd = prog_fd,
- })).ptr;
- } else if (builtin.link_libc) {
- break :m (try process.createEnvironFromExisting(arena, std.c.environ, .{
- .zig_progress_fd = prog_fd,
- })).ptr;
- } else if (builtin.output_mode == .Exe) {
- // Then we have Zig start code and this works.
- // TODO type-safety for null-termination of `os.environ`.
- break :m (try process.createEnvironFromExisting(arena, @ptrCast(std.os.environ.ptr), .{
- .zig_progress_fd = prog_fd,
- })).ptr;
- } else {
- // TODO come up with a solution for this.
- @panic("missing std lib enhancement: std.process.Child implementation has no way to collect the environment variables to forward to the child process");
- }
- };
-
- // This pipe communicates to the parent errors in the child between `fork` and `execvpe`.
- // It is closed by the child (via CLOEXEC) without writing if `execvpe` succeeds.
- const err_pipe: [2]posix.fd_t = try posix.pipe2(.{ .CLOEXEC = true });
- errdefer destroyPipe(err_pipe);
-
- const pid_result = try posix.fork();
- if (pid_result == 0) {
- // we are the child
- setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err);
- setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err);
- setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err);
-
- if (self.cwd_dir) |cwd| {
- posix.fchdir(cwd.handle) catch |err| forkChildErrReport(io, err_pipe[1], err);
- } else if (self.cwd) |cwd| {
- posix.chdir(cwd) catch |err| forkChildErrReport(io, err_pipe[1], err);
- }
-
- // Must happen after fchdir above, the cwd file descriptor might be
- // equal to prog_fileno and be clobbered by this dup2 call.
- if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkChildErrReport(io, err_pipe[1], err);
-
- if (self.gid) |gid| {
- posix.setregid(gid, gid) catch |err| forkChildErrReport(io, err_pipe[1], err);
- }
-
- if (self.uid) |uid| {
- posix.setreuid(uid, uid) catch |err| forkChildErrReport(io, err_pipe[1], err);
- }
-
- if (self.pgid) |pid| {
- posix.setpgid(0, pid) catch |err| forkChildErrReport(io, err_pipe[1], err);
- }
-
- if (self.start_suspended) {
- posix.kill(posix.getpid(), .STOP) catch |err| forkChildErrReport(io, err_pipe[1], err);
- }
-
- const err = switch (self.expand_arg0) {
- .expand => posix.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
- .no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
- };
- forkChildErrReport(io, err_pipe[1], err);
- }
-
- // we are the parent
- errdefer comptime unreachable; // The child is forked; we must not error from now on
-
- posix.close(err_pipe[1]); // make sure only the child holds the write end open
- self.err_pipe = err_pipe[0];
-
- const pid: i32 = @intCast(pid_result);
- if (self.stdin_behavior == .Pipe) {
- self.stdin = .{ .handle = stdin_pipe[1] };
- } else {
- self.stdin = null;
- }
- if (self.stdout_behavior == .Pipe) {
- self.stdout = .{ .handle = stdout_pipe[0] };
- } else {
- self.stdout = null;
- }
- if (self.stderr_behavior == .Pipe) {
- self.stderr = .{ .handle = stderr_pipe[0] };
- } else {
- self.stderr = null;
- }
-
- self.id = pid;
- self.term = null;
-
- if (self.stdin_behavior == .Pipe) {
- posix.close(stdin_pipe[0]);
- }
- if (self.stdout_behavior == .Pipe) {
- posix.close(stdout_pipe[1]);
- }
- if (self.stderr_behavior == .Pipe) {
- posix.close(stderr_pipe[1]);
- }
-
- if (prog_pipe[1] != -1) {
- posix.close(prog_pipe[1]);
- }
- self.progress_node.setIpcFd(prog_pipe[0]);
-}
-
-fn spawnWindows(self: *Child, io: Io) SpawnError!void {
- var saAttr = windows.SECURITY_ATTRIBUTES{
- .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
- .bInheritHandle = windows.TRUE,
- .lpSecurityDescriptor = null,
- };
-
- const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
-
- const nul_handle = if (any_ignore)
- // "\Device\Null" or "\??\NUL"
- windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{
- .access_mask = .{
- .STANDARD = .{ .SYNCHRONIZE = true },
- .GENERIC = .{ .WRITE = true, .READ = true },
- },
- .sa = &saAttr,
- .creation = .OPEN,
- }) catch |err| switch (err) {
- error.PathAlreadyExists => return error.Unexpected, // not possible for "NUL"
- error.PipeBusy => return error.Unexpected, // not possible for "NUL"
- error.NoDevice => return error.Unexpected, // not possible for "NUL"
- error.FileNotFound => return error.Unexpected, // not possible for "NUL"
- error.AccessDenied => return error.Unexpected, // not possible for "NUL"
- error.NameTooLong => return error.Unexpected, // not possible for "NUL"
- error.WouldBlock => return error.Unexpected, // not possible for "NUL"
- error.NetworkNotFound => return error.Unexpected, // not possible for "NUL"
- error.AntivirusInterference => return error.Unexpected, // not possible for "NUL"
- error.OperationCanceled => return error.Unexpected, // we're not canceling the operation
- else => |e| return e,
- }
- else
- undefined;
- defer {
- if (any_ignore) posix.close(nul_handle);
- }
-
- var g_hChildStd_IN_Rd: ?windows.HANDLE = null;
- var g_hChildStd_IN_Wr: ?windows.HANDLE = null;
- switch (self.stdin_behavior) {
- StdIo.Pipe => {
- try windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr);
- },
- StdIo.Ignore => {
- g_hChildStd_IN_Rd = nul_handle;
- },
- StdIo.Inherit => {
- g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null;
- },
- StdIo.Close => {
- g_hChildStd_IN_Rd = null;
- },
- }
- errdefer if (self.stdin_behavior == StdIo.Pipe) {
- windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr);
- };
-
- var g_hChildStd_OUT_Rd: ?windows.HANDLE = null;
- var g_hChildStd_OUT_Wr: ?windows.HANDLE = null;
- switch (self.stdout_behavior) {
- StdIo.Pipe => {
- try windowsMakeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr);
- },
- StdIo.Ignore => {
- g_hChildStd_OUT_Wr = nul_handle;
- },
- StdIo.Inherit => {
- g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null;
- },
- StdIo.Close => {
- g_hChildStd_OUT_Wr = null;
- },
- }
- errdefer if (self.stdout_behavior == StdIo.Pipe) {
- windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr);
- };
-
- var g_hChildStd_ERR_Rd: ?windows.HANDLE = null;
- var g_hChildStd_ERR_Wr: ?windows.HANDLE = null;
- switch (self.stderr_behavior) {
- StdIo.Pipe => {
- try windowsMakeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr);
- },
- StdIo.Ignore => {
- g_hChildStd_ERR_Wr = nul_handle;
- },
- StdIo.Inherit => {
- g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null;
- },
- StdIo.Close => {
- g_hChildStd_ERR_Wr = null;
- },
- }
- errdefer if (self.stderr_behavior == StdIo.Pipe) {
- windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr);
- };
-
- var siStartInfo = windows.STARTUPINFOW{
- .cb = @sizeOf(windows.STARTUPINFOW),
- .hStdError = g_hChildStd_ERR_Wr,
- .hStdOutput = g_hChildStd_OUT_Wr,
- .hStdInput = g_hChildStd_IN_Rd,
- .dwFlags = windows.STARTF_USESTDHANDLES,
-
- .lpReserved = null,
- .lpDesktop = null,
- .lpTitle = null,
- .dwX = 0,
- .dwY = 0,
- .dwXSize = 0,
- .dwYSize = 0,
- .dwXCountChars = 0,
- .dwYCountChars = 0,
- .dwFillAttribute = 0,
- .wShowWindow = 0,
- .cbReserved2 = 0,
- .lpReserved2 = null,
- };
- var piProcInfo: windows.PROCESS_INFORMATION = undefined;
-
- const cwd_w = if (self.cwd) |cwd| try unicode.wtf8ToWtf16LeAllocZ(self.allocator, cwd) else null;
- defer if (cwd_w) |cwd| self.allocator.free(cwd);
- const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
-
- const maybe_envp_buf = if (self.env_map) |env_map| try process.createWindowsEnvBlock(self.allocator, env_map) else null;
- defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
- const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null;
-
- const app_name_wtf8 = self.argv[0];
- const app_name_is_absolute = fs.path.isAbsolute(app_name_wtf8);
-
- // the cwd set in Child is in effect when choosing the executable path
- // to match posix semantics
- var cwd_path_w_needs_free = false;
- const cwd_path_w = x: {
- // If the app name is absolute, then we need to use its dirname as the cwd
- if (app_name_is_absolute) {
- cwd_path_w_needs_free = true;
- const dir = fs.path.dirname(app_name_wtf8).?;
- break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, dir);
- } else if (self.cwd) |cwd| {
- cwd_path_w_needs_free = true;
- break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, cwd);
- } else {
- break :x &[_:0]u16{}; // empty for cwd
- }
- };
- defer if (cwd_path_w_needs_free) self.allocator.free(cwd_path_w);
-
- // If the app name has more than just a filename, then we need to separate that
- // into the basename and dirname and use the dirname as an addition to the cwd
- // path. This is because NtQueryDirectoryFile cannot accept FileName params with
- // path separators.
- const app_basename_wtf8 = fs.path.basename(app_name_wtf8);
- // If the app name is absolute, then the cwd will already have the app's dirname in it,
- // so only populate app_dirname if app name is a relative path with > 0 path separators.
- const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) fs.path.dirname(app_name_wtf8) else null;
- const app_dirname_w: ?[:0]u16 = x: {
- if (maybe_app_dirname_wtf8) |app_dirname_wtf8| {
- break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_dirname_wtf8);
- }
- break :x null;
- };
- defer if (app_dirname_w != null) self.allocator.free(app_dirname_w.?);
-
- const app_name_w = try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_basename_wtf8);
- defer self.allocator.free(app_name_w);
-
- const flags: windows.CreateProcessFlags = .{
- .create_suspended = self.start_suspended,
- .create_unicode_environment = true,
- .create_no_window = self.create_no_window,
- };
-
- run: {
- const PATH: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
- const PATHEXT: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
-
- // In case the command ends up being a .bat/.cmd script, we need to escape things using the cmd.exe rules
- // and invoke cmd.exe ourselves in order to mitigate arbitrary command execution from maliciously
- // constructed arguments.
- //
- // We'll need to wait until we're actually trying to run the command to know for sure
- // if the resolved command has the `.bat` or `.cmd` extension, so we defer actually
- // serializing the command line until we determine how it should be serialized.
- var cmd_line_cache = WindowsCommandLineCache.init(self.allocator, self.argv);
- defer cmd_line_cache.deinit();
-
- var app_buf: ArrayList(u16) = .empty;
- defer app_buf.deinit(self.allocator);
-
- try app_buf.appendSlice(self.allocator, app_name_w);
-
- var dir_buf: ArrayList(u16) = .empty;
- defer dir_buf.deinit(self.allocator);
-
- if (cwd_path_w.len > 0) {
- try dir_buf.appendSlice(self.allocator, cwd_path_w);
- }
- if (app_dirname_w) |app_dir| {
- if (dir_buf.items.len > 0) try dir_buf.append(self.allocator, fs.path.sep);
- try dir_buf.appendSlice(self.allocator, app_dir);
- }
-
- windowsCreateProcessPathExt(self.allocator, io, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| {
- const original_err = switch (no_path_err) {
- // argv[0] contains unsupported characters that will never resolve to a valid exe.
- error.InvalidArg0 => return error.FileNotFound,
- error.FileNotFound, error.InvalidExe, error.AccessDenied => |e| e,
- error.UnrecoverableInvalidExe => return error.InvalidExe,
- else => |e| return e,
- };
-
- // If the app name had path separators, that disallows PATH searching,
- // and there's no need to search the PATH if the app name is absolute.
- // We still search the path if the cwd is absolute because of the
- // "cwd set in Child is in effect when choosing the executable path
- // to match posix semantics" behavior--we don't want to skip searching
- // the PATH just because we were trying to set the cwd of the child process.
- if (app_dirname_w != null or app_name_is_absolute) {
- return original_err;
- }
-
- var it = mem.tokenizeScalar(u16, PATH, ';');
- while (it.next()) |search_path| {
- dir_buf.clearRetainingCapacity();
- try dir_buf.appendSlice(self.allocator, search_path);
-
- if (windowsCreateProcessPathExt(self.allocator, io, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) {
- break :run;
- } else |err| switch (err) {
- // argv[0] contains unsupported characters that will never resolve to a valid exe.
- error.InvalidArg0 => return error.FileNotFound,
- error.FileNotFound, error.AccessDenied, error.InvalidExe => continue,
- error.UnrecoverableInvalidExe => return error.InvalidExe,
- else => |e| return e,
- }
- } else {
- return original_err;
- }
- };
- }
-
- if (g_hChildStd_IN_Wr) |h| {
- self.stdin = File{ .handle = h };
- } else {
- self.stdin = null;
- }
- if (g_hChildStd_OUT_Rd) |h| {
- self.stdout = File{ .handle = h };
- } else {
- self.stdout = null;
- }
- if (g_hChildStd_ERR_Rd) |h| {
- self.stderr = File{ .handle = h };
- } else {
- self.stderr = null;
- }
-
- self.id = piProcInfo.hProcess;
- self.thread_handle = piProcInfo.hThread;
- self.term = null;
-
- if (self.stdin_behavior == StdIo.Pipe) {
- posix.close(g_hChildStd_IN_Rd.?);
- }
- if (self.stderr_behavior == StdIo.Pipe) {
- posix.close(g_hChildStd_ERR_Wr.?);
- }
- if (self.stdout_behavior == StdIo.Pipe) {
- posix.close(g_hChildStd_OUT_Wr.?);
- }
-}
-
-fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
- switch (stdio) {
- .Pipe => try posix.dup2(pipe_fd, std_fileno),
- .Close => posix.close(std_fileno),
- .Inherit => {},
- .Ignore => try posix.dup2(dev_null_fd, std_fileno),
- }
-}
-
-fn destroyPipe(pipe: [2]posix.fd_t) void {
- if (pipe[0] != -1) posix.close(pipe[0]);
- if (pipe[0] != pipe[1]) posix.close(pipe[1]);
-}
-
-// Child of fork calls this to report an error to the fork parent.
-// Then the child exits.
-fn forkChildErrReport(io: Io, fd: i32, err: Child.SpawnError) noreturn {
- writeIntFd(io, fd, @as(ErrInt, @intFromError(err))) catch {};
- // If we're linking libc, some naughty applications may have registered atexit handlers
- // which we really do not want to run in the fork child. I caught LLVM doing this and
- // it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne,
- // "Why'd you have to go and make things so complicated?"
- if (builtin.link_libc) {
- // The _exit(2) function does nothing but make the exit syscall, unlike exit(3)
- std.c._exit(1);
- }
- posix.system.exit(1);
-}
-
-fn writeIntFd(io: Io, fd: i32, value: ErrInt) !void {
- var buffer: [8]u8 = undefined;
- var fw: File.Writer = .initStreaming(.{ .handle = fd }, io, &buffer);
- fw.interface.writeInt(u64, value, .little) catch unreachable;
- fw.interface.flush() catch return error.SystemResources;
-}
-
-fn readIntFd(fd: i32) !ErrInt {
- var buffer: [8]u8 = undefined;
- var i: usize = 0;
- while (i < buffer.len) {
- const n = try std.posix.read(fd, buffer[i..]);
- if (n == 0) return error.EndOfStream;
- i += n;
- }
- const int = mem.readInt(u64, &buffer, .little);
- return @intCast(int);
-}
-
-const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
-
-/// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path.
-/// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path.
-/// Note: `app_buf` should not contain any leading path separators.
-/// Note: If the dir is the cwd, dir_buf should be empty (len = 0).
-fn windowsCreateProcessPathExt(
- allocator: Allocator,
- io: Io,
- dir_buf: *ArrayList(u16),
- app_buf: *ArrayList(u16),
- pathext: [:0]const u16,
- cmd_line_cache: *WindowsCommandLineCache,
- envp_ptr: ?[*]u16,
- cwd_ptr: ?[*:0]u16,
- flags: windows.CreateProcessFlags,
- lpStartupInfo: *windows.STARTUPINFOW,
- lpProcessInformation: *windows.PROCESS_INFORMATION,
-) !void {
- const app_name_len = app_buf.items.len;
- const dir_path_len = dir_buf.items.len;
-
- if (app_name_len == 0) return error.FileNotFound;
-
- defer app_buf.shrinkRetainingCapacity(app_name_len);
- defer dir_buf.shrinkRetainingCapacity(dir_path_len);
-
- // The name of the game here is to avoid CreateProcessW calls at all costs,
- // and only ever try calling it when we have a real candidate for execution.
- // Secondarily, we want to minimize the number of syscalls used when checking
- // for each PATHEXT-appended version of the app name.
- //
- // An overview of the technique used:
- // - Open the search directory for iteration (either cwd or a path from PATH)
- // - Use NtQueryDirectoryFile with a wildcard filename of `*` to
- // check if anything that could possibly match either the unappended version
- // of the app name or any of the versions with a PATHEXT value appended exists.
- // - If the wildcard NtQueryDirectoryFile call found nothing, we can exit early
- // without needing to use PATHEXT at all.
- //
- // This allows us to use a sequence
- // for any directory that doesn't contain any possible matches, instead of having
- // to use a separate look up for each individual filename combination (unappended +
- // each PATHEXT appended). For directories where the wildcard *does* match something,
- // we iterate the matches and take note of any that are either the unappended version,
- // or a version with a supported PATHEXT appended. We then try calling CreateProcessW
- // with the found versions in the appropriate order.
-
- // In the future, child process execution needs to move to Io implementation.
- // Under those conditions, here we will have access to lower level directory
- // opening function knowing which implementation we are in. Here, we imitate
- // that scenario.
- var dir = dir: {
- // needs to be null-terminated
- try dir_buf.append(allocator, 0);
- defer dir_buf.shrinkRetainingCapacity(dir_path_len);
- const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
- const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z);
- break :dir Io.Threaded.dirOpenDirWindows(.cwd(), prefixed_path.span(), .{
- .iterate = true,
- }) catch return error.FileNotFound;
- };
- defer dir.close(io);
-
- // Add wildcard and null-terminator
- try app_buf.append(allocator, '*');
- try app_buf.append(allocator, 0);
- const app_name_wildcard = app_buf.items[0 .. app_buf.items.len - 1 :0];
-
- // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries
- // returned per NtQueryDirectoryFile call.
- var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined;
- const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2);
- if (file_information_buf.len < file_info_maximum_single_entry_size) {
- @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry");
- }
- var io_status: windows.IO_STATUS_BLOCK = undefined;
-
- const num_supported_pathext = @typeInfo(WindowsExtension).@"enum".fields.len;
- var pathext_seen = [_]bool{false} ** num_supported_pathext;
- var any_pathext_seen = false;
- var unappended_exists = false;
-
- // Fully iterate the wildcard matches via NtQueryDirectoryFile and take note of all versions
- // of the app_name we should try to spawn.
- // Note: This is necessary because the order of the files returned is filesystem-dependent:
- // On NTFS, `blah.exe*` will always return `blah.exe` first if it exists.
- // On FAT32, it's possible for something like `blah.exe.obj` to be returned first.
- while (true) {
- const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong;
- var app_name_unicode_string = windows.UNICODE_STRING{
- .Length = app_name_len_bytes,
- .MaximumLength = app_name_len_bytes,
- .Buffer = @constCast(app_name_wildcard.ptr),
- };
- const rc = windows.ntdll.NtQueryDirectoryFile(
- dir.handle,
- null,
- null,
- null,
- &io_status,
- &file_information_buf,
- file_information_buf.len,
- .Directory,
- windows.FALSE, // single result
- &app_name_unicode_string,
- windows.FALSE, // restart iteration
- );
-
- // If we get nothing with the wildcard, then we can just bail out
- // as we know appending PATHEXT will not yield anything.
- switch (rc) {
- .SUCCESS => {},
- .NO_SUCH_FILE => return error.FileNotFound,
- .NO_MORE_FILES => break,
- .ACCESS_DENIED => return error.AccessDenied,
- else => return windows.unexpectedStatus(rc),
- }
-
- // According to the docs, this can only happen if there is not enough room in the
- // buffer to write at least one complete FILE_DIRECTORY_INFORMATION entry.
- // Therefore, this condition should not be possible to hit with the buffer size we use.
- std.debug.assert(io_status.Information != 0);
-
- var it = windows.FileInformationIterator(windows.FILE_DIRECTORY_INFORMATION){ .buf = &file_information_buf };
- while (it.next()) |info| {
- // Skip directories
- if (info.FileAttributes.DIRECTORY) continue;
- const filename = @as([*]u16, @ptrCast(&info.FileName))[0 .. info.FileNameLength / 2];
- // Because all results start with the app_name since we're using the wildcard `app_name*`,
- // if the length is equal to app_name then this is an exact match
- if (filename.len == app_name_len) {
- // Note: We can't break early here because it's possible that the unappended version
- // fails to spawn, in which case we still want to try the PATHEXT appended versions.
- unappended_exists = true;
- } else if (windowsCreateProcessSupportsExtension(filename[app_name_len..])) |pathext_ext| {
- pathext_seen[@intFromEnum(pathext_ext)] = true;
- any_pathext_seen = true;
- }
- }
- }
-
- const unappended_err = unappended: {
- if (unappended_exists) {
- if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) {
- '/', '\\' => {},
- else => try dir_buf.append(allocator, fs.path.sep),
- };
- try dir_buf.appendSlice(allocator, app_buf.items[0..app_name_len]);
- try dir_buf.append(allocator, 0);
- const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
-
- const is_bat_or_cmd = bat_or_cmd: {
- const app_name = app_buf.items[0..app_name_len];
- const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :bat_or_cmd false;
- const ext = app_name[ext_start..];
- const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse break :bat_or_cmd false;
- switch (ext_enum) {
- .cmd, .bat => break :bat_or_cmd true,
- else => break :bat_or_cmd false,
- }
- };
- const cmd_line_w = if (is_bat_or_cmd)
- try cmd_line_cache.scriptCommandLine(full_app_name)
- else
- try cmd_line_cache.commandLine();
- const app_name_w = if (is_bat_or_cmd)
- try cmd_line_cache.cmdExePath()
- else
- full_app_name;
-
- if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| {
- return;
- } else |err| switch (err) {
- error.FileNotFound,
- error.AccessDenied,
- => break :unappended err,
- error.InvalidExe => {
- // On InvalidExe, if the extension of the app name is .exe then
- // it's treated as an unrecoverable error. Otherwise, it'll be
- // skipped as normal.
- const app_name = app_buf.items[0..app_name_len];
- const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :unappended err;
- const ext = app_name[ext_start..];
- if (windows.eqlIgnoreCaseWtf16(ext, unicode.utf8ToUtf16LeStringLiteral(".EXE"))) {
- return error.UnrecoverableInvalidExe;
- }
- break :unappended err;
- },
- else => return err,
- }
- }
- break :unappended error.FileNotFound;
- };
-
- if (!any_pathext_seen) return unappended_err;
-
- // Now try any PATHEXT appended versions that we've seen
- var ext_it = mem.tokenizeScalar(u16, pathext, ';');
- while (ext_it.next()) |ext| {
- const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse continue;
- if (!pathext_seen[@intFromEnum(ext_enum)]) continue;
-
- dir_buf.shrinkRetainingCapacity(dir_path_len);
- if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) {
- '/', '\\' => {},
- else => try dir_buf.append(allocator, fs.path.sep),
- };
- try dir_buf.appendSlice(allocator, app_buf.items[0..app_name_len]);
- try dir_buf.appendSlice(allocator, ext);
- try dir_buf.append(allocator, 0);
- const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
-
- const is_bat_or_cmd = switch (ext_enum) {
- .cmd, .bat => true,
- else => false,
- };
- const cmd_line_w = if (is_bat_or_cmd)
- try cmd_line_cache.scriptCommandLine(full_app_name)
- else
- try cmd_line_cache.commandLine();
- const app_name_w = if (is_bat_or_cmd)
- try cmd_line_cache.cmdExePath()
- else
- full_app_name;
-
- if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| {
- return;
- } else |err| switch (err) {
- error.FileNotFound => continue,
- error.AccessDenied => continue,
- error.InvalidExe => {
- // On InvalidExe, if the extension of the app name is .exe then
- // it's treated as an unrecoverable error. Otherwise, it'll be
- // skipped as normal.
- if (windows.eqlIgnoreCaseWtf16(ext, unicode.utf8ToUtf16LeStringLiteral(".EXE"))) {
- return error.UnrecoverableInvalidExe;
- }
- continue;
- },
- else => return err,
- }
- }
-
- return unappended_err;
-}
-
-fn windowsCreateProcess(
- app_name: [*:0]u16,
- cmd_line: [*:0]u16,
- envp_ptr: ?[*]u16,
- cwd_ptr: ?[*:0]u16,
- flags: windows.CreateProcessFlags,
- lpStartupInfo: *windows.STARTUPINFOW,
- lpProcessInformation: *windows.PROCESS_INFORMATION,
-) !void {
- // TODO the docs for environment pointer say:
- // > A pointer to the environment block for the new process. If this parameter
- // > is NULL, the new process uses the environment of the calling process.
- // > ...
- // > An environment block can contain either Unicode or ANSI characters. If
- // > the environment block pointed to by lpEnvironment contains Unicode
- // > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
- // > If this parameter is NULL and the environment block of the parent process
- // > contains Unicode characters, you must also ensure that dwCreationFlags
- // > includes CREATE_UNICODE_ENVIRONMENT.
- // This seems to imply that we have to somehow know whether our process parent passed
- // CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
- // Since we do not know this information that would imply that we must not pass NULL
- // for the parameter.
- // However this would imply that programs compiled with -DUNICODE could not pass
- // environment variables to programs that were not, which seems unlikely.
- // More investigation is needed.
- return windows.CreateProcessW(
- app_name,
- cmd_line,
- null,
- null,
- windows.TRUE,
- flags,
- @as(?*anyopaque, @ptrCast(envp_ptr)),
- cwd_ptr,
- lpStartupInfo,
- lpProcessInformation,
- );
-}
-
-fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
- var rd_h: windows.HANDLE = undefined;
- var wr_h: windows.HANDLE = undefined;
- try windows.CreatePipe(&rd_h, &wr_h, sattr);
- errdefer windowsDestroyPipe(rd_h, wr_h);
- try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0);
- rd.* = rd_h;
- wr.* = wr_h;
-}
-
-fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
- if (rd) |h| posix.close(h);
- if (wr) |h| posix.close(h);
-}
-
-fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
- var tmp_bufw: [128]u16 = undefined;
-
- // Anonymous pipes are built upon Named pipes.
- // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
- // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes.
- // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations
- const pipe_path = blk: {
- var tmp_buf: [128]u8 = undefined;
- // Forge a random path for the pipe.
- const pipe_path = std.fmt.bufPrintSentinel(
- &tmp_buf,
- "\\\\.\\pipe\\zig-childprocess-{d}-{d}",
- .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) },
- 0,
- ) catch unreachable;
- const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable;
- tmp_bufw[len] = 0;
- break :blk tmp_bufw[0..len :0];
- };
-
- // Create the read handle that can be used with overlapped IO ops.
- const read_handle = windows.kernel32.CreateNamedPipeW(
- pipe_path.ptr,
- windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED,
- windows.PIPE_TYPE_BYTE,
- 1,
- 4096,
- 4096,
- 0,
- sattr,
- );
- if (read_handle == windows.INVALID_HANDLE_VALUE) {
- switch (windows.GetLastError()) {
- else => |err| return windows.unexpectedError(err),
- }
- }
- errdefer posix.close(read_handle);
-
- var sattr_copy = sattr.*;
- const write_handle = windows.kernel32.CreateFileW(
- pipe_path.ptr,
- .{ .GENERIC = .{ .WRITE = true } },
- 0,
- &sattr_copy,
- windows.OPEN_EXISTING,
- @bitCast(windows.FILE.ATTRIBUTE{ .NORMAL = true }),
- null,
- );
- if (write_handle == windows.INVALID_HANDLE_VALUE) {
- switch (windows.GetLastError()) {
- else => |err| return windows.unexpectedError(err),
- }
- }
- errdefer posix.close(write_handle);
-
- try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0);
-
- rd.* = read_handle;
- wr.* = write_handle;
-}
-
-var pipe_name_counter = std.atomic.Value(u32).init(1);
-
-/// File name extensions supported natively by `CreateProcess()` on Windows.
-// Should be kept in sync with `windowsCreateProcessSupportsExtension`.
-pub const WindowsExtension = enum {
- bat,
- cmd,
- com,
- exe,
-};
-
-/// Case-insensitive WTF-16 lookup
-fn windowsCreateProcessSupportsExtension(ext: []const u16) ?WindowsExtension {
- if (ext.len != 4) return null;
- const State = enum {
- start,
- dot,
- b,
- ba,
- c,
- cm,
- co,
- e,
- ex,
- };
- var state: State = .start;
- for (ext) |c| switch (state) {
- .start => switch (c) {
- '.' => state = .dot,
- else => return null,
- },
- .dot => switch (c) {
- 'b', 'B' => state = .b,
- 'c', 'C' => state = .c,
- 'e', 'E' => state = .e,
- else => return null,
- },
- .b => switch (c) {
- 'a', 'A' => state = .ba,
- else => return null,
- },
- .c => switch (c) {
- 'm', 'M' => state = .cm,
- 'o', 'O' => state = .co,
- else => return null,
- },
- .e => switch (c) {
- 'x', 'X' => state = .ex,
- else => return null,
- },
- .ba => switch (c) {
- 't', 'T' => return .bat,
- else => return null,
- },
- .cm => switch (c) {
- 'd', 'D' => return .cmd,
- else => return null,
- },
- .co => switch (c) {
- 'm', 'M' => return .com,
- else => return null,
- },
- .ex => switch (c) {
- 'e', 'E' => return .exe,
- else => return null,
- },
- };
- return null;
-}
-
-test windowsCreateProcessSupportsExtension {
- try std.testing.expectEqual(WindowsExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?);
- try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null);
-}
-
-/// Serializes argv into a WTF-16 encoded command-line string for use with CreateProcessW.
-///
-/// Serialization is done on-demand and the result is cached in order to allow for:
-/// - Only serializing the particular type of command line needed (`.bat`/`.cmd`
-/// command line serialization is different from `.exe`/etc)
-/// - Reusing the serialized command lines if necessary (i.e. if the execution
-/// of a command fails and the PATH is going to be continued to be searched
-/// for more candidates)
-const WindowsCommandLineCache = struct {
- cmd_line: ?[:0]u16 = null,
- script_cmd_line: ?[:0]u16 = null,
- cmd_exe_path: ?[:0]u16 = null,
- argv: []const []const u8,
- allocator: Allocator,
-
- fn init(allocator: Allocator, argv: []const []const u8) WindowsCommandLineCache {
- return .{
- .allocator = allocator,
- .argv = argv,
- };
- }
-
- fn deinit(self: *WindowsCommandLineCache) void {
- if (self.cmd_line) |cmd_line| self.allocator.free(cmd_line);
- if (self.script_cmd_line) |script_cmd_line| self.allocator.free(script_cmd_line);
- if (self.cmd_exe_path) |cmd_exe_path| self.allocator.free(cmd_exe_path);
- }
-
- fn commandLine(self: *WindowsCommandLineCache) ![:0]u16 {
- if (self.cmd_line == null) {
- self.cmd_line = try argvToCommandLineWindows(self.allocator, self.argv);
- }
- return self.cmd_line.?;
- }
-
- /// Not cached, since the path to the batch script will change during PATH searching.
- /// `script_path` should be as qualified as possible, e.g. if the PATH is being searched,
- /// then script_path should include both the search path and the script filename
- /// (this allows avoiding cmd.exe having to search the PATH again).
- fn scriptCommandLine(self: *WindowsCommandLineCache, script_path: []const u16) ![:0]u16 {
- if (self.script_cmd_line) |v| self.allocator.free(v);
- self.script_cmd_line = try argvToScriptCommandLineWindows(
- self.allocator,
- script_path,
- self.argv[1..],
- );
- return self.script_cmd_line.?;
- }
-
- fn cmdExePath(self: *WindowsCommandLineCache) ![:0]u16 {
- if (self.cmd_exe_path == null) {
- self.cmd_exe_path = try windowsCmdExePath(self.allocator);
- }
- return self.cmd_exe_path.?;
- }
-};
-
-/// Returns the absolute path of `cmd.exe` within the Windows system directory.
-/// The caller owns the returned slice.
-fn windowsCmdExePath(allocator: Allocator) error{ OutOfMemory, Unexpected }![:0]u16 {
- var buf = try ArrayList(u16).initCapacity(allocator, 128);
- errdefer buf.deinit(allocator);
- while (true) {
- const unused_slice = buf.unusedCapacitySlice();
- // TODO: Get the system directory from PEB.ReadOnlyStaticServerData
- const len = windows.kernel32.GetSystemDirectoryW(@ptrCast(unused_slice), @intCast(unused_slice.len));
- if (len == 0) {
- switch (windows.GetLastError()) {
- else => |err| return windows.unexpectedError(err),
- }
- }
- if (len > unused_slice.len) {
- try buf.ensureUnusedCapacity(allocator, len);
- } else {
- buf.items.len = len;
- break;
- }
- }
- switch (buf.items[buf.items.len - 1]) {
- '/', '\\' => {},
- else => try buf.append(allocator, fs.path.sep),
- }
- try buf.appendSlice(allocator, unicode.utf8ToUtf16LeStringLiteral("cmd.exe"));
- return try buf.toOwnedSliceSentinel(allocator, 0);
-}
-
-const ArgvToCommandLineError = error{ OutOfMemory, InvalidWtf8, InvalidArg0 };
-
-/// Serializes `argv` to a Windows command-line string suitable for passing to a child process and
-/// parsing by the `CommandLineToArgvW` algorithm. The caller owns the returned slice.
-///
-/// To avoid arbitrary command execution, this function should not be used when spawning `.bat`/`.cmd` scripts.
-/// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
-///
-/// When executing `.bat`/`.cmd` scripts, use `argvToScriptCommandLineWindows` instead.
-fn argvToCommandLineWindows(
- allocator: Allocator,
- argv: []const []const u8,
-) ArgvToCommandLineError![:0]u16 {
- var buf = std.array_list.Managed(u8).init(allocator);
- defer buf.deinit();
-
- if (argv.len != 0) {
- const arg0 = argv[0];
-
- // The first argument must be quoted if it contains spaces or ASCII control characters
- // (excluding DEL). It also follows special quoting rules where backslashes have no special
- // interpretation, which makes it impossible to pass certain first arguments containing
- // double quotes to a child process without characters from the first argument leaking into
- // subsequent ones (which could have security implications).
- //
- // Empty arguments technically don't need quotes, but we quote them anyway for maximum
- // compatibility with different implementations of the 'CommandLineToArgvW' algorithm.
- //
- // Double quotes are illegal in paths on Windows, so for the sake of simplicity we reject
- // all first arguments containing double quotes, even ones that we could theoretically
- // serialize in unquoted form.
- var needs_quotes = arg0.len == 0;
- for (arg0) |c| {
- if (c <= ' ') {
- needs_quotes = true;
- } else if (c == '"') {
- return error.InvalidArg0;
- }
- }
- if (needs_quotes) {
- try buf.append('"');
- try buf.appendSlice(arg0);
- try buf.append('"');
- } else {
- try buf.appendSlice(arg0);
- }
-
- for (argv[1..]) |arg| {
- try buf.append(' ');
-
- // Subsequent arguments must be quoted if they contain spaces, tabs or double quotes,
- // or if they are empty. For simplicity and for maximum compatibility with different
- // implementations of the 'CommandLineToArgvW' algorithm, we also quote all ASCII
- // control characters (again, excluding DEL).
- needs_quotes = for (arg) |c| {
- if (c <= ' ' or c == '"') {
- break true;
- }
- } else arg.len == 0;
- if (!needs_quotes) {
- try buf.appendSlice(arg);
- continue;
- }
-
- try buf.append('"');
- var backslash_count: usize = 0;
- for (arg) |byte| {
- switch (byte) {
- '\\' => {
- backslash_count += 1;
- },
- '"' => {
- try buf.appendNTimes('\\', backslash_count * 2 + 1);
- try buf.append('"');
- backslash_count = 0;
- },
- else => {
- try buf.appendNTimes('\\', backslash_count);
- try buf.append(byte);
- backslash_count = 0;
- },
- }
- }
- try buf.appendNTimes('\\', backslash_count * 2);
- try buf.append('"');
- }
- }
-
- return try unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items);
-}
-
-test argvToCommandLineWindows {
- const t = testArgvToCommandLineWindows;
-
- try t(&.{
- \\C:\Program Files\zig\zig.exe
- ,
- \\run
- ,
- \\.\src\main.zig
- ,
- \\-target
- ,
- \\x86_64-windows-gnu
- ,
- \\-O
- ,
- \\ReleaseSafe
- ,
- \\--
- ,
- \\--emoji=🗿
- ,
- \\--eval=new Regex("Dwayne \"The Rock\" Johnson")
- ,
- },
- \\"C:\Program Files\zig\zig.exe" run .\src\main.zig -target x86_64-windows-gnu -O ReleaseSafe -- --emoji=🗿 "--eval=new Regex(\"Dwayne \\\"The Rock\\\" Johnson\")"
- );
-
- try t(&.{}, "");
- try t(&.{""}, "\"\"");
- try t(&.{" "}, "\" \"");
- try t(&.{"\t"}, "\"\t\"");
- try t(&.{"\x07"}, "\"\x07\"");
- try t(&.{"🦎"}, "🦎");
-
- try t(
- &.{ "zig", "aa aa", "bb\tbb", "cc\ncc", "dd\r\ndd", "ee\x7Fee" },
- "zig \"aa aa\" \"bb\tbb\" \"cc\ncc\" \"dd\r\ndd\" ee\x7Fee",
- );
-
- try t(
- &.{ "\\\\foo bar\\foo bar\\", "\\\\zig zag\\zig zag\\" },
- "\"\\\\foo bar\\foo bar\\\" \"\\\\zig zag\\zig zag\\\\\"",
- );
-
- try std.testing.expectError(
- error.InvalidArg0,
- argvToCommandLineWindows(std.testing.allocator, &.{"\"quotes\"quotes\""}),
- );
- try std.testing.expectError(
- error.InvalidArg0,
- argvToCommandLineWindows(std.testing.allocator, &.{"quotes\"quotes"}),
- );
- try std.testing.expectError(
- error.InvalidArg0,
- argvToCommandLineWindows(std.testing.allocator, &.{"q u o t e s \" q u o t e s"}),
- );
-}
-
-fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []const u8) !void {
- const cmd_line_w = try argvToCommandLineWindows(std.testing.allocator, argv);
- defer std.testing.allocator.free(cmd_line_w);
-
- const cmd_line = try unicode.wtf16LeToWtf8Alloc(std.testing.allocator, cmd_line_w);
- defer std.testing.allocator.free(cmd_line);
-
- try std.testing.expectEqualStrings(expected_cmd_line, cmd_line);
-}
-
-const ArgvToScriptCommandLineError = error{
- OutOfMemory,
- InvalidWtf8,
- /// NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed
- /// within arguments when executing a `.bat`/`.cmd` script.
- /// - NUL/LF signifiies end of arguments, so anything afterwards
- /// would be lost after execution.
- /// - CR is stripped by `cmd.exe`, so any CR codepoints
- /// would be lost after execution.
- InvalidBatchScriptArg,
-};
-
-/// Serializes `argv` to a Windows command-line string that uses `cmd.exe /c` and `cmd.exe`-specific
-/// escaping rules. The caller owns the returned slice.
-///
-/// Escapes `argv` using the suggested mitigation against arbitrary command execution from:
-/// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
-///
-/// The return of this function will look like
-/// `cmd.exe /d /e:ON /v:OFF /c ""`
-/// and should be used as the `lpCommandLine` of `CreateProcessW`, while the
-/// return of `windowsCmdExePath` should be used as `lpApplicationName`.
-///
-/// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise.
-/// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem.
-fn argvToScriptCommandLineWindows(
- allocator: Allocator,
- /// Path to the `.bat`/`.cmd` script. If this path is relative, it is assumed to be relative to the CWD.
- /// The script must have been verified to exist at this path before calling this function.
- script_path: []const u16,
- /// Arguments, not including the script name itself. Expected to be encoded as WTF-8.
- script_args: []const []const u8,
-) ArgvToScriptCommandLineError![:0]u16 {
- var buf = try std.array_list.Managed(u8).initCapacity(allocator, 64);
- defer buf.deinit();
-
- // `/d` disables execution of AutoRun commands.
- // `/e:ON` and `/v:OFF` are needed for BatBadBut mitigation:
- // > If delayed expansion is enabled via the registry value DelayedExpansion,
- // > it must be disabled by explicitly calling cmd.exe with the /V:OFF option.
- // > Escaping for % requires the command extension to be enabled.
- // > If it’s disabled via the registry value EnableExtensions, it must be enabled with the /E:ON option.
- // https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
- buf.appendSliceAssumeCapacity("cmd.exe /d /e:ON /v:OFF /c \"");
-
- // Always quote the path to the script arg
- buf.appendAssumeCapacity('"');
- // We always want the path to the batch script to include a path separator in order to
- // avoid cmd.exe searching the PATH for the script. This is not part of the arbitrary
- // command execution mitigation, we just know exactly what script we want to execute
- // at this point, and potentially making cmd.exe re-find it is unnecessary.
- //
- // If the script path does not have a path separator, then we know its relative to CWD and
- // we can just put `.\` in the front.
- if (mem.findAny(u16, script_path, &[_]u16{ mem.nativeToLittle(u16, '\\'), mem.nativeToLittle(u16, '/') }) == null) {
- try buf.appendSlice(".\\");
- }
- // Note that we don't do any escaping/mitigations for this argument, since the relevant
- // characters (", %, etc) are illegal in file paths and this function should only be called
- // with script paths that have been verified to exist.
- try unicode.wtf16LeToWtf8ArrayList(&buf, script_path);
- buf.appendAssumeCapacity('"');
-
- for (script_args) |arg| {
- // Literal carriage returns get stripped when run through cmd.exe
- // and NUL/newlines act as 'end of command.' Because of this, it's basically
- // always a mistake to include these characters in argv, so it's
- // an error condition in order to ensure that the return of this
- // function can always roundtrip through cmd.exe.
- if (std.mem.findAny(u8, arg, "\x00\r\n") != null) {
- return error.InvalidBatchScriptArg;
- }
-
- // Separate args with a space.
- try buf.append(' ');
-
- // Need to quote if the argument is empty (otherwise the arg would just be lost)
- // or if the last character is a `\`, since then something like "%~2" in a .bat
- // script would cause the closing " to be escaped which we don't want.
- var needs_quotes = arg.len == 0 or arg[arg.len - 1] == '\\';
- if (!needs_quotes) {
- for (arg) |c| {
- switch (c) {
- // Known good characters that don't need to be quoted
- 'A'...'Z', 'a'...'z', '0'...'9', '#', '$', '*', '+', '-', '.', '/', ':', '?', '@', '\\', '_' => {},
- // When in doubt, quote
- else => {
- needs_quotes = true;
- break;
- },
- }
- }
- }
- if (needs_quotes) {
- try buf.append('"');
- }
- var backslashes: usize = 0;
- for (arg) |c| {
- switch (c) {
- '\\' => {
- backslashes += 1;
- },
- '"' => {
- try buf.appendNTimes('\\', backslashes);
- try buf.append('"');
- backslashes = 0;
- },
- // Replace `%` with `%%cd:~,%`.
- //
- // cmd.exe allows extracting a substring from an environment
- // variable with the syntax: `%foo:~,%`.
- // Therefore, `%cd:~,%` will always expand to an empty string
- // since both the start and end index are blank, and it is assumed
- // that `%cd%` is always available since it is a built-in variable
- // that corresponds to the current directory.
- //
- // This means that replacing `%foo%` with `%%cd:~,%foo%%cd:~,%`
- // will stop `%foo%` from being expanded and *after* expansion
- // we'll still be left with `%foo%` (the literal string).
- '%' => {
- // the trailing `%` is appended outside the switch
- try buf.appendSlice("%%cd:~,");
- backslashes = 0;
- },
- else => {
- backslashes = 0;
- },
- }
- try buf.append(c);
- }
- if (needs_quotes) {
- try buf.appendNTimes('\\', backslashes);
- try buf.append('"');
- }
- }
-
- try buf.append('"');
-
- return try unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items);
-}
diff --git a/lib/std/process/Environ.zig b/lib/std/process/Environ.zig
new file mode 100644
index 0000000000000000000000000000000000000000..5255e1af16ab86f4e1e29158cbfda3c8c8830c66
--- /dev/null
+++ b/lib/std/process/Environ.zig
@@ -0,0 +1,822 @@
+const Environ = @This();
+
+const builtin = @import("builtin");
+const native_os = builtin.os.tag;
+
+const std = @import("../std.zig");
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const testing = std.testing;
+const unicode = std.unicode;
+const posix = std.posix;
+const mem = std.mem;
+
+/// Unmodified, unprocessed data provided by the operating system.
+block: Block,
+
+pub const empty: Environ = .{
+ .block = switch (Block) {
+ void => {},
+ else => &.{},
+ },
+};
+
+/// On WASI without libc, this is `void` because the environment has to be
+/// queried and heap-allocated at runtime.
+///
+/// On Windows, the memory pointed at by the PEB changes when the environment
+/// is modified, so a long-lived pointer cannot be used. Therefore, on this
+/// operating system `void` is also used.
+pub const Block = switch (native_os) {
+ .windows => void,
+ .wasi => switch (builtin.link_libc) {
+ false => void,
+ true => [:null]const ?[*:0]const u8,
+ },
+ .freestanding, .other => void,
+ else => [:null]const ?[*:0]const u8,
+};
+
+pub const Map = struct {
+ array_hash_map: ArrayHashMap,
+ allocator: Allocator,
+
+ const ArrayHashMap = std.ArrayHashMapUnmanaged([]const u8, []const u8, EnvNameHashContext, false);
+
+ pub const Size = usize;
+
+ pub const EnvNameHashContext = struct {
+ fn upcase(c: u21) u21 {
+ if (c <= std.math.maxInt(u16))
+ return std.os.windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c)));
+ return c;
+ }
+
+ pub fn hash(self: @This(), s: []const u8) u32 {
+ _ = self;
+ if (native_os == .windows) {
+ var h = std.hash.Wyhash.init(0);
+ var it = unicode.Wtf8View.initUnchecked(s).iterator();
+ while (it.nextCodepoint()) |cp| {
+ const cp_upper = upcase(cp);
+ h.update(&[_]u8{
+ @as(u8, @intCast((cp_upper >> 16) & 0xff)),
+ @as(u8, @intCast((cp_upper >> 8) & 0xff)),
+ @as(u8, @intCast((cp_upper >> 0) & 0xff)),
+ });
+ }
+ return @truncate(h.final());
+ }
+ return std.array_hash_map.hashString(s);
+ }
+
+ pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool {
+ _ = self;
+ _ = b_index;
+ if (native_os == .windows) {
+ var it_a = unicode.Wtf8View.initUnchecked(a).iterator();
+ var it_b = unicode.Wtf8View.initUnchecked(b).iterator();
+ while (true) {
+ const c_a = it_a.nextCodepoint() orelse break;
+ const c_b = it_b.nextCodepoint() orelse return false;
+ if (upcase(c_a) != upcase(c_b))
+ return false;
+ }
+ return if (it_b.nextCodepoint()) |_| false else true;
+ }
+ return std.array_hash_map.eqlString(a, b);
+ }
+ };
+
+ /// Create a Map backed by a specific allocator.
+ /// That allocator will be used for both backing allocations
+ /// and string deduplication.
+ pub fn init(allocator: Allocator) Map {
+ return .{ .array_hash_map = .empty, .allocator = allocator };
+ }
+
+ /// Free the backing storage of the map, as well as all
+ /// of the stored keys and values.
+ pub fn deinit(self: *Map) void {
+ const gpa = self.allocator;
+ var it = self.array_hash_map.iterator();
+ while (it.next()) |entry| {
+ gpa.free(entry.key_ptr.*);
+ gpa.free(entry.value_ptr.*);
+ }
+ self.array_hash_map.deinit(gpa);
+ self.* = undefined;
+ }
+
+ pub fn keys(m: *const Map) [][]const u8 {
+ return m.array_hash_map.keys();
+ }
+
+ pub fn values(m: *const Map) [][]const u8 {
+ return m.array_hash_map.values();
+ }
+
+ /// Same as `put` but the key and value become owned by the Map rather
+ /// than being copied.
+ /// If `putMove` fails, the ownership of key and value does not transfer.
+ /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
+ pub fn putMove(self: *Map, key: []u8, value: []u8) !void {
+ const gpa = self.allocator;
+ assert(unicode.wtf8ValidateSlice(key));
+ const get_or_put = try self.array_hash_map.getOrPut(gpa, key);
+ if (get_or_put.found_existing) {
+ gpa.free(get_or_put.key_ptr.*);
+ gpa.free(get_or_put.value_ptr.*);
+ get_or_put.key_ptr.* = key;
+ }
+ get_or_put.value_ptr.* = value;
+ }
+
+ /// `key` and `value` are copied into the Map.
+ /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
+ pub fn put(self: *Map, key: []const u8, value: []const u8) !void {
+ assert(unicode.wtf8ValidateSlice(key));
+ const gpa = self.allocator;
+ const value_copy = try gpa.dupe(u8, value);
+ errdefer gpa.free(value_copy);
+ const get_or_put = try self.array_hash_map.getOrPut(gpa, key);
+ errdefer {
+ if (!get_or_put.found_existing) assert(self.array_hash_map.pop() != null);
+ }
+ if (get_or_put.found_existing) {
+ gpa.free(get_or_put.value_ptr.*);
+ } else {
+ get_or_put.key_ptr.* = try gpa.dupe(u8, key);
+ }
+ get_or_put.value_ptr.* = value_copy;
+ }
+
+ /// Find the address of the value associated with a key.
+ /// The returned pointer is invalidated if the map resizes.
+ /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
+ pub fn getPtr(self: Map, key: []const u8) ?*[]const u8 {
+ assert(unicode.wtf8ValidateSlice(key));
+ return self.array_hash_map.getPtr(key);
+ }
+
+ /// Return the map's copy of the value associated with
+ /// a key. The returned string is invalidated if this
+ /// key is removed from the map.
+ /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
+ pub fn get(self: Map, key: []const u8) ?[]const u8 {
+ assert(unicode.wtf8ValidateSlice(key));
+ return self.array_hash_map.get(key);
+ }
+
+ pub fn contains(m: *const Map, key: []const u8) bool {
+ return m.array_hash_map.contains(key);
+ }
+
+ /// If there is an entry with a matching key, it is deleted from the hash
+ /// map. The entry is removed from the underlying array by swapping it with
+ /// the last element.
+ ///
+ /// Returns true if an entry was removed, false otherwise.
+ ///
+ /// This invalidates the value returned by get() for this key.
+ /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
+ pub fn swapRemove(self: *Map, key: []const u8) bool {
+ assert(unicode.wtf8ValidateSlice(key));
+ const kv = self.array_hash_map.fetchSwapRemove(key) orelse return false;
+ const gpa = self.allocator;
+ gpa.free(kv.key);
+ gpa.free(kv.value);
+ return true;
+ }
+
+ /// If there is an entry with a matching key, it is deleted from the map.
+ /// The entry is removed from the underlying array by shifting all elements
+ /// forward, thereby maintaining the current ordering.
+ ///
+ /// Returns true if an entry was removed, false otherwise.
+ ///
+ /// This invalidates the value returned by get() for this key.
+ /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string.
+ pub fn orderedRemove(self: *Map, key: []const u8) bool {
+ assert(unicode.wtf8ValidateSlice(key));
+ const kv = self.array_hash_map.fetchOrderedRemove(key) orelse return false;
+ const gpa = self.allocator;
+ gpa.free(kv.key);
+ gpa.free(kv.value);
+ return true;
+ }
+
+ /// Returns the number of KV pairs stored in the map.
+ pub fn count(self: Map) Size {
+ return self.array_hash_map.count();
+ }
+
+ /// Returns an iterator over entries in the map.
+ pub fn iterator(self: *const Map) ArrayHashMap.Iterator {
+ return self.array_hash_map.iterator();
+ }
+
+ /// Returns a full copy of `em` allocated with `gpa`, which is not necessarily
+ /// the same allocator used to allocate `em`.
+ pub fn clone(m: *const Map, gpa: Allocator) Allocator.Error!Map {
+ // Since we need to dupe the keys and values, the only way for error handling to not be a
+ // nightmare is to add keys to an empty map one-by-one. This could be avoided if this
+ // abstraction were a bit less... OOP-esque.
+ var new: Map = .init(gpa);
+ errdefer new.deinit();
+ try new.array_hash_map.ensureUnusedCapacity(gpa, m.array_hash_map.count());
+ for (m.array_hash_map.keys(), m.array_hash_map.values()) |key, value| {
+ try new.put(key, value);
+ }
+ return new;
+ }
+
+ /// Creates a null-delimited environment variable block in the format
+ /// expected by POSIX, from a hash map plus options.
+ pub fn createBlockPosix(
+ map: *const Map,
+ arena: Allocator,
+ options: CreateBlockPosixOptions,
+ ) Allocator.Error![:null]?[*:0]u8 {
+ const ZigProgressAction = enum { nothing, edit, delete, add };
+ const zig_progress_action: ZigProgressAction = a: {
+ const fd = options.zig_progress_fd orelse break :a .nothing;
+ const exists = map.get("ZIG_PROGRESS") != null;
+ if (fd >= 0) {
+ break :a if (exists) .edit else .add;
+ } else {
+ if (exists) break :a .delete;
+ }
+ break :a .nothing;
+ };
+
+ const envp_count: usize = c: {
+ var c: usize = map.count();
+ switch (zig_progress_action) {
+ .add => c += 1,
+ .delete => c -= 1,
+ .nothing, .edit => {},
+ }
+ break :c c;
+ };
+
+ const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);
+ var i: usize = 0;
+
+ if (zig_progress_action == .add) {
+ envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
+ i += 1;
+ }
+
+ {
+ var it = map.iterator();
+ while (it.next()) |pair| {
+ if (mem.eql(u8, pair.key_ptr.*, "ZIG_PROGRESS")) switch (zig_progress_action) {
+ .add => unreachable,
+ .delete => continue,
+ .edit => {
+ envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{
+ pair.key_ptr.*, options.zig_progress_fd.?,
+ }, 0);
+ i += 1;
+ continue;
+ },
+ .nothing => {},
+ };
+
+ envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0);
+ i += 1;
+ }
+ }
+
+ assert(i == envp_count);
+ return envp_buf;
+ }
+
+ /// Caller owns result.
+ pub fn createBlockWindows(map: *const Map, gpa: Allocator) error{ OutOfMemory, InvalidWtf8 }![:0]u16 {
+ // count bytes needed
+ const max_chars_needed = x: {
+ // Only need 2 trailing NUL code units for an empty environment
+ var max_chars_needed: usize = if (map.count() == 0) 2 else 1;
+ var it = map.iterator();
+ while (it.next()) |pair| {
+ // +1 for '='
+ // +1 for null byte
+ max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2;
+ }
+ break :x max_chars_needed;
+ };
+ const result = try gpa.alloc(u16, max_chars_needed);
+ errdefer gpa.free(result);
+
+ var it = map.iterator();
+ var i: usize = 0;
+ while (it.next()) |pair| {
+ i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*);
+ result[i] = '=';
+ i += 1;
+ i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*);
+ result[i] = 0;
+ i += 1;
+ }
+ result[i] = 0;
+ i += 1;
+ // An empty environment is a special case that requires a redundant
+ // NUL terminator. CreateProcess will read the second code unit even
+ // though theoretically the first should be enough to recognize that the
+ // environment is empty (see https://nullprogram.com/blog/2023/08/23/)
+ if (map.count() == 0) {
+ result[i] = 0;
+ i += 1;
+ }
+ const reallocated = try gpa.realloc(result, i);
+ return reallocated[0 .. i - 1 :0];
+ }
+};
+
+pub const CreateMapError = error{
+ OutOfMemory,
+ /// WASI-only. `environ_sizes_get` or `environ_get` failed for an
+ /// unanticipated, undocumented reason.
+ Unexpected,
+};
+
+/// Allocates a `Map` and copies environment block into it.
+pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map {
+ if (native_os == .windows)
+ return createMapWide(std.os.windows.peb().ProcessParameters.Environment, allocator);
+
+ var result = Map.init(allocator);
+ errdefer result.deinit();
+
+ if (native_os == .wasi and !builtin.link_libc) {
+ var environ_count: usize = undefined;
+ var environ_buf_size: usize = undefined;
+
+ const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
+ if (environ_sizes_get_ret != .SUCCESS) {
+ return posix.unexpectedErrno(environ_sizes_get_ret);
+ }
+
+ if (environ_count == 0) {
+ return result;
+ }
+
+ const environ = try allocator.alloc([*:0]u8, environ_count);
+ defer allocator.free(environ);
+ const environ_buf = try allocator.alloc(u8, environ_buf_size);
+ defer allocator.free(environ_buf);
+
+ const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr);
+ if (environ_get_ret != .SUCCESS) {
+ return posix.unexpectedErrno(environ_get_ret);
+ }
+
+ for (environ) |line| {
+ const pair = mem.sliceTo(line, 0);
+ var parts = mem.splitScalar(u8, pair, '=');
+ const key = parts.first();
+ const value = parts.rest();
+ try result.put(key, value);
+ }
+ return result;
+ } else {
+ for (env.block) |opt_line| {
+ const line = opt_line.?;
+ var line_i: usize = 0;
+ while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
+ const key = line[0..line_i];
+
+ var end_i: usize = line_i;
+ while (line[end_i] != 0) : (end_i += 1) {}
+ const value = line[line_i + 1 .. end_i];
+
+ try result.put(key, value);
+ }
+ return result;
+ }
+}
+
+pub fn createMapWide(ptr: [*:0]u16, gpa: Allocator) CreateMapError!Map {
+ var result = Map.init(gpa);
+ errdefer result.deinit();
+
+ var i: usize = 0;
+ while (ptr[i] != 0) {
+ const key_start = i;
+
+ // There are some special environment variables that start with =,
+ // so we need a special case to not treat = as a key/value separator
+ // if it's the first character.
+ // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
+ if (ptr[key_start] == '=') i += 1;
+
+ while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
+ const key_w = ptr[key_start..i];
+ const key = try unicode.wtf16LeToWtf8Alloc(gpa, key_w);
+ errdefer gpa.free(key);
+
+ if (ptr[i] == '=') i += 1;
+
+ const value_start = i;
+ while (ptr[i] != 0) : (i += 1) {}
+ const value_w = ptr[value_start..i];
+ const value = try unicode.wtf16LeToWtf8Alloc(gpa, value_w);
+ errdefer gpa.free(value);
+
+ i += 1; // skip over null byte
+
+ try result.putMove(key, value);
+ }
+ return result;
+}
+
+pub const ContainsError = error{
+ OutOfMemory,
+ /// On Windows, environment variable keys provided by the user must be
+ /// valid [WTF-8](https://wtf-8.codeberg.page/). This error is unreachable
+ /// if the key is statically known to be valid.
+ InvalidWtf8,
+ /// WASI-only. `environ_sizes_get` or `environ_get` failed for an
+ /// unexpected reason.
+ Unexpected,
+};
+
+/// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/),
+/// then `error.InvalidWtf8` is returned.
+///
+/// See also:
+/// * `createMap`
+/// * `containsConstant`
+/// * `containsUnempty`
+pub fn contains(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool {
+ var map = try createMap(environ, gpa);
+ defer map.deinit();
+ return map.contains(key);
+}
+
+/// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/),
+/// then `error.InvalidWtf8` is returned.
+///
+/// See also:
+/// * `createMap`
+/// * `containsUnemptyConstant`
+/// * `contains`
+pub fn containsUnempty(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool {
+ var map = try createMap(environ, gpa);
+ defer map.deinit();
+ const value = map.get(key) orelse return false;
+ return value.len != 0;
+}
+
+/// This function is unavailable on WASI without libc due to the memory
+/// allocation requirement.
+///
+/// On Windows, `key` must be valid [WTF-8](https://wtf-8.codeberg.page/),
+///
+/// See also:
+/// * `contains`
+/// * `containsUnemptyConstant`
+/// * `createMap`
+pub inline fn containsConstant(environ: Environ, comptime key: []const u8) bool {
+ if (native_os == .windows) {
+ const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key);
+ return getWindows(environ, key_w) != null;
+ } else {
+ return getPosix(environ, key) != null;
+ }
+}
+
+/// This function is unavailable on WASI without libc due to the memory
+/// allocation requirement.
+///
+/// On Windows, `key` must be valid [WTF-8](https://wtf-8.codeberg.page/),
+///
+/// See also:
+/// * `containsUnempty`
+/// * `containsConstant`
+/// * `createMap`
+pub inline fn containsUnemptyConstant(environ: Environ, comptime key: []const u8) bool {
+ if (native_os == .windows) {
+ const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key);
+ const value = getWindows(environ, key_w) orelse return false;
+ return value.len != 0;
+ } else {
+ const value = getPosix(environ, key) orelse return false;
+ return value.len != 0;
+ }
+}
+
+/// This function is unavailable on WASI without libc due to the memory
+/// allocation requirement.
+///
+/// See also:
+/// * `getWindows`
+/// * `createMap`
+pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 {
+ if (mem.findScalar(u8, key, '=') != null) return null;
+ for (environ.block) |opt_line| {
+ const line = opt_line.?;
+ var line_i: usize = 0;
+ while (line[line_i] != 0) : (line_i += 1) {
+ if (line_i == key.len) break;
+ if (line[line_i] != key[line_i]) break;
+ }
+ if ((line_i != key.len) or (line[line_i] != '=')) continue;
+
+ return mem.sliceTo(line + line_i + 1, 0);
+ }
+ return null;
+}
+
+/// Windows-only. Get an environment variable with a null-terminated, WTF-16
+/// encoded name.
+///
+/// This function performs a Unicode-aware case-insensitive lookup using
+/// RtlEqualUnicodeString.
+///
+/// See also:
+/// * `createMap`
+/// * `containsConstant`
+/// * `contains`
+pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 {
+ comptime assert(native_os == .windows);
+ comptime assert(@TypeOf(environ.block) == void);
+
+ // '=' anywhere but the start makes this an invalid environment variable name.
+ const key_slice = mem.sliceTo(key, 0);
+ if (key_slice.len > 0 and mem.findScalar(u16, key_slice[1..], '=') != null) return null;
+
+ const ptr = std.os.windows.peb().ProcessParameters.Environment;
+
+ var i: usize = 0;
+ while (ptr[i] != 0) {
+ const key_value = mem.sliceTo(ptr[i..], 0);
+
+ // There are some special environment variables that start with =,
+ // so we need a special case to not treat = as a key/value separator
+ // if it's the first character.
+ // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
+ const equal_search_start: usize = if (key_value[0] == '=') 1 else 0;
+ const equal_index = mem.findScalarPos(u16, key_value, equal_search_start, '=') orelse {
+ // This is enforced by CreateProcess.
+ // If violated, CreateProcess will fail with INVALID_PARAMETER.
+ unreachable; // must contain a =
+ };
+
+ const this_key = key_value[0..equal_index];
+ if (std.os.windows.eqlIgnoreCaseWtf16(key_slice, this_key)) {
+ return key_value[equal_index + 1 ..];
+ }
+
+ // skip past the NUL terminator
+ i += key_value.len + 1;
+ }
+ return null;
+}
+
+pub const GetAllocError = error{
+ OutOfMemory,
+ EnvironmentVariableMissing,
+ /// On Windows, environment variable keys provided by the user must be
+ /// valid [WTF-8](https://wtf-8.codeberg.page/). This error is unreachable
+ /// if the key is statically known to be valid.
+ InvalidWtf8,
+};
+
+/// Caller owns returned memory.
+///
+/// On Windows:
+/// * If `key` is not valid [WTF-8](https://wtf-8.codeberg.page/), then
+/// `error.InvalidWtf8` is returned.
+/// * The returned value is encoded as [WTF-8](https://wtf-8.codeberg.page/).
+///
+/// On other platforms, the value is an opaque sequence of bytes with no
+/// particular encoding.
+///
+/// See also:
+/// * `createMap`
+pub fn getAlloc(environ: Environ, gpa: Allocator, key: []const u8) GetAllocError![]u8 {
+ var map = createMap(environ, gpa) catch return error.OutOfMemory;
+ defer map.deinit();
+ const val = map.get(key) orelse return error.EnvironmentVariableMissing;
+ return gpa.dupe(u8, val);
+}
+
+pub const CreateBlockPosixOptions = struct {
+ /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified.
+ /// If non-null, negative means to remove the environment variable, and >= 0
+ /// means to provide it with the given integer.
+ zig_progress_fd: ?i32 = null,
+};
+
+/// Creates a null-delimited environment variable block in the format expected
+/// by POSIX, from a different one.
+pub fn createBlockPosix(
+ existing: Environ,
+ arena: Allocator,
+ options: CreateBlockPosixOptions,
+) Allocator.Error![:null]?[*:0]u8 {
+ const contains_zig_progress = for (existing.block) |opt_line| {
+ if (mem.eql(u8, mem.sliceTo(opt_line.?, '='), "ZIG_PROGRESS")) break true;
+ } else false;
+
+ const ZigProgressAction = enum { nothing, edit, delete, add };
+ const zig_progress_action: ZigProgressAction = a: {
+ const fd = options.zig_progress_fd orelse break :a .nothing;
+ if (fd >= 0) {
+ break :a if (contains_zig_progress) .edit else .add;
+ } else {
+ if (contains_zig_progress) break :a .delete;
+ }
+ break :a .nothing;
+ };
+
+ const envp_count: usize = c: {
+ var count: usize = existing.block.len;
+ switch (zig_progress_action) {
+ .add => count += 1,
+ .delete => count -= 1,
+ .nothing, .edit => {},
+ }
+ break :c count;
+ };
+
+ const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);
+ var i: usize = 0;
+ var existing_index: usize = 0;
+
+ if (zig_progress_action == .add) {
+ envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
+ i += 1;
+ }
+
+ while (existing.block[existing_index]) |line| : (existing_index += 1) {
+ if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) {
+ .add => unreachable,
+ .delete => continue,
+ .edit => {
+ envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0);
+ i += 1;
+ continue;
+ },
+ .nothing => {},
+ };
+ envp_buf[i] = try arena.dupeZ(u8, mem.span(line));
+ i += 1;
+ }
+
+ assert(i == envp_count);
+ return envp_buf;
+}
+
+test "Map.createBlock" {
+ const allocator = testing.allocator;
+ var envmap = Map.init(allocator);
+ defer envmap.deinit();
+
+ try envmap.put("HOME", "/home/ifreund");
+ try envmap.put("WAYLAND_DISPLAY", "wayland-1");
+ try envmap.put("DISPLAY", ":1");
+ try envmap.put("DEBUGINFOD_URLS", " ");
+ try envmap.put("XCURSOR_SIZE", "24");
+
+ var arena = std.heap.ArenaAllocator.init(allocator);
+ defer arena.deinit();
+ const environ = try envmap.createBlockPosix(arena.allocator(), .{});
+
+ try testing.expectEqual(@as(usize, 5), environ.len);
+
+ inline for (.{
+ "HOME=/home/ifreund",
+ "WAYLAND_DISPLAY=wayland-1",
+ "DISPLAY=:1",
+ "DEBUGINFOD_URLS= ",
+ "XCURSOR_SIZE=24",
+ }) |target| {
+ for (environ) |variable| {
+ if (mem.eql(u8, mem.span(variable orelse continue), target)) break;
+ } else {
+ try testing.expect(false); // Environment variable not found
+ }
+ }
+}
+
+test Map {
+ var env = Map.init(testing.allocator);
+ defer env.deinit();
+
+ try env.put("SOMETHING_NEW", "hello");
+ try testing.expectEqualStrings("hello", env.get("SOMETHING_NEW").?);
+ try testing.expectEqual(@as(Map.Size, 1), env.count());
+
+ // overwrite
+ try env.put("SOMETHING_NEW", "something");
+ try testing.expectEqualStrings("something", env.get("SOMETHING_NEW").?);
+ try testing.expectEqual(@as(Map.Size, 1), env.count());
+
+ // a new longer name to test the Windows-specific conversion buffer
+ try env.put("SOMETHING_NEW_AND_LONGER", "1");
+ try testing.expectEqualStrings("1", env.get("SOMETHING_NEW_AND_LONGER").?);
+ try testing.expectEqual(@as(Map.Size, 2), env.count());
+
+ // case insensitivity on Windows only
+ if (native_os == .windows) {
+ try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?);
+ } else {
+ try testing.expect(null == env.get("something_New_aNd_LONGER"));
+ }
+
+ var it = env.iterator();
+ var count: Map.Size = 0;
+ while (it.next()) |entry| {
+ const is_an_expected_name = mem.eql(u8, "SOMETHING_NEW", entry.key_ptr.*) or mem.eql(u8, "SOMETHING_NEW_AND_LONGER", entry.key_ptr.*);
+ try testing.expect(is_an_expected_name);
+ count += 1;
+ }
+ try testing.expectEqual(@as(Map.Size, 2), count);
+
+ try testing.expect(env.swapRemove("SOMETHING_NEW"));
+ try testing.expect(!env.swapRemove("SOMETHING_NEW"));
+ try testing.expect(env.get("SOMETHING_NEW") == null);
+
+ try testing.expectEqual(@as(Map.Size, 1), env.count());
+
+ if (native_os == .windows) {
+ // test Unicode case-insensitivity on Windows
+ try env.put("КИРиллИЦА", "something else");
+ try testing.expectEqualStrings("something else", env.get("кириллица").?);
+
+ // and WTF-8 that's not valid UTF-8
+ const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{
+ mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate
+ });
+ defer testing.allocator.free(wtf8_with_surrogate_pair);
+
+ try env.put(wtf8_with_surrogate_pair, wtf8_with_surrogate_pair);
+ try testing.expectEqualSlices(u8, wtf8_with_surrogate_pair, env.get(wtf8_with_surrogate_pair).?);
+ }
+}
+
+test "convert from Environ to Map and back again" {
+ if (native_os == .windows) return;
+ if (native_os == .wasi and !builtin.link_libc) return;
+
+ const gpa = testing.allocator;
+
+ var map: Map = .init(gpa);
+ defer map.deinit();
+ try map.put("FOO", "BAR");
+ try map.put("A", "");
+ try map.put("", "B");
+
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ defer arena_allocator.deinit();
+ const arena = arena_allocator.allocator();
+
+ const environ: Environ = .{ .block = try map.createBlockPosix(arena, .{}) };
+
+ try testing.expectEqual(true, environ.contains(gpa, "FOO"));
+ try testing.expectEqual(false, environ.contains(gpa, "BAR"));
+ try testing.expectEqual(true, environ.contains(gpa, "A"));
+ try testing.expectEqual(true, environ.containsConstant("A"));
+ try testing.expectEqual(false, environ.containsUnempty(gpa, "A"));
+ try testing.expectEqual(false, environ.containsUnemptyConstant("A"));
+ try testing.expectEqual(true, environ.contains(gpa, ""));
+ try testing.expectEqual(false, environ.contains(gpa, "B"));
+
+ try testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(gpa, "BOGUS"));
+ {
+ const value = try environ.getAlloc(gpa, "FOO");
+ defer gpa.free(value);
+ try testing.expectEqualStrings("BAR", value);
+ }
+
+ var map2 = try environ.createMap(gpa);
+ defer map2.deinit();
+
+ try testing.expectEqualDeep(map.keys(), map2.keys());
+ try testing.expectEqualDeep(map.values(), map2.values());
+}
+
+test createMapWide {
+ if (builtin.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO
+
+ const gpa = testing.allocator;
+
+ var map: Map = .init(gpa);
+ defer map.deinit();
+ try map.put("FOO", "BAR");
+ try map.put("A", "");
+ try map.put("", "B");
+
+ const environ: [:0]u16 = try map.createBlockWindows(gpa);
+ defer gpa.free(environ);
+
+ var map2 = try createMapWide(environ, gpa);
+ defer map2.deinit();
+
+ try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "=B" }, map2.keys());
+ try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "" }, map2.values());
+}
diff --git a/lib/std/start.zig b/lib/std/start.zig
index c6a9f06724e965482cc67a8021aebff67f59d265..8d2e2a9df2df2ff95459d6a9d5cfd695d24aa1d7 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -1,128 +1,76 @@
// This file is included in the compilation unit when exporting an executable.
-const root = @import("root");
-const std = @import("std.zig");
const builtin = @import("builtin");
+const native_arch = builtin.cpu.arch;
+const native_os = builtin.os.tag;
+const is_wasm = native_arch.isWasm();
+
+const std = @import("std.zig");
const assert = std.debug.assert;
const uefi = std.os.uefi;
const elf = std.elf;
-const native_arch = builtin.cpu.arch;
-const native_os = builtin.os.tag;
+
+const root = @import("root");
const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start";
-// The self-hosted compiler is not fully capable of handling all of this start.zig file.
-// Until then, we have simplified logic here for self-hosted. TODO remove this once
-// self-hosted is capable enough to handle all of the real start.zig logic.
-pub const simplified_logic = switch (builtin.zig_backend) {
- .stage2_aarch64,
- .stage2_arm,
- .stage2_powerpc,
- .stage2_sparc64,
- .stage2_spirv,
- .stage2_x86,
- => true,
- else => false,
-};
-
comptime {
// No matter what, we import the root file, so that any export, test, comptime
// decls there get run.
_ = root;
- if (simplified_logic) {
- if (builtin.output_mode == .Exe) {
- if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) {
- if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) {
- @export(&main2, .{ .name = "main" });
- }
- } else if (builtin.os.tag == .windows) {
- if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {
- @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
- }
- } else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) {
- if (@hasDecl(root, "main"))
- @export(&spirvMain2, .{ .name = "main" });
- } else {
- if (!@hasDecl(root, "_start")) {
- @export(&_start2, .{ .name = "_start" });
- }
- }
+ if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) {
+ if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
+ @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" });
}
- } else {
- if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) {
- if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
- @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" });
+ } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
+ if (builtin.link_libc and @hasDecl(root, "main")) {
+ if (is_wasm) {
+ @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" });
+ } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) {
+ @export(&main, .{ .name = "main" });
}
- } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
- if (builtin.link_libc and @hasDecl(root, "main")) {
- if (native_arch.isWasm()) {
- @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" });
- } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) {
- @export(&main, .{ .name = "main" });
- }
- } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) {
- if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) {
- @export(&wWinMain, .{ .name = "wWinMain" });
- }
- } else if (native_os == .windows) {
- if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
- !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
- {
- @export(&WinStartup, .{ .name = "wWinMainCRTStartup" });
- } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
- !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
- {
- @compileError("WinMain not supported; declare wWinMain or main instead");
- } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and
- !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup"))
- {
- @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" });
- }
- } else if (native_os == .uefi) {
- if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" });
- } else if (native_os == .wasi) {
- const wasm_start_sym = switch (builtin.wasi_exec_model) {
- .reactor => "_initialize",
- .command => "_start",
- };
- if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) {
- // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
- // case it's not required to provide an entrypoint such as main.
- @export(&wasi_start, .{ .name = wasm_start_sym });
- }
- } else if (native_arch.isWasm() and native_os == .freestanding) {
+ } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) {
+ if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) {
+ @export(&wWinMain, .{ .name = "wWinMain" });
+ }
+ } else if (native_os == .windows) {
+ if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
+ !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
+ {
+ @export(&WinStartup, .{ .name = "wWinMainCRTStartup" });
+ } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
+ !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
+ {
+ @compileError("WinMain not supported; declare wWinMain or main instead");
+ } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and
+ !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup"))
+ {
+ @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" });
+ }
+ } else if (native_os == .uefi) {
+ if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" });
+ } else if (native_os == .wasi) {
+ const wasm_start_sym = switch (builtin.wasi_exec_model) {
+ .reactor => "_initialize",
+ .command => "_start",
+ };
+ if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) {
// Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
// case it's not required to provide an entrypoint such as main.
- if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name });
- } else switch (native_os) {
- .other, .freestanding, .@"3ds", .vita => {},
- else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }),
+ @export(&startWasi, .{ .name = wasm_start_sym });
}
+ } else if (is_wasm and native_os == .freestanding) {
+ // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
+ // case it's not required to provide an entrypoint such as main.
+ if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name });
+ } else switch (native_os) {
+ .other, .freestanding, .@"3ds", .vita => {},
+ else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }),
}
}
}
-// Simplified start code for stage2 until it supports more language features ///
-
-fn main2() callconv(.c) c_int {
- return callMain();
-}
-
-fn _start2() callconv(.withStackAlign(.c, 1)) noreturn {
- std.process.exit(callMain());
-}
-
-fn spirvMain2() callconv(.kernel) void {
- root.main();
-}
-
-fn wWinMainCRTStartup2() callconv(.c) noreturn {
- std.process.exit(callMain());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
fn _DllMainCRTStartup(
hinstDLL: std.os.windows.HINSTANCE,
fdwReason: std.os.windows.DWORD,
@@ -142,15 +90,15 @@ 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, .{});
+ _ = @call(.always_inline, callMain, .{ {}, {} });
}
-fn wasi_start() callconv(.c) void {
+fn startWasi() 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 => _ = @call(.always_inline, callMain, .{ {}, {} }),
+ .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{ {}, {} })),
}
}
@@ -524,7 +472,10 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
std.debug.maybeEnableSegfaultHandler();
- std.os.windows.ntdll.RtlExitUserProcess(callMain());
+ const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
+ const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
+
+ std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, {}));
}
fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn {
@@ -556,7 +507,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1));
var envp_count: usize = 0;
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
- const envp = @as([*][*:0]u8, @ptrCast(envp_optional))[0..envp_count];
+ const envp = envp_optional[0..envp_count :null];
// Find the beginning of the auxiliary vector
const auxv: [*]elf.Auxv = @ptrCast(@alignCast(envp.ptr + envp_count + 1));
@@ -631,6 +582,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
}
fn expandStackSize(phdrs: []elf.Phdr) void {
+ @disableInstrumentation();
for (phdrs) |*phdr| {
switch (phdr.p_type) {
elf.PT_GNU_STACK => {
@@ -665,92 +617,137 @@ 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: [:null]?[*:0]u8) u8 {
if (std.Options.debug_threaded_io) |t| {
if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0];
- t.environ = .{ .block = envp };
+ t.environ = .{ .process_environ = .{ .block = envp } };
}
-
std.debug.maybeEnableSegfaultHandler();
-
- return callMain();
+ return callMain(argv[0..argc], envp);
}
fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int {
var env_count: usize = 0;
while (c_envp[env_count] != null) : (env_count += 1) {}
- const envp = @as([*][*:0]u8, @ptrCast(c_envp))[0..env_count];
+ const envp = c_envp[0..env_count :null];
- if (builtin.os.tag == .linux) {
- const at_phdr = std.c.getauxval(elf.AT_PHDR);
- const at_phnum = std.c.getauxval(elf.AT_PHNUM);
- const phdrs = (@as([*]elf.Phdr, @ptrFromInt(at_phdr)))[0..at_phnum];
- expandStackSize(phdrs);
+ switch (builtin.os.tag) {
+ .linux => {
+ const at_phdr = std.c.getauxval(elf.AT_PHDR);
+ const at_phnum = std.c.getauxval(elf.AT_PHNUM);
+ const phdrs = (@as([*]elf.Phdr, @ptrFromInt(at_phdr)))[0..at_phnum];
+ expandStackSize(phdrs);
+ },
+ .windows => {
+ // On Windows, we ignore libc environment and argv and get those
+ // values in their intended encoding from the PEB instead.
+ std.debug.maybeEnableSegfaultHandler();
+ const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
+ const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
+ return callMain(cmd_line_w, {});
+ },
+ else => {},
}
- return callMainWithArgs(@as(usize, @intCast(c_argc)), @as([*][*:0]u8, @ptrCast(c_argv)), envp);
+ return callMainWithArgs(@as(usize, @intCast(c_argc)), @as([*][*:0]u8, @ptrCast(c_argv)), @ptrCast(envp));
}
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..@intCast(c_argc)];
-
+ const argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)];
if (@sizeOf(std.Io.Threaded.Argv0) != 0) {
- if (std.Options.debug_threaded_io) |t| t.argv0.value = std.os.argv[0];
+ if (std.Options.debug_threaded_io) |t| t.argv0.value = argv[0];
}
-
- return callMain();
+ return callMain(argv, &.{});
}
-// General error message for a malformed return type
+/// 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.?;
+const use_debug_allocator = !is_wasm and switch (builtin.mode) {
+ .Debug => true,
+ .ReleaseSafe => !builtin.link_libc, // Not ideal, but the best we have for now.
+ .ReleaseFast, .ReleaseSmall => !builtin.link_libc and builtin.single_threaded, // Also not ideal.
+};
+var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
+inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.Block) u8 {
+ const fn_info = @typeInfo(@TypeOf(root.main)).@"fn";
+ if (fn_info.params.len == 0) return wrapMain(root.main());
+ if (fn_info.params[0].type.? == std.process.Init.Minimal) return wrapMain(root.main(.{
+ .args = .{ .vector = args },
+ .environ = .{ .block = environ },
+ }));
+
+ const gpa = if (use_debug_allocator)
+ debug_allocator.allocator()
+ else if (builtin.link_libc)
+ std.heap.c_allocator
+ else if (is_wasm)
+ std.heap.wasm_allocator
+ else if (!builtin.single_threaded)
+ std.heap.smp_allocator
+ else
+ comptime unreachable;
+
+ defer if (use_debug_allocator) {
+ _ = debug_allocator.deinit(); // Leaks do not affect return code.
+ };
+
+ const arena_backing_allocator = if (is_wasm) gpa else std.heap.page_allocator;
+
+ var arena_allocator = std.heap.ArenaAllocator.init(arena_backing_allocator);
+ defer arena_allocator.deinit();
+
+ var threaded: std.Io.Threaded = .init(gpa, .{
+ .argv0 = .init(.{ .vector = args }),
+ .environ = .{ .block = environ },
+ });
+ defer threaded.deinit();
+
+ var environ_map = std.process.Environ.createMap(.{ .block = environ }, gpa) catch |err|
+ std.process.fatal("failed to parse environment variables: {t}", .{err});
+ defer environ_map.deinit();
+
+ return wrapMain(root.main(.{
+ .minimal = .{
+ .args = .{ .vector = args },
+ .environ = .{ .block = environ },
+ },
+ .arena = &arena_allocator,
+ .gpa = gpa,
+ .io = threaded.io(),
+ .environ_map = &environ_map,
+ }));
+}
+
+inline fn wrapMain(result: anytype) u8 {
+ const ReturnType = @TypeOf(result);
switch (ReturnType) {
- void => {
- root.main();
- return 0;
- },
- noreturn, u8 => {
- return root.main();
- },
- else => {
- if (@typeInfo(ReturnType) != .error_union) @compileError(bad_main_ret);
-
- const result = root.main() catch |err| {
- switch (builtin.zig_backend) {
- .stage2_powerpc,
- .stage2_riscv64,
- => {
- _ = std.posix.write(std.posix.STDERR_FILENO, "error: failed with error\n") catch {};
- return 1;
- },
- else => {},
- }
- std.log.err("{s}", .{@errorName(err)});
- switch (native_os) {
- .freestanding, .other => {},
- else => if (@errorReturnTrace()) |trace| {
- std.debug.dumpStackTrace(trace);
- },
- }
- return 1;
- };
-
- return switch (@TypeOf(result)) {
- void => 0,
- u8 => result,
- else => @compileError(bad_main_ret),
- };
- },
+ void => return 0,
+ noreturn => unreachable,
+ u8 => return result,
+ else => {},
}
+ if (@typeInfo(ReturnType) != .error_union) @compileError(bad_main_ret);
+
+ const unwrapped_result = result catch |err| {
+ std.log.err("{t}", .{err});
+ switch (native_os) {
+ .freestanding, .other => {},
+ else => if (@errorReturnTrace()) |trace| std.debug.dumpStackTrace(trace),
+ }
+ return 1;
+ };
+
+ return switch (@TypeOf(unwrapped_result)) {
+ noreturn => unreachable,
+ void => 0,
+ u8 => unwrapped_result,
+ else => @compileError(bad_main_ret),
+ };
}
-pub fn call_wWinMain() std.os.windows.INT {
+fn call_wWinMain() std.os.windows.INT {
const peb = std.os.windows.peb();
const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).@"fn".params[0].type.?;
const hInstance: MAIN_HINSTANCE = @ptrCast(peb.ImageBaseAddress);
diff --git a/lib/std/std.zig b/lib/std/std.zig
index 6ec39306ea949d257a572f90fcc917724979f97d..c5d71dcb602a06e4f4aeef18dc604e01eb056ba5 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -114,9 +114,6 @@ pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options
pub const Options = struct {
enable_segfault_handler: bool = debug.default_enable_segfault_handler,
- /// Function used to implement `std.Io.Dir.cwd` for WASI.
- wasiCwd: fn () os.wasi.fd_t = os.defaultWasiCwd,
-
/// The current log level.
log_level: log.Level = log.default_level,
@@ -176,10 +173,21 @@ pub const Options = struct {
/// stack traces will just print an error to the relevant `Io.Writer` and return.
allow_stack_tracing: bool = !@import("builtin").strip_debug_info,
+ /// TODO This is a separate decl instead of a field as a workaround around
+ /// compilation errors due to zig not being lazy enough.
+ pub const elf_debug_info_search_paths: ?fn (exe_path: []const u8) switch (@import("builtin").object_format) {
+ .elf => debug.ElfFile.DebugInfoSearchPaths,
+ else => void,
+ } = if (@hasDecl(root, "std_options_elf_debug_info_search_paths"))
+ root.std_options_elf_debug_info_search_paths
+ else
+ null;
+
pub const debug_threaded_io: ?*Io.Threaded = if (@hasDecl(root, "std_options_debug_threaded_io"))
root.std_options_debug_threaded_io
else
Io.Threaded.global_single_threaded;
+
/// The `Io` instance that `std.debug` uses for `std.debug.print`,
/// capturing stack traces, loading debug info, finding the executable's
/// own path, and environment variables that affect terminal mode
@@ -193,6 +201,9 @@ pub const Options = struct {
/// Overrides `std.Io.File.Permissions`.
pub const FilePermissions: ?type = if (@hasDecl(root, "std_options_FilePermissions")) root.std_options_FilePermissions else null;
+
+ /// Overrides `std.Io.Dir.cwd`.
+ pub const cwd: ?fn () Io.Dir = if (@hasDecl(root, "std_options_cwd")) root.std_options_cwd else null;
};
// This forces the start.zig file to be imported, and the comptime logic inside that
diff --git a/lib/std/zig.zig b/lib/std/zig.zig
index abc213ba27a93920df72ed6d77e74ea08abcc011..4f0f47b11f3dfe5b39bcbbef50b562f7a3f87189 100644
--- a/lib/std/zig.zig
+++ b/lib/std/zig.zig
@@ -739,28 +739,39 @@ pub const EnvVar = enum {
ZIG_VERBOSE_CC,
ZIG_BTRFS_WORKAROUND,
ZIG_DEBUG_CMD,
+ ZIG_IS_DETECTING_LIBC_PATHS,
+ ZIG_IS_TRYING_TO_NOT_CALL_ITSELF,
+
+ // C toolchain integration
+ NIX_CFLAGS_COMPILE,
+ NIX_CFLAGS_LINK,
+ NIX_LDFLAGS,
+ C_INCLUDE_PATH,
+ CPLUS_INCLUDE_PATH,
+ LIBRARY_PATH,
CC,
+
+ // Terminal integration
NO_COLOR,
CLICOLOR_FORCE,
+
+ // Debug info integration
XDG_CACHE_HOME,
LOCALAPPDATA,
HOME,
- pub fn isSet(comptime ev: EnvVar) bool {
- return std.process.hasNonEmptyEnvVarConstant(@tagName(ev));
- }
+ // Windows SDK integration
+ PROGRAMDATA,
+
+ // Homebrew integration
+ HOMEBREW_PREFIX,
- pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 {
- if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
- return value;
- } else |err| switch (err) {
- error.EnvironmentVariableNotFound => return null,
- else => |e| return e,
- }
+ pub fn isSet(ev: EnvVar, map: *const std.process.Environ.Map) bool {
+ return map.contains(@tagName(ev));
}
- pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
- return std.posix.getenvZ(@tagName(ev));
+ pub fn get(ev: EnvVar, map: *const std.process.Environ.Map) ?[]const u8 {
+ return map.get(@tagName(ev));
}
};
diff --git a/lib/std/zig/LibCDirs.zig b/lib/std/zig/LibCDirs.zig
index e05ccde5895c7eae0426a64ff1b5c176490065df..ca43fe127f8f51c7446c93793fd6b94119d1cf3e 100644
--- a/lib/std/zig/LibCDirs.zig
+++ b/lib/std/zig/LibCDirs.zig
@@ -28,6 +28,7 @@ pub fn detect(
is_native_abi: bool,
link_libc: bool,
libc_installation: ?*const LibCInstallation,
+ environ_map: *const std.process.Environ.Map,
) LibCInstallation.FindError!LibCDirs {
if (!link_libc) {
return .{
@@ -47,7 +48,10 @@ pub fn detect(
// using the system libc installation.
if (is_native_abi and !target.isMinGW()) {
const libc = try arena.create(LibCInstallation);
- libc.* = LibCInstallation.findNative(arena, io, .{ .target = target }) catch |err| switch (err) {
+ libc.* = LibCInstallation.findNative(arena, io, .{
+ .target = target,
+ .environ_map = environ_map,
+ }) catch |err| switch (err) {
error.CCompilerExitCode,
error.CCompilerCrashed,
error.CCompilerCannotFindHeaders,
@@ -84,12 +88,16 @@ pub fn detect(
if (use_system_abi) {
const libc = try arena.create(LibCInstallation);
- libc.* = try LibCInstallation.findNative(arena, io, .{ .verbose = true, .target = target });
+ libc.* = try LibCInstallation.findNative(arena, io, .{
+ .verbose = true,
+ .target = target,
+ .environ_map = environ_map,
+ });
return detectFromInstallation(arena, target, libc);
}
return .{
- .libc_include_dir_list = &[0][]u8{},
+ .libc_include_dir_list = &.{},
.libc_installation = null,
.libc_framework_dir_list = &.{},
.sysroot = null,
diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig
index f2489f9ee741ac30a063ca81170e2a4976d314d9..02b3df54dce74ee89913d717747f3740d08c8d37 100644
--- a/lib/std/zig/LibCInstallation.zig
+++ b/lib/std/zig/LibCInstallation.zig
@@ -13,6 +13,7 @@ const fs = std.fs;
const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path;
const log = std.log.scoped(.libc_installation);
+const Environ = std.process.Environ;
include_dir: ?[]const u8 = null,
sys_include_dir: ?[]const u8 = null,
@@ -167,6 +168,7 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void {
pub const FindNativeOptions = struct {
target: *const std.Target,
+ environ_map: *const Environ.Map,
/// If enabled, will print human-friendly errors to stderr.
verbose: bool = false,
@@ -191,7 +193,7 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib
});
return self;
} else if (is_windows) {
- const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch) catch |err| switch (err) {
+ const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch, args.environ_map) catch |err| switch (err) {
error.NotFound => return error.WindowsSdkNotFound,
error.PathTooLong => return error.WindowsSdkNotFound,
error.OutOfMemory => return error.OutOfMemory,
@@ -206,16 +208,16 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib
} else if (is_haiku) {
try self.findNativeIncludeDirPosix(gpa, io, args);
try self.findNativeGccDirHaiku(gpa, io, args);
- self.crt_dir = try gpa.dupeZ(u8, "/system/develop/lib");
+ self.crt_dir = try gpa.dupe(u8, "/system/develop/lib");
} else if (builtin.target.os.tag == .illumos) {
// There is only one libc, and its headers/libraries are always in the same spot.
- self.include_dir = try gpa.dupeZ(u8, "/usr/include");
- self.sys_include_dir = try gpa.dupeZ(u8, "/usr/include");
- self.crt_dir = try gpa.dupeZ(u8, "/usr/lib/64");
+ self.include_dir = try gpa.dupe(u8, "/usr/include");
+ self.sys_include_dir = try gpa.dupe(u8, "/usr/include");
+ self.crt_dir = try gpa.dupe(u8, "/usr/lib/64");
} else if (std.process.can_spawn) {
try self.findNativeIncludeDirPosix(gpa, io, args);
switch (builtin.target.os.tag) {
- .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try gpa.dupeZ(u8, "/usr/lib"),
+ .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try gpa.dupe(u8, "/usr/lib"),
.linux => try self.findNativeCrtDirPosix(gpa, io, args),
else => {},
}
@@ -238,20 +240,17 @@ pub fn deinit(self: *LibCInstallation, allocator: Allocator) void {
fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void {
// Detect infinite loops.
- var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) {
- error.Unexpected => unreachable, // WASI-only
- else => |e| return e,
- };
- defer env_map.deinit();
- const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
+ var environ_map = try args.environ_map.clone(gpa);
+ defer environ_map.deinit();
+ const skip_cc_env_var = if (environ_map.get(inf_loop_env_key)) |phase| blk: {
if (std.mem.eql(u8, phase, "1")) {
- try env_map.put(inf_loop_env_key, "2");
+ try environ_map.put(inf_loop_env_key, "2");
break :blk true;
} else {
return error.ZigIsTheCCompiler;
}
} else blk: {
- try env_map.put(inf_loop_env_key, "1");
+ try environ_map.put(inf_loop_env_key, "1");
break :blk false;
};
@@ -260,7 +259,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar
var argv = std.array_list.Managed([]const u8).init(gpa);
defer argv.deinit();
- try appendCcExe(&argv, skip_cc_env_var);
+ try appendCcExe(&argv, skip_cc_env_var, &environ_map);
try argv.appendSlice(&.{
"-E",
"-Wp,-v",
@@ -268,10 +267,10 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar
dev_null,
});
- const run_res = std.process.Child.run(gpa, io, .{
- .argv = argv.items,
+ const run_res = std.process.run(gpa, io, .{
.max_output_bytes = 1024 * 1024,
- .env_map = &env_map,
+ .argv = argv.items,
+ .environ_map = &environ_map,
// Some C compilers, such as Clang, are known to rely on argv[0] to find the path
// to their own executable, without even bothering to resolve PATH. This results in the message:
// error: unable to execute command: Executable "" doesn't exist!
@@ -289,7 +288,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar
gpa.free(run_res.stderr);
}
switch (run_res.term) {
- .Exited => |code| if (code != 0) {
+ .exited => |code| if (code != 0) {
printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
return error.CCompilerExitCode;
},
@@ -336,7 +335,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar
if (self.include_dir == null) {
if (search_dir.access(io, include_dir_example_file, .{})) |_| {
- self.include_dir = try gpa.dupeZ(u8, search_path);
+ self.include_dir = try gpa.dupe(u8, search_path);
} else |err| switch (err) {
error.FileNotFound => {},
else => return error.FileSystem,
@@ -345,7 +344,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar
if (self.sys_include_dir == null) {
if (search_dir.access(io, sys_include_dir_example_file, .{})) |_| {
- self.sys_include_dir = try gpa.dupeZ(u8, search_path);
+ self.sys_include_dir = try gpa.dupe(u8, search_path);
} else |err| switch (err) {
error.FileNotFound => {},
else => return error.FileSystem,
@@ -447,6 +446,7 @@ fn findNativeCrtDirWindows(
fn findNativeCrtDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void {
self.crt_dir = try ccPrintFileName(gpa, io, .{
+ .environ_map = args.environ_map,
.search_basename = switch (args.target.os.tag) {
.linux => if (args.target.abi.isAndroid()) "crtbegin_dynamic.o" else "crt1.o",
else => "crt1.o",
@@ -551,28 +551,26 @@ fn findNativeMsvcLibDir(
}
pub const CCPrintFileNameOptions = struct {
+ environ_map: *const Environ.Map,
search_basename: []const u8,
want_dirname: enum { full_path, only_dir },
verbose: bool = false,
};
/// caller owns returned memory
-fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 {
+fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![]u8 {
// Detect infinite loops.
- var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) {
- error.Unexpected => unreachable, // WASI-only
- else => |e| return e,
- };
- defer env_map.deinit();
- const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
+ var environ_map = try args.environ_map.clone(gpa);
+ defer environ_map.deinit();
+ const skip_cc_env_var = if (environ_map.get(inf_loop_env_key)) |phase| blk: {
if (std.mem.eql(u8, phase, "1")) {
- try env_map.put(inf_loop_env_key, "2");
+ try environ_map.put(inf_loop_env_key, "2");
break :blk true;
} else {
return error.ZigIsTheCCompiler;
}
} else blk: {
- try env_map.put(inf_loop_env_key, "1");
+ try environ_map.put(inf_loop_env_key, "1");
break :blk false;
};
@@ -582,13 +580,13 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8
const arg1 = try std.fmt.allocPrint(gpa, "-print-file-name={s}", .{args.search_basename});
defer gpa.free(arg1);
- try appendCcExe(&argv, skip_cc_env_var);
+ try appendCcExe(&argv, skip_cc_env_var, &environ_map);
try argv.append(arg1);
- const run_res = std.process.Child.run(gpa, io, .{
- .argv = argv.items,
+ const run_res = std.process.run(gpa, io, .{
.max_output_bytes = 1024 * 1024,
- .env_map = &env_map,
+ .argv = argv.items,
+ .environ_map = &environ_map,
// Some C compilers, such as Clang, are known to rely on argv[0] to find the path
// to their own executable, without even bothering to resolve PATH. This results in the message:
// error: unable to execute command: Executable "" doesn't exist!
@@ -603,7 +601,7 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8
gpa.free(run_res.stderr);
}
switch (run_res.term) {
- .Exited => |code| if (code != 0) {
+ .exited => |code| if (code != 0) {
printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
return error.CCompilerExitCode;
},
@@ -619,10 +617,10 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8
// So we detect failure by checking if the output matches exactly the input.
if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
switch (args.want_dirname) {
- .full_path => return gpa.dupeZ(u8, line),
+ .full_path => return gpa.dupe(u8, line),
.only_dir => {
const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
- return gpa.dupeZ(u8, dirname);
+ return gpa.dupe(u8, dirname);
},
}
}
@@ -668,14 +666,18 @@ fn fillInstallations(
const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
-fn appendCcExe(args: *std.array_list.Managed([]const u8), skip_cc_env_var: bool) !void {
+fn appendCcExe(
+ args: *std.array_list.Managed([]const u8),
+ skip_cc_env_var: bool,
+ environ_map: *const Environ.Map,
+) !void {
const default_cc_exe = if (is_windows) "cc.exe" else "cc";
try args.ensureUnusedCapacity(1);
if (skip_cc_env_var) {
args.appendAssumeCapacity(default_cc_exe);
return;
}
- const cc_env_var = std.zig.EnvVar.CC.getPosix() orelse {
+ const cc_env_var = std.zig.EnvVar.CC.get(environ_map) orelse {
args.appendAssumeCapacity(default_cc_exe);
return;
};
diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig
index 1b172e4358eb195549526a727b7d9046189c2d55..d6e7e35130c5da5f50405f06d1afd73005fcf358 100644
--- a/lib/std/zig/WindowsSdk.zig
+++ b/lib/std/zig/WindowsSdk.zig
@@ -6,6 +6,7 @@ const Io = std.Io;
const Dir = std.Io.Dir;
const Writer = std.Io.Writer;
const Allocator = std.mem.Allocator;
+const Environ = std.process.Environ;
windows10sdk: ?Installation,
windows81sdk: ?Installation,
@@ -24,7 +25,12 @@ const product_version_max_length = version_major_minor_max_length + ".65535".len
/// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
/// Caller owns the result's fields.
/// Returns memory allocated by `gpa`
-pub fn find(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk {
+pub fn find(
+ gpa: Allocator,
+ io: Io,
+ arch: std.Target.Cpu.Arch,
+ environ_map: *const Environ.Map,
+) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk {
if (builtin.os.tag != .windows) return error.NotFound;
//note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
@@ -49,7 +55,7 @@ pub fn find(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemor
};
errdefer if (windows81sdk) |*w| w.free(gpa);
- const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(gpa, io, arch) catch |err| switch (err) {
+ const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(gpa, io, arch, environ_map) catch |err| switch (err) {
error.MsvcLibDirNotFound => null,
error.OutOfMemory => return error.OutOfMemory,
};
@@ -671,7 +677,11 @@ const MsvcLibDir = struct {
return Dir.openDirAbsolute(io, instances_path, .{ .iterate = true }) catch return error.PathNotFound;
}
- fn findInstancesDir(gpa: Allocator, io: Io) error{ OutOfMemory, PathNotFound }!Dir {
+ fn findInstancesDir(
+ gpa: Allocator,
+ io: Io,
+ environ_map: *const Environ.Map,
+ ) error{ OutOfMemory, PathNotFound }!Dir {
// First, try getting the packages cache path from the registry.
// This only seems to exist when the path is different from the default.
method1: {
@@ -691,16 +701,13 @@ const MsvcLibDir = struct {
// If that can't be found, fall back to manually appending
// `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
method3: {
- const program_data = std.process.getEnvVarOwned(gpa, "PROGRAMDATA") catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.InvalidWtf8 => unreachable,
- error.EnvironmentVariableNotFound => break :method3,
- };
- defer gpa.free(program_data);
+ const program_data = std.zig.EnvVar.PROGRAMDATA.get(environ_map) orelse break :method3;
if (!Dir.path.isAbsolute(program_data)) break :method3;
- const instances_path = try Dir.path.join(gpa, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });
+ const instances_path = try Dir.path.join(gpa, &.{
+ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances",
+ });
defer gpa.free(instances_path);
return Dir.openDirAbsolute(io, instances_path, .{ .iterate = true }) catch break :method3;
@@ -754,12 +761,17 @@ const MsvcLibDir = struct {
///
/// The logic in this function is intended to match what ISetupConfiguration does
/// under-the-hood, as verified using Procmon.
- fn findViaCOM(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 {
+ fn findViaCOM(
+ gpa: Allocator,
+ io: Io,
+ arch: std.Target.Cpu.Arch,
+ environ_map: *const Environ.Map,
+ ) error{ OutOfMemory, PathNotFound }![]const u8 {
// Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances`
// This will contain directories with names of instance IDs like 80a758ca,
// which will contain `state.json` files that have the version and
// installation directory.
- var instances_dir = try findInstancesDir(gpa, io);
+ var instances_dir = try findInstancesDir(gpa, io, environ_map);
defer instances_dir.close(io);
var state_subpath_buf: [Dir.max_name_bytes + 32]u8 = undefined;
@@ -856,15 +868,16 @@ const MsvcLibDir = struct {
}
// https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance
- fn findViaRegistry(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 {
+ fn findViaRegistry(
+ gpa: Allocator,
+ io: Io,
+ arch: std.Target.Cpu.Arch,
+ environ_map: *const Environ.Map,
+ ) error{ OutOfMemory, PathNotFound }![]const u8 {
// %localappdata%\Microsoft\VisualStudio\
// %appdata%\Local\Microsoft\VisualStudio\
- const local_app_data_path = (std.zig.EnvVar.LOCALAPPDATA.get(gpa) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.InvalidWtf8 => return error.PathNotFound,
- }) orelse return error.PathNotFound;
- defer gpa.free(local_app_data_path);
+ const local_app_data_path = std.zig.EnvVar.LOCALAPPDATA.get(environ_map) orelse return error.PathNotFound;
const visualstudio_folder_path = try Dir.path.join(gpa, &.{
local_app_data_path, "Microsoft\\VisualStudio\\",
});
@@ -951,16 +964,15 @@ const MsvcLibDir = struct {
return msvc_dir;
}
- fn findViaVs7Key(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 {
+ fn findViaVs7Key(
+ gpa: Allocator,
+ io: Io,
+ arch: std.Target.Cpu.Arch,
+ environ_map: *const Environ.Map,
+ ) error{ OutOfMemory, PathNotFound }![]const u8 {
var base_path: std.array_list.Managed(u8) = base_path: {
try_env: {
- var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => break :try_env,
- };
- defer env_map.deinit();
-
- if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| {
+ if (environ_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| {
if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env;
if (!Dir.path.isAbsolute(VS140COMNTOOLS)) break :try_env;
var list = std.array_list.Managed(u8).init(gpa);
@@ -1030,12 +1042,17 @@ const MsvcLibDir = struct {
/// Find path to MSVC's `lib/` directory.
/// Caller owns the result.
- pub fn find(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
- const full_path = MsvcLibDir.findViaCOM(gpa, io, arch) catch |err1| switch (err1) {
+ pub fn find(
+ gpa: Allocator,
+ io: Io,
+ arch: std.Target.Cpu.Arch,
+ environ_map: *const Environ.Map,
+ ) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
+ const full_path = MsvcLibDir.findViaCOM(gpa, io, arch, environ_map) catch |err1| switch (err1) {
error.OutOfMemory => return error.OutOfMemory,
- error.PathNotFound => MsvcLibDir.findViaRegistry(gpa, io, arch) catch |err2| switch (err2) {
+ error.PathNotFound => MsvcLibDir.findViaRegistry(gpa, io, arch, environ_map) catch |err2| switch (err2) {
error.OutOfMemory => return error.OutOfMemory,
- error.PathNotFound => MsvcLibDir.findViaVs7Key(gpa, io, arch) catch |err3| switch (err3) {
+ error.PathNotFound => MsvcLibDir.findViaVs7Key(gpa, io, arch, environ_map) catch |err3| switch (err3) {
error.OutOfMemory => return error.OutOfMemory,
error.PathNotFound => return error.MsvcLibDirNotFound,
},
diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig
index bf66f912ae360e1798a8981a21c4a68ccc2181f0..f8003cd62061b4cfeb936533448e9a4b9f20dbb4 100644
--- a/lib/std/zig/system/NativePaths.zig
+++ b/lib/std/zig/system/NativePaths.zig
@@ -14,10 +14,16 @@ framework_dirs: std.ArrayList([]const u8) = .empty,
rpaths: std.ArrayList([]const u8) = .empty,
warnings: std.ArrayList([]const u8) = .empty,
-pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !NativePaths {
+pub fn detect(
+ arena: Allocator,
+ io: Io,
+ native_target: *const std.Target,
+ environ_map: *process.Environ.Map,
+) !NativePaths {
var self: NativePaths = .{ .arena = arena };
var is_nix = false;
- if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
+
+ if (std.zig.EnvVar.NIX_CFLAGS_COMPILE.get(environ_map)) |nix_cflags_compile| {
is_nix = true;
var it = mem.tokenizeScalar(u8, nix_cflags_compile, ' ');
while (true) {
@@ -41,12 +47,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ
try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word});
}
}
- } else |err| switch (err) {
- error.InvalidWtf8 => unreachable,
- error.EnvironmentVariableNotFound => {},
- error.OutOfMemory => |e| return e,
}
- if (process.getEnvVarOwned(arena, "NIX_LDFLAGS")) |nix_ldflags| {
+
+ if (std.zig.EnvVar.NIX_LDFLAGS.get(environ_map)) |nix_ldflags| {
is_nix = true;
var it = mem.tokenizeScalar(u8, nix_ldflags, ' ');
while (true) {
@@ -73,12 +76,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ
break;
}
}
- } else |err| switch (err) {
- error.InvalidWtf8 => unreachable,
- error.EnvironmentVariableNotFound => {},
- error.OutOfMemory => |e| return e,
}
- if (process.getEnvVarOwned(arena, "NIX_CFLAGS_LINK")) |nix_cflags_link| {
+
+ if (std.zig.EnvVar.NIX_CFLAGS_LINK.get(environ_map)) |nix_cflags_link| {
is_nix = true;
var it = mem.tokenizeScalar(u8, nix_cflags_link, ' ');
while (true) {
@@ -105,11 +105,8 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ
break;
}
}
- } else |err| switch (err) {
- error.InvalidWtf8 => unreachable,
- error.EnvironmentVariableNotFound => {},
- error.OutOfMemory => |e| return e,
}
+
if (is_nix) {
return self;
}
@@ -124,7 +121,7 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ
}
// Check for homebrew paths
- if (std.posix.getenv("HOMEBREW_PREFIX")) |prefix| {
+ if (std.zig.EnvVar.HOMEBREW_PREFIX.get(environ_map)) |prefix| {
try self.addLibDir(try std.fs.path.join(arena, &.{ prefix, "/lib" }));
try self.addIncludeDir(try std.fs.path.join(arena, &.{ prefix, "/include" }));
}
@@ -180,23 +177,21 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ
// Distros like guix don't use FHS, so they rely on environment
// variables to search for headers and libraries.
- // We use os.getenv here since this part won't be executed on
- // windows, to get rid of unnecessary error handling.
- if (std.posix.getenv("C_INCLUDE_PATH")) |c_include_path| {
+ if (std.zig.EnvVar.C_INCLUDE_PATH.get(environ_map)) |c_include_path| {
var it = mem.tokenizeScalar(u8, c_include_path, ':');
while (it.next()) |dir| {
try self.addIncludeDir(dir);
}
}
- if (std.posix.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| {
+ if (std.zig.EnvVar.CPLUS_INCLUDE_PATH.get(environ_map)) |cplus_include_path| {
var it = mem.tokenizeScalar(u8, cplus_include_path, ':');
while (it.next()) |dir| {
try self.addIncludeDir(dir);
}
}
- if (std.posix.getenv("LIBRARY_PATH")) |library_path| {
+ if (std.zig.EnvVar.LIBRARY_PATH.get(environ_map)) |library_path| {
var it = mem.tokenizeScalar(u8, library_path, ':');
while (it.next()) |dir| {
try self.addLibDir(dir);
diff --git a/lib/std/zig/system/darwin.zig b/lib/std/zig/system/darwin.zig
index b493ccf0ec378bd24fd33fbea534b00f07c53fb0..b31dc484ceb3ce3a87a049610114a547ef5cdf2f 100644
--- a/lib/std/zig/system/darwin.zig
+++ b/lib/std/zig/system/darwin.zig
@@ -17,7 +17,7 @@ pub const macos = @import("darwin/macos.zig");
///
/// If error.OutOfMemory occurs in Allocator, this function returns null.
pub fn isSdkInstalled(gpa: Allocator, io: Io) bool {
- const result = std.process.Child.run(gpa, io, .{
+ const result = std.process.run(gpa, io, .{
.argv = &.{ "xcode-select", "--print-path" },
}) catch return false;
defer {
@@ -25,7 +25,7 @@ pub fn isSdkInstalled(gpa: Allocator, io: Io) bool {
gpa.free(result.stdout);
}
return switch (result.term) {
- .Exited => |code| if (code == 0) result.stdout.len > 0 else false,
+ .exited => |code| if (code == 0) result.stdout.len > 0 else false,
else => false,
};
}
@@ -47,13 +47,13 @@ pub fn getSdk(gpa: Allocator, io: Io, target: *const Target) ?[]const u8 {
else => return null,
};
const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" };
- const result = std.process.Child.run(gpa, io, .{ .argv = argv }) catch return null;
+ const result = std.process.run(gpa, io, .{ .argv = argv }) catch return null;
defer {
gpa.free(result.stderr);
gpa.free(result.stdout);
}
switch (result.term) {
- .Exited => |code| if (code != 0) return null,
+ .exited => |code| if (code != 0) return null,
else => return null,
}
return gpa.dupe(u8, mem.trimEnd(u8, result.stdout, "\r\n")) catch null;
diff --git a/src/Compilation.zig b/src/Compilation.zig
index a11519d07a84ef11c073955890d137352bee61aa..82f45ee9f0189bdc1d26b292624e4c2ab42726ea 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -54,6 +54,7 @@ gpa: Allocator,
/// threads at once.
arena: Allocator,
io: Io,
+environ_map: *const std.process.Environ.Map,
thread_limit: usize,
/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
zcu: ?*Zcu,
@@ -761,11 +762,12 @@ pub const Directories = struct {
.wasi => void,
else => []const u8,
},
+ environ_map: *const std.process.Environ.Map,
) Directories {
const wasi = builtin.target.os.tag == .wasi;
const cwd = introspect.getResolvedCwd(arena) catch |err| {
- fatal("unable to get cwd: {s}", .{@errorName(err)});
+ fatal("unable to get cwd: {t}", .{err});
};
const zig_lib: Cache.Directory = d: {
@@ -779,7 +781,7 @@ pub const Directories = struct {
const global_cache: Cache.Directory = d: {
if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache");
if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache");
- const path = introspect.resolveGlobalCacheDir(arena) catch |err| {
+ const path = introspect.resolveGlobalCacheDir(arena, environ_map) catch |err| {
fatal("unable to resolve zig cache directory: {t}", .{err});
};
break :d openUnresolved(arena, io, cwd, path, .@"global cache");
@@ -1797,6 +1799,8 @@ pub const CreateOptions = struct {
parent_whole_cache: ?ParentWholeCache = null,
+ environ_map: *const std.process.Environ.Map,
+
pub const Entry = link.File.OpenOptions.Entry;
/// Which fields are valid depends on the `cache_mode` given.
@@ -1967,6 +1971,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
options.root_mod.resolved_target.is_native_abi,
link_libc,
options.libc_installation,
+ options.environ_map,
) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
// Every other error is specifically related to finding the native installation
@@ -2123,6 +2128,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.manifest_dir = options.dirs.local_cache.handle.createDirPathOpen(io, "h", .{}) catch |err| {
return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = "h", .err = err } });
},
+ .cwd = options.dirs.cwd,
};
// These correspond to std.zig.Server.Message.PathPrefix.
cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
@@ -2306,6 +2312,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir),
.emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc),
.emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
+ .environ_map = options.environ_map,
};
errdefer {
@@ -5503,6 +5510,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU
.verbose_llvm_bc = comp.verbose_llvm_bc,
.verbose_cimport = comp.verbose_cimport,
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .environ_map = comp.environ_map,
}) catch |err| switch (err) {
error.CreateFail => {
comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: {f}", .{sub_create_diag});
@@ -5705,6 +5713,7 @@ pub fn translateC(
translated_basename: []const u8,
owner_mod: *Package.Module,
prog_node: std.Progress.Node,
+ environ_map: *const std.process.Environ.Map,
) !CImportResult {
dev.check(.translate_c_command);
@@ -5774,7 +5783,7 @@ pub fn translateC(
}
var stdout: []u8 = undefined;
- try @import("main.zig").translateC(gpa, arena, io, argv.items, prog_node, &stdout);
+ try @import("main.zig").translateC(gpa, arena, io, argv.items, environ_map, prog_node, &stdout);
if (out_dep_path) |dep_file_path| add_deps: {
if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_file_path});
@@ -5861,7 +5870,8 @@ pub fn cImport(
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
- break :result try comp.translateC(
+ break :result try translateC(
+ comp,
arena,
&man,
.c,
@@ -5869,6 +5879,7 @@ pub fn cImport(
translated_basename,
owner_mod,
prog_node,
+ comp.environ_map,
);
};
@@ -6249,7 +6260,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
// that we could "tail call" clang by doing an execve, and any use of
// the caching system would actually be problematic since the user is
// presumably doing their own caching by using dep file flags.
- if (std.process.can_execv and direct_o and
+ if (std.process.can_replace and direct_o and
comp.disable_c_depfile and comp.clang_passthrough_mode)
{
try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner);
@@ -6281,8 +6292,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
try dumpArgv(io, argv.items);
}
- const err = std.process.execv(arena, argv.items);
- fatal("unable to execv clang: {s}", .{@errorName(err)});
+ const err = std.process.replace(io, .{ .argv = argv.items });
+ fatal("unable to replace process with clang: {t}", .{err});
}
// We can't know the digest until we do the C compiler invocation,
@@ -6337,17 +6348,24 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }),
};
if (std.process.can_spawn) {
- var child = std.process.Child.init(argv.items, arena);
if (comp.clang_passthrough_mode) {
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
-
- const term = child.spawnAndWait(io) catch |err| {
- return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {s}", .{ argv.items[0], @errorName(err) });
+ var child = std.process.spawn(io, .{
+ .argv = argv.items,
+ .stdin = .inherit,
+ .stdout = .inherit,
+ .stderr = .inherit,
+ }) catch |err| {
+ return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {t}", .{
+ argv.items[0], err,
+ });
+ };
+ const term = child.wait(io) catch |err| {
+ return comp.failCObj(c_object, "failed to wait zig clang (passthrough mode) {s}: {t}", .{
+ argv.items[0], err,
+ });
};
switch (term) {
- .Exited => |code| {
+ .exited => |code| {
if (code != 0) {
std.process.exit(code);
}
@@ -6357,21 +6375,21 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
else => std.process.abort(),
}
} else {
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Ignore;
- child.stderr_behavior = .Pipe;
-
- try child.spawn(io);
+ var child = try std.process.spawn(io, .{
+ .argv = argv.items,
+ .stdin = .ignore,
+ .stdout = .ignore,
+ .stderr = .pipe,
+ });
var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32)));
- const term = child.wait(io) catch |err| {
- return comp.failCObj(c_object, "failed to spawn zig clang {s}: {s}", .{ argv.items[0], @errorName(err) });
- };
+ const term = child.wait(io) catch |err|
+ return comp.failCObj(c_object, "failed to spawn zig clang {s}: {t}", .{ argv.items[0], err });
switch (term) {
- .Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| {
+ .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| {
const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| {
log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr });
return comp.failCObj(c_object, "clang exited with code {d}", .{code});
@@ -6381,6 +6399,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
log.err("clang failed with stderr: {s}", .{stderr});
return comp.failCObj(c_object, "clang exited with code {d}", .{code});
},
+ .signal => |sig| {
+ log.err("clang failed with stderr: {s}", .{stderr});
+ return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig});
+ },
else => {
log.err("clang terminated with stderr: {s}", .{stderr});
return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
@@ -6741,15 +6763,16 @@ fn spawnZigRc(
var node_name: std.ArrayList(u8) = .empty;
defer node_name.deinit(arena);
- var child = std.process.Child.init(argv, arena);
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
- child.progress_node = child_progress_node;
-
- child.spawn(io) catch |err| {
- return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{ argv[0], err });
- };
+ var child = std.process.spawn(io, .{
+ .argv = argv,
+ .stdin = .ignore,
+ .stdout = .pipe,
+ .stderr = .pipe,
+ .progress_node = child_progress_node,
+ }) catch |err| return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{
+ argv[0], err,
+ });
+ defer child.kill(io);
var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
@@ -6781,16 +6804,20 @@ fn spawnZigRc(
const stderr = poller.reader(.stderr);
const term = child.wait(io) catch |err| {
- return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) });
+ return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {t}", .{ argv[0], err });
};
switch (term) {
- .Exited => |code| {
+ .exited => |code| {
if (code != 0) {
log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()});
return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
}
},
+ .signal => |sig| {
+ log.err("zig rc signaled {t} with stderr:\n{s}", .{ sig, stderr.buffered() });
+ return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
+ },
else => {
log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()});
return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
@@ -7959,6 +7986,7 @@ fn buildOutputFromZig(
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| switch (err) {
error.CreateFail => {
comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag });
@@ -8096,6 +8124,7 @@ pub fn build_crt_file(
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| switch (err) {
error.CreateFail => {
comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag });
diff --git a/src/DarwinPosixSpawn.zig b/src/DarwinPosixSpawn.zig
deleted file mode 100644
index 98197119845f0fa2fcadaea02f012111a473f687..0000000000000000000000000000000000000000
--- a/src/DarwinPosixSpawn.zig
+++ /dev/null
@@ -1,225 +0,0 @@
-const errno = std.posix.errno;
-const unexpectedErrno = std.posix.unexpectedErrno;
-
-pub const Error = error{
- SystemResources,
- InvalidFileDescriptor,
- NameTooLong,
- TooBig,
- AccessDenied,
- PermissionDenied,
- InputOutput,
- FileSystem,
- FileNotFound,
- InvalidExe,
- NotDir,
- FileBusy,
- /// Returned when the child fails to execute either in the pre-exec() initialization step, or
- /// when exec(3) is invoked.
- ChildExecFailed,
-} || std.posix.UnexpectedError;
-
-pub const Attr = struct {
- attr: std.c.posix_spawnattr_t,
-
- pub fn init() Error!Attr {
- var attr: std.c.posix_spawnattr_t = undefined;
- switch (errno(std.c.posix_spawnattr_init(&attr))) {
- .SUCCESS => return Attr{ .attr = attr },
- .NOMEM => return error.SystemResources,
- .INVAL => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn deinit(self: *Attr) void {
- defer self.* = undefined;
- switch (errno(std.c.posix_spawnattr_destroy(&self.attr))) {
- .SUCCESS => return,
- .INVAL => unreachable, // Invalid parameters.
- else => unreachable,
- }
- }
-
- pub fn get(self: Attr) Error!std.c.POSIX_SPAWN {
- var flags: std.c.POSIX_SPAWN = undefined;
- switch (errno(std.c.posix_spawnattr_getflags(&self.attr, &flags))) {
- .SUCCESS => return flags,
- .INVAL => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn set(self: *Attr, flags: std.c.POSIX_SPAWN) Error!void {
- switch (errno(std.c.posix_spawnattr_setflags(&self.attr, flags))) {
- .SUCCESS => return,
- .INVAL => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-};
-
-pub const Actions = struct {
- actions: std.c.posix_spawn_file_actions_t,
-
- pub fn init() Error!Actions {
- var actions: std.c.posix_spawn_file_actions_t = undefined;
- switch (errno(std.c.posix_spawn_file_actions_init(&actions))) {
- .SUCCESS => return Actions{ .actions = actions },
- .NOMEM => return error.SystemResources,
- .INVAL => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn deinit(self: *Actions) void {
- defer self.* = undefined;
- switch (errno(std.c.posix_spawn_file_actions_destroy(&self.actions))) {
- .SUCCESS => return,
- .INVAL => unreachable, // Invalid parameters.
- else => unreachable,
- }
- }
-
- pub fn open(self: *Actions, fd: std.c.fd_t, path: []const u8, flags: u32, mode: std.c.mode_t) Error!void {
- const posix_path = try std.posix.toPosixPath(path);
- return self.openZ(fd, &posix_path, flags, mode);
- }
-
- pub fn openZ(self: *Actions, fd: std.c.fd_t, path: [*:0]const u8, flags: u32, mode: std.c.mode_t) Error!void {
- switch (errno(std.c.posix_spawn_file_actions_addopen(&self.actions, fd, path, @as(c_int, @bitCast(flags)), mode))) {
- .SUCCESS => return,
- .BADF => return error.InvalidFileDescriptor,
- .NOMEM => return error.SystemResources,
- .NAMETOOLONG => return error.NameTooLong,
- .INVAL => unreachable, // the value of file actions is invalid
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn close(self: *Actions, fd: std.c.fd_t) Error!void {
- switch (errno(std.c.posix_spawn_file_actions_addclose(&self.actions, fd))) {
- .SUCCESS => return,
- .BADF => return error.InvalidFileDescriptor,
- .NOMEM => return error.SystemResources,
- .INVAL => unreachable, // the value of file actions is invalid
- .NAMETOOLONG => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn dup2(self: *Actions, fd: std.c.fd_t, newfd: std.c.fd_t) Error!void {
- switch (errno(std.c.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
- .SUCCESS => return,
- .BADF => return error.InvalidFileDescriptor,
- .NOMEM => return error.SystemResources,
- .INVAL => unreachable, // the value of file actions is invalid
- .NAMETOOLONG => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn inherit(self: *Actions, fd: std.c.fd_t) Error!void {
- switch (errno(std.c.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
- .SUCCESS => return,
- .BADF => return error.InvalidFileDescriptor,
- .NOMEM => return error.SystemResources,
- .INVAL => unreachable, // the value of file actions is invalid
- .NAMETOOLONG => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn chdir(self: *Actions, path: []const u8) Error!void {
- const posix_path = try std.posix.toPosixPath(path);
- return self.chdirZ(&posix_path);
- }
-
- pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
- switch (errno(std.c.posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
- .SUCCESS => return,
- .NOMEM => return error.SystemResources,
- .NAMETOOLONG => return error.NameTooLong,
- .BADF => unreachable,
- .INVAL => unreachable, // the value of file actions is invalid
- else => |err| return unexpectedErrno(err),
- }
- }
-
- pub fn fchdir(self: *Actions, fd: std.c.fd_t) Error!void {
- switch (errno(std.c.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
- .SUCCESS => return,
- .BADF => return error.InvalidFileDescriptor,
- .NOMEM => return error.SystemResources,
- .INVAL => unreachable, // the value of file actions is invalid
- .NAMETOOLONG => unreachable,
- else => |err| return unexpectedErrno(err),
- }
- }
-};
-
-pub fn spawn(
- path: []const u8,
- actions: ?Actions,
- attr: ?Attr,
- argv: [*:null]const ?[*:0]const u8,
- envp: [*:null]const ?[*:0]const u8,
-) Error!std.c.pid_t {
- const posix_path = try std.posix.toPosixPath(path);
- return spawnZ(&posix_path, actions, attr, argv, envp);
-}
-
-pub fn spawnZ(
- path: [*:0]const u8,
- actions: ?Actions,
- attr: ?Attr,
- argv: [*:null]const ?[*:0]const u8,
- envp: [*:null]const ?[*:0]const u8,
-) Error!std.c.pid_t {
- var pid: std.c.pid_t = undefined;
- switch (errno(std.c.posix_spawn(
- &pid,
- path,
- if (actions) |a| &a.actions else null,
- if (attr) |a| &a.attr else null,
- argv,
- envp,
- ))) {
- .SUCCESS => return pid,
- .@"2BIG" => return error.TooBig,
- .NOMEM => return error.SystemResources,
- .BADF => return error.InvalidFileDescriptor,
- .ACCES => return error.AccessDenied,
- .IO => return error.InputOutput,
- .LOOP => return error.FileSystem,
- .NAMETOOLONG => return error.NameTooLong,
- .NOENT => return error.FileNotFound,
- .NOEXEC => return error.InvalidExe,
- .NOTDIR => return error.NotDir,
- .TXTBSY => return error.FileBusy,
- .BADARCH => return error.InvalidExe,
- .BADEXEC => return error.InvalidExe,
- .FAULT => unreachable,
- .INVAL => unreachable,
- else => |err| return unexpectedErrno(err),
- }
-}
-
-pub fn waitpid(pid: std.c.pid_t, flags: u32) Error!std.posix.WaitPidResult {
- var status: c_int = undefined;
- while (true) {
- const rc = waitpid(pid, &status, @as(c_int, @intCast(flags)));
- switch (errno(rc)) {
- .SUCCESS => return std.posix.WaitPidResult{
- .pid = @as(std.c.pid_t, @intCast(rc)),
- .status = @as(u32, @bitCast(status)),
- },
- .INTR => continue,
- .CHILD => return error.ChildExecFailed,
- .INVAL => unreachable, // Invalid flags.
- else => unreachable,
- }
- }
-}
-
-const std = @import("std");
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
index 103cbaaaaec0e1b14c0a281fa752ccbcfda26101..9825c1e87e7f16178a87ff1238b7dc8985fbeb7a 100644
--- a/src/Zcu/PerThread.zig
+++ b/src/Zcu/PerThread.zig
@@ -2571,10 +2571,7 @@ fn newEmbedFile(
try whole.cache_manifest_mutex.lock(io);
defer whole.cache_manifest_mutex.unlock(io);
- man.addFilePostContents(path_str, contents, new_file.stat) catch |err| switch (err) {
- error.Unexpected => unreachable,
- else => |e| return e,
- };
+ try man.addFilePostContents(path_str, contents, new_file.stat);
}
return new_file;
diff --git a/src/introspect.zig b/src/introspect.zig
index 9481201c3bfdab5691ff5805733ca71a7b342f89..fa04e7de58a1fda94f9311fc1bb21a7266558e3f 100644
--- a/src/introspect.zig
+++ b/src/introspect.zig
@@ -6,6 +6,7 @@ const Dir = std.Io.Dir;
const mem = std.mem;
const Allocator = std.mem.Allocator;
const Cache = std.Build.Cache;
+const assert = std.debug.assert;
const build_options = @import("build_options");
@@ -62,14 +63,14 @@ pub fn getResolvedCwd(gpa: Allocator) error{
if (std.debug.runtime_safety) {
const cwd = try std.process.getCwdAlloc(gpa);
defer gpa.free(cwd);
- std.debug.assert(mem.eql(u8, cwd, "."));
+ assert(mem.eql(u8, cwd, "."));
}
return "";
}
const cwd = try std.process.getCwdAlloc(gpa);
defer gpa.free(cwd);
const resolved = try Dir.path.resolve(gpa, &.{cwd});
- std.debug.assert(Dir.path.isAbsolute(resolved));
+ assert(Dir.path.isAbsolute(resolved));
return resolved;
}
@@ -101,31 +102,27 @@ pub fn findZigLibDirFromSelfExe(
return error.FileNotFound;
}
-/// Caller owns returned memory.
-pub fn resolveGlobalCacheDir(gpa: Allocator) ![]u8 {
- if (try std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(gpa)) |value| return value;
+pub fn resolveGlobalCacheDir(arena: Allocator, environ_map: *const std.process.Environ.Map) ![]const u8 {
+ if (std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map)) |value| return value;
const app_name = "zig";
switch (builtin.os.tag) {
.wasi => @compileError("on WASI the global cache dir must be resolved with preopens"),
.windows => {
- const local_app_data_dir = (std.zig.EnvVar.LOCALAPPDATA.get(gpa) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.InvalidWtf8 => return error.AppDataDirUnavailable,
- }) orelse return error.AppDataDirUnavailable;
- defer gpa.free(local_app_data_dir);
- return Dir.path.join(gpa, &.{ local_app_data_dir, app_name });
+ const local_app_data_dir = std.zig.EnvVar.LOCALAPPDATA.get(environ_map) orelse
+ return error.AppDataDirUnavailable;
+ return Dir.path.join(arena, &.{ local_app_data_dir, app_name });
},
else => {
- if (std.zig.EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| {
+ if (std.zig.EnvVar.XDG_CACHE_HOME.get(environ_map)) |cache_root| {
if (cache_root.len > 0) {
- return Dir.path.join(gpa, &.{ cache_root, app_name });
+ return Dir.path.join(arena, &.{ cache_root, app_name });
}
}
- if (std.zig.EnvVar.HOME.getPosix()) |home| {
+ if (std.zig.EnvVar.HOME.get(environ_map)) |home| {
if (home.len > 0) {
- return Dir.path.join(gpa, &.{ home, ".cache", app_name });
+ return Dir.path.join(arena, &.{ home, ".cache", app_name });
}
}
return error.AppDataDirUnavailable;
@@ -144,7 +141,7 @@ pub fn resolvePath(
paths: []const []const u8,
) Allocator.Error![]u8 {
if (builtin.target.os.tag == .wasi) {
- std.debug.assert(mem.eql(u8, cwd_resolved, ""));
+ assert(mem.eql(u8, cwd_resolved, ""));
const res = try Dir.path.resolve(gpa, paths);
if (mem.eql(u8, res, ".")) {
gpa.free(res);
@@ -164,8 +161,8 @@ pub fn resolvePath(
gpa.free(res);
return "";
}
- std.debug.assert(!Dir.path.isAbsolute(res));
- std.debug.assert(!isUpDir(res));
+ assert(!Dir.path.isAbsolute(res));
+ assert(!isUpDir(res));
return res;
}
@@ -184,8 +181,8 @@ pub fn resolvePath(
};
errdefer gpa.free(path_resolved);
- std.debug.assert(Dir.path.isAbsolute(path_resolved));
- std.debug.assert(Dir.path.isAbsolute(cwd_resolved));
+ assert(Dir.path.isAbsolute(path_resolved));
+ assert(Dir.path.isAbsolute(cwd_resolved));
if (!std.mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd
if (path_resolved.len == cwd_resolved.len) {
diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig
index ba85f45830c26385f7d9cbc6af73fa735d6f2eb3..9ad7eb7e1a47b8788b9a6cd48a21032f6712822c 100644
--- a/src/libs/freebsd.zig
+++ b/src/libs/freebsd.zig
@@ -445,6 +445,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
.gpa = gpa,
.io = io,
.manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}),
+ .cwd = comp.dirs.cwd,
};
cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
cache.addPrefix(comp.dirs.zig_lib);
@@ -1119,6 +1120,7 @@ fn buildSharedLib(
.soname = soname,
.c_source_files = &c_source_files,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| switch (err) {
error.CreateFail => {
comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig
index e9b6ce18823fe6b684f3636a0219e8840c607f54..10e8446fa4ebde278042bb0da18fc79d858850ba 100644
--- a/src/libs/glibc.zig
+++ b/src/libs/glibc.zig
@@ -680,6 +680,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
.gpa = gpa,
.io = io,
.manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}),
+ .cwd = comp.dirs.cwd,
};
cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
cache.addPrefix(comp.dirs.zig_lib);
@@ -1258,6 +1259,7 @@ fn buildSharedLib(
.soname = soname,
.c_source_files = &c_source_files,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| switch (err) {
error.CreateFail => {
comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig
index d293a3b8999a2b44fdfd3c223eef2f5af352b872..bd8863991d63db6ed10d14cc426f0961fec0d676 100644
--- a/src/libs/libcxx.zig
+++ b/src/libs/libcxx.zig
@@ -275,6 +275,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| {
switch (err) {
else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {t}", .{err}),
@@ -468,6 +469,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| {
switch (err) {
else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}),
diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig
index 3dcbc132b13633e4b840030521a45b9ccade7c24..2f9574c47343aad0bf7b90013cbdd8c49f46c6cc 100644
--- a/src/libs/libtsan.zig
+++ b/src/libs/libtsan.zig
@@ -301,6 +301,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
.linker_allow_shlib_undefined = linker_allow_shlib_undefined,
.install_name = install_name,
.headerpad_size = headerpad_size,
+ .environ_map = comp.environ_map,
}) catch |err| {
switch (err) {
else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }),
diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig
index 7ab079b20511c5712f23c64fe5940c71d51ca4ed..787a87f951094245fe042a22da043b41b833373f 100644
--- a/src/libs/libunwind.zig
+++ b/src/libs/libunwind.zig
@@ -166,6 +166,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
.clang_passthrough_mode = comp.clang_passthrough_mode,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| {
switch (err) {
else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }),
diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig
index 03ed917c4fe7830d42fe59d5ce82f9b6118b39a6..752c62312f1fbed7fdd489fe077181a1a8d6be67 100644
--- a/src/libs/mingw.zig
+++ b/src/libs/mingw.zig
@@ -259,6 +259,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
.gpa = gpa,
.io = io,
.manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}),
+ .cwd = comp.dirs.cwd,
};
cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
cache.addPrefix(comp.dirs.zig_lib);
diff --git a/src/libs/musl.zig b/src/libs/musl.zig
index 1a1807f250c1fe96d118ec664225496e89558cd0..b847c279becb028610485e4dfefe0656f2f07e09 100644
--- a/src/libs/musl.zig
+++ b/src/libs/musl.zig
@@ -272,6 +272,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro
},
.skip_linker_dependencies = true,
.soname = "libc.so",
+ .environ_map = comp.environ_map,
}) catch |err| switch (err) {
error.CreateFail => {
comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig
index 9e4213d23726dd67a1b27fa4dcefe5c01ca347c2..87d2909e0782d2d1a9be2ec5c29f057bef03b6dc 100644
--- a/src/libs/netbsd.zig
+++ b/src/libs/netbsd.zig
@@ -386,6 +386,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
.gpa = gpa,
.io = io,
.manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}),
+ .cwd = comp.dirs.cwd,
};
cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
cache.addPrefix(comp.dirs.zig_lib);
@@ -761,6 +762,7 @@ fn buildSharedLib(
.soname = soname,
.c_source_files = &c_source_files,
.skip_linker_dependencies = true,
+ .environ_map = comp.environ_map,
}) catch |err| switch (err) {
error.CreateFail => {
comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
diff --git a/src/link/Lld.zig b/src/link/Lld.zig
index b2a0f6e396399307fddbd2e04ea7255461163f56..174777ac2964851620adc09c6efc279182c048ad 100644
--- a/src/link/Lld.zig
+++ b/src/link/Lld.zig
@@ -1604,19 +1604,24 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
var stderr: []u8 = &.{};
defer gpa.free(stderr);
- var child = std.process.Child.init(argv, arena);
+ // TODO rework this awkward logic to call child.kill() in the failure case
const term = (if (comp.clang_passthrough_mode) term: {
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
+ var child = std.process.spawn(io, .{
+ .argv = argv,
+ .stdin = .inherit,
+ .stdout = .inherit,
+ .stderr = .inherit,
+ }) catch |err| break :term err;
- break :term child.spawnAndWait(io);
+ break :term child.wait(io);
} else term: {
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Ignore;
- child.stderr_behavior = .Pipe;
+ var child = std.process.spawn(io, .{
+ .argv = argv,
+ .stdin = .ignore,
+ .stdout = .ignore,
+ .stderr = .pipe,
+ }) catch |err| break :term err;
- child.spawn(io) catch |err| break :term err;
var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited);
break :term child.wait(io);
@@ -1650,23 +1655,21 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
try rsp_writer.flush();
}
- var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint(
- arena,
- "@{s}",
- .{try comp.dirs.local_cache.join(arena, &.{rsp_path})},
- ) }, arena);
+ var rsp_child = std.process.spawn(io, .{
+ .argv = &.{
+ argv[0],
+ argv[1],
+ try std.fmt.allocPrint(arena, "@{s}", .{
+ try comp.dirs.local_cache.join(arena, &.{rsp_path}),
+ }),
+ },
+ .stdin = if (comp.clang_passthrough_mode) .inherit else .ignore,
+ .stdout = if (comp.clang_passthrough_mode) .inherit else .ignore,
+ .stderr = if (comp.clang_passthrough_mode) .inherit else .pipe,
+ }) catch |err| break :err err;
if (comp.clang_passthrough_mode) {
- rsp_child.stdin_behavior = .Inherit;
- rsp_child.stdout_behavior = .Inherit;
- rsp_child.stderr_behavior = .Inherit;
-
- break :term rsp_child.spawnAndWait(io) catch |err| break :err err;
+ break :term rsp_child.wait(io) catch |err| break :err err;
} else {
- rsp_child.stdin_behavior = .Ignore;
- rsp_child.stdout_behavior = .Ignore;
- rsp_child.stderr_behavior = .Pipe;
-
- rsp_child.spawn(io) catch |err| break :err err;
var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{});
stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited);
break :term rsp_child.wait(io) catch |err| break :err err;
@@ -1674,13 +1677,13 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
},
else => first_err,
};
- log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) });
+ log.err("unable to spawn LLD {s}: {t}", .{ argv[0], err });
return error.UnableToSpawnSelf;
};
const diags = &comp.link_diags;
switch (term) {
- .Exited => |code| if (code != 0) {
+ .exited => |code| if (code != 0) {
if (comp.clang_passthrough_mode) std.process.exit(code);
diags.lockAndParseLldStderr(argv[1], stderr);
return error.LinkFailure;
diff --git a/src/main.zig b/src/main.zig
index f71cf5d144cd1ff1f254b6e50fd93154941368ff..8bc3b583910ca58db23dbfdae9fb9f8a46e2632e 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -42,7 +42,6 @@ test {
const thread_stack_size = 60 << 20;
pub const std_options: std.Options = .{
- .wasiCwd = wasi_cwd,
.logFn = log,
.log_level = switch (builtin.mode) {
@@ -51,16 +50,17 @@ pub const std_options: std.Options = .{
.ReleaseSmall => .err,
},
};
+pub const std_options_cwd = if (native_os == .wasi) wasi_cwd else null;
pub const panic = crash_report.panic;
pub const debug = crash_report.debug;
var wasi_preopens: fs.wasi.Preopens = undefined;
-pub fn wasi_cwd() std.os.wasi.fd_t {
+pub fn wasi_cwd() Io.Dir {
// Expect the first preopen to be current working directory.
const cwd_fd: std.posix.fd_t = 3;
assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
- return cwd_fd;
+ return .{ .handle = cwd_fd };
}
const fatal = std.process.fatal;
@@ -168,7 +168,7 @@ const use_debug_allocator = build_options.debug_gpa or
.ReleaseFast, .ReleaseSmall => false,
});
-pub fn main() anyerror!void {
+pub fn main(init: std.process.Init.Minimal) anyerror!void {
const gpa = gpa: {
if (use_debug_allocator) break :gpa debug_allocator.allocator();
if (native_os == .wasi) break :gpa std.heap.wasm_allocator;
@@ -182,34 +182,48 @@ pub fn main() anyerror!void {
defer arena_instance.deinit();
const arena = arena_instance.allocator();
- const args = try process.argsAlloc(arena);
+ const args = try init.args.toSlice(arena);
if (args.len > 0) crash_report.zig_argv0 = args[0];
+ if (args.len <= 1) {
+ std.log.info("{s}", .{usage});
+ fatal("expected command argument", .{});
+ }
+
+ var environ_map = init.environ.createMap(arena) catch |err| fatal("failed to parse environment: {t}", .{err});
+
+ Compilation.setMainThread();
+
+ var threaded: Io.Threaded = .init(gpa, .{
+ .argv0 = .init(init.args),
+ .environ = init.environ,
+ });
+ defer threaded.deinit();
+ threaded_impl_ptr = &threaded;
+ threaded.stack_size = thread_stack_size;
+ const io = threaded.io();
+
if (tracy.enable_allocation) {
var gpa_tracy = tracy.tracyAllocator(gpa);
- return mainArgs(gpa_tracy.allocator(), arena, args);
+ return mainArgs(gpa_tracy.allocator(), arena, io, args, &environ_map);
}
if (native_os == .wasi) {
wasi_preopens = try fs.wasi.preopensAlloc(arena);
}
- return mainArgs(gpa, arena, args);
+ return mainArgs(gpa, arena, io, args, &environ_map);
}
-fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void {
- const tr = tracy.trace(@src());
- defer tr.end();
-
- Compilation.setMainThread();
-
- if (args.len <= 1) {
- std.log.info("{s}", .{usage});
- fatal("expected command argument", .{});
- }
-
- if (process.can_execv and std.posix.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {
+fn mainArgs(
+ gpa: Allocator,
+ arena: Allocator,
+ io: Io,
+ args: []const [:0]const u8,
+ environ_map: *process.Environ.Map,
+) !void {
+ if (process.can_replace and EnvVar.ZIG_IS_DETECTING_LIBC_PATHS.isSet(environ_map)) {
dev.check(.cc_command);
// In this case we have accidentally invoked ourselves as "the system C compiler"
// to figure out where libc is installed. This is essentially infinite recursion
@@ -217,58 +231,51 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void
// Here we ignore the CC environment variable and exec `cc` as a child process.
// However it's possible Zig is installed as *that* C compiler as well, which is
// why we have this additional environment variable here to check.
- var env_map = try process.getEnvMap(arena);
- const inf_loop_env_key = "ZIG_IS_TRYING_TO_NOT_CALL_ITSELF";
- if (env_map.get(inf_loop_env_key) != null) {
- fatal("The compilation links against libc, but Zig is unable to provide a libc " ++
- "for this operating system, and no --libc " ++
- "parameter was provided, so Zig attempted to invoke the system C compiler " ++
- "in order to determine where libc is installed. However the system C " ++
- "compiler is `zig cc`, so no libc installation was found.", .{});
+ const inf_loop_env_key: EnvVar = .ZIG_IS_TRYING_TO_NOT_CALL_ITSELF;
+ if (inf_loop_env_key.isSet(environ_map)) {
+ fatal("{s}", .{
+ "The compilation links against libc, but Zig is unable to provide a libc " ++
+ "for this operating system, and no --libc " ++
+ "parameter was provided, so Zig attempted to invoke the system C compiler " ++
+ "in order to determine where libc is installed. However the system C " ++
+ "compiler is `zig cc`, so no libc installation was found.",
+ });
}
- try env_map.put(inf_loop_env_key, "1");
+ try environ_map.put(@tagName(inf_loop_env_key), "1");
// Some programs such as CMake will strip the `cc` and subsequent args from the
// CC environment variable. We detect and support this scenario here because of
// the ZIG_IS_DETECTING_LIBC_PATHS environment variable.
if (mem.eql(u8, args[1], "cc")) {
- return process.execve(arena, args[1..], &env_map);
+ return process.replace(io, .{ .argv = args[1..], .environ_map = environ_map });
} else {
const modified_args = try arena.dupe([]const u8, args);
modified_args[0] = "cc";
- return process.execve(arena, modified_args, &env_map);
+ return process.replace(io, .{ .argv = modified_args, .environ_map = environ_map });
}
}
- var threaded: Io.Threaded = .init(gpa, .{
- .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{},
- });
- defer threaded.deinit();
- threaded_impl_ptr = &threaded;
- threaded.stack_size = thread_stack_size;
- const io = threaded.io();
-
const cmd = args[1];
const cmd_args = args[2..];
if (mem.eql(u8, cmd, "build-exe")) {
dev.check(.build_exe_command);
- return buildOutputType(gpa, arena, io, args, .{ .build = .Exe });
+ return buildOutputType(gpa, arena, io, args, .{ .build = .Exe }, environ_map);
} else if (mem.eql(u8, cmd, "build-lib")) {
dev.check(.build_lib_command);
- return buildOutputType(gpa, arena, io, args, .{ .build = .Lib });
+ return buildOutputType(gpa, arena, io, args, .{ .build = .Lib }, environ_map);
} else if (mem.eql(u8, cmd, "build-obj")) {
dev.check(.build_obj_command);
- return buildOutputType(gpa, arena, io, args, .{ .build = .Obj });
+ return buildOutputType(gpa, arena, io, args, .{ .build = .Obj }, environ_map);
} else if (mem.eql(u8, cmd, "test")) {
dev.check(.test_command);
- return buildOutputType(gpa, arena, io, args, .zig_test);
+ return buildOutputType(gpa, arena, io, args, .zig_test, environ_map);
} else if (mem.eql(u8, cmd, "test-obj")) {
dev.check(.test_command);
- return buildOutputType(gpa, arena, io, args, .zig_test_obj);
+ return buildOutputType(gpa, arena, io, args, .zig_test_obj, environ_map);
} else if (mem.eql(u8, cmd, "run")) {
dev.check(.run_command);
- return buildOutputType(gpa, arena, io, args, .run);
+ return buildOutputType(gpa, arena, io, args, .run, environ_map);
} else if (mem.eql(u8, cmd, "dlltool") or
mem.eql(u8, cmd, "ranlib") or
mem.eql(u8, cmd, "lib") or
@@ -278,7 +285,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void
return process.exit(try llvmArMain(arena, args));
} else if (mem.eql(u8, cmd, "build")) {
dev.check(.build_command);
- return cmdBuild(gpa, arena, io, cmd_args);
+ return cmdBuild(gpa, arena, io, cmd_args, environ_map);
} else if (mem.eql(u8, cmd, "clang") or
mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
{
@@ -292,16 +299,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void
return process.exit(try lldMain(arena, args, true));
} else if (mem.eql(u8, cmd, "cc")) {
dev.check(.cc_command);
- return buildOutputType(gpa, arena, io, args, .cc);
+ return buildOutputType(gpa, arena, io, args, .cc, environ_map);
} else if (mem.eql(u8, cmd, "c++")) {
dev.check(.cc_command);
- return buildOutputType(gpa, arena, io, args, .cpp);
+ return buildOutputType(gpa, arena, io, args, .cpp, environ_map);
} else if (mem.eql(u8, cmd, "translate-c")) {
dev.check(.translate_c_command);
- return buildOutputType(gpa, arena, io, args, .translate_c);
+ return buildOutputType(gpa, arena, io, args, .translate_c, environ_map);
} else if (mem.eql(u8, cmd, "rc")) {
const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration");
- return jitCmd(gpa, arena, io, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, environ_map, .{
.cmd_name = "resinator",
.root_src_path = "resinator/main.zig",
.depend_on_aro = true,
@@ -312,20 +319,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void
dev.check(.fmt_command);
return @import("fmt.zig").run(gpa, arena, io, cmd_args);
} else if (mem.eql(u8, cmd, "objcopy")) {
- return jitCmd(gpa, arena, io, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, environ_map, .{
.cmd_name = "objcopy",
.root_src_path = "objcopy.zig",
});
} else if (mem.eql(u8, cmd, "fetch")) {
- return cmdFetch(gpa, arena, io, cmd_args);
+ return cmdFetch(gpa, arena, io, cmd_args, environ_map);
} else if (mem.eql(u8, cmd, "libc")) {
- return jitCmd(gpa, arena, io, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, environ_map, .{
.cmd_name = "libc",
.root_src_path = "libc.zig",
.prepend_zig_lib_dir_path = true,
});
} else if (mem.eql(u8, cmd, "std")) {
- return jitCmd(gpa, arena, io, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, environ_map, .{
.cmd_name = "std",
.root_src_path = "std-docs.zig",
.prepend_zig_lib_dir_path = true,
@@ -355,10 +362,11 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void
args,
if (native_os == .wasi) wasi_preopens,
&host,
+ environ_map,
);
return stdout_writer.interface.flush();
} else if (mem.eql(u8, cmd, "reduce")) {
- return jitCmd(gpa, arena, io, cmd_args, .{
+ return jitCmd(gpa, arena, io, cmd_args, environ_map, .{
.cmd_name = "reduce",
.root_src_path = "reduce.zig",
});
@@ -803,6 +811,7 @@ fn buildOutputType(
io: Io,
all_args: []const []const u8,
arg_mode: ArgMode,
+ environ_map: *process.Environ.Map,
) !void {
var provided_name: ?[]const u8 = null;
var root_src_file: ?[]const u8 = null;
@@ -815,9 +824,9 @@ fn buildOutputType(
var debug_compile_errors = false;
var debug_incremental = false;
var verbose_link = (native_os != .wasi or builtin.link_libc) and
- EnvVar.ZIG_VERBOSE_LINK.isSet();
+ EnvVar.ZIG_VERBOSE_LINK.isSet(environ_map);
var verbose_cc = (native_os != .wasi or builtin.link_libc) and
- EnvVar.ZIG_VERBOSE_CC.isSet();
+ EnvVar.ZIG_VERBOSE_CC.isSet(environ_map);
var verbose_air = false;
var verbose_intern_pool = false;
var verbose_generic_instances = false;
@@ -889,9 +898,9 @@ fn buildOutputType(
var runtime_args_start: ?usize = null;
var test_filters: std.ArrayList([]const u8) = .empty;
var test_runner_path: ?[]const u8 = null;
- var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena);
- var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
- var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
+ var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);
+ var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
+ var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
var subsystem: ?std.zig.Subsystem = null;
var major_subsystem_version: ?u16 = null;
@@ -988,7 +997,7 @@ fn buildOutputType(
.framework_dirs = .{},
.rpath_list = .{},
.each_lib_rpath = null,
- .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),
+ .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map),
.native_system_include_paths = &.{},
};
defer create_module.link_inputs.deinit(gpa);
@@ -997,9 +1006,9 @@ fn buildOutputType(
// if set, default the color setting to .off or .on, respectively
// explicit --color arguments will still override this setting.
// Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162
- var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet())
+ var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet(environ_map))
.off
- else if (EnvVar.CLICOLOR_FORCE.isSet())
+ else if (EnvVar.CLICOLOR_FORCE.isSet(environ_map))
.on
else
.auto;
@@ -3097,6 +3106,7 @@ fn buildOutputType(
},
if (native_os == .wasi) wasi_preopens,
self_exe_path,
+ environ_map,
);
defer dirs.deinit(io);
@@ -3108,7 +3118,7 @@ fn buildOutputType(
create_module.opts.emit_bin = emit_bin != .no;
create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0;
- const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color);
+ const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color, environ_map);
for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
if (cli_mod.resolved == null)
fatal("module '{s}' declared but not used", .{key});
@@ -3585,6 +3595,7 @@ fn buildOutputType(
.global_cc_argv = try cc_argv.toOwnedSlice(arena),
.file_system_inputs = &file_system_inputs,
.debug_compiler_runtime_libs = debug_compiler_runtime_libs,
+ .environ_map = environ_map,
}) catch |err| switch (err) {
error.CreateFail => switch (create_diag) {
.cross_libc_unavailable => {
@@ -3648,6 +3659,7 @@ fn buildOutputType(
arg_mode,
all_args,
runtime_args_start,
+ environ_map,
);
return cleanExit(io);
},
@@ -3674,6 +3686,7 @@ fn buildOutputType(
arg_mode,
all_args,
runtime_args_start,
+ environ_map,
);
return cleanExit(io);
},
@@ -3686,7 +3699,7 @@ fn buildOutputType(
defer root_prog_node.end();
if (arg_mode == .translate_c) {
- return cmdTranslateC(comp, arena, null, null, root_prog_node);
+ return cmdTranslateC(comp, arena, null, null, root_prog_node, environ_map);
}
updateModule(comp, color, root_prog_node) catch |err| switch (err) {
@@ -3754,6 +3767,7 @@ fn buildOutputType(
all_args,
runtime_args_start,
create_module.resolved_options.link_libc,
+ environ_map,
);
}
@@ -3809,6 +3823,7 @@ fn createModule(
index: usize,
parent: ?*Package.Module,
color: std.zig.Color,
+ environ_map: *process.Environ.Map,
) Allocator.Error!*Package.Module {
const cli_mod = &create_module.modules.values()[index];
if (cli_mod.resolved) |m| return m;
@@ -3988,7 +4003,7 @@ fn createModule(
resolved_target.is_native_os and resolved_target.is_native_abi and
create_module.want_native_include_dirs)
{
- var paths = std.zig.system.NativePaths.detect(arena, io, target) catch |err|
+ var paths = std.zig.system.NativePaths.detect(arena, io, target, environ_map) catch |err|
fatal("unable to detect native system paths: {t}", .{err});
for (paths.warnings.items) |warning| {
warn("{s}", .{warning});
@@ -4015,6 +4030,7 @@ fn createModule(
create_module.libc_installation = LibCInstallation.findNative(arena, io, .{
.verbose = true,
.target = target,
+ .environ_map = environ_map,
}) catch |err| {
fatal("unable to find native libc installation: {t}", .{err});
};
@@ -4119,7 +4135,7 @@ fn createModule(
for (cli_mod.deps) |dep| {
const dep_index = create_module.modules.getIndex(dep.value) orelse
fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
- const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color);
+ const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color, environ_map);
try mod.deps.put(arena, dep.key, dep_mod);
}
@@ -4128,9 +4144,7 @@ fn createModule(
fn saveState(comp: *Compilation, incremental: bool) void {
if (incremental) {
- comp.saveState() catch |err| {
- warn("unable to save incremental compilation state: {s}", .{@errorName(err)});
- };
+ comp.saveState() catch |err| warn("unable to save incremental compilation state: {t}", .{err});
}
}
@@ -4143,6 +4157,7 @@ fn serve(
arg_mode: ArgMode,
all_args: []const []const u8,
runtime_args_start: ?usize,
+ environ_map: *process.Environ.Map,
) !void {
const gpa = comp.gpa;
const io = comp.io;
@@ -4190,7 +4205,7 @@ fn serve(
defer arena_instance.deinit();
const arena = arena_instance.allocator();
var output: Compilation.CImportResult = undefined;
- try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node);
+ try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node, environ_map);
defer output.deinit(gpa);
if (file_system_inputs.items.len != 0) {
@@ -4390,6 +4405,7 @@ fn runOrTest(
all_args: []const []const u8,
runtime_args_start: ?usize,
link_libc: bool,
+ environ_map: *process.Environ.Map,
) !void {
const raw_emit_bin = comp.emit_bin orelse return;
const exe_path = switch (comp.cache_use) {
@@ -4426,77 +4442,90 @@ fn runOrTest(
if (runtime_args_start) |i| {
try argv.appendSlice(all_args[i..]);
}
- var env_map = try process.getEnvMap(arena);
- try env_map.put("ZIG_EXE", self_exe_path);
+ try environ_map.put("ZIG_EXE", self_exe_path);
// We do not execve for tests because if the test fails we want to print
// the error message and invocation below.
- if (process.can_execv and arg_mode == .run) {
- // execv releases the locks; no need to destroy the Compilation here.
+ if (process.can_replace and arg_mode == .run) {
+ // process replacement releases the locks; no need to destroy the Compilation here.
_ = try io.lockStderr(&.{}, .no_color);
- const err = process.execve(gpa, argv.items, &env_map);
+ const err = process.replace(io, .{ .argv = argv.items, .environ_map = environ_map });
io.unlockStderr();
try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
const cmd = try std.mem.join(arena, " ", argv.items);
fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd });
- } else if (process.can_spawn) {
- var child = std.process.Child.init(argv.items, gpa);
- child.env_map = &env_map;
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
-
+ } else if (!process.can_spawn) {
+ const cmd = try std.mem.join(arena, " ", argv.items);
+ fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{
+ native_os, cmd,
+ });
+ }
+ const term_result = (term: {
// Here we release all the locks associated with the Compilation so
// that whatever this child process wants to do won't deadlock.
comp.destroy();
comp_destroyed.* = true;
- const term_result = t: {
- _ = try io.lockStderr(&.{}, .no_color);
- defer io.unlockStderr();
- break :t child.spawnAndWait(io);
- };
- const term = term_result catch |err| {
- try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
- const cmd = try std.mem.join(arena, " ", argv.items);
- fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
- };
- switch (arg_mode) {
- .run, .build => {
- switch (term) {
- .Exited => |code| {
- if (code == 0) {
- return cleanExit(io);
- } else {
- process.exit(code);
- }
- },
- else => {
- process.exit(1);
- },
- }
- },
- .zig_test => {
- switch (term) {
- .Exited => |code| {
- if (code == 0) {
- return cleanExit(io);
- } else {
- const cmd = try std.mem.join(arena, " ", argv.items);
- fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
- }
- },
- else => {
+ _ = try io.lockStderr(&.{}, .no_color);
+ defer io.unlockStderr();
+
+ var child = std.process.spawn(io, .{
+ .argv = argv.items,
+ .environ_map = environ_map,
+ .stdin = .inherit,
+ .stdout = .inherit,
+ .stderr = .inherit,
+ }) catch |err| break :term err;
+ defer child.kill(io);
+
+ break :term child.wait(io);
+ });
+
+ const term = term_result catch |err| {
+ try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
+ const cmd = try std.mem.join(arena, " ", argv.items);
+ fatal("the following command failed with {t}:\n{s}", .{ err, cmd });
+ };
+ switch (arg_mode) {
+ .run, .build => {
+ switch (term) {
+ .exited => |code| {
+ if (code == 0) {
+ return cleanExit(io);
+ } else {
+ process.exit(code);
+ }
+ },
+ .signal => |sig| {
+ const cmd = try std.mem.join(arena, " ", argv.items);
+ fatal("the following command terminated with signal {t}:\n{s}", .{ sig, cmd });
+ },
+ else => {
+ process.exit(1);
+ },
+ }
+ },
+ .zig_test => {
+ switch (term) {
+ .exited => |code| {
+ if (code == 0) {
+ return cleanExit(io);
+ } else {
const cmd = try std.mem.join(arena, " ", argv.items);
- fatal("the following test command crashed:\n{s}", .{cmd});
- },
- }
- },
- else => unreachable,
- }
- } else {
- const cmd = try std.mem.join(arena, " ", argv.items);
- fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
+ fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
+ }
+ },
+ .signal => |sig| {
+ const cmd = try std.mem.join(arena, " ", argv.items);
+ fatal("the following test command terminated with signal {t}:\n{s}", .{ sig, cmd });
+ },
+ else => {
+ const cmd = try std.mem.join(arena, " ", argv.items);
+ fatal("the following test command crashed:\n{s}", .{cmd});
+ },
+ }
+ },
+ else => unreachable,
}
}
@@ -4559,43 +4588,13 @@ fn runOrTestHotSwap(
try argv.appendSlice(all_args[i..]);
}
- switch (builtin.target.os.tag) {
- .macos => {
- const PosixSpawn = @import("DarwinPosixSpawn.zig");
-
- var attr = try PosixSpawn.Attr.init();
- defer attr.deinit();
-
- // ASLR is probably a good default for better debugging experience/programming
- // with hot-code updates in mind. However, we can also make it work with ASLR on.
- try attr.set(.{
- .SETSIGDEF = true,
- .SETSIGMASK = true,
- .DISABLE_ASLR = true,
- });
-
- var arena_allocator = std.heap.ArenaAllocator.init(gpa);
- defer arena_allocator.deinit();
- const arena = arena_allocator.allocator();
-
- const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null);
- for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
-
- const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ);
- return pid;
- },
- else => {
- var child = std.process.Child.init(argv.items, gpa);
-
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
-
- try child.spawn(io);
-
- return child.id;
- },
- }
+ var child = try std.process.spawn(io, .{
+ .argv = argv.items,
+ .stdin = .inherit,
+ .stdout = .inherit,
+ .stderr = .inherit,
+ });
+ return child.id.?;
}
const UpdateModuleError = Compilation.UpdateError || error{
@@ -4627,6 +4626,7 @@ fn cmdTranslateC(
fancy_output: ?*Compilation.CImportResult,
file_system_inputs: ?*std.ArrayList(u8),
prog_node: std.Progress.Node,
+ environ_map: *process.Environ.Map,
) !void {
dev.check(.translate_c_command);
@@ -4660,6 +4660,7 @@ fn cmdTranslateC(
translated_basename,
comp.root_mod,
prog_node,
+ environ_map,
);
if (result.errors.errorMessageCount() != 0) {
@@ -4707,10 +4708,11 @@ pub fn translateC(
arena: Allocator,
io: Io,
argv: []const []const u8,
+ environ_map: *const process.Environ.Map,
prog_node: std.Progress.Node,
capture: ?*[]u8,
) !void {
- try jitCmd(gpa, arena, io, argv, .{
+ try jitCmd(gpa, arena, io, argv, environ_map, .{
.cmd_name = "translate-c",
.root_src_path = "translate-c/main.zig",
.depend_on_aro = true,
@@ -4867,21 +4869,21 @@ test sanitizeExampleName {
try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
}
-fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void {
+fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, environ_map: *process.Environ.Map) !void {
dev.check(.build_command);
var build_file: ?[]const u8 = null;
- var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
- var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
- var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena);
- var override_build_runner: ?[]const u8 = try EnvVar.ZIG_BUILD_RUNNER.get(arena);
+ var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
+ var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
+ var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map);
+ var override_build_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map);
var child_argv = std.array_list.Managed([]const u8).init(arena);
var reference_trace: ?u32 = null;
var debug_compile_errors = false;
var verbose_link = (native_os != .wasi or builtin.link_libc) and
- EnvVar.ZIG_VERBOSE_LINK.isSet();
+ EnvVar.ZIG_VERBOSE_LINK.isSet(environ_map);
var verbose_cc = (native_os != .wasi or builtin.link_libc) and
- EnvVar.ZIG_VERBOSE_CC.isSet();
+ EnvVar.ZIG_VERBOSE_CC.isSet(environ_map);
var verbose_air = false;
var verbose_intern_pool = false;
var verbose_generic_instances = false;
@@ -5078,7 +5080,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
}
const work_around_btrfs_bug = native_os == .linux and
- EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
+ EnvVar.ZIG_BTRFS_WORKAROUND.isSet(environ_map);
const root_prog_node = std.Progress.start(io, .{
.disable_printing = (color == .off),
.root_name = "Compile Build Script",
@@ -5138,6 +5140,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
} },
{},
self_exe_path,
+ environ_map,
);
defer dirs.deinit(io);
@@ -5240,7 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
job_queue.read_only = true;
cleanup_build_dir = job_queue.global_cache.handle;
} else {
- try http_client.initDefaultProxies(arena);
+ try http_client.initDefaultProxies(arena, environ_map);
}
try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
@@ -5394,6 +5397,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
.cache_mode = .whole,
.reference_trace = reference_trace,
.debug_compile_errors = debug_compile_errors,
+ .environ_map = environ_map,
}) catch |err| switch (err) {
error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
@@ -5415,81 +5419,81 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8)
});
}
- if (process.can_spawn) {
- var child = std.process.Child.init(child_argv.items, gpa);
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
-
- const term = t: {
- _ = try io.lockStderr(&.{}, .no_color);
- defer io.unlockStderr();
- break :t child.spawnAndWait(io) catch |err|
- fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err });
- };
-
- switch (term) {
- .Exited => |code| {
- if (code == 0) return cleanExit(io);
- // Indicates that the build runner has reported compile errors
- // and this parent process does not need to report any further
- // diagnostics.
- if (code == 2) process.exit(2);
-
- if (code == 3) {
- if (!dev.env.supports(.fetch_command)) process.exit(3);
- // Indicates the configure phase failed due to missing lazy
- // dependencies and stdout contains the hashes of the ones
- // that are missing.
- const s = fs.path.sep_str;
- const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;
- const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| {
- fatal("unable to read results of configure phase from '{f}{s}': {s}", .{
- dirs.local_cache, tmp_sub_path, @errorName(err),
- });
- };
- dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {};
-
- var it = mem.splitScalar(u8, stdout, '\n');
- var any_errors = false;
- while (it.next()) |hash| {
- if (hash.len == 0) continue;
- if (hash.len > Package.Hash.max_len) {
- std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{
- hash.len, hash,
- });
- any_errors = true;
- continue;
- }
- try unlazy_set.put(arena, .fromSlice(hash), {});
- }
- if (any_errors) process.exit(3);
- if (system_pkg_dir_path) |p| {
- // In this mode, the system needs to provide these packages; they
- // cannot be fetched by Zig.
- for (unlazy_set.keys()) |*hash| {
- std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{
- p, hash.toSlice(),
- });
- }
- std.log.info("remote package fetching disabled due to --system mode", .{});
- std.log.info("dependencies might be avoidable depending on build configuration", .{});
- process.exit(3);
- }
- continue;
- }
-
- const cmd = try std.mem.join(arena, " ", child_argv.items);
- fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
- },
- else => {
- const cmd = try std.mem.join(arena, " ", child_argv.items);
- fatal("the following build command crashed:\n{s}", .{cmd});
- },
- }
- } else {
+ if (!process.can_spawn) {
const cmd = try std.mem.join(arena, " ", child_argv.items);
- fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
+ fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd });
+ }
+ switch (term: {
+ _ = try io.lockStderr(&.{}, .no_color);
+ defer io.unlockStderr();
+ var child = std.process.spawn(io, .{
+ .argv = child_argv.items,
+ }) catch |err| fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err });
+ defer child.kill(io);
+ break :term child.wait(io) catch |err|
+ fatal("failed to wait build runner {s}: {t}", .{ child_argv.items[0], err });
+ }) {
+ .exited => |code| {
+ if (code == 0) return cleanExit(io);
+ // Indicates that the build runner has reported compile errors
+ // and this parent process does not need to report any further
+ // diagnostics.
+ if (code == 2) process.exit(2);
+
+ if (code == 3) {
+ if (!dev.env.supports(.fetch_command)) process.exit(3);
+ // Indicates the configure phase failed due to missing lazy
+ // dependencies and stdout contains the hashes of the ones
+ // that are missing.
+ const s = fs.path.sep_str;
+ const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;
+ const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| {
+ fatal("unable to read results of configure phase from '{f}{s}': {t}", .{
+ dirs.local_cache, tmp_sub_path, err,
+ });
+ };
+ dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {};
+
+ var it = mem.splitScalar(u8, stdout, '\n');
+ var any_errors = false;
+ while (it.next()) |hash| {
+ if (hash.len == 0) continue;
+ if (hash.len > Package.Hash.max_len) {
+ std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{
+ hash.len, hash,
+ });
+ any_errors = true;
+ continue;
+ }
+ try unlazy_set.put(arena, .fromSlice(hash), {});
+ }
+ if (any_errors) process.exit(3);
+ if (system_pkg_dir_path) |p| {
+ // In this mode, the system needs to provide these packages; they
+ // cannot be fetched by Zig.
+ for (unlazy_set.keys()) |*hash| {
+ std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{
+ p, hash.toSlice(),
+ });
+ }
+ std.log.info("remote package fetching disabled due to --system mode", .{});
+ std.log.info("dependencies might be avoidable depending on build configuration", .{});
+ process.exit(3);
+ }
+ continue;
+ }
+
+ const cmd = try std.mem.join(arena, " ", child_argv.items);
+ fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
+ },
+ .signal => |sig| {
+ const cmd = try std.mem.join(arena, " ", child_argv.items);
+ fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd });
+ },
+ else => {
+ const cmd = try std.mem.join(arena, " ", child_argv.items);
+ fatal("the following build command crashed:\n{s}", .{cmd});
+ },
}
}
}
@@ -5512,6 +5516,7 @@ fn jitCmd(
arena: Allocator,
io: Io,
args: []const []const u8,
+ environ_map: *const process.Environ.Map,
options: JitCmdOptions,
) !void {
dev.check(.jit_command);
@@ -5533,13 +5538,13 @@ fn jitCmd(
const self_exe_path = process.executablePathAlloc(io, arena) catch |err|
fatal("unable to find self exe path: {t}", .{err});
- const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet())
+ const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map))
.Debug
else
.ReleaseFast;
const strip = optimize_mode != .Debug;
- const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
- const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
+ const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
+ const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
// This `init` calls `fatal` on error.
var dirs: Compilation.Directories = .init(
@@ -5550,6 +5555,7 @@ fn jitCmd(
.global,
if (native_os == .wasi) wasi_preopens,
self_exe_path,
+ environ_map,
);
defer dirs.deinit(io);
@@ -5623,6 +5629,7 @@ fn jitCmd(
.self_exe_path = self_exe_path,
.thread_limit = thread_limit,
.cache_mode = .whole,
+ .environ_map = environ_map,
}) catch |err| switch (err) {
error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
@@ -5668,32 +5675,34 @@ fn jitCmd(
child_argv.appendSliceAssumeCapacity(args);
- if (process.can_execv and options.capture == null) {
- if (EnvVar.ZIG_DEBUG_CMD.isSet()) {
+ if (process.can_replace and options.capture == null) {
+ if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) {
const cmd = try std.mem.join(arena, " ", child_argv.items);
std.debug.print("{s}\n", .{cmd});
}
- const err = process.execv(gpa, child_argv.items);
+ const err = process.replace(io, .{ .argv = child_argv.items, .environ_map = environ_map });
const cmd = try std.mem.join(arena, " ", child_argv.items);
fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd });
}
if (!process.can_spawn) {
const cmd = try std.mem.join(arena, " ", child_argv.items);
- fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
- @tagName(native_os), cmd,
+ fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{
+ native_os, cmd,
});
}
- var child = std.process.Child.init(child_argv.items, gpa);
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe;
- child.stderr_behavior = .Inherit;
-
- const term = t: {
+ switch (t: {
_ = try io.lockStderr(&.{}, .no_color);
defer io.unlockStderr();
- try child.spawn(io);
+
+ var child = std.process.spawn(io, .{
+ .argv = child_argv.items,
+ .stdin = .inherit,
+ .stdout = if (options.capture == null) .inherit else .pipe,
+ .stderr = .inherit,
+ }) catch |err| fatal("failed to spawn {s}: {t}", .{ child_argv.items[0], err });
+ defer child.kill(io);
if (options.capture) |ptr| {
var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
@@ -5701,9 +5710,8 @@ fn jitCmd(
}
break :t try child.wait(io);
- };
- switch (term) {
- .Exited => |code| {
+ }) {
+ .exited => |code| {
if (code == 0) {
if (options.capture != null) return;
return cleanExit(io);
@@ -5711,6 +5719,10 @@ fn jitCmd(
const cmd = try std.mem.join(arena, " ", child_argv.items);
fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
},
+ .signal => |sig| {
+ const cmd = try std.mem.join(arena, " ", child_argv.items);
+ fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd });
+ },
else => {
const cmd = try std.mem.join(arena, " ", child_argv.items);
fatal("the following build command crashed:\n{s}", .{cmd});
@@ -5826,7 +5838,7 @@ pub fn lldMain(
return @intFromBool(!ok);
}
-const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, .single_quotes = true });
+const ArgIteratorResponseFile = process.Args.IteratorGeneral(.{ .comments = true, .single_quotes = true });
/// Initialize the arguments from a Response File. "*.rsp"
fn initArgIteratorResponseFile(allocator: Allocator, io: Io, resp_file_path: []const u8) !ArgIteratorResponseFile {
@@ -6902,14 +6914,15 @@ fn cmdFetch(
arena: Allocator,
io: Io,
args: []const []const u8,
+ environ_map: *process.Environ.Map,
) !void {
dev.check(.fetch_command);
const color: Color = .auto;
const work_around_btrfs_bug = native_os == .linux and
- EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
+ EnvVar.ZIG_BTRFS_WORKAROUND.isSet(environ_map);
var opt_path_or_url: ?[]const u8 = null;
- var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
+ var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
var debug_hash: bool = false;
var save: union(enum) {
no,
@@ -6955,7 +6968,7 @@ fn cmdFetch(
var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
defer http_client.deinit();
- try http_client.initDefaultProxies(arena);
+ try http_client.initDefaultProxies(arena, environ_map);
var root_prog_node = std.Progress.start(io, .{
.root_name = "Fetch",
@@ -6963,7 +6976,7 @@ fn cmdFetch(
defer root_prog_node.end();
var global_cache_directory: Directory = l: {
- const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
+ const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena, environ_map);
break :l .{
.handle = try Io.Dir.cwd().createDirPathOpen(io, p, .{}),
.path = p,
diff --git a/src/print_env.zig b/src/print_env.zig
index 3540a58d188605726c41338ccffeb4a6bc382ca1..ac0578852e5f9d43c925ee4894904cd78062e826 100644
--- a/src/print_env.zig
+++ b/src/print_env.zig
@@ -19,9 +19,10 @@ pub fn cmdEnv(
else => void,
},
host: *const std.Target,
+ environ_map: *std.process.Environ.Map,
) !void {
- const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
- const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
+ const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map);
+ const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map);
const self_exe_path = switch (builtin.target.os.tag) {
.wasi => args[0],
@@ -38,6 +39,7 @@ pub fn cmdEnv(
.global,
if (builtin.target.os.tag == .wasi) wasi_preopens,
if (builtin.target.os.tag != .wasi) self_exe_path,
+ environ_map,
);
defer dirs.deinit(io);
@@ -56,8 +58,8 @@ pub fn cmdEnv(
try root.field("version", build_options.version, .{});
try root.field("target", triple, .{});
var env = try root.beginStructField("env", .{});
- inline for (@typeInfo(std.zig.EnvVar).@"enum".fields) |field| {
- try env.field(field.name, try @field(std.zig.EnvVar, field.name).get(arena), .{});
+ inline for (@typeInfo(EnvVar).@"enum".fields) |field| {
+ try env.field(field.name, @field(EnvVar, field.name).get(environ_map), .{});
}
try env.end();
try root.end();
diff --git a/test/src/Debugger.zig b/test/src/Debugger.zig
index d975c8fbb56e31ea7e60f9f01300db7e3225d14f..0d389f2508b2e4304d0f7ded9859659d9aaca102 100644
--- a/test/src/Debugger.zig
+++ b/test/src/Debugger.zig
@@ -2306,7 +2306,7 @@ fn addTest(
run.addArgs(db_argv2);
run.addArtifactArg(exe);
for (expected_output) |expected| run.addCheck(.{ .expect_stdout_match = db.b.fmt("{s}\n", .{expected}) });
- run.addCheck(.{ .expect_term = .{ .Exited = success } });
+ run.addCheck(.{ .expect_term = .{ .exited = success } });
run.setStdIn(.{ .bytes = "" });
db.root_step.dependOn(&run.step);
}
diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig
index 57e29dd8000ab3f892ab354ee2ea2dbed251f943..4e5c946682681ad47f458e67de7b7fd7c8a3dc24 100644
--- a/test/src/StackTrace.zig
+++ b/test/src/StackTrace.zig
@@ -193,9 +193,9 @@ fn addCaseInstance(
run.removeEnvironmentVariable("CLICOLOR_FORCE");
run.setEnvironmentVariable("NO_COLOR", "1");
run.addCheck(.{ .expect_term = term: {
- if (!expect_panic) break :term .{ .Exited = 0 };
- if (target.result.os.tag == .windows) break :term .{ .Exited = 3 };
- break :term .{ .Signal = 6 };
+ if (!expect_panic) break :term .{ .exited = 0 };
+ if (target.result.os.tag == .windows) break :term .{ .exited = 3 };
+ break :term .{ .signal = @enumFromInt(6) };
} });
run.expectStdOutEqual("");
diff --git a/test/src/convert-stack-trace.zig b/test/src/convert-stack-trace.zig
index d23623c396826b9488a75eb67dcb2a3fb5137567..272c43e31171494f8df667690e4a725c404bb9b7 100644
--- a/test/src/convert-stack-trace.zig
+++ b/test/src/convert-stack-trace.zig
@@ -24,20 +24,13 @@
//!
//! With these transformations, the test harness can safely do string comparisons.
-pub fn main() !void {
- var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- const args = try std.process.argsAlloc(arena);
if (args.len != 2) std.process.fatal("usage: convert-stack-trace path/to/test/output", .{});
- const gpa = arena;
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
var read_buf: [1024]u8 = undefined;
var write_buf: [1024]u8 = undefined;
diff --git a/test/standalone/child_process/child.zig b/test/standalone/child_process/child.zig
index 80e2edaa7f3bed3af38b9961fb364ec5dd0d422c..8e19d53b3c625c39de7ac19347270888200983a4 100644
--- a/test/standalone/child_process/child.zig
+++ b/test/standalone/child_process/child.zig
@@ -4,31 +4,23 @@ const Io = std.Io;
// 42 is expected by parent; other values result in test failure
var exit_code: u8 = 42;
-pub fn main() !void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- const arena = arena_state.allocator();
-
- var threaded: std.Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- try run(arena, io);
- arena_state.deinit();
+pub fn main(init: std.process.Init) !void {
+ try run(init.arena.allocator(), init.io, init.minimal.args);
std.process.exit(exit_code);
}
-fn run(allocator: std.mem.Allocator, io: Io) !void {
- var args = try std.process.argsWithAllocator(allocator);
- defer args.deinit();
- _ = args.next() orelse unreachable; // skip binary name
+fn run(arena: std.mem.Allocator, io: Io, args: std.process.Args) !void {
+ var it = try args.iterateAllocator(arena);
+ defer it.deinit();
+ _ = it.next() orelse unreachable; // skip binary name
// test cmd args
const hello_arg = "hello arg";
- const a1 = args.next() orelse unreachable;
+ const a1 = it.next() orelse unreachable;
if (!std.mem.eql(u8, a1, hello_arg)) {
testError(io, "first arg: '{s}'; want '{s}'", .{ a1, hello_arg });
}
- if (args.next()) |a2| {
+ if (it.next()) |a2| {
testError(io, "expected only one arg; got more: {s}", .{a2});
}
diff --git a/test/standalone/child_process/main.zig b/test/standalone/child_process/main.zig
index 98d38bdee3677184a554fcbe01961b0e930e9865..2776c3e95c88d5158b9db122d6b9669871ba23e9 100644
--- a/test/standalone/child_process/main.zig
+++ b/test/standalone/child_process/main.zig
@@ -1,15 +1,21 @@
const std = @import("std");
const Io = std.Io;
-pub fn main() !void {
+pub fn main(init: std.process.Init.Minimal) !void {
// make sure safety checks are enabled even in release modes
- var gpa_state = std.heap.GeneralPurposeAllocator(.{ .safety = true }){};
+ var gpa_state: std.heap.GeneralPurposeAllocator(.{ .safety = true }) = .{};
defer if (gpa_state.deinit() != .ok) {
@panic("found memory leaks");
};
const gpa = gpa_state.allocator();
- var it = try std.process.argsWithAllocator(gpa);
+ const process_cwd_path = try std.process.getCwdAlloc(gpa);
+ defer gpa.free(process_cwd_path);
+
+ var environ_map = try init.environ.createMap(gpa);
+ defer environ_map.deinit();
+
+ var it = try init.args.iterateAllocator(gpa);
defer it.deinit();
_ = it.next() orelse unreachable; // skip binary name
const child_path, const needs_free = child_path: {
@@ -17,19 +23,24 @@ pub fn main() !void {
const cwd_path = it.next() orelse break :child_path .{ child_path, false };
// If there is a third argument, it is the current CWD somewhere within the cache directory.
// In that case, modify the child path in order to test spawning a path with a leading `..` component.
- break :child_path .{ try std.fs.path.relative(gpa, cwd_path, child_path), true };
+ break :child_path .{ try std.fs.path.relative(gpa, process_cwd_path, &environ_map, cwd_path, child_path), true };
};
defer if (needs_free) gpa.free(child_path);
- var threaded: Io.Threaded = .init(gpa, .{});
+ var threaded: Io.Threaded = .init(gpa, .{
+ .argv0 = .init(init.args),
+ .environ = init.environ,
+ });
defer threaded.deinit();
const io = threaded.io();
- var child = std.process.Child.init(&.{ child_path, "hello arg" }, gpa);
- child.stdin_behavior = .Pipe;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Inherit;
- try child.spawn(io);
+ var child = try std.process.spawn(io, .{
+ .argv = &.{ child_path, "hello arg" },
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .inherit,
+ });
+
const child_stdin = child.stdin.?;
try child_stdin.writeStreamingAll(io, "hello from stdin"); // verified in child
child_stdin.close(io);
@@ -44,7 +55,7 @@ pub fn main() !void {
}
switch (try child.wait(io)) {
- .Exited => |code| {
+ .exited => |code| {
const child_ok_code = 42; // set by child if no test errors
if (code != child_ok_code) {
testError(io, "child exit code: {d}; want {d}", .{ code, child_ok_code });
@@ -57,7 +68,7 @@ pub fn main() !void {
// Check that FileNotFound is consistent across platforms when trying to spawn an executable that doesn't exist
const missing_child_path = try std.mem.concat(gpa, u8, &.{ child_path, "_intentionally_missing" });
defer gpa.free(missing_child_path);
- try std.testing.expectError(error.FileNotFound, std.process.Child.run(gpa, io, .{ .argv = &.{missing_child_path} }));
+ try std.testing.expectError(error.FileNotFound, std.process.run(gpa, io, .{ .argv = &.{missing_child_path} }));
}
var parent_test_error = false;
diff --git a/test/standalone/cmakedefine/check.zig b/test/standalone/cmakedefine/check.zig
index c2f89ad1127ccba3f663886e84d085cab2f7cb26..8db602f39d6db5f20bb016ade3853b1bf78b7ec3 100644
--- a/test/standalone/cmakedefine/check.zig
+++ b/test/standalone/cmakedefine/check.zig
@@ -1,16 +1,12 @@
-pub fn main() !void {
- var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
-
- const args = try std.process.argsAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
if (args.len != 3) return error.BadUsage;
const actual_path = args[1];
const expected_path = args[2];
- const io = std.Io.Threaded.global_single_threaded.ioBasic();
-
const actual = try std.Io.Dir.cwd().readFileAlloc(io, actual_path, arena, .limited(1024 * 1024));
const expected = try std.Io.Dir.cwd().readFileAlloc(io, expected_path, arena, .limited(1024 * 1024));
diff --git a/test/standalone/coff_dwarf/main.zig b/test/standalone/coff_dwarf/main.zig
index 7e314d5b287cb781791474a12e599ff5d6b0b175..79caaeb95a2f569e7a0a4e62e35083216b49cdec 100644
--- a/test/standalone/coff_dwarf/main.zig
+++ b/test/standalone/coff_dwarf/main.zig
@@ -3,18 +3,13 @@ const fatal = std.process.fatal;
extern fn add(a: u32, b: u32, addr: *usize) u32;
-pub fn main() void {
- var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init;
- defer std.debug.assert(debug_alloc_inst.deinit() == .ok);
- const gpa = debug_alloc_inst.allocator();
+pub fn main(init: std.process.Init) void {
+ const gpa = init.gpa;
+ const io = init.io;
var di: std.debug.SelfInfo = .init;
defer di.deinit(gpa);
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
var add_addr: usize = undefined;
_ = add(1, 2, &add_addr);
diff --git a/test/standalone/dirname/exists_in.zig b/test/standalone/dirname/exists_in.zig
index ba2de2777fe2c05fb3d6c61e366a127543337915..5900e76b574b1dd5d1628bc38e51133423b785cd 100644
--- a/test/standalone/dirname/exists_in.zig
+++ b/test/standalone/dirname/exists_in.zig
@@ -11,16 +11,8 @@
const std = @import("std");
-pub fn main() !void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- const arena = arena_state.allocator();
- defer arena_state.deinit();
-
- try run(arena);
-}
-
-fn run(allocator: std.mem.Allocator) !void {
- var args = try std.process.argsWithAllocator(allocator);
+pub fn main(init: std.process.Init) !void {
+ var args = try init.minimal.args.iterateAllocator(init.gpa);
defer args.deinit();
_ = args.next() orelse unreachable; // skip binary name
diff --git a/test/standalone/dirname/has_basename.zig b/test/standalone/dirname/has_basename.zig
index 49eefa3b48e9b79b5794c17a1334602465a07090..e0e408a3a491446cc17ed72c64d958bd88b62ec0 100644
--- a/test/standalone/dirname/has_basename.zig
+++ b/test/standalone/dirname/has_basename.zig
@@ -13,16 +13,8 @@
const std = @import("std");
-pub fn main() !void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- const arena = arena_state.allocator();
- defer arena_state.deinit();
-
- try run(arena);
-}
-
-fn run(allocator: std.mem.Allocator) !void {
- var args = try std.process.argsWithAllocator(allocator);
+pub fn main(init: std.process.Init) !void {
+ var args = try init.minimal.args.iterateAllocator(init.gpa);
defer args.deinit();
_ = args.next() orelse unreachable; // skip binary name
diff --git a/test/standalone/dirname/touch.zig b/test/standalone/dirname/touch.zig
index 134d53d2fc75ae6919ac091125e7c9c92968a133..6b0dad536e4661244c70a5ecfb5c41035aec8f17 100644
--- a/test/standalone/dirname/touch.zig
+++ b/test/standalone/dirname/touch.zig
@@ -8,16 +8,8 @@
const std = @import("std");
-pub fn main() !void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- const arena = arena_state.allocator();
- defer arena_state.deinit();
-
- try run(arena);
-}
-
-fn run(allocator: std.mem.Allocator) !void {
- var args = try std.process.argsWithAllocator(allocator);
+pub fn main(init: std.process.Init) !void {
+ var args = try init.minimal.args.iterateAllocator(init.gpa);
defer args.deinit();
_ = args.next() orelse unreachable; // skip binary name
diff --git a/test/standalone/empty_env/build.zig b/test/standalone/empty_env/build.zig
index 0eb8087ed2b9f2df6494113feb16812602ab4b01..97f77b9e3ed30f8fee19cac3c9f0fe4a1b0a1cfe 100644
--- a/test/standalone/empty_env/build.zig
+++ b/test/standalone/empty_env/build.zig
@@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void {
const optimize: std.builtin.OptimizeMode = .Debug;
- if (builtin.os.tag == .windows and std.process.hasEnvVarConstant("ConEmuHWND")) {
+ if (builtin.os.tag == .windows and b.graph.environ_map.contains("ConEmuHWND")) {
// ConEmu injects environment variables into processes before they are executed
// depending on user settings. This obviously invalidates the test, so skipping
// it is the best option.
diff --git a/test/standalone/empty_env/main.zig b/test/standalone/empty_env/main.zig
index 1dc435d9fa90d3e9b046d79224617d887a2ac31d..170a46de9e28856fc4d257df5f721bef8db9da1c 100644
--- a/test/standalone/empty_env/main.zig
+++ b/test/standalone/empty_env/main.zig
@@ -1,8 +1,5 @@
const std = @import("std");
-pub fn main() !void {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- defer _ = gpa.deinit();
- const env_map = std.process.getEnvMap(gpa.allocator()) catch @panic("unable to get env map");
- try std.testing.expect(env_map.count() == 0);
+pub fn main(init: std.process.Init) !void {
+ try std.testing.expectEqual(0, init.environ_map.count());
}
diff --git a/test/standalone/entry_point/check_differ.zig b/test/standalone/entry_point/check_differ.zig
index 29b333632fcd0f7d823f4b1caf501a2b7e7ca06d..be761d46ed0370f505bb9e86c1ef31324b2234e1 100644
--- a/test/standalone/entry_point/check_differ.zig
+++ b/test/standalone/entry_point/check_differ.zig
@@ -1,13 +1,12 @@
-pub fn main() !void {
- var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
+const std = @import("std");
+
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- const args = try std.process.argsAlloc(arena);
if (args.len != 3) return error.BadUsage; // usage: 'check_differ '
- const io = std.Io.Threaded.global_single_threaded.ioBasic();
-
const contents_1 = try std.Io.Dir.cwd().readFileAlloc(io, args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty
const contents_2 = try std.Io.Dir.cwd().readFileAlloc(io, args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty
@@ -16,4 +15,3 @@ pub fn main() !void {
}
// success, files differ
}
-const std = @import("std");
diff --git a/test/standalone/env_vars/main.zig b/test/standalone/env_vars/main.zig
index b85105642e1da6a5530246195658f04189e027a2..09167f285fe9960fbedb8f9fcb179bc354ca15f4 100644
--- a/test/standalone/env_vars/main.zig
+++ b/test/standalone/env_vars/main.zig
@@ -2,174 +2,150 @@ const std = @import("std");
const builtin = @import("builtin");
// Note: the environment variables under test are set by the build.zig
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
@setEvalBranchQuota(10000);
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- defer _ = gpa.deinit();
- const allocator = gpa.allocator();
+ const allocator = init.gpa;
+ const arena = init.arena.allocator();
+ const environ = init.minimal.environ;
- var arena_state = std.heap.ArenaAllocator.init(allocator);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
-
- // hasNonEmptyEnvVar
+ // containsUnempty
{
- try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "FOO"));
- try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "FOO=")));
- try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "FO")));
- try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "FOOO")));
+ try std.testing.expect(try environ.containsUnempty(allocator, "FOO"));
+ try std.testing.expect(!(try environ.containsUnempty(allocator, "FOO=")));
+ try std.testing.expect(!(try environ.containsUnempty(allocator, "FO")));
+ try std.testing.expect(!(try environ.containsUnempty(allocator, "FOOO")));
if (builtin.os.tag == .windows) {
- try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "foo"));
+ try std.testing.expect(try environ.containsUnempty(allocator, "foo"));
}
- try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "EQUALS"));
- try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "EQUALS=ABC")));
- try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "КИРиллИЦА"));
+ try std.testing.expect(try environ.containsUnempty(allocator, "EQUALS"));
+ try std.testing.expect(!(try environ.containsUnempty(allocator, "EQUALS=ABC")));
+ try std.testing.expect(try environ.containsUnempty(allocator, "КИРиллИЦА"));
if (builtin.os.tag == .windows) {
- try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "кирИЛЛица"));
+ try std.testing.expect(try environ.containsUnempty(allocator, "кирИЛЛица"));
}
- try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "NO_VALUE")));
- try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "NOT_SET")));
+ try std.testing.expect(!(try environ.containsUnempty(allocator, "NO_VALUE")));
+ try std.testing.expect(!(try environ.containsUnempty(allocator, "NOT_SET")));
if (builtin.os.tag == .windows) {
- try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "=HIDDEN"));
- try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "INVALID_UTF16_\xed\xa0\x80"));
+ try std.testing.expect(try environ.containsUnempty(allocator, "=HIDDEN"));
+ try std.testing.expect(try environ.containsUnempty(allocator, "INVALID_UTF16_\xed\xa0\x80"));
}
}
- // hasNonEmptyEnvVarContstant
+ // containsUnemptyConstant
{
- try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("FOO"));
- try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("FOO="));
- try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("FO"));
- try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("FOOO"));
+ try std.testing.expect(environ.containsUnemptyConstant("FOO"));
+ try std.testing.expect(!environ.containsUnemptyConstant("FOO="));
+ try std.testing.expect(!environ.containsUnemptyConstant("FO"));
+ try std.testing.expect(!environ.containsUnemptyConstant("FOOO"));
if (builtin.os.tag == .windows) {
- try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("foo"));
+ try std.testing.expect(environ.containsUnemptyConstant("foo"));
}
- try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("EQUALS"));
- try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("EQUALS=ABC"));
- try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("КИРиллИЦА"));
+ try std.testing.expect(environ.containsUnemptyConstant("EQUALS"));
+ try std.testing.expect(!environ.containsUnemptyConstant("EQUALS=ABC"));
+ try std.testing.expect(environ.containsUnemptyConstant("КИРиллИЦА"));
if (builtin.os.tag == .windows) {
- try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("кирИЛЛица"));
+ try std.testing.expect(environ.containsUnemptyConstant("кирИЛЛица"));
}
- try std.testing.expect(!(std.process.hasNonEmptyEnvVarConstant("NO_VALUE")));
- try std.testing.expect(!(std.process.hasNonEmptyEnvVarConstant("NOT_SET")));
+ try std.testing.expect(!(environ.containsUnemptyConstant("NO_VALUE")));
+ try std.testing.expect(!(environ.containsUnemptyConstant("NOT_SET")));
if (builtin.os.tag == .windows) {
- try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("=HIDDEN"));
- try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("INVALID_UTF16_\xed\xa0\x80"));
+ try std.testing.expect(environ.containsUnemptyConstant("=HIDDEN"));
+ try std.testing.expect(environ.containsUnemptyConstant("INVALID_UTF16_\xed\xa0\x80"));
}
}
- // hasEnvVar
+ // contains
{
- try std.testing.expect(try std.process.hasEnvVar(allocator, "FOO"));
- try std.testing.expect(!(try std.process.hasEnvVar(allocator, "FOO=")));
- try std.testing.expect(!(try std.process.hasEnvVar(allocator, "FO")));
- try std.testing.expect(!(try std.process.hasEnvVar(allocator, "FOOO")));
+ try std.testing.expect(try environ.contains(allocator, "FOO"));
+ try std.testing.expect(!(try environ.contains(allocator, "FOO=")));
+ try std.testing.expect(!(try environ.contains(allocator, "FO")));
+ try std.testing.expect(!(try environ.contains(allocator, "FOOO")));
if (builtin.os.tag == .windows) {
- try std.testing.expect(try std.process.hasEnvVar(allocator, "foo"));
+ try std.testing.expect(try environ.contains(allocator, "foo"));
}
- try std.testing.expect(try std.process.hasEnvVar(allocator, "EQUALS"));
- try std.testing.expect(!(try std.process.hasEnvVar(allocator, "EQUALS=ABC")));
- try std.testing.expect(try std.process.hasEnvVar(allocator, "КИРиллИЦА"));
+ try std.testing.expect(try environ.contains(allocator, "EQUALS"));
+ try std.testing.expect(!(try environ.contains(allocator, "EQUALS=ABC")));
+ try std.testing.expect(try environ.contains(allocator, "КИРиллИЦА"));
if (builtin.os.tag == .windows) {
- try std.testing.expect(try std.process.hasEnvVar(allocator, "кирИЛЛица"));
+ try std.testing.expect(try environ.contains(allocator, "кирИЛЛица"));
}
- try std.testing.expect(try std.process.hasEnvVar(allocator, "NO_VALUE"));
- try std.testing.expect(!(try std.process.hasEnvVar(allocator, "NOT_SET")));
+ try std.testing.expect(try environ.contains(allocator, "NO_VALUE"));
+ try std.testing.expect(!(try environ.contains(allocator, "NOT_SET")));
if (builtin.os.tag == .windows) {
- try std.testing.expect(try std.process.hasEnvVar(allocator, "=HIDDEN"));
- try std.testing.expect(try std.process.hasEnvVar(allocator, "INVALID_UTF16_\xed\xa0\x80"));
+ try std.testing.expect(try environ.contains(allocator, "=HIDDEN"));
+ try std.testing.expect(try environ.contains(allocator, "INVALID_UTF16_\xed\xa0\x80"));
}
}
- // hasEnvVarConstant
+ // containsConstant
{
- try std.testing.expect(std.process.hasEnvVarConstant("FOO"));
- try std.testing.expect(!std.process.hasEnvVarConstant("FOO="));
- try std.testing.expect(!std.process.hasEnvVarConstant("FO"));
- try std.testing.expect(!std.process.hasEnvVarConstant("FOOO"));
+ try std.testing.expect(environ.containsConstant("FOO"));
+ try std.testing.expect(!environ.containsConstant("FOO="));
+ try std.testing.expect(!environ.containsConstant("FO"));
+ try std.testing.expect(!environ.containsConstant("FOOO"));
if (builtin.os.tag == .windows) {
- try std.testing.expect(std.process.hasEnvVarConstant("foo"));
+ try std.testing.expect(environ.containsConstant("foo"));
}
- try std.testing.expect(std.process.hasEnvVarConstant("EQUALS"));
- try std.testing.expect(!std.process.hasEnvVarConstant("EQUALS=ABC"));
- try std.testing.expect(std.process.hasEnvVarConstant("КИРиллИЦА"));
+ try std.testing.expect(environ.containsConstant("EQUALS"));
+ try std.testing.expect(!environ.containsConstant("EQUALS=ABC"));
+ try std.testing.expect(environ.containsConstant("КИРиллИЦА"));
if (builtin.os.tag == .windows) {
- try std.testing.expect(std.process.hasEnvVarConstant("кирИЛЛица"));
+ try std.testing.expect(environ.containsConstant("кирИЛЛица"));
}
- try std.testing.expect(std.process.hasEnvVarConstant("NO_VALUE"));
- try std.testing.expect(!(std.process.hasEnvVarConstant("NOT_SET")));
+ try std.testing.expect(environ.containsConstant("NO_VALUE"));
+ try std.testing.expect(!(environ.containsConstant("NOT_SET")));
if (builtin.os.tag == .windows) {
- try std.testing.expect(std.process.hasEnvVarConstant("=HIDDEN"));
- try std.testing.expect(std.process.hasEnvVarConstant("INVALID_UTF16_\xed\xa0\x80"));
+ try std.testing.expect(environ.containsConstant("=HIDDEN"));
+ try std.testing.expect(environ.containsConstant("INVALID_UTF16_\xed\xa0\x80"));
}
}
- // getEnvVarOwned
+ // getAlloc
{
- try std.testing.expectEqualSlices(u8, "123", try std.process.getEnvVarOwned(arena, "FOO"));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "FOO="));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "FO"));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "FOOO"));
- if (builtin.os.tag == .windows) {
- try std.testing.expectEqualSlices(u8, "123", try std.process.getEnvVarOwned(arena, "foo"));
- }
- try std.testing.expectEqualSlices(u8, "ABC=123", try std.process.getEnvVarOwned(arena, "EQUALS"));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "EQUALS=ABC"));
- try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try std.process.getEnvVarOwned(arena, "КИРиллИЦА"));
- if (builtin.os.tag == .windows) {
- try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try std.process.getEnvVarOwned(arena, "кирИЛЛица"));
- }
- try std.testing.expectEqualSlices(u8, "", try std.process.getEnvVarOwned(arena, "NO_VALUE"));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "NOT_SET"));
+ try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "FOO"));
+ try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FOO="));
+ try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FO"));
+ try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FOOO"));
if (builtin.os.tag == .windows) {
- try std.testing.expectEqualSlices(u8, "hi", try std.process.getEnvVarOwned(arena, "=HIDDEN"));
- try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", try std.process.getEnvVarOwned(arena, "INVALID_UTF16_\xed\xa0\x80"));
+ try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "foo"));
}
- }
-
- // parseEnvVarInt
- {
- try std.testing.expectEqual(123, try std.process.parseEnvVarInt("FOO", u32, 10));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("FO", u32, 10));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("FOOO", u32, 10));
- try std.testing.expectEqual(0x123, try std.process.parseEnvVarInt("FOO", u32, 16));
+ try std.testing.expectEqualSlices(u8, "ABC=123", try environ.getAlloc(arena, "EQUALS"));
+ try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "EQUALS=ABC"));
+ try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "КИРиллИЦА"));
if (builtin.os.tag == .windows) {
- try std.testing.expectEqual(123, try std.process.parseEnvVarInt("foo", u32, 10));
+ try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "кирИЛЛица"));
}
- try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("EQUALS", u32, 10));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("EQUALS=ABC", u32, 10));
- try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("КИРиллИЦА", u32, 10));
- try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("NO_VALUE", u32, 10));
- try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("NOT_SET", u32, 10));
+ try std.testing.expectEqualSlices(u8, "", try environ.getAlloc(arena, "NO_VALUE"));
+ try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "NOT_SET"));
if (builtin.os.tag == .windows) {
- try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("=HIDDEN", u32, 10));
- try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("INVALID_UTF16_\xed\xa0\x80", u32, 10));
+ try std.testing.expectEqualSlices(u8, "hi", try environ.getAlloc(arena, "=HIDDEN"));
+ try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", try environ.getAlloc(arena, "INVALID_UTF16_\xed\xa0\x80"));
}
}
- // EnvMap
+ // Environ.Map
{
- var env_map = try std.process.getEnvMap(allocator);
- defer env_map.deinit();
+ var environ_map = try environ.createMap(allocator);
+ defer environ_map.deinit();
- try std.testing.expectEqualSlices(u8, "123", env_map.get("FOO").?);
- try std.testing.expectEqual(null, env_map.get("FO"));
- try std.testing.expectEqual(null, env_map.get("FOOO"));
+ try std.testing.expectEqualSlices(u8, "123", environ_map.get("FOO").?);
+ try std.testing.expectEqual(null, environ_map.get("FO"));
+ try std.testing.expectEqual(null, environ_map.get("FOOO"));
if (builtin.os.tag == .windows) {
- try std.testing.expectEqualSlices(u8, "123", env_map.get("foo").?);
+ try std.testing.expectEqualSlices(u8, "123", environ_map.get("foo").?);
}
- try std.testing.expectEqualSlices(u8, "ABC=123", env_map.get("EQUALS").?);
- try std.testing.expectEqual(null, env_map.get("EQUALS=ABC"));
- try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", env_map.get("КИРиллИЦА").?);
+ try std.testing.expectEqualSlices(u8, "ABC=123", environ_map.get("EQUALS").?);
+ try std.testing.expectEqual(null, environ_map.get("EQUALS=ABC"));
+ try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("КИРиллИЦА").?);
if (builtin.os.tag == .windows) {
- try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", env_map.get("кирИЛЛица").?);
+ try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("кирИЛЛица").?);
}
- try std.testing.expectEqualSlices(u8, "", env_map.get("NO_VALUE").?);
- try std.testing.expectEqual(null, env_map.get("NOT_SET"));
+ try std.testing.expectEqualSlices(u8, "", environ_map.get("NO_VALUE").?);
+ try std.testing.expectEqual(null, environ_map.get("NOT_SET"));
if (builtin.os.tag == .windows) {
- try std.testing.expectEqualSlices(u8, "hi", env_map.get("=HIDDEN").?);
- try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", env_map.get("INVALID_UTF16_\xed\xa0\x80").?);
+ try std.testing.expectEqualSlices(u8, "hi", environ_map.get("=HIDDEN").?);
+ try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", environ_map.get("INVALID_UTF16_\xed\xa0\x80").?);
}
}
}
diff --git a/test/standalone/install_headers/check_exists.zig b/test/standalone/install_headers/check_exists.zig
index 50ad4d08188c5d333411f2c7720b72695c74a40f..ac7dc62592da91dea3bf4582ef3da04f0c268853 100644
--- a/test/standalone/install_headers/check_exists.zig
+++ b/test/standalone/install_headers/check_exists.zig
@@ -2,17 +2,13 @@ const std = @import("std");
/// Checks the existence of files relative to cwd.
/// A path starting with ! should not exist.
-pub fn main() !void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_state.deinit();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
- const arena = arena_state.allocator();
-
- var arg_it = try std.process.argsWithAllocator(arena);
+ var arg_it = try init.minimal.args.iterateAllocator(arena);
_ = arg_it.next();
- const io = std.Io.Threaded.global_single_threaded.ioBasic();
-
const cwd = std.Io.Dir.cwd();
const cwd_realpath = try cwd.realPathFileAlloc(io, ".", arena);
diff --git a/test/standalone/libfuzzer/main.zig b/test/standalone/libfuzzer/main.zig
index 0bc093d8705ba63221cd87114931a0fc3e8f0bbc..836f25285b45d89f58b2c924ee0621cc65243e6e 100644
--- a/test/standalone/libfuzzer/main.zig
+++ b/test/standalone/libfuzzer/main.zig
@@ -6,19 +6,14 @@ fn testOne(in: abi.Slice) callconv(.c) void {
std.debug.assertReadable(in.toSlice());
}
-pub fn main() !void {
- var debug_gpa_ctx: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_gpa_ctx.deinit();
- const gpa = debug_gpa_ctx.allocator();
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const io = init.io;
- var args = try std.process.argsWithAllocator(gpa);
+ var args = try init.minimal.args.iterateAllocator(gpa);
defer args.deinit();
_ = args.skip(); // executable name
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
const cache_dir_path = args.next() orelse @panic("expected cache directory path argument");
var cache_dir = try std.Io.Dir.cwd().openDir(io, cache_dir_path, .{});
defer cache_dir.close(io);
diff --git a/test/standalone/load_dynamic_library/main.zig b/test/standalone/load_dynamic_library/main.zig
index b9d2340d4c745450d362b754b360e7bb071d2807..ac64e67cdaddf18d0904f5ecff78306542930fef 100644
--- a/test/standalone/load_dynamic_library/main.zig
+++ b/test/standalone/load_dynamic_library/main.zig
@@ -1,10 +1,7 @@
const std = @import("std");
-pub fn main() !void {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- defer _ = gpa.deinit();
- const args = try std.process.argsAlloc(gpa.allocator());
- defer std.process.argsFree(gpa.allocator(), args);
+pub fn main(init: std.process.Init) !void {
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
const dynlib_name = args[1];
diff --git a/test/standalone/posix/cwd.zig b/test/standalone/posix/cwd.zig
index 3bd1ac066ae48bf8592227ee05fee20dea78cb36..d43713247595bf1a2206a6a84e5f37b52bfc6af3 100644
--- a/test/standalone/posix/cwd.zig
+++ b/test/standalone/posix/cwd.zig
@@ -7,24 +7,16 @@ const assert = std.debug.assert;
const path_max = std.fs.max_path_bytes;
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
switch (builtin.target.os.tag) {
.wasi => return, // WASI doesn't support changing the working directory at all.
.windows => return, // POSIX is not implemented by Windows
else => {},
}
- var debug_allocator: std.heap.DebugAllocator(.{}) = .{};
- defer assert(debug_allocator.deinit() == .ok);
- const gpa = debug_allocator.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
try test_chdir_self();
try test_chdir_absolute();
- try test_chdir_relative(gpa, io);
+ try test_chdir_relative(init.gpa, init.io);
}
// get current working directory and expect it to match given path
@@ -39,7 +31,7 @@ fn test_chdir_self() !void {
const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]);
// Try changing to the current directory
- try std.posix.chdir(old_cwd);
+ try std.Io.Threaded.chdir(old_cwd);
try expect_cwd(old_cwd);
}
@@ -50,7 +42,7 @@ fn test_chdir_absolute() !void {
const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
// Try changing to the parent via a full path
- try std.posix.chdir(parent);
+ try std.Io.Threaded.chdir(parent);
try expect_cwd(parent);
}
@@ -71,7 +63,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io) !void {
defer gpa.free(expected_path);
// change current working directory to new test directory
- try std.posix.chdir(relative_dir_name);
+ try std.Io.Threaded.chdir(relative_dir_name);
var new_cwd_buf: [path_max]u8 = undefined;
const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]);
diff --git a/test/standalone/posix/getenv.zig b/test/standalone/posix/getenv.zig
index 8b28d09ae76d0d91cf60a41a6f0a4d6a94083378..62c348e085b93dfee378b84953096879cbc575cc 100644
--- a/test/standalone/posix/getenv.zig
+++ b/test/standalone/posix/getenv.zig
@@ -1,32 +1,28 @@
-// test getting environment variables
+//! test getting environment variables
const std = @import("std");
const builtin = @import("builtin");
-pub fn main() !void {
- if (builtin.target.os.tag == .windows) {
- return; // Windows env strings are WTF-16, so not supported by Zig's std.posix.getenv()
- }
+pub fn main(init: std.process.Init.Minimal) !void {
+ if (builtin.target.os.tag == .windows) return;
+ if (builtin.target.os.tag == .wasi and !builtin.link_libc) return;
- if (builtin.target.os.tag == .wasi and !builtin.link_libc) {
- return; // std.posix.getenv is not supported on WASI due to the need of allocation
- }
+ const environ = init.environ;
// Test some unset env vars:
-
- try std.testing.expectEqual(std.posix.getenv(""), null);
- try std.testing.expectEqual(std.posix.getenv("BOGUSDOESNOTEXISTENVVAR"), null);
- try std.testing.expectEqual(std.posix.getenvZ("BOGUSDOESNOTEXISTENVVAR"), null);
+ try std.testing.expectEqual(environ.getPosix(""), null);
+ try std.testing.expectEqual(environ.getPosix("BOGUSDOESNOTEXISTENVVAR"), null);
+ try std.testing.expectEqual(environ.getPosix("BOGUSDOESNOTEXISTENVVAR"), null);
if (builtin.link_libc) {
// Test if USER matches what C library sees
const expected = std.mem.span(std.c.getenv("USER") orelse "");
- const actual = std.posix.getenv("USER") orelse "";
+ const actual = environ.getPosix("USER") orelse "";
try std.testing.expectEqualStrings(expected, actual);
}
// env vars set by our build.zig run step:
- try std.testing.expectEqualStrings("", std.posix.getenv("ZIG_TEST_POSIX_EMPTY") orelse "invalid");
- try std.testing.expectEqualStrings("test=variable", std.posix.getenv("ZIG_TEST_POSIX_1EQ") orelse "invalid");
- try std.testing.expectEqualStrings("=test=variable=", std.posix.getenv("ZIG_TEST_POSIX_3EQ") orelse "invalid");
+ try std.testing.expectEqualStrings("", environ.getPosix("ZIG_TEST_POSIX_EMPTY") orelse "invalid");
+ try std.testing.expectEqualStrings("test=variable", environ.getPosix("ZIG_TEST_POSIX_1EQ") orelse "invalid");
+ try std.testing.expectEqualStrings("=test=variable=", environ.getPosix("ZIG_TEST_POSIX_3EQ") orelse "invalid");
}
diff --git a/test/standalone/posix/relpaths.zig b/test/standalone/posix/relpaths.zig
index b5b2bb5f61876a75213f8b734379977e1259d21a..e31e497875d4d9b556a9680fcdc017b85f7e5b34 100644
--- a/test/standalone/posix/relpaths.zig
+++ b/test/standalone/posix/relpaths.zig
@@ -6,16 +6,10 @@ const builtin = @import("builtin");
const std = @import("std");
const Io = std.Io;
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- const gpa = debug_allocator.allocator();
- defer std.debug.assert(debug_allocator.deinit() == .ok);
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
+ const io = init.io;
var tmp = tmpDir(io, .{});
defer tmp.cleanup(io);
diff --git a/test/standalone/run_cwd/check_file_exists.zig b/test/standalone/run_cwd/check_file_exists.zig
index a885c7dafde7c501ba794ecbe4b377aa61eab705..bd4a26b6668cdd328b86ebe43fd56f280e59b0f7 100644
--- a/test/standalone/run_cwd/check_file_exists.zig
+++ b/test/standalone/run_cwd/check_file_exists.zig
@@ -1,9 +1,6 @@
-pub fn main() !void {
- var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
-
- const args = try std.process.argsAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
if (args.len != 2) return error.BadUsage;
const path = args[1];
diff --git a/test/standalone/run_output_caching/main.zig b/test/standalone/run_output_caching/main.zig
index 9786101d325ebf6098dc4416e47257279febdf05..8a9855f5ecef4709e02b20a6b302370562895224 100644
--- a/test/standalone/run_output_caching/main.zig
+++ b/test/standalone/run_output_caching/main.zig
@@ -1,8 +1,8 @@
const std = @import("std");
-pub fn main() !void {
- const io = std.Io.Threaded.global_single_threaded.ioBasic();
- var args = try std.process.argsWithAllocator(std.heap.page_allocator);
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ var args = try init.minimal.args.iterateAllocator(init.arena.allocator());
_ = args.skip();
const filename = args.next().?;
const file = try std.Io.Dir.cwd().createFile(io, filename, .{});
diff --git a/test/standalone/run_output_paths/create_file.zig b/test/standalone/run_output_paths/create_file.zig
index 7efa8e051a29d0f0e380fc092827859090fa4b5b..8490c1c5e9e9241a88035f2c2d8502bf7a370a3c 100644
--- a/test/standalone/run_output_paths/create_file.zig
+++ b/test/standalone/run_output_paths/create_file.zig
@@ -1,8 +1,8 @@
const std = @import("std");
-pub fn main() !void {
- const io = std.Io.Threaded.global_single_threaded.ioBasic();
- var args = try std.process.argsWithAllocator(std.heap.page_allocator);
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ var args = try init.minimal.args.iterateAllocator(init.arena.allocator());
_ = args.skip();
const dir_name = args.next().?;
const dir = try std.Io.Dir.cwd().openDir(io, if (std.mem.startsWith(u8, dir_name, "--dir="))
diff --git a/test/standalone/self_exe_symlink/create-symlink.zig b/test/standalone/self_exe_symlink/create-symlink.zig
index d725207320811408d453104f022b4c6409b67681..32610ebcde6d07a4d1ef14b1dca4fab3dd3bd90b 100644
--- a/test/standalone/self_exe_symlink/create-symlink.zig
+++ b/test/standalone/self_exe_symlink/create-symlink.zig
@@ -1,21 +1,19 @@
const std = @import("std");
-pub fn main() anyerror!void {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- defer if (gpa.deinit() == .leak) @panic("found memory leaks");
- const allocator = gpa.allocator();
-
- var it = try std.process.argsWithAllocator(allocator);
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const gpa = init.gpa;
+ var it = try init.minimal.args.iterateAllocator(gpa);
defer it.deinit();
_ = it.next() orelse unreachable; // skip binary name
const exe_path = it.next() orelse unreachable;
const symlink_path = it.next() orelse unreachable;
+ const cwd = try std.process.getCwdAlloc(init.arena.allocator());
+
// If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`.
- const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path);
- defer allocator.free(exe_rel_path);
-
- const io = std.Io.Threaded.global_single_threaded.ioBasic();
+ const exe_rel_path = try std.fs.path.relative(gpa, cwd, init.environ_map, std.fs.path.dirname(symlink_path) orelse ".", exe_path);
+ defer gpa.free(exe_rel_path);
try std.Io.Dir.cwd().symLink(io, exe_rel_path, symlink_path, .{});
}
diff --git a/test/standalone/self_exe_symlink/main.zig b/test/standalone/self_exe_symlink/main.zig
index fa2c3380b580548d01bf00ba9274a0e7db46cdd3..0b1704b18a0463d3e012fc212be931be058a8cf3 100644
--- a/test/standalone/self_exe_symlink/main.zig
+++ b/test/standalone/self_exe_symlink/main.zig
@@ -1,13 +1,8 @@
const std = @import("std");
-pub fn main() !void {
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks");
- const gpa = debug_allocator.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const io = init.io;
const self_path = try std.process.executablePathAlloc(io, gpa);
defer gpa.free(self_path);
diff --git a/test/standalone/simple/cat/main.zig b/test/standalone/simple/cat/main.zig
index 0135ac4b50943e5fc4c2fe136ea611fc20bc70cf..bc894d3881fd59658dc17ca65afc3dd934226560 100644
--- a/test/standalone/simple/cat/main.zig
+++ b/test/standalone/simple/cat/main.zig
@@ -4,16 +4,10 @@ const mem = std.mem;
const warn = std.log.warn;
const fatal = std.process.fatal;
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var threaded: std.Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
const exe = args[0];
var catted_anything = false;
diff --git a/test/standalone/simple/guess_number/main.zig b/test/standalone/simple/guess_number/main.zig
index b98d109f211f2d8f26761e6a0726cab4efdfb469..e7b30867ec6ec5675822915750cd2c236653a26e 100644
--- a/test/standalone/simple/guess_number/main.zig
+++ b/test/standalone/simple/guess_number/main.zig
@@ -1,22 +1,11 @@
-const builtin = @import("builtin");
const std = @import("std");
-// See https://github.com/ziglang/zig/issues/24510
-// for the plan to simplify this code.
-pub fn main() !void {
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_allocator.deinit();
- const gpa = debug_allocator.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{});
+pub fn main(init: std.process.Init) !void {
+ var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
const out = &stdout_writer.interface;
var line_buffer: [20]u8 = undefined;
- var stdin_reader: std.Io.File.Reader = .init(.stdin(), io, &line_buffer);
+ var stdin_reader: std.Io.File.Reader = .init(.stdin(), init.io, &line_buffer);
const in = &stdin_reader.interface;
try out.writeAll("Welcome to the Guess Number Game in Zig.\n");
diff --git a/test/standalone/simple/hello_world/hello.zig b/test/standalone/simple/hello_world/hello.zig
index a031d6c6f0482bce684392fda834c0784212801b..27cedd428ee7bc8dad67c216878f84ad963c28f8 100644
--- a/test/standalone/simple/hello_world/hello.zig
+++ b/test/standalone/simple/hello_world/hello.zig
@@ -1,15 +1,5 @@
const std = @import("std");
-// See https://github.com/ziglang/zig/issues/24510
-// for the plan to simplify this code.
-pub fn main() !void {
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_allocator.deinit();
- const gpa = debug_allocator.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
+pub fn main(init: std.process.Init) !void {
+ try std.Io.File.stdout().writeStreamingAll(init.io, "Hello, World!\n");
}
diff --git a/test/standalone/windows_argv/build.zig b/test/standalone/windows_argv/build.zig
index afe6dd80e5da2a950a317aa13b0b290c88a14781..9b507e978a1d53665800f9c89830690943c26270 100644
--- a/test/standalone/windows_argv/build.zig
+++ b/test/standalone/windows_argv/build.zig
@@ -67,7 +67,7 @@ pub fn build(b: *std.Build) !void {
// Only target the MSVC ABI if MSVC/Windows SDK is available
const has_msvc = has_msvc: {
- const sdk = std.zig.WindowsSdk.find(b.allocator, b.graph.io, builtin.cpu.arch) catch |err| switch (err) {
+ const sdk = std.zig.WindowsSdk.find(b.allocator, b.graph.io, builtin.cpu.arch, &b.graph.environ_map) catch |err| switch (err) {
error.OutOfMemory => @panic("oom"),
else => break :has_msvc false,
};
diff --git a/test/standalone/windows_argv/fuzz.zig b/test/standalone/windows_argv/fuzz.zig
index bdf40aa4b1d3991b24abde4aa710d10c07a92ee5..7e7f43a6af0742221f5da394f1be1bda90cb0ce8 100644
--- a/test/standalone/windows_argv/fuzz.zig
+++ b/test/standalone/windows_argv/fuzz.zig
@@ -3,19 +3,15 @@ const builtin = @import("builtin");
const windows = std.os.windows;
const Allocator = std.mem.Allocator;
-pub fn main() !void {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- defer std.debug.assert(gpa.deinit() == .ok);
- const allocator = gpa.allocator();
-
- const args = try std.process.argsAlloc(allocator);
- defer std.process.argsFree(allocator, args);
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
if (args.len < 2) return error.MissingArgs;
const verify_path_wtf8 = args[1];
- const verify_path_w = try std.unicode.wtf8ToWtf16LeAllocZ(allocator, verify_path_wtf8);
- defer allocator.free(verify_path_w);
+ const verify_path_w = try std.unicode.wtf8ToWtf16LeAllocZ(gpa, verify_path_wtf8);
+ defer gpa.free(verify_path_w);
const iterations: u64 = iterations: {
if (args.len < 3) break :iterations 0;
@@ -41,14 +37,14 @@ pub fn main() !void {
std.debug.print("rand seed: {}\n", .{seed});
}
- var cmd_line_w_buf = std.array_list.Managed(u16).init(allocator);
+ var cmd_line_w_buf = std.array_list.Managed(u16).init(gpa);
defer cmd_line_w_buf.deinit();
var i: u64 = 0;
var errors: u64 = 0;
while (iterations == 0 or i < iterations) {
- const cmd_line_w = try randomCommandLineW(allocator, rand);
- defer allocator.free(cmd_line_w);
+ const cmd_line_w = try randomCommandLineW(gpa, rand);
+ defer gpa.free(cmd_line_w);
// avoid known difference for 0-length command lines
if (cmd_line_w.len == 0 or cmd_line_w[0] == '\x00') continue;
@@ -56,8 +52,8 @@ pub fn main() !void {
const exit_code = try spawnVerify(verify_path_w, cmd_line_w);
if (exit_code != 0) {
std.debug.print(">>> found discrepancy <<<\n", .{});
- const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, cmd_line_w);
- defer allocator.free(cmd_line_wtf8);
+ const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(gpa, cmd_line_w);
+ defer gpa.free(cmd_line_wtf8);
std.debug.print("\"{f}\"\n\n", .{std.zig.fmtString(cmd_line_wtf8)});
errors += 1;
diff --git a/test/standalone/windows_argv/lib.zig b/test/standalone/windows_argv/lib.zig
index 4171d13dab40f06b0308bf7c7f0a3ceb8eb88293..750501edd21539e5eebe51848aaabd3ace812f59 100644
--- a/test/standalone/windows_argv/lib.zig
+++ b/test/standalone/windows_argv/lib.zig
@@ -5,7 +5,7 @@ export fn verify(argc: c_int, argv: [*]const [*:0]const u16) c_int {
const argv_slice = argv[0..@intCast(argc)];
testArgv(argv_slice) catch |err| switch (err) {
error.OutOfMemory => @panic("oom"),
- error.Overflow => @panic("bytes needed to contain args would overflow usize"),
+ error.Unexpected => @panic("unexpected error"),
error.ArgvMismatch => return 0,
};
return 1;
@@ -16,7 +16,10 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {
defer arena_state.deinit();
const allocator = arena_state.allocator();
- const args = try std.process.argsAlloc(allocator);
+ const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
+ const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
+ const raw_args: std.process.Args = .{ .vector = cmd_line_w };
+ const args = try raw_args.toSlice(allocator);
var wtf8_buf = std.array_list.Managed(u8).init(allocator);
var eql = true;
diff --git a/test/standalone/windows_bat_args/echo-args.zig b/test/standalone/windows_bat_args/echo-args.zig
index 6aeb43d56ca6fb70b5d6d9b8a918c943d66cda9a..a867a1bb2482ac376412958c6f052026653c2dc3 100644
--- a/test/standalone/windows_bat_args/echo-args.zig
+++ b/test/standalone/windows_bat_args/echo-args.zig
@@ -1,15 +1,12 @@
const std = @import("std");
-pub fn main() !void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
-
- const io = std.Options.debug_io;
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{});
const stdout = &stdout_writer.interface;
- var args = try std.process.argsAlloc(arena);
for (args[1..], 1..) |arg, i| {
try stdout.writeAll(arg);
if (i != args.len - 1) try stdout.writeByte('\x00');
diff --git a/test/standalone/windows_bat_args/fuzz.zig b/test/standalone/windows_bat_args/fuzz.zig
index 28749259f7d832ed5de43ed2ed552adb7ac1a8ad..b82ec8b458e7e589b9d5c5d09d3c84458fe14713 100644
--- a/test/standalone/windows_bat_args/fuzz.zig
+++ b/test/standalone/windows_bat_args/fuzz.zig
@@ -4,16 +4,11 @@ const std = @import("std");
const Io = std.Io;
const Allocator = std.mem.Allocator;
-pub fn main() anyerror!void {
- var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init;
- defer std.debug.assert(debug_alloc_inst.deinit() == .ok);
- const gpa = debug_alloc_inst.allocator();
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const io = init.io;
- var threaded: Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- var it = try std.process.argsWithAllocator(gpa);
+ var it = try init.minimal.args.iterateAllocator(gpa);
defer it.deinit();
_ = it.next() orelse unreachable; // skip binary name
const child_exe_path_orig = it.next() orelse unreachable;
@@ -84,13 +79,13 @@ pub fn main() anyerror!void {
}
}
-fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.EnvMap) !void {
+fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.Environ.Map) !void {
try testExecBat(gpa, io, "args1.bat", args, env);
try testExecBat(gpa, io, "args2.bat", args, env);
try testExecBat(gpa, io, "args3.bat", args, env);
}
-fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void {
+fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.Environ.Map) !void {
const argv = try gpa.alloc([]const u8, 1 + args.len);
defer gpa.free(argv);
argv[0] = bat;
@@ -98,8 +93,8 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8
const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat");
- const result = try std.process.Child.run(gpa, io, .{
- .env_map = env,
+ const result = try std.process.run(gpa, io, .{
+ .environ_map = env,
.argv = argv,
});
defer gpa.free(result.stdout);
diff --git a/test/standalone/windows_bat_args/test.zig b/test/standalone/windows_bat_args/test.zig
index e0d1abe80640bcab15592c416ca973efc5584231..fde627a5fec983005acace78a0c92773ded362bc 100644
--- a/test/standalone/windows_bat_args/test.zig
+++ b/test/standalone/windows_bat_args/test.zig
@@ -2,15 +2,11 @@ const std = @import("std");
const Io = std.Io;
const Allocator = std.mem.Allocator;
-pub fn main() anyerror!void {
- var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init;
- defer std.debug.assert(debug_alloc_inst.deinit() == .ok);
- const gpa = debug_alloc_inst.allocator();
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const io = init.io;
- var threaded: Io.Threaded = .init(gpa, .{});
- const io = threaded.io();
-
- var it = try std.process.argsWithAllocator(gpa);
+ var it = try init.minimal.args.iterateAllocator(gpa);
defer it.deinit();
_ = it.next() orelse unreachable; // skip binary name
const child_exe_path_orig = it.next() orelse unreachable;
@@ -109,7 +105,7 @@ pub fn main() anyerror!void {
try std.testing.expectError(error.FileNotFound, testExecBat(gpa, io, absolute_with_trailing, &.{"abc"}, null));
var env = env: {
- var env = try std.process.getEnvMap(gpa);
+ var env = try init.environ_map.clone(gpa);
errdefer env.deinit();
// No escaping
try env.put("FOO", "123");
@@ -130,13 +126,13 @@ fn testExecError(err: anyerror, gpa: Allocator, io: Io, args: []const []const u8
return std.testing.expectError(err, testExec(gpa, io, args, null));
}
-fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.EnvMap) !void {
+fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.Environ.Map) !void {
try testExecBat(gpa, io, "args1.bat", args, env);
try testExecBat(gpa, io, "args2.bat", args, env);
try testExecBat(gpa, io, "args3.bat", args, env);
}
-fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void {
+fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.Environ.Map) !void {
const argv = try gpa.alloc([]const u8, 1 + args.len);
defer gpa.free(argv);
argv[0] = bat;
@@ -144,8 +140,8 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8
const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat");
- const result = try std.process.Child.run(gpa, io, .{
- .env_map = env,
+ const result = try std.process.run(gpa, io, .{
+ .environ_map = env,
.argv = argv,
});
defer gpa.free(result.stdout);
diff --git a/test/standalone/windows_paths/relative.zig b/test/standalone/windows_paths/relative.zig
index 7b6a51b283a54f6e765900116a9218103727b753..7b3725e4eec0b5e5774f7ac3650321b959193504 100644
--- a/test/standalone/windows_paths/relative.zig
+++ b/test/standalone/windows_paths/relative.zig
@@ -1,21 +1,14 @@
const std = @import("std");
-pub fn main() !void {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- defer std.debug.assert(gpa.deinit() == .ok);
- const allocator = gpa.allocator();
-
- const args = try std.process.argsAlloc(allocator);
- defer std.process.argsFree(allocator, args);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
+ const io = init.io;
+ const cwd_path = try std.process.getCwdAlloc(arena);
if (args.len < 3) return error.MissingArgs;
- var threaded: std.Io.Threaded = .init(allocator, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const relative = try std.fs.path.relative(allocator, args[1], args[2]);
- defer allocator.free(relative);
+ const relative = try std.fs.path.relative(arena, cwd_path, init.environ_map, args[1], args[2]);
var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{});
const stdout = &stdout_writer.interface;
diff --git a/test/standalone/windows_paths/test.zig b/test/standalone/windows_paths/test.zig
index ed4069dc61e6a5121936b34e9628fd21831da3bc..94afe163372b1cfdceaa5bd4ac32908b0c711657 100644
--- a/test/standalone/windows_paths/test.zig
+++ b/test/standalone/windows_paths/test.zig
@@ -1,17 +1,13 @@
const std = @import("std");
const Io = std.Io;
-pub fn main() anyerror!void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
-
- const args = try std.process.argsAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
+ const io = init.io;
if (args.len < 2) return error.MissingArgs;
- const io = std.Io.Threaded.global_single_threaded.ioBasic();
-
const exe_path = args[1];
const cwd_path = try std.process.getCwdAlloc(arena);
@@ -25,10 +21,10 @@ pub fn main() anyerror!void {
const alt_drive_letter = try getAltDriveLetter(cwd_path);
const alt_drive_cwd_key = try std.fmt.allocPrint(arena, "={c}:", .{alt_drive_letter});
const alt_drive_cwd = try std.fmt.allocPrint(arena, "{c}:\\baz", .{alt_drive_letter});
- var alt_drive_env_map = std.process.EnvMap.init(arena);
+ var alt_drive_env_map = std.process.Environ.Map.init(arena);
try alt_drive_env_map.put(alt_drive_cwd_key, alt_drive_cwd);
- const empty_env = std.process.EnvMap.init(arena);
+ const empty_env = std.process.Environ.Map.init(arena);
{
const drive_rel = try std.fmt.allocPrint(arena, "{c}:foo", .{alt_drive_letter});
@@ -96,12 +92,12 @@ fn checkRelative(
expected_stdout: []const u8,
argv: []const []const u8,
cwd: ?[]const u8,
- env_map: ?*const std.process.EnvMap,
+ environ_map: ?*const std.process.Environ.Map,
) !void {
- const result = try std.process.Child.run(allocator, io, .{
+ const result = try std.process.run(allocator, io, .{
.argv = argv,
.cwd = cwd,
- .env_map = env_map,
+ .environ_map = environ_map,
});
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);
diff --git a/test/standalone/windows_spawn/main.zig b/test/standalone/windows_spawn/main.zig
index c9522bf4de301022d4b8e39d0f5e71fb97040ba2..4a64ca48d6260475b927cea0f0f1cf3252f0dbcb 100644
--- a/test/standalone/windows_spawn/main.zig
+++ b/test/standalone/windows_spawn/main.zig
@@ -5,16 +5,12 @@ const Allocator = std.mem.Allocator;
const windows = std.os.windows;
const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
-pub fn main() anyerror!void {
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks");
- const gpa = debug_allocator.allocator();
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const io = init.io;
+ const process_cwd_path = try std.process.getCwdAlloc(init.arena.allocator());
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- var it = try std.process.argsWithAllocator(gpa);
+ var it = try init.minimal.args.iterateAllocator(gpa);
defer it.deinit();
_ = it.next() orelse unreachable; // skip binary name
const hello_exe_cache_path = it.next() orelse unreachable;
@@ -28,7 +24,7 @@ pub fn main() anyerror!void {
defer gpa.free(tmp_absolute_path_w);
const cwd_absolute_path = try Io.Dir.cwd().realPathFileAlloc(io, ".", gpa);
defer gpa.free(cwd_absolute_path);
- const tmp_relative_path = try std.fs.path.relative(gpa, cwd_absolute_path, tmp_absolute_path);
+ const tmp_relative_path = try std.fs.path.relative(gpa, process_cwd_path, init.environ_map, cwd_absolute_path, tmp_absolute_path);
defer gpa.free(tmp_relative_path);
// Clear PATH
@@ -212,7 +208,7 @@ fn testExec(gpa: Allocator, io: Io, command: []const u8, expected_stdout: []cons
}
fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void {
- const result = try std.process.Child.run(gpa, io, .{
+ const result = try std.process.run(gpa, io, .{
.argv = &[_][]const u8{command},
.cwd = cwd,
});
diff --git a/test/tests.zig b/test/tests.zig
index aa3c018a627ce2558374a0e787372b4d2cf0b194..5fc4a967af41d3666230ad0e94db498464e7a302 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -2062,7 +2062,7 @@ pub fn addCliTests(b: *std.Build) *Step {
run_run.setCwd(.{ .cwd_relative = tmp_path });
run_run.setName("zig build run");
run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n");
- run_run.expectStdErrEqual("All your codebase are belong to us.\n");
+ run_run.expectStdErrMatch("All your codebase are belong to us.\n");
run_run.step.dependOn(&init_exe.step);
const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
@@ -2718,7 +2718,7 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons
if (b.enable_wasmtime) run.addArg("-fwasmtime");
if (b.enable_darling) run.addArg("-fdarling");
- run.addCheck(.{ .expect_term = .{ .Exited = 0 } });
+ run.addCheck(.{ .expect_term = .{ .exited = 0 } });
test_step.dependOn(&run.step);
}
diff --git a/tools/docgen.zig b/tools/docgen.zig
index ac0d26b995c9ed8cfb9a2c2f13dff2082550b093..c526233f517c2a570b9f300a240d5a0da3312df9 100644
--- a/tools/docgen.zig
+++ b/tools/docgen.zig
@@ -28,21 +28,13 @@ const usage =
\\
;
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
- const arena = arena_instance.allocator();
-
- var args_it = try process.argsWithAllocator(arena);
+ var args_it = try init.minimal.args.iterateAllocator(arena);
if (!args_it.skip()) @panic("expected self arg");
- const gpa = arena;
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
var opt_code_dir: ?[]const u8 = null;
var opt_input: ?[]const u8 = null;
var opt_output: ?[]const u8 = null;
diff --git a/tools/doctest.zig b/tools/doctest.zig
index 3a67210a592b0daaf471f1853897d3635602b61e..7f4b8fbda803714765b1b616b1e60677ea0911d5 100644
--- a/tools/doctest.zig
+++ b/tools/doctest.zig
@@ -29,21 +29,17 @@ const usage =
\\
;
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const environ_map = init.environ_map;
+ const cwd_path = try std.process.getCwdAlloc(arena);
- const arena = arena_instance.allocator();
+ try environ_map.put("CLICOLOR_FORCE", "1");
- var args_it = try process.argsWithAllocator(arena);
+ var args_it = try init.minimal.args.iterateAllocator(arena);
if (!args_it.skip()) fatal("missing argv[0]", .{});
- const gpa = arena;
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
var opt_input: ?[]const u8 = null;
var opt_output: ?[]const u8 = null;
var opt_zig: ?[]const u8 = null;
@@ -105,12 +101,13 @@ pub fn main() !void {
out,
code,
tmp_dir_path,
- try Dir.path.relative(arena, tmp_dir_path, zig_path),
- try Dir.path.relative(arena, tmp_dir_path, input_path),
+ try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, zig_path),
+ try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, input_path),
if (opt_zig_lib_dir) |zig_lib_dir|
- try Dir.path.relative(arena, tmp_dir_path, zig_lib_dir)
+ try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, zig_lib_dir)
else
null,
+ environ_map,
);
try out_file_writer.end();
@@ -129,10 +126,8 @@ fn printOutput(
input_path: []const u8,
/// Relative to `tmp_dir_path`.
opt_zig_lib_dir: ?[]const u8,
+ environ_map: *const process.Environ.Map,
) !void {
- var env_map = try process.getEnvMap(arena);
- try env_map.put("CLICOLOR_FORCE", "1");
-
const host = try std.zig.system.resolveTargetQuery(io, .{});
const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);
const print = std.debug.print;
@@ -201,14 +196,14 @@ fn printOutput(
try shell_out.print("\n", .{});
if (expected_outcome == .build_fail) {
- const result = try process.Child.run(arena, io, .{
+ const result = try process.run(arena, io, .{
.argv = build_args.items,
.cwd = tmp_dir_path,
- .env_map = &env_map,
+ .environ_map = environ_map,
.max_output_bytes = max_doc_file_size,
});
switch (result.term) {
- .Exited => |exit_code| {
+ .exited => |exit_code| {
if (exit_code == 0) {
print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
dumpArgs(build_args.items);
@@ -226,7 +221,7 @@ fn printOutput(
try shell_out.writeAll(colored_stderr);
break :code_block;
}
- const exec_result = run(arena, io, &env_map, tmp_dir_path, build_args.items) catch
+ const exec_result = run(arena, io, environ_map, tmp_dir_path, build_args.items) catch
fatal("example failed to compile", .{});
if (code.verbose_cimport) {
@@ -257,26 +252,26 @@ fn printOutput(
var exited_with_signal = false;
const result = if (expected_outcome == .fail) blk: {
- const result = try process.Child.run(arena, io, .{
+ const result = try process.run(arena, io, .{
.argv = run_args,
- .env_map = &env_map,
+ .environ_map = environ_map,
.cwd = tmp_dir_path,
.max_output_bytes = max_doc_file_size,
});
switch (result.term) {
- .Exited => |exit_code| {
+ .exited => |exit_code| {
if (exit_code == 0) {
print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
dumpArgs(run_args);
fatal("example incorrectly compiled", .{});
}
},
- .Signal => exited_with_signal = true,
+ .signal => exited_with_signal = true,
else => {},
}
break :blk result;
} else blk: {
- break :blk run(arena, io, &env_map, tmp_dir_path, run_args) catch
+ break :blk run(arena, io, environ_map, tmp_dir_path, run_args) catch
fatal("example crashed", .{});
};
@@ -345,7 +340,7 @@ fn printOutput(
}
}
- const result = run(arena, io, &env_map, tmp_dir_path, test_args.items) catch
+ const result = run(arena, io, environ_map, tmp_dir_path, test_args.items) catch
fatal("test failed", .{});
const escaped_stderr = try escapeHtml(arena, result.stderr);
const escaped_stdout = try escapeHtml(arena, result.stdout);
@@ -376,14 +371,14 @@ fn printOutput(
try test_args.append("-lc");
try shell_out.print("-lc ", .{});
}
- const result = try process.Child.run(arena, io, .{
+ const result = try process.run(arena, io, .{
.argv = test_args.items,
- .env_map = &env_map,
+ .environ_map = environ_map,
.cwd = tmp_dir_path,
.max_output_bytes = max_doc_file_size,
});
switch (result.term) {
- .Exited => |exit_code| {
+ .exited => |exit_code| {
if (exit_code == 0) {
print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
dumpArgs(test_args.items);
@@ -432,14 +427,14 @@ fn printOutput(
},
}
- const result = try process.Child.run(arena, io, .{
+ const result = try process.run(arena, io, .{
.argv = test_args.items,
- .env_map = &env_map,
+ .environ_map = environ_map,
.cwd = tmp_dir_path,
.max_output_bytes = max_doc_file_size,
});
switch (result.term) {
- .Exited => |exit_code| {
+ .exited => |exit_code| {
if (exit_code == 0) {
print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
dumpArgs(test_args.items);
@@ -508,14 +503,14 @@ fn printOutput(
}
if (maybe_error_match) |error_match| {
- const result = try process.Child.run(arena, io, .{
+ const result = try process.run(arena, io, .{
.argv = build_args.items,
- .env_map = &env_map,
+ .environ_map = environ_map,
.cwd = tmp_dir_path,
.max_output_bytes = max_doc_file_size,
});
switch (result.term) {
- .Exited => |exit_code| {
+ .exited => |exit_code| {
if (exit_code == 0) {
print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
dumpArgs(build_args.items);
@@ -536,7 +531,7 @@ fn printOutput(
const colored_stderr = try termColor(arena, escaped_stderr);
try shell_out.print("\n{s} ", .{colored_stderr});
} else {
- _ = run(arena, io, &env_map, tmp_dir_path, build_args.items) catch fatal("example failed to compile", .{});
+ _ = run(arena, io, environ_map, tmp_dir_path, build_args.items) catch fatal("example failed to compile", .{});
}
try shell_out.writeAll("\n");
},
@@ -595,7 +590,7 @@ fn printOutput(
try test_args.append(option);
try shell_out.print("{s} ", .{option});
}
- const result = run(arena, io, &env_map, tmp_dir_path, test_args.items) catch fatal("test failed", .{});
+ const result = run(arena, io, environ_map, tmp_dir_path, test_args.items) catch fatal("test failed", .{});
const escaped_stderr = try escapeHtml(arena, result.stderr);
const escaped_stdout = try escapeHtml(arena, result.stdout);
try shell_out.print("\n{s}{s}\n", .{ escaped_stderr, escaped_stdout });
@@ -1128,24 +1123,29 @@ fn in(slice: []const u8, number: u8) bool {
fn run(
allocator: Allocator,
io: Io,
- env_map: *process.EnvMap,
+ environ_map: *const process.Environ.Map,
cwd: []const u8,
args: []const []const u8,
-) !process.Child.RunResult {
- const result = try process.Child.run(allocator, io, .{
+) !process.RunResult {
+ const result = try process.run(allocator, io, .{
.argv = args,
- .env_map = env_map,
+ .environ_map = environ_map,
.cwd = cwd,
.max_output_bytes = max_doc_file_size,
});
switch (result.term) {
- .Exited => |exit_code| {
+ .exited => |exit_code| {
if (exit_code != 0) {
std.debug.print("{s}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code });
dumpArgs(args);
return error.ChildExitError;
}
},
+ .signal => |sig| {
+ std.debug.print("{s}\nThe following command terminated with signal {t}:\n", .{ result.stderr, sig });
+ dumpArgs(args);
+ return error.ChildCrashed;
+ },
else => {
std.debug.print("{s}\nThe following command crashed:\n", .{result.stderr});
dumpArgs(args);
diff --git a/tools/dump-cov.zig b/tools/dump-cov.zig
index 1a8ebb324e57becbb8a60a42333086e2ce1d529c..4a54d9f5199299a75dd8fcae46a7d3506ee53d50 100644
--- a/tools/dump-cov.zig
+++ b/tools/dump-cov.zig
@@ -8,20 +8,11 @@ const Path = std.Build.Cache.Path;
const assert = std.debug.assert;
const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader;
-pub fn main() !void {
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_allocator.deinit();
- const gpa = debug_allocator.allocator();
-
- var arena_instance: std.heap.ArenaAllocator = .init(gpa);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var threaded: Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
const target_query_str = switch (args.len) {
3 => "native",
diff --git a/tools/fetch_them_macos_headers.zig b/tools/fetch_them_macos_headers.zig
index c55a569e9fdbac6ceb1ee05f42ce7e84a6f7ab32..cac4675bd42aaf5f6af8cb35d3cb621b0e352103 100644
--- a/tools/fetch_them_macos_headers.zig
+++ b/tools/fetch_them_macos_headers.zig
@@ -6,13 +6,9 @@ const process = std.process;
const assert = std.debug.assert;
const fatal = std.process.fatal;
const info = std.log.info;
-
-const Allocator = mem.Allocator;
+const Allocator = std.mem.Allocator;
const OsTag = std.Target.Os.Tag;
-var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
-const gpa = general_purpose_allocator.allocator();
-
const Arch = enum {
aarch64,
x86_64,
@@ -66,14 +62,12 @@ const usage =
\\-h, --help Print this help and exit
;
-pub fn main() anyerror!void {
- var arena = std.heap.ArenaAllocator.init(gpa);
- defer arena.deinit();
- const allocator = arena.allocator();
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
- const args = try std.process.argsAlloc(allocator);
-
- var argv = std.array_list.Managed([]const u8).init(allocator);
+ var argv = std.array_list.Managed([]const u8).init(arena);
var sysroot: ?[]const u8 = null;
var args_iter = ArgsIterator{ .args = args[1..] };
@@ -85,23 +79,19 @@ pub fn main() anyerror!void {
} else try argv.append(arg);
}
- var threaded: Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
const sysroot_path = sysroot orelse blk: {
const target = try std.zig.system.resolveTargetQuery(io, .{});
- break :blk std.zig.system.darwin.getSdk(allocator, io, &target) orelse
+ break :blk std.zig.system.darwin.getSdk(arena, io, &target) orelse
fatal("no SDK found; you can provide one explicitly with '--sysroot' flag", .{});
};
var sdk_dir = try Dir.cwd().openDir(io, sysroot_path, .{});
defer sdk_dir.close(io);
- const sdk_info = try sdk_dir.readFileAlloc(io, "SDKSettings.json", allocator, .limited(std.math.maxInt(u32)));
+ const sdk_info = try sdk_dir.readFileAlloc(io, "SDKSettings.json", arena, .limited(std.math.maxInt(u32)));
const parsed_json = try std.json.parseFromSlice(struct {
DefaultProperties: struct { MACOSX_DEPLOYMENT_TARGET: []const u8 },
- }, allocator, sdk_info, .{ .ignore_unknown_fields = true });
+ }, arena, sdk_info, .{ .ignore_unknown_fields = true });
const version = Version.parse(parsed_json.value.DefaultProperties.MACOSX_DEPLOYMENT_TARGET) orelse
fatal("don't know how to parse SDK version: {s}", .{
@@ -117,7 +107,7 @@ pub fn main() anyerror!void {
.arch = arch,
.os_ver = os_ver,
};
- try fetchTarget(allocator, io, argv.items, sysroot_path, target, version, tmp_dir);
+ try fetchTarget(arena, io, argv.items, sysroot_path, target, version, tmp_dir);
}
}
@@ -164,7 +154,7 @@ fn fetchTarget(
});
try cc_argv.appendSlice(args);
- const res = try std.process.Child.run(arena, io, .{ .argv = cc_argv.items });
+ const res = try std.process.run(arena, io, .{ .argv = cc_argv.items });
if (res.stderr.len != 0) {
std.log.err("{s}", .{res.stderr});
diff --git a/tools/gen_macos_headers_c.zig b/tools/gen_macos_headers_c.zig
index 95880fe3424b1be2949ba05de16a83918c8b2ee1..3e83761923e55de8fc9f54eb019d7b7f91007d5c 100644
--- a/tools/gen_macos_headers_c.zig
+++ b/tools/gen_macos_headers_c.zig
@@ -6,9 +6,6 @@ const info = std.log.info;
const fatal = std.process.fatal;
const Allocator = std.mem.Allocator;
-var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
-const gpa = general_purpose_allocator.allocator();
-
const usage =
\\gen_macos_headers_c [dir]
\\
@@ -16,16 +13,11 @@ const usage =
\\-h, --help Print this help and exit
;
-pub fn main() anyerror!void {
- var arena_allocator = std.heap.ArenaAllocator.init(gpa);
- defer arena_allocator.deinit();
- const arena = arena_allocator.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- var threaded: Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
if (args.len == 1) fatal("no command or option specified", .{});
var positionals = std.array_list.Managed([]const u8).init(arena);
diff --git a/tools/gen_outline_atomics.zig b/tools/gen_outline_atomics.zig
index 4d87e531bd823c3cc2c64ba269f15507ea3b2390..bd36484e1e9b697deba71a6d32169c0bfde880e2 100644
--- a/tools/gen_outline_atomics.zig
+++ b/tools/gen_outline_atomics.zig
@@ -11,14 +11,9 @@ const AtomicOp = enum {
ldset,
};
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var threaded: std.Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
//const args = try std.process.argsAlloc(arena);
diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig
index e96c98b6c01c776476ca37fe624c7bbb029eb3b6..f53b10cbb1bfa1d61957bf37001c3a933875ba2f 100644
--- a/tools/gen_spirv_spec.zig
+++ b/tools/gen_spirv_spec.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const Io = std.Io;
const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
const g = @import("spirv/grammar.zig");
const CoreRegistry = g.CoreRegistry;
@@ -54,28 +55,22 @@ const set_names = std.StaticStringMap(struct { []const u8, []const u8 }).initCom
.{ "zig", .{ "zig", "Zig" } },
});
-var arena = std.heap.ArenaAllocator.init(std.heap.smp_allocator);
-const allocator = arena.allocator();
-
-pub fn main() !void {
- defer arena.deinit();
-
- const args = try std.process.argsAlloc(allocator);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
if (args.len != 3) {
usageAndExit(args[0], 1);
}
- var threaded: std.Io.Threaded = .init(allocator, .{});
- defer threaded.deinit();
- const io = threaded.io();
+ const io = init.io;
- const json_path = try Io.Dir.path.join(allocator, &.{ args[1], "include/spirv/unified1/" });
+ const json_path = try Io.Dir.path.join(arena, &.{ args[1], "include/spirv/unified1/" });
const dir = try Io.Dir.cwd().openDir(io, json_path, .{ .iterate = true });
- const core_spec = try readRegistry(io, CoreRegistry, dir, "spirv.core.grammar.json");
+ const core_spec = try readRegistry(io, arena, CoreRegistry, dir, "spirv.core.grammar.json");
std.mem.sortUnstable(Instruction, core_spec.instructions, CmpInst{}, CmpInst.lt);
- var exts = std.array_list.Managed(Extension).init(allocator);
+ var exts = std.array_list.Managed(Extension).init(arena);
var it = dir.iterate();
while (try it.next(io)) |entry| {
@@ -83,48 +78,48 @@ pub fn main() !void {
continue;
}
- try readExtRegistry(io, &exts, dir, entry.name);
+ try readExtRegistry(io, arena, &exts, dir, entry.name);
}
- try readExtRegistry(io, &exts, Io.Dir.cwd(), args[2]);
+ try readExtRegistry(io, arena, &exts, Io.Dir.cwd(), args[2]);
- var allocating: std.Io.Writer.Allocating = .init(allocator);
+ var allocating: std.Io.Writer.Allocating = .init(arena);
defer allocating.deinit();
- try render(&allocating.writer, core_spec, exts.items);
+ try render(arena, &allocating.writer, core_spec, exts.items);
try allocating.writer.writeByte(0);
const output = allocating.written()[0 .. allocating.written().len - 1 :0];
- var tree = try std.zig.Ast.parse(allocator, output, .zig);
+ var tree = try std.zig.Ast.parse(arena, output, .zig);
if (tree.errors.len != 0) {
- try std.zig.printAstErrorsToStderr(allocator, io, tree, "", .auto);
+ try std.zig.printAstErrorsToStderr(arena, io, tree, "", .auto);
return;
}
- var zir = try std.zig.AstGen.generate(allocator, tree);
+ var zir = try std.zig.AstGen.generate(arena, tree);
if (zir.hasCompileErrors()) {
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
- try wip_errors.init(allocator);
+ try wip_errors.init(arena);
defer wip_errors.deinit();
try wip_errors.addZirErrorMessages(zir, tree, output, "");
var error_bundle = try wip_errors.toOwnedBundle("");
- defer error_bundle.deinit(allocator);
+ defer error_bundle.deinit(arena);
try error_bundle.renderToStderr(io, .{}, .auto);
}
- const formatted_output = try tree.renderAlloc(allocator);
+ const formatted_output = try tree.renderAlloc(arena);
try Io.File.stdout().writeStreamingAll(io, formatted_output);
}
-fn readExtRegistry(io: Io, exts: *std.array_list.Managed(Extension), dir: Io.Dir, sub_path: []const u8) !void {
+fn readExtRegistry(io: Io, arena: Allocator, exts: *std.array_list.Managed(Extension), dir: Io.Dir, sub_path: []const u8) !void {
const filename = Io.Dir.path.basename(sub_path);
if (!std.mem.startsWith(u8, filename, "extinst.")) {
return;
}
- std.debug.assert(std.mem.endsWith(u8, filename, ".grammar.json"));
+ assert(std.mem.endsWith(u8, filename, ".grammar.json"));
const name = filename["extinst.".len .. filename.len - ".grammar.json".len];
- const spec = try readRegistry(io, ExtensionRegistry, dir, sub_path);
+ const spec = try readRegistry(io, arena, ExtensionRegistry, dir, sub_path);
const set_name = set_names.get(name) orelse {
std.log.info("ignored instruction set '{s}'", .{name});
@@ -140,16 +135,16 @@ fn readExtRegistry(io: Io, exts: *std.array_list.Managed(Extension), dir: Io.Dir
});
}
-fn readRegistry(io: Io, comptime RegistryType: type, dir: Io.Dir, path: []const u8) !RegistryType {
- const spec = try dir.readFileAlloc(io, path, allocator, .unlimited);
+fn readRegistry(io: Io, arena: Allocator, comptime RegistryType: type, dir: Io.Dir, path: []const u8) !RegistryType {
+ const spec = try dir.readFileAlloc(io, path, arena, .unlimited);
// Required for json parsing.
// TODO: ALI
@setEvalBranchQuota(10000);
- var scanner = std.json.Scanner.initCompleteInput(allocator, spec);
+ var scanner = std.json.Scanner.initCompleteInput(arena, spec);
var diagnostics = std.json.Diagnostics{};
scanner.enableDiagnostics(&diagnostics);
- const parsed = std.json.parseFromTokenSource(RegistryType, allocator, &scanner, .{}) catch |err| {
+ const parsed = std.json.parseFromTokenSource(RegistryType, arena, &scanner, .{}) catch |err| {
std.debug.print("{s}:{}:{}:\n", .{ path, diagnostics.getLine(), diagnostics.getColumn() });
return err;
};
@@ -158,8 +153,8 @@ fn readRegistry(io: Io, comptime RegistryType: type, dir: Io.Dir, path: []const
/// Returns a set with types that require an extra struct for the `Instruction` interface
/// to the spir-v spec, or whether the original type can be used.
-fn extendedStructs(kinds: []const OperandKind) !ExtendedStructSet {
- var map = ExtendedStructSet.init(allocator);
+fn extendedStructs(arena: Allocator, kinds: []const OperandKind) !ExtendedStructSet {
+ var map = ExtendedStructSet.init(arena);
try map.ensureTotalCapacity(@as(u32, @intCast(kinds.len)));
for (kinds) |kind| {
@@ -194,6 +189,7 @@ fn tagPriorityScore(tag: []const u8) usize {
}
fn render(
+ arena: Allocator,
writer: *std.Io.Writer,
registry: CoreRegistry,
extensions: []const Extension,
@@ -299,7 +295,7 @@ fn render(
);
// Merge the operand kinds from all extensions together.
- var all_operand_kinds = OperandKindMap.init(allocator);
+ var all_operand_kinds = OperandKindMap.init(arena);
for (registry.operand_kinds) |kind| {
try all_operand_kinds.putNoClobber(.{ "core", kind.kind }, kind);
}
@@ -312,22 +308,22 @@ fn render(
try all_operand_kinds.ensureUnusedCapacity(ext.spec.operand_kinds.len);
for (ext.spec.operand_kinds) |kind| {
var new_kind = kind;
- new_kind.kind = try std.mem.join(allocator, ".", &.{ ext.name, kind.kind });
+ new_kind.kind = try std.mem.join(arena, ".", &.{ ext.name, kind.kind });
try all_operand_kinds.putNoClobber(.{ ext.name, kind.kind }, new_kind);
}
}
- const extended_structs = try extendedStructs(all_operand_kinds.values());
+ const extended_structs = try extendedStructs(arena, all_operand_kinds.values());
// Note: extensions don't seem to have class.
- try renderClass(writer, registry.instructions);
+ try renderClass(arena, writer, registry.instructions);
try renderOperandKind(writer, all_operand_kinds.values());
- try renderOpcodes(writer, "Opcode", true, registry.instructions, extended_structs);
+ try renderOpcodes(arena, writer, "Opcode", true, registry.instructions, extended_structs);
for (extensions) |ext| {
- try renderOpcodes(writer, ext.opcode_name, false, ext.spec.instructions, extended_structs);
+ try renderOpcodes(arena, writer, ext.opcode_name, false, ext.spec.instructions, extended_structs);
}
- try renderOperandKinds(writer, all_operand_kinds.values(), extended_structs);
+ try renderOperandKinds(arena, writer, all_operand_kinds.values(), extended_structs);
try renderInstructionSet(writer, registry, extensions, all_operand_kinds);
}
@@ -414,8 +410,8 @@ fn renderInstructionsCase(
);
}
-fn renderClass(writer: *std.Io.Writer, instructions: []const Instruction) !void {
- var class_map = std.StringArrayHashMap(void).init(allocator);
+fn renderClass(arena: Allocator, writer: *std.Io.Writer, instructions: []const Instruction) !void {
+ var class_map = std.StringArrayHashMap(void).init(arena);
for (instructions) |inst| {
if (std.mem.eql(u8, inst.class.?, "@exclude")) continue;
@@ -535,16 +531,17 @@ fn renderEnumerant(writer: *std.Io.Writer, enumerant: Enumerant) !void {
}
fn renderOpcodes(
+ arena: Allocator,
writer: *std.Io.Writer,
opcode_type_name: []const u8,
want_operands: bool,
instructions: []const Instruction,
extended_structs: ExtendedStructSet,
) !void {
- var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator);
+ var inst_map = std.AutoArrayHashMap(u32, usize).init(arena);
try inst_map.ensureTotalCapacity(instructions.len);
- var aliases = std.array_list.Managed(struct { inst: usize, alias: usize }).init(allocator);
+ var aliases = std.array_list.Managed(struct { inst: usize, alias: usize }).init(arena);
try aliases.ensureTotalCapacity(instructions.len);
for (instructions, 0..) |inst, i| {
@@ -634,30 +631,32 @@ fn renderOpcodes(
}
fn renderOperandKinds(
+ arena: Allocator,
writer: *std.Io.Writer,
kinds: []const OperandKind,
extended_structs: ExtendedStructSet,
) !void {
for (kinds) |kind| {
switch (kind.category) {
- .ValueEnum => try renderValueEnum(writer, kind, extended_structs),
- .BitEnum => try renderBitEnum(writer, kind, extended_structs),
+ .ValueEnum => try renderValueEnum(arena, writer, kind, extended_structs),
+ .BitEnum => try renderBitEnum(arena, writer, kind, extended_structs),
else => {},
}
}
}
fn renderValueEnum(
+ arena: Allocator,
writer: *std.Io.Writer,
enumeration: OperandKind,
extended_structs: ExtendedStructSet,
) !void {
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
- var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator);
+ var enum_map = std.AutoArrayHashMap(u32, usize).init(arena);
try enum_map.ensureTotalCapacity(enumerants.len);
- var aliases = std.array_list.Managed(struct { enumerant: usize, alias: usize }).init(allocator);
+ var aliases = std.array_list.Managed(struct { enumerant: usize, alias: usize }).init(arena);
try aliases.ensureTotalCapacity(enumerants.len);
for (enumerants, 0..) |enumerant, i| {
@@ -726,6 +725,7 @@ fn renderValueEnum(
}
fn renderBitEnum(
+ arena: Allocator,
writer: *std.Io.Writer,
enumeration: OperandKind,
extended_structs: ExtendedStructSet,
@@ -735,7 +735,7 @@ fn renderBitEnum(
var flags_by_bitpos = [_]?usize{null} ** 32;
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
- var aliases = std.array_list.Managed(struct { flag: usize, alias: u5 }).init(allocator);
+ var aliases = std.array_list.Managed(struct { flag: usize, alias: u5 }).init(arena);
try aliases.ensureTotalCapacity(enumerants.len);
for (enumerants, 0..) |enumerant, i| {
@@ -749,7 +749,7 @@ fn renderBitEnum(
continue;
}
- std.debug.assert(@popCount(value) == 1);
+ assert(@popCount(value) == 1);
const bitpos = std.math.log2_int(u32, value);
if (flags_by_bitpos[bitpos]) |*existing| {
diff --git a/tools/gen_stubs.zig b/tools/gen_stubs.zig
index 51e83e1a08c08afe91287c27099a398d079540e8..bad27fd2015cd57ae34f00b8448127b0a35630bd 100644
--- a/tools/gen_stubs.zig
+++ b/tools/gen_stubs.zig
@@ -281,16 +281,10 @@ const Parse = struct {
arch: Arch,
};
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var threaded: std.Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
const build_all_path = args[1];
var build_all_dir = try Io.Dir.cwd().openDir(io, build_all_path, .{});
diff --git a/tools/generate_JSONTestSuite.zig b/tools/generate_JSONTestSuite.zig
index e445a1badf150b56b490861e625becd8233a494d..d1bdd2e5cee24a61428ae2bd51013fe5abf68b27 100644
--- a/tools/generate_JSONTestSuite.zig
+++ b/tools/generate_JSONTestSuite.zig
@@ -3,13 +3,9 @@
const std = @import("std");
const Io = std.Io;
-pub fn main() !void {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- var allocator = gpa.allocator();
-
- var threaded: std.Io.Threaded = .init(allocator, .{});
- defer threaded.deinit();
- const io = threaded.io();
+pub fn main(init: std.process.Init) !void {
+ const allocator = init.gpa;
+ const io = init.io;
var stdout_buffer: [2000]u8 = undefined;
var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
diff --git a/tools/generate_c_size_and_align_checks.zig b/tools/generate_c_size_and_align_checks.zig
index 833fa50f5c21a044283e245166e285e766d012ee..6041a550da6f3ac04cf366cf9d1913c3cbbed5da 100644
--- a/tools/generate_c_size_and_align_checks.zig
+++ b/tools/generate_c_size_and_align_checks.zig
@@ -28,22 +28,15 @@ fn cName(ty: std.Target.CType) []const u8 {
var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
-pub fn main() !void {
- const gpa = general_purpose_allocator.allocator();
- defer std.debug.assert(general_purpose_allocator.deinit() == .ok);
-
- const args = try std.process.argsAlloc(gpa);
- defer std.process.argsFree(gpa, args);
+pub fn main(init: std.process.Init) !void {
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
+ const io = init.io;
if (args.len != 2) {
std.debug.print("Usage: {s} [target_triple]\n", .{args[0]});
std.process.exit(1);
}
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] });
const target = try std.zig.system.resolveTargetQuery(io, query);
diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig
index fd632a6c97bd44eb7f9786da2d3047006d37bc92..70d410f806b35999af5950f67cf8faecfb951fa7 100644
--- a/tools/generate_linux_syscalls.zig
+++ b/tools/generate_linux_syscalls.zig
@@ -170,16 +170,11 @@ const architectures: []const Arch = &.{
// .{ .@"var" = "Microblaze", .table = .{ .specific = "arch/microblaze/kernel/syscalls/syscall.tbl" } },
};
-pub fn main() !void {
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena.deinit();
- const gpa = arena.allocator();
+pub fn main(init: std.process.Init) !void {
+ const gpa = init.gpa;
+ const io = init.io;
- var threaded: Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(gpa);
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
if (args.len < 2 or mem.eql(u8, args[1], "--help")) {
const stderr = std.debug.lockStderr(&.{});
const w = &stderr.file_writer.interface;
@@ -217,8 +212,7 @@ pub fn main() !void {
};
try Io.Writer.print(stdout,
- \\// This file is automatically generated, DO NOT edit it manually.
- \\// See tools/generate_linux_syscalls.zig for more info.
+ \\// This file is automatically generated by tools/generate_linux_syscalls.zig
\\// This list current as of kernel: {f}
\\
\\
diff --git a/tools/incr-check.zig b/tools/incr-check.zig
index 05a6afc8b846818deb52877fe19e1c02f838541e..2dfb23447a13f5744124b306159d018507f3b6d2 100644
--- a/tools/incr-check.zig
+++ b/tools/incr-check.zig
@@ -27,18 +27,12 @@ fn logImpl(
);
}
-pub fn main() !void {
+pub fn main(init: std.process.Init) !void {
const fatal = std.process.fatal;
-
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- const gpa = arena;
-
- var threaded: Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const environ_map = init.environ_map;
+ const cwd_path = try std.process.getCwdAlloc(arena);
var opt_zig_exe: ?[]const u8 = null;
var opt_input_file_name: ?[]const u8 = null;
@@ -52,7 +46,7 @@ pub fn main() !void {
var debug_log_args: std.ArrayList([]const u8) = .empty;
- var arg_it = try std.process.argsWithAllocator(arena);
+ var arg_it = try init.minimal.args.iterateAllocator(arena);
_ = arg_it.skip();
while (arg_it.next()) |arg| {
if (arg.len > 0 and arg[0] == '-') {
@@ -119,9 +113,9 @@ pub fn main() !void {
}
// Convert paths to be relative to the cwd of the subprocess.
- const resolved_zig_exe = try Dir.path.relative(arena, tmp_dir_path, zig_exe);
+ const resolved_zig_exe = try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, zig_exe);
const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir|
- try Dir.path.relative(arena, tmp_dir_path, lib_dir)
+ try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, lib_dir)
else
null;
@@ -179,18 +173,10 @@ pub fn main() !void {
const zig_prog_node = target_prog_node.start("zig build-exe", 0);
defer zig_prog_node.end();
- var child = std.process.Child.init(child_args.items, arena);
- child.stdin_behavior = .Pipe;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
- child.progress_node = zig_prog_node;
- child.cwd_dir = tmp_dir;
- child.cwd = tmp_dir_path;
-
var cc_child_args: std.ArrayList([]const u8) = .empty;
if (target.backend == .cbe) {
const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe|
- try Dir.path.relative(arena, tmp_dir_path, cc_zig_exe)
+ try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, cc_zig_exe)
else
resolved_zig_exe;
@@ -209,6 +195,17 @@ pub fn main() !void {
try cc_child_args.append(arena, "-o");
}
+ var child = try std.process.spawn(io, .{
+ .argv = child_args.items,
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .pipe,
+ .progress_node = zig_prog_node,
+ .cwd_dir = tmp_dir,
+ .cwd = tmp_dir_path,
+ });
+ defer child.kill(io);
+
var eval: Eval = .{
.arena = arena,
.io = io,
@@ -227,11 +224,6 @@ pub fn main() !void {
.enable_darling = enable_darling,
};
- try child.spawn(io);
- errdefer {
- _ = child.kill(io) catch {};
- }
-
var poller = Io.poll(arena, Eval.StreamEnum, .{
.stdout = child.stdout.?,
.stderr = child.stderr.?,
@@ -536,7 +528,7 @@ const Eval = struct {
const run_prog_node = prog_node.start("run generated executable", 0);
defer run_prog_node.end();
- const result = std.process.Child.run(eval.arena, io, .{
+ const result = std.process.run(eval.arena, io, .{
.argv = argv,
.cwd_dir = eval.tmp_dir,
.cwd = eval.tmp_dir_path,
@@ -564,7 +556,7 @@ const Eval = struct {
}
switch (result.term) {
- .Exited => |code| switch (update.outcome) {
+ .exited => |code| switch (update.outcome) {
.unknown, .compile_errors => unreachable,
.stdout => |expected_stdout| {
if (code != 0) {
@@ -572,9 +564,12 @@ const Eval = struct {
}
try std.testing.expectEqualStrings(expected_stdout, result.stdout);
},
- .exit_code => |expected_code| try std.testing.expectEqual(expected_code, result.term.Exited),
+ .exit_code => |expected_code| try std.testing.expectEqual(expected_code, code),
},
- .Signal, .Stopped, .Unknown => {
+ .signal => |sig| {
+ eval.fatal("generated executable '{s}' terminated with signal {t}", .{ binary_path, sig });
+ },
+ .stopped, .unknown => {
eval.fatal("generated executable '{s}' terminated unexpectedly", .{binary_path});
},
}
@@ -622,7 +617,7 @@ const Eval = struct {
try eval.cc_child_args.appendSlice(eval.arena, &.{ out_path, c_path });
defer eval.cc_child_args.items.len -= 2;
- const result = std.process.Child.run(eval.arena, eval.io, .{
+ const result = std.process.run(eval.arena, eval.io, .{
.argv = eval.cc_child_args.items,
.cwd_dir = eval.tmp_dir,
.cwd = eval.tmp_dir_path,
@@ -631,13 +626,13 @@ const Eval = struct {
eval.fatal("failed to spawn zig cc for '{s}': {t}", .{ c_path, err });
};
switch (result.term) {
- .Exited => |code| if (code != 0) {
+ .exited => |code| if (code != 0) {
if (result.stderr.len != 0) {
std.log.err("zig cc stderr:\n{s}", .{result.stderr});
}
eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code });
},
- .Signal, .Stopped, .Unknown => {
+ .signal, .stopped, .unknown => {
if (result.stderr.len != 0) {
std.log.err("zig cc stderr:\n{s}", .{result.stderr});
}
@@ -651,7 +646,7 @@ const Eval = struct {
eval.tmp_dir.close(io);
if (!eval.preserve_tmp_on_fatal) {
// Kill the child since it holds an open handle to its CWD which is the tmp dir path
- _ = eval.child.kill(io) catch {};
+ eval.child.kill(io);
Dir.cwd().deleteTree(io, eval.tmp_dir_path) catch |err| {
std.log.warn("failed to delete tree '{s}': {t}", .{ eval.tmp_dir_path, err });
};
@@ -917,8 +912,9 @@ fn waitChild(child: *std.process.Child, eval: *Eval) void {
requestExit(child, eval);
const term = child.wait(io) catch |err| eval.fatal("child process failed: {t}", .{err});
switch (term) {
- .Exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}),
- .Signal, .Stopped, .Unknown => eval.fatal("compiler terminated unexpectedly", .{}),
+ .exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}),
+ .signal => |sig| eval.fatal("compiler terminated with signal {t}", .{sig}),
+ .stopped, .unknown => eval.fatal("compiler terminated unexpectedly", .{}),
}
}
diff --git a/tools/migrate_langref.zig b/tools/migrate_langref.zig
index 24ddd5941d3956bd42b59b05c8a7505d290a18c6..581d1294ff57cc8f3f9bdb7bb65560b467d40db4 100644
--- a/tools/migrate_langref.zig
+++ b/tools/migrate_langref.zig
@@ -11,21 +11,14 @@ const fatal = std.process.fatal;
const max_doc_file_size = 10 * 1024 * 1024;
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- const gpa = arena;
-
- const args = try std.process.argsAlloc(arena);
const input_file = args[1];
const output_file = args[2];
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
var in_file = try Dir.cwd().openFile(io, input_file, .{ .mode = .read_only });
defer in_file.close(io);
diff --git a/tools/process_headers.zig b/tools/process_headers.zig
index cbd94c6292fffecd177572ee6962328bb169b7a2..deb1f01b5ebde66a342846d105c8fd426c52cedf 100644
--- a/tools/process_headers.zig
+++ b/tools/process_headers.zig
@@ -127,16 +127,14 @@ const LibCVendor = enum {
netbsd,
};
-pub fn main() !void {
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- const allocator = arena.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
+ const cwd_path = try std.process.getCwdAlloc(arena);
+ const environ_map = init.environ_map;
- var threaded: Io.Threaded = .init(allocator, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(allocator);
- var search_paths = std.array_list.Managed([]const u8).init(allocator);
+ var search_paths = std.array_list.Managed([]const u8).init(arena);
var opt_out_dir: ?[]const u8 = null;
var opt_abi: ?[]const u8 = null;
@@ -172,7 +170,7 @@ pub fn main() !void {
usageAndExit(args[0]);
};
- const generic_name = try std.fmt.allocPrint(allocator, "generic-{s}", .{abi_name});
+ const generic_name = try std.fmt.allocPrint(arena, "generic-{s}", .{abi_name});
const libc_targets = switch (vendor) {
.glibc => &glibc_targets,
.musl => &musl_targets,
@@ -180,8 +178,8 @@ pub fn main() !void {
.netbsd => &netbsd_targets,
};
- var path_table = PathTable.init(allocator);
- var hash_to_contents = HashToContents.init(allocator);
+ var path_table = PathTable.init(arena);
+ var hash_to_contents = HashToContents.init(arena);
var max_bytes_saved: usize = 0;
var total_bytes: usize = 0;
@@ -189,7 +187,7 @@ pub fn main() !void {
for (libc_targets) |libc_target| {
const libc_dir = switch (vendor) {
- .glibc => try std.zig.target.glibcRuntimeTriple(allocator, libc_target.arch, .linux, libc_target.abi),
+ .glibc => try std.zig.target.glibcRuntimeTriple(arena, libc_target.arch, .linux, libc_target.abi),
.musl => std.zig.target.muslArchName(libc_target.arch, libc_target.abi),
.freebsd => switch (libc_target.arch) {
.arm => "armv7",
@@ -221,7 +219,7 @@ pub fn main() !void {
},
};
- const dest_target = if (libc_target.dest) |dest| dest else try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{
+ const dest_target = if (libc_target.dest) |dest| dest else try std.fmt.allocPrint(arena, "{s}-{s}-{s}", .{
@tagName(libc_target.arch),
switch (vendor) {
.musl, .glibc => "linux",
@@ -239,8 +237,8 @@ pub fn main() !void {
=> &[_][]const u8{ search_path, libc_dir, "usr", "include" },
.musl => &[_][]const u8{ search_path, libc_dir, "usr", "local", "musl", "include" },
};
- const target_include_dir = try Dir.path.join(allocator, sub_path);
- var dir_stack = std.array_list.Managed([]const u8).init(allocator);
+ const target_include_dir = try Dir.path.join(arena, sub_path);
+ var dir_stack = std.array_list.Managed([]const u8).init(arena);
try dir_stack.append(target_include_dir);
while (dir_stack.pop()) |full_dir_name| {
@@ -254,16 +252,16 @@ pub fn main() !void {
var dir_it = dir.iterate();
while (try dir_it.next(io)) |entry| {
- const full_path = try Dir.path.join(allocator, &[_][]const u8{ full_dir_name, entry.name });
+ const full_path = try Dir.path.join(arena, &[_][]const u8{ full_dir_name, entry.name });
switch (entry.kind) {
.directory => try dir_stack.append(full_path),
.file, .sym_link => {
- const rel_path = try Dir.path.relative(allocator, target_include_dir, full_path);
+ const rel_path = try Dir.path.relative(arena, cwd_path, environ_map, target_include_dir, full_path);
const max_size = 2 * 1024 * 1024 * 1024;
- const raw_bytes = try Dir.cwd().readFileAlloc(io, full_path, allocator, .limited(max_size));
+ const raw_bytes = try Dir.cwd().readFileAlloc(io, full_path, arena, .limited(max_size));
const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");
total_bytes += raw_bytes.len;
- const hash = try allocator.alloc(u8, 32);
+ const hash = try arena.alloc(u8, 32);
hasher = Blake3.init(.{});
hasher.update(rel_path);
hasher.update(trimmed);
@@ -285,8 +283,8 @@ pub fn main() !void {
}
const path_gop = try path_table.getOrPut(rel_path);
const target_to_hash = if (path_gop.found_existing) path_gop.value_ptr.* else blk: {
- const ptr = try allocator.create(TargetToHash);
- ptr.* = TargetToHash.init(allocator);
+ const ptr = try arena.create(TargetToHash);
+ ptr.* = TargetToHash.init(arena);
path_gop.value_ptr.* = ptr;
break :blk ptr;
};
@@ -327,7 +325,7 @@ pub fn main() !void {
// gets their header in a separate arch directory.
var path_it = path_table.iterator();
while (path_it.next()) |path_kv| {
- var contents_list = std.array_list.Managed(*Contents).init(allocator);
+ var contents_list = std.array_list.Managed(*Contents).init(arena);
{
var hash_it = path_kv.value_ptr.*.iterator();
while (hash_it.next()) |hash_kv| {
@@ -339,7 +337,7 @@ pub fn main() !void {
const best_contents = contents_list.pop().?;
if (best_contents.hit_count > 1) {
// worth it to make it generic
- const full_path = try Dir.path.join(allocator, &[_][]const u8{ out_dir, generic_name, path_kv.key_ptr.* });
+ const full_path = try Dir.path.join(arena, &[_][]const u8{ out_dir, generic_name, path_kv.key_ptr.* });
try Dir.cwd().createDirPath(io, Dir.path.dirname(full_path).?);
try Dir.cwd().writeFile(io, .{ .sub_path = full_path, .data = best_contents.bytes });
best_contents.is_generic = true;
@@ -360,7 +358,7 @@ pub fn main() !void {
if (contents.is_generic) continue;
const dest_target = hash_kv.key_ptr.*;
- const full_path = try Dir.path.join(allocator, &[_][]const u8{ out_dir, dest_target, path_kv.key_ptr.* });
+ const full_path = try Dir.path.join(arena, &[_][]const u8{ out_dir, dest_target, path_kv.key_ptr.* });
try Dir.cwd().createDirPath(io, Dir.path.dirname(full_path).?);
try Dir.cwd().writeFile(io, .{ .sub_path = full_path, .data = contents.bytes });
}
diff --git a/tools/update-linux-headers.zig b/tools/update-linux-headers.zig
index 5091b18dbe74e88eeec1516beb8e67091672b413..e05ba082031cbee62f0aab9638f127bfbc2ae0ff 100644
--- a/tools/update-linux-headers.zig
+++ b/tools/update-linux-headers.zig
@@ -141,15 +141,13 @@ const HashToContents = std.StringHashMap(Contents);
const TargetToHash = std.ArrayHashMap(DestTarget, []const u8, DestTarget.HashContext, true);
const PathTable = std.StringHashMap(*TargetToHash);
-pub fn main() !void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- const arena = arena_state.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
+ const environ_map = init.environ_map;
+ const cwd = try std.process.getCwdAlloc(arena);
- var threaded: Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
var search_paths = std.array_list.Managed([]const u8).init(arena);
var opt_out_dir: ?[]const u8 = null;
@@ -211,7 +209,7 @@ pub fn main() !void {
switch (entry.kind) {
.directory => try dir_stack.append(full_path),
.file => {
- const rel_path = try Dir.path.relative(arena, target_include_dir, full_path);
+ const rel_path = try Dir.path.relative(arena, cwd, environ_map, target_include_dir, full_path);
const max_size = 2 * 1024 * 1024 * 1024;
const raw_bytes = try Dir.cwd().readFileAlloc(io, full_path, arena, .limited(max_size));
const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");
diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig
index 3c7762bab6cda7a8d4b4ec9aa60139c414642393..b52267a3fbd2c7efe37b58efe19615c5c8a8c556 100644
--- a/tools/update_clang_options.zig
+++ b/tools/update_clang_options.zig
@@ -627,16 +627,10 @@ const cpu_targets = struct {
pub const xtensa = std.Target.xtensa;
};
-pub fn main() anyerror!void {
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena.deinit();
-
- const allocator = arena.allocator();
- const args = try std.process.argsAlloc(allocator);
-
- var threaded: std.Io.Threaded = .init(allocator, .{});
- defer threaded.deinit();
- const io = threaded.io();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const args = try init.minimal.args.toSlice(arena);
+ const io = init.io;
var stdout_buffer: [4000]u8 = undefined;
var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
@@ -658,7 +652,7 @@ pub fn main() anyerror!void {
const llvm_src_root = args[2];
if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]);
- var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator);
+ var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(arena);
inline for (@typeInfo(cpu_targets).@"struct".decls) |decl| {
const Feature = @field(cpu_targets, decl.name).Feature;
@@ -675,12 +669,12 @@ pub fn main() anyerror!void {
const child_args = [_][]const u8{
llvm_tblgen_exe,
"--dump-json",
- try std.fmt.allocPrint(allocator, "{s}/clang/include/clang/Driver/Options.td", .{llvm_src_root}),
- try std.fmt.allocPrint(allocator, "-I={s}/llvm/include", .{llvm_src_root}),
- try std.fmt.allocPrint(allocator, "-I={s}/clang/include/clang/Driver", .{llvm_src_root}),
+ try std.fmt.allocPrint(arena, "{s}/clang/include/clang/Driver/Options.td", .{llvm_src_root}),
+ try std.fmt.allocPrint(arena, "-I={s}/llvm/include", .{llvm_src_root}),
+ try std.fmt.allocPrint(arena, "-I={s}/clang/include/clang/Driver", .{llvm_src_root}),
};
- const child_result = try std.process.Child.run(allocator, io, .{
+ const child_result = try std.process.run(arena, io, .{
.argv = &child_args,
.max_output_bytes = 100 * 1024 * 1024,
});
@@ -688,7 +682,7 @@ pub fn main() anyerror!void {
std.debug.print("{s}\n", .{child_result.stderr});
const json_text = switch (child_result.term) {
- .Exited => |code| if (code == 0) child_result.stdout else {
+ .exited => |code| if (code == 0) child_result.stdout else {
std.debug.print("llvm-tblgen exited with code {d}\n", .{code});
std.process.exit(1);
},
@@ -698,11 +692,11 @@ pub fn main() anyerror!void {
},
};
- const parsed = try json.parseFromSlice(json.Value, allocator, json_text, .{});
+ const parsed = try json.parseFromSlice(json.Value, arena, json_text, .{});
defer parsed.deinit();
const root_map = &parsed.value.object;
- var all_objects = std.array_list.Managed(*json.ObjectMap).init(allocator);
+ var all_objects = std.array_list.Managed(*json.ObjectMap).init(arena);
{
var it = root_map.iterator();
it_map: while (it.next()) |kv| {
diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig
index 3852a85193f059e74fb7befb118d68cee5e2b10b..3041ee6acc5129a3bfbe9b8d1295a94f69a529e7 100644
--- a/tools/update_cpu_features.zig
+++ b/tools/update_cpu_features.zig
@@ -1883,20 +1883,11 @@ const targets = [_]ArchTarget{
},
};
-pub fn main() anyerror!void {
- var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_allocator.deinit();
- const gpa = debug_allocator.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
- var arena_state: std.heap.ArenaAllocator = .init(gpa);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
-
- var threaded: std.Io.Threaded = .init(gpa, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- var args = try std.process.argsWithAllocator(arena);
+ var args = try init.minimal.args.iterateAllocator(arena);
const args0 = args.next().?;
const llvm_tblgen_exe = args.next() orelse
@@ -1994,7 +1985,7 @@ fn processOneTarget(io: Io, job: Job) void {
}),
};
- const child_result = try std.process.Child.run(arena, io, .{
+ const child_result = try std.process.run(arena, io, .{
.argv = &child_args,
.max_output_bytes = 500 * 1024 * 1024,
});
@@ -2004,7 +1995,7 @@ fn processOneTarget(io: Io, job: Job) void {
}
const json_text = switch (child_result.term) {
- .Exited => |code| if (code == 0) child_result.stdout else {
+ .exited => |code| if (code == 0) child_result.stdout else {
std.debug.print("llvm-tblgen exited with code {d}\n", .{code});
std.process.exit(1);
},
diff --git a/tools/update_crc_catalog.zig b/tools/update_crc_catalog.zig
index 29856aacf8d1ae2b039b20aca40b9eea698e2d42..57eedf375b29c1195dcfbf4dcea1f42f1235a192 100644
--- a/tools/update_crc_catalog.zig
+++ b/tools/update_crc_catalog.zig
@@ -6,16 +6,14 @@ const ascii = std.ascii;
const catalog_txt = @embedFile("crc/catalog.txt");
-pub fn main() anyerror!void {
- var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_state.deinit();
- const arena = arena_state.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
+ return @"i like cheese"(arena, io, args);
+}
- var threaded: Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
+fn @"i like cheese"(arena: std.mem.Allocator, io: Io, args: []const []const u8) !void {
if (args.len <= 1) printUsageAndExit(args[0]);
const zig_src_root = args[1];
diff --git a/tools/update_freebsd_libc.zig b/tools/update_freebsd_libc.zig
index d50364351f599e7501e0b910b6f532abb34fa351..c2b251da0fdae79803e10f8c3c41e37142ea1500 100644
--- a/tools/update_freebsd_libc.zig
+++ b/tools/update_freebsd_libc.zig
@@ -12,16 +12,11 @@ const exempt_files = [_][]const u8{
"abilists",
};
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- var threaded: Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
const freebsd_src_path = args[1];
const zig_src_path = args[2];
diff --git a/tools/update_glibc.zig b/tools/update_glibc.zig
index 296d677d45e0a60bcb485acfa6ba2a52f1575b31..29df298bf6c49d5e5f140069d5b607550ecca979 100644
--- a/tools/update_glibc.zig
+++ b/tools/update_glibc.zig
@@ -38,16 +38,11 @@ const exempt_extensions = [_][]const u8{
"-2.33.c",
};
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- var threaded: Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
const glibc_src_path = args[1];
const zig_src_path = args[2];
diff --git a/tools/update_mingw.zig b/tools/update_mingw.zig
index 678c3dbdca45564d32cfe76c2a4c6153533878cb..ad3237d1ff086e164744845f0741f4eff295390d 100644
--- a/tools/update_mingw.zig
+++ b/tools/update_mingw.zig
@@ -2,16 +2,11 @@ const std = @import("std");
const Io = std.Io;
const Dir = std.Io.Dir;
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- var threaded: Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
const zig_src_lib_path = args[1];
const mingw_src_path = args[2];
diff --git a/tools/update_netbsd_libc.zig b/tools/update_netbsd_libc.zig
index a5eeca35c7f2499affd28221931f0a27e137a03b..7a466c3246573cbcae9104000b9734d42fe89d7a 100644
--- a/tools/update_netbsd_libc.zig
+++ b/tools/update_netbsd_libc.zig
@@ -12,16 +12,11 @@ const exempt_files = [_][]const u8{
"abilists",
};
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
+pub fn main(init: std.process.Init) !void {
+ const arena = init.arena.allocator();
+ const io = init.io;
+ const args = try init.minimal.args.toSlice(arena);
- var threaded: Io.Threaded = .init(arena, .{});
- defer threaded.deinit();
- const io = threaded.io();
-
- const args = try std.process.argsAlloc(arena);
const netbsd_src_path = args[1];
const zig_src_path = args[2];