| author | |
| committer | |
| log | de25a6ffee7c4b055b235f4b1f0116530592e81b |
| tree | d2b1f439a15ad31594a1bb971ff2ec4f9d21e0f5 |
| parent | 86a9a9048e28efc8b49d42db12db123822c3450b |
| parent | ef1ddbe2f03a165c86af4e2dd36178b1d8661ebe |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30644342 files changed, 6772 insertions(+), 7357 deletions(-)
CMakeLists.txt-1| ... | @@ -513,7 +513,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -513,7 +513,6 @@ set(ZIG_STAGE2_SOURCES |
| 513 | src/Builtin.zig | 513 | src/Builtin.zig |
| 514 | src/Compilation.zig | 514 | src/Compilation.zig |
| 515 | src/Compilation/Config.zig | 515 | src/Compilation/Config.zig |
| 516 | src/DarwinPosixSpawn.zig | ||
| 517 | src/InternPool.zig | 516 | src/InternPool.zig |
| 518 | src/Package.zig | 517 | src/Package.zig |
| 519 | src/Package/Fetch.zig | 518 | src/Package/Fetch.zig |
build.zig+3-29| ... | @@ -261,7 +261,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -261,7 +261,7 @@ pub fn build(b: *std.Build) !void { |
| 261 | "--git-dir", ".git", // affected by the -C argument | 261 | "--git-dir", ".git", // affected by the -C argument |
| 262 | "describe", "--match", "*.*.*", // | 262 | "describe", "--match", "*.*.*", // |
| 263 | "--tags", "--abbrev=9", | 263 | "--tags", "--abbrev=9", |
| 264 | }, &code, .Ignore) catch { | 264 | }, &code, .ignore) catch { |
| 265 | break :v version_string; | 265 | break :v version_string; |
| 266 | }; | 266 | }; |
| 267 | const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r"); | 267 | const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r"); |
| ... | @@ -470,10 +470,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -470,10 +470,7 @@ pub fn build(b: *std.Build) !void { |
| 470 | .skip_llvm = skip_llvm, | 470 | .skip_llvm = skip_llvm, |
| 471 | .skip_libc = skip_libc, | 471 | .skip_libc = skip_libc, |
| 472 | .max_rss = switch (b.graph.host.result.os.tag) { | 472 | .max_rss = switch (b.graph.host.result.os.tag) { |
| 473 | .freebsd => switch (b.graph.host.result.cpu.arch) { | 473 | .freebsd => 2_000_000_000, |
| 474 | .x86_64 => 1_060_217_241, | ||
| 475 | else => 1_100_000_000, | ||
| 476 | }, | ||
| 477 | .linux => switch (b.graph.host.result.cpu.arch) { | 474 | .linux => switch (b.graph.host.result.cpu.arch) { |
| 478 | .aarch64 => 659_809_075, | 475 | .aarch64 => 659_809_075, |
| 479 | .loongarch64 => 598_902_374, | 476 | .loongarch64 => 598_902_374, |
| ... | @@ -829,30 +826,7 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu | ... | @@ -829,30 +826,7 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu |
| 829 | fn addCompilerStep(b: *std.Build, options: AddCompilerModOptions) *std.Build.Step.Compile { | 826 | fn addCompilerStep(b: *std.Build, options: AddCompilerModOptions) *std.Build.Step.Compile { |
| 830 | const exe = b.addExecutable(.{ | 827 | const exe = b.addExecutable(.{ |
| 831 | .name = "zig", | 828 | .name = "zig", |
| 832 | .max_rss = switch (b.graph.host.result.os.tag) { | 829 | .max_rss = 7_000_000_000, |
| 833 | .freebsd => switch (b.graph.host.result.cpu.arch) { | ||
| 834 | .x86_64 => 6_044_158_771, | ||
| 835 | else => 6_100_000_000, | ||
| 836 | }, | ||
| 837 | .linux => switch (b.graph.host.result.cpu.arch) { | ||
| 838 | .aarch64 => 6_240_805_683, | ||
| 839 | .loongarch64 => 5_024_158_515, | ||
| 840 | .powerpc64le => 5_224_914_534, | ||
| 841 | .riscv64 => 6_996_309_196, | ||
| 842 | .s390x => 4_997_174_476, | ||
| 843 | .x86_64 => 6_664_025_702, | ||
| 844 | else => 7_000_000_000, | ||
| 845 | }, | ||
| 846 | .macos => switch (b.graph.host.result.cpu.arch) { | ||
| 847 | .aarch64 => 6_639_145_779, | ||
| 848 | else => 6_700_000_000, | ||
| 849 | }, | ||
| 850 | .windows => switch (b.graph.host.result.cpu.arch) { | ||
| 851 | .x86_64 => 5_770_394_009, | ||
| 852 | else => 5_800_000_000, | ||
| 853 | }, | ||
| 854 | else => 7_000_000_000, | ||
| 855 | }, | ||
| 856 | .root_module = addCompilerMod(b, options), | 830 | .root_module = addCompilerMod(b, options), |
| 857 | }); | 831 | }); |
| 858 | exe.stack_size = stack_size; | 832 | exe.stack_size = stack_size; |
doc/langref.html.in+1-2| ... | @@ -7027,8 +7027,7 @@ WebAssembly.instantiate(typedArray, { | ... | @@ -7027,8 +7027,7 @@ WebAssembly.instantiate(typedArray, { |
| 7027 | The result is 3{#end_shell_samp#} | 7027 | The result is 3{#end_shell_samp#} |
| 7028 | {#header_close#} | 7028 | {#header_close#} |
| 7029 | {#header_open|WASI#} | 7029 | {#header_open|WASI#} |
| 7030 | <p>Zig's support for WebAssembly System Interface (WASI) is under active development. | 7030 | <p>Zig standard library has first-class support for WebAssembly System Interface.</p> |
| 7031 | Example of using the standard library and reading command line arguments:</p> | ||
| 7032 | {#code|wasi_args.zig#} | 7031 | {#code|wasi_args.zig#} |
| 7033 | 7032 | ||
| 7034 | {#shell_samp#}$ wasmtime wasi_args.wasm 123 hello | 7033 | {#shell_samp#}$ wasmtime wasi_args.wasm 123 hello |
doc/langref/hello.zig+2-12| ... | @@ -1,17 +1,7 @@ | ... | @@ -1,17 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | // See https://github.com/ziglang/zig/issues/24510 | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | // for the plan to simplify this code. | 4 | try std.Io.File.stdout().writeStreamingAll(init.io, "Hello, World!\n"); |
| 5 | pub fn main() !void { | ||
| 6 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | ||
| 7 | defer _ = debug_allocator.deinit(); | ||
| 8 | const gpa = debug_allocator.allocator(); | ||
| 9 | |||
| 10 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 11 | defer threaded.deinit(); | ||
| 12 | const io = threaded.io(); | ||
| 13 | |||
| 14 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | ||
| 15 | } | 5 | } |
| 16 | 6 | ||
| 17 | // exe=succeed | 7 | // exe=succeed |
doc/langref/wasi_args.zig+4-8| ... | @@ -1,13 +1,9 @@ | ... | @@ -1,13 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | 4 | const args = try init.minimal.args.toSlice(init.arena.allocator()); |
| 5 | const gpa = general_purpose_allocator.allocator(); | 5 | for (0.., args) |i, arg| { |
| 6 | const args = try std.process.argsAlloc(gpa); | 6 | std.debug.print("{d}: {s}\n", .{ i, arg }); |
| 7 | defer std.process.argsFree(gpa, args); | ||
| 8 | |||
| 9 | for (args, 0..) |arg, i| { | ||
| 10 | std.debug.print("{}: {s}\n", .{ i, arg }); | ||
| 11 | } | 7 | } |
| 12 | } | 8 | } |
| 13 | 9 |
doc/langref/wasi_preopens.zig+3-11| ... | @@ -1,18 +1,10 @@ | ... | @@ -1,18 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const fs = std.fs; | ||
| 3 | 2 | ||
| 4 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 5 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | 4 | const preopens = try std.fs.wasi.preopensAlloc(init.arena.allocator()); |
| 6 | const gpa = general_purpose_allocator.allocator(); | ||
| 7 | |||
| 8 | var arena_instance = std.heap.ArenaAllocator.init(gpa); | ||
| 9 | defer arena_instance.deinit(); | ||
| 10 | const arena = arena_instance.allocator(); | ||
| 11 | |||
| 12 | const preopens = try fs.wasi.preopensAlloc(arena); | ||
| 13 | 5 | ||
| 14 | for (preopens.names, 0..) |preopen, i| { | 6 | for (preopens.names, 0..) |preopen, i| { |
| 15 | std.debug.print("{}: {s}\n", .{ i, preopen }); | 7 | std.debug.print("{d}: {s}\n", .{ i, preopen }); |
| 16 | } | 8 | } |
| 17 | } | 9 | } |
| 18 | 10 |
lib/compiler/aro/aro/Compilation.zig+11-10| ... | @@ -74,9 +74,7 @@ pub const Environment = struct { | ... | @@ -74,9 +74,7 @@ pub const Environment = struct { |
| 74 | pub const default: @This() = .{ .provided = 0 }; | 74 | pub const default: @This() = .{ .provided = 0 }; |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | /// 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 | 77 | pub fn loadAll(allocator: std.mem.Allocator, environ_map: *const std.process.Environ.Map) !Environment { |
| 78 | /// See https://github.com/ziglang/zig/issues/4524 | ||
| 79 | pub fn loadAll(allocator: std.mem.Allocator) !Environment { | ||
| 80 | var env: Environment = .{}; | 78 | var env: Environment = .{}; |
| 81 | errdefer env.deinit(allocator); | 79 | errdefer env.deinit(allocator); |
| 82 | 80 | ||
| ... | @@ -85,11 +83,7 @@ pub const Environment = struct { | ... | @@ -85,11 +83,7 @@ pub const Environment = struct { |
| 85 | 83 | ||
| 86 | var env_var_buf: [field.name.len]u8 = undefined; | 84 | var env_var_buf: [field.name.len]u8 = undefined; |
| 87 | const env_var_name = std.ascii.upperString(&env_var_buf, field.name); | 85 | const env_var_name = std.ascii.upperString(&env_var_buf, field.name); |
| 88 | const val: ?[]const u8 = std.process.getEnvVarOwned(allocator, env_var_name) catch |err| switch (err) { | 86 | const val: ?[]const u8 = if (environ_map.get(env_var_name)) |v| try allocator.dupe(u8, v) else null; |
| 89 | error.OutOfMemory => |e| return e, | ||
| 90 | error.EnvironmentVariableNotFound => null, | ||
| 91 | error.InvalidWtf8 => null, | ||
| 92 | }; | ||
| 93 | @field(env, field.name) = val; | 87 | @field(env, field.name) = val; |
| 94 | } | 88 | } |
| 95 | return env; | 89 | return env; |
| ... | @@ -193,13 +187,20 @@ pub fn init(gpa: Allocator, arena: Allocator, io: Io, diagnostics: *Diagnostics, | ... | @@ -193,13 +187,20 @@ pub fn init(gpa: Allocator, arena: Allocator, io: Io, diagnostics: *Diagnostics, |
| 193 | 187 | ||
| 194 | /// Initialize Compilation with default environment, | 188 | /// Initialize Compilation with default environment, |
| 195 | /// pragma handlers and emulation mode set to target. | 189 | /// pragma handlers and emulation mode set to target. |
| 196 | pub fn initDefault(gpa: Allocator, arena: Allocator, io: Io, diagnostics: *Diagnostics, cwd: Io.Dir) !Compilation { | 190 | pub fn initDefault( |
| 191 | gpa: Allocator, | ||
| 192 | arena: Allocator, | ||
| 193 | io: Io, | ||
| 194 | diagnostics: *Diagnostics, | ||
| 195 | cwd: Io.Dir, | ||
| 196 | environ_map: *const std.process.Environ.Map, | ||
| 197 | ) !Compilation { | ||
| 197 | var comp: Compilation = .{ | 198 | var comp: Compilation = .{ |
| 198 | .gpa = gpa, | 199 | .gpa = gpa, |
| 199 | .arena = arena, | 200 | .arena = arena, |
| 200 | .io = io, | 201 | .io = io, |
| 201 | .diagnostics = diagnostics, | 202 | .diagnostics = diagnostics, |
| 202 | .environment = try Environment.loadAll(gpa), | 203 | .environment = try Environment.loadAll(gpa, environ_map), |
| 203 | .cwd = cwd, | 204 | .cwd = cwd, |
| 204 | }; | 205 | }; |
| 205 | errdefer comp.deinit(); | 206 | errdefer comp.deinit(); |
lib/compiler/aro/aro/Driver.zig+24-16| ... | @@ -1250,21 +1250,25 @@ fn getOutFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u8) ! | ... | @@ -1250,21 +1250,25 @@ fn getOutFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u8) ! |
| 1250 | } | 1250 | } |
| 1251 | 1251 | ||
| 1252 | fn invokeAssembler(d: *Driver, tc: *Toolchain, input_path: []const u8, output_path: []const u8) !void { | 1252 | fn invokeAssembler(d: *Driver, tc: *Toolchain, input_path: []const u8, output_path: []const u8) !void { |
| 1253 | const io = d.comp.io; | ||
| 1253 | var assembler_path_buf: [std.fs.max_path_bytes]u8 = undefined; | 1254 | var assembler_path_buf: [std.fs.max_path_bytes]u8 = undefined; |
| 1254 | const assembler_path = try tc.getAssemblerPath(&assembler_path_buf); | 1255 | const assembler_path = try tc.getAssemblerPath(&assembler_path_buf); |
| 1255 | const argv = [_][]const u8{ assembler_path, input_path, "-o", output_path }; | 1256 | const argv = [_][]const u8{ assembler_path, input_path, "-o", output_path }; |
| 1256 | 1257 | ||
| 1257 | var child = std.process.Child.init(&argv, d.comp.gpa); | 1258 | var child = std.process.spawn(io, .{ |
| 1258 | // TODO handle better | 1259 | .argv = &argv, |
| 1259 | child.stdin_behavior = .Inherit; | 1260 | // TODO handle better |
| 1260 | child.stdout_behavior = .Inherit; | 1261 | .stdin = .inherit, |
| 1261 | child.stderr_behavior = .Inherit; | 1262 | .stdout = .inherit, |
| 1262 | 1263 | .stderr = .inherit, | |
| 1263 | const term = child.spawnAndWait() catch |er| { | 1264 | }) catch |er| { |
| 1264 | return d.fatal("unable to spawn linker: {s}", .{errorDescription(er)}); | 1265 | return d.fatal("unable to spawn linker: {s}", .{errorDescription(er)}); |
| 1265 | }; | 1266 | }; |
| 1267 | const term = child.wait(io) catch |er| { | ||
| 1268 | return d.fatal("unable to wait linker: {s}", .{errorDescription(er)}); | ||
| 1269 | }; | ||
| 1266 | switch (term) { | 1270 | switch (term) { |
| 1267 | .Exited => |code| if (code != 0) { | 1271 | .exited => |code| if (code != 0) { |
| 1268 | const e = d.fatal("assembler exited with an error code", .{}); | 1272 | const e = d.fatal("assembler exited with an error code", .{}); |
| 1269 | return e; | 1273 | return e; |
| 1270 | }, | 1274 | }, |
| ... | @@ -1490,6 +1494,7 @@ fn dumpLinkerArgs(w: *std.Io.Writer, items: []const []const u8) !void { | ... | @@ -1490,6 +1494,7 @@ fn dumpLinkerArgs(w: *std.Io.Writer, items: []const []const u8) !void { |
| 1490 | /// **MAY call `exit` if `fast_exit` is set.** | 1494 | /// **MAY call `exit` if `fast_exit` is set.** |
| 1491 | pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) Compilation.Error!void { | 1495 | pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) Compilation.Error!void { |
| 1492 | const gpa = d.comp.gpa; | 1496 | const gpa = d.comp.gpa; |
| 1497 | const io = d.comp.io; | ||
| 1493 | var argv: std.ArrayList([]const u8) = .empty; | 1498 | var argv: std.ArrayList([]const u8) = .empty; |
| 1494 | defer argv.deinit(gpa); | 1499 | defer argv.deinit(gpa); |
| 1495 | 1500 | ||
| ... | @@ -1506,17 +1511,20 @@ pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) Compil | ... | @@ -1506,17 +1511,20 @@ pub fn invokeLinker(d: *Driver, tc: *Toolchain, comptime fast_exit: bool) Compil |
| 1506 | return d.fatal("unable to dump linker args: {s}", .{errorDescription(stdout.err.?)}); | 1511 | return d.fatal("unable to dump linker args: {s}", .{errorDescription(stdout.err.?)}); |
| 1507 | }; | 1512 | }; |
| 1508 | } | 1513 | } |
| 1509 | var child = std.process.Child.init(argv.items, d.comp.gpa); | 1514 | var child = std.process.spawn(io, .{ |
| 1510 | // TODO handle better | 1515 | .argv = argv.items, |
| 1511 | child.stdin_behavior = .Inherit; | 1516 | // TODO handle better |
| 1512 | child.stdout_behavior = .Inherit; | 1517 | .stdin = .inherit, |
| 1513 | child.stderr_behavior = .Inherit; | 1518 | .stdout = .inherit, |
| 1514 | 1519 | .stderr = .inherit, | |
| 1515 | const term = child.spawnAndWait() catch |er| { | 1520 | }) catch |er| { |
| 1516 | return d.fatal("unable to spawn linker: {s}", .{errorDescription(er)}); | 1521 | return d.fatal("unable to spawn linker: {s}", .{errorDescription(er)}); |
| 1517 | }; | 1522 | }; |
| 1523 | const term = child.wait(io) catch |er| { | ||
| 1524 | return d.fatal("unable to wait linker: {s}", .{errorDescription(er)}); | ||
| 1525 | }; | ||
| 1518 | switch (term) { | 1526 | switch (term) { |
| 1519 | .Exited => |code| if (code != 0) { | 1527 | .exited => |code| if (code != 0) { |
| 1520 | const e = d.fatal("linker exited with an error code", .{}); | 1528 | const e = d.fatal("linker exited with an error code", .{}); |
| 1521 | if (fast_exit) d.exitWithCleanup(code); | 1529 | if (fast_exit) d.exitWithCleanup(code); |
| 1522 | return e; | 1530 | return e; |
lib/compiler/aro/main.zig+2-2| ... | @@ -18,7 +18,7 @@ var debug_allocator: std.heap.DebugAllocator(.{ | ... | @@ -18,7 +18,7 @@ var debug_allocator: std.heap.DebugAllocator(.{ |
| 18 | .canary = @truncate(0xc647026dc6875134), | 18 | .canary = @truncate(0xc647026dc6875134), |
| 19 | }) = .{}; | 19 | }) = .{}; |
| 20 | 20 | ||
| 21 | pub fn main() u8 { | 21 | pub fn main(init: std.process.Init.Minimal) u8 { |
| 22 | const gpa = if (@import("builtin").link_libc) | 22 | const gpa = if (@import("builtin").link_libc) |
| 23 | std.heap.c_allocator | 23 | std.heap.c_allocator |
| 24 | else | 24 | else |
| ... | @@ -37,7 +37,7 @@ pub fn main() u8 { | ... | @@ -37,7 +37,7 @@ pub fn main() u8 { |
| 37 | 37 | ||
| 38 | const fast_exit = @import("builtin").mode != .Debug; | 38 | const fast_exit = @import("builtin").mode != .Debug; |
| 39 | 39 | ||
| 40 | const args = process.argsAlloc(arena) catch { | 40 | const args = init.args.toSlice(arena) catch { |
| 41 | std.debug.print("out of memory\n", .{}); | 41 | std.debug.print("out of memory\n", .{}); |
| 42 | if (fast_exit) process.exit(1); | 42 | if (fast_exit) process.exit(1); |
| 43 | return 1; | 43 | return 1; |
lib/compiler/build_runner.zig+13-9| ... | @@ -24,7 +24,7 @@ pub const std_options: std.Options = .{ | ... | @@ -24,7 +24,7 @@ pub const std_options: std.Options = .{ |
| 24 | .crypto_fork_safety = false, | 24 | .crypto_fork_safety = false, |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | pub fn main() !void { | 27 | pub fn main(init: process.Init.Minimal) !void { |
| 28 | // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not | 28 | // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not |
| 29 | // always the case. So, we do need a true gpa for some things. | 29 | // always the case. So, we do need a true gpa for some things. |
| 30 | var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init; | 30 | var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init; |
| ... | @@ -37,9 +37,12 @@ pub fn main() !void { | ... | @@ -37,9 +37,12 @@ pub fn main() !void { |
| 37 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ .child_allocator = single_threaded_arena.allocator() }; | 37 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ .child_allocator = single_threaded_arena.allocator() }; |
| 38 | const arena = thread_safe_arena.allocator(); | 38 | const arena = thread_safe_arena.allocator(); |
| 39 | 39 | ||
| 40 | const args = try process.argsAlloc(arena); | 40 | const args = try init.args.toSlice(arena); |
| 41 | 41 | ||
| 42 | var threaded: std.Io.Threaded = .init(gpa, .{}); | 42 | var threaded: std.Io.Threaded = .init(gpa, .{ |
| 43 | .environ = init.environ, | ||
| 44 | .argv0 = .init(init.args), | ||
| 45 | }); | ||
| 43 | defer threaded.deinit(); | 46 | defer threaded.deinit(); |
| 44 | const io = threaded.io(); | 47 | const io = threaded.io(); |
| 45 | 48 | ||
| ... | @@ -81,9 +84,10 @@ pub fn main() !void { | ... | @@ -81,9 +84,10 @@ pub fn main() !void { |
| 81 | .io = io, | 84 | .io = io, |
| 82 | .gpa = arena, | 85 | .gpa = arena, |
| 83 | .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}), | 86 | .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}), |
| 87 | .cwd = try process.getCwdAlloc(single_threaded_arena.allocator()), | ||
| 84 | }, | 88 | }, |
| 85 | .zig_exe = zig_exe, | 89 | .zig_exe = zig_exe, |
| 86 | .env_map = try process.getEnvMap(arena), | 90 | .environ_map = try init.environ.createMap(arena), |
| 87 | .global_cache_root = global_cache_directory, | 91 | .global_cache_root = global_cache_directory, |
| 88 | .zig_lib_directory = zig_lib_directory, | 92 | .zig_lib_directory = zig_lib_directory, |
| 89 | .host = .{ | 93 | .host = .{ |
| ... | @@ -126,13 +130,13 @@ pub fn main() !void { | ... | @@ -126,13 +130,13 @@ pub fn main() !void { |
| 126 | var debounce_interval_ms: u16 = 50; | 130 | var debounce_interval_ms: u16 = 50; |
| 127 | var webui_listen: ?Io.net.IpAddress = null; | 131 | var webui_listen: ?Io.net.IpAddress = null; |
| 128 | 132 | ||
| 129 | if (try std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(arena)) |str| { | 133 | if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| { |
| 130 | if (std.meta.stringToEnum(ErrorStyle, str)) |style| { | 134 | if (std.meta.stringToEnum(ErrorStyle, str)) |style| { |
| 131 | error_style = style; | 135 | error_style = style; |
| 132 | } | 136 | } |
| 133 | } | 137 | } |
| 134 | 138 | ||
| 135 | if (try std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(arena)) |str| { | 139 | if (std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| { |
| 136 | if (std.meta.stringToEnum(MultilineErrors, str)) |style| { | 140 | if (std.meta.stringToEnum(MultilineErrors, str)) |style| { |
| 137 | multiline_errors = style; | 141 | multiline_errors = style; |
| 138 | } | 142 | } |
| ... | @@ -429,8 +433,8 @@ pub fn main() !void { | ... | @@ -429,8 +433,8 @@ pub fn main() !void { |
| 429 | } | 433 | } |
| 430 | } | 434 | } |
| 431 | 435 | ||
| 432 | const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(); | 436 | const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map); |
| 433 | const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(); | 437 | const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map); |
| 434 | 438 | ||
| 435 | graph.stderr_mode = switch (color) { | 439 | graph.stderr_mode = switch (color) { |
| 436 | .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE), | 440 | .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE), |
| ... | @@ -540,7 +544,7 @@ pub fn main() !void { | ... | @@ -540,7 +544,7 @@ pub fn main() !void { |
| 540 | var w: Watch = w: { | 544 | var w: Watch = w: { |
| 541 | if (!watch) break :w undefined; | 545 | if (!watch) break :w undefined; |
| 542 | if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag}); | 546 | if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag}); |
| 543 | break :w try .init(); | 547 | break :w try .init(graph.cache.cwd); |
| 544 | }; | 548 | }; |
| 545 | 549 | ||
| 546 | const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err}); | 550 | const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err}); |
lib/compiler/libc.zig+12-13| ... | @@ -24,23 +24,19 @@ const usage_libc = | ... | @@ -24,23 +24,19 @@ const usage_libc = |
| 24 | 24 | ||
| 25 | var stdout_buffer: [4096]u8 = undefined; | 25 | var stdout_buffer: [4096]u8 = undefined; |
| 26 | 26 | ||
| 27 | pub fn main() !void { | 27 | pub fn main(init: std.process.Init) !void { |
| 28 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 28 | const arena = init.arena.allocator(); |
| 29 | defer arena_instance.deinit(); | 29 | const gpa = init.gpa; |
| 30 | const arena = arena_instance.allocator(); | 30 | const io = init.io; |
| 31 | const gpa = arena; | 31 | const args = try init.minimal.args.toSlice(arena); |
| 32 | const environ_map = init.environ_map; | ||
| 32 | 33 | ||
| 33 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 34 | defer threaded.deinit(); | ||
| 35 | const io = threaded.io(); | ||
| 36 | |||
| 37 | const args = try std.process.argsAlloc(arena); | ||
| 38 | const zig_lib_directory = args[1]; | 34 | const zig_lib_directory = args[1]; |
| 39 | 35 | ||
| 40 | var input_file: ?[]const u8 = null; | 36 | var input_file: ?[]const u8 = null; |
| 41 | var target_arch_os_abi: []const u8 = "native"; | 37 | var target_arch_os_abi: []const u8 = "native"; |
| 42 | var print_includes: bool = false; | 38 | var print_includes: bool = false; |
| 43 | var stdout_writer = Io.File.stdout().writer(&stdout_buffer); | 39 | var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); |
| 44 | const stdout = &stdout_writer.interface; | 40 | const stdout = &stdout_writer.interface; |
| 45 | { | 41 | { |
| 46 | var i: usize = 2; | 42 | var i: usize = 2; |
| ... | @@ -77,7 +73,7 @@ pub fn main() !void { | ... | @@ -77,7 +73,7 @@ pub fn main() !void { |
| 77 | const libc_installation: ?*LibCInstallation = libc: { | 73 | const libc_installation: ?*LibCInstallation = libc: { |
| 78 | if (input_file) |libc_file| { | 74 | if (input_file) |libc_file| { |
| 79 | const libc = try arena.create(LibCInstallation); | 75 | const libc = try arena.create(LibCInstallation); |
| 80 | libc.* = LibCInstallation.parse(arena, libc_file, &target) catch |err| { | 76 | libc.* = LibCInstallation.parse(arena, io, libc_file, &target) catch |err| { |
| 81 | fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); | 77 | fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); |
| 82 | }; | 78 | }; |
| 83 | break :libc libc; | 79 | break :libc libc; |
| ... | @@ -90,11 +86,13 @@ pub fn main() !void { | ... | @@ -90,11 +86,13 @@ pub fn main() !void { |
| 90 | 86 | ||
| 91 | const libc_dirs = std.zig.LibCDirs.detect( | 87 | const libc_dirs = std.zig.LibCDirs.detect( |
| 92 | arena, | 88 | arena, |
| 89 | io, | ||
| 93 | zig_lib_directory, | 90 | zig_lib_directory, |
| 94 | &target, | 91 | &target, |
| 95 | is_native_abi, | 92 | is_native_abi, |
| 96 | true, | 93 | true, |
| 97 | libc_installation, | 94 | libc_installation, |
| 95 | environ_map, | ||
| 98 | ) catch |err| { | 96 | ) catch |err| { |
| 99 | const zig_target = try target.zigTriple(arena); | 97 | const zig_target = try target.zigTriple(arena); |
| 100 | fatal("unable to detect libc for target {s}: {t}", .{ zig_target, err }); | 98 | fatal("unable to detect libc for target {s}: {t}", .{ zig_target, err }); |
| ... | @@ -114,7 +112,7 @@ pub fn main() !void { | ... | @@ -114,7 +112,7 @@ pub fn main() !void { |
| 114 | } | 112 | } |
| 115 | 113 | ||
| 116 | if (input_file) |libc_file| { | 114 | if (input_file) |libc_file| { |
| 117 | var libc = LibCInstallation.parse(gpa, libc_file, &target) catch |err| { | 115 | var libc = LibCInstallation.parse(gpa, io, libc_file, &target) catch |err| { |
| 118 | fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); | 116 | fatal("unable to parse libc file at path {s}: {t}", .{ libc_file, err }); |
| 119 | }; | 117 | }; |
| 120 | defer libc.deinit(gpa); | 118 | defer libc.deinit(gpa); |
| ... | @@ -125,6 +123,7 @@ pub fn main() !void { | ... | @@ -125,6 +123,7 @@ pub fn main() !void { |
| 125 | var libc = LibCInstallation.findNative(gpa, io, .{ | 123 | var libc = LibCInstallation.findNative(gpa, io, .{ |
| 126 | .verbose = true, | 124 | .verbose = true, |
| 127 | .target = &target, | 125 | .target = &target, |
| 126 | .environ_map = environ_map, | ||
| 128 | }) catch |err| { | 127 | }) catch |err| { |
| 129 | fatal("unable to detect native libc: {t}", .{err}); | 128 | fatal("unable to detect native libc: {t}", .{err}); |
| 130 | }; | 129 | }; |
lib/compiler/objcopy.zig+4-14| ... | @@ -17,20 +17,10 @@ var stdout_buffer: [1024]u8 = undefined; | ... | @@ -17,20 +17,10 @@ var stdout_buffer: [1024]u8 = undefined; |
| 17 | var input_buffer: [1024]u8 = undefined; | 17 | var input_buffer: [1024]u8 = undefined; |
| 18 | var output_buffer: [1024]u8 = undefined; | 18 | var output_buffer: [1024]u8 = undefined; |
| 19 | 19 | ||
| 20 | pub fn main() !void { | 20 | pub fn main(init: std.process.Init) !void { |
| 21 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 21 | const arena = init.arena.allocator(); |
| 22 | defer arena_instance.deinit(); | 22 | const args = try init.minimal.args.toSlice(arena); |
| 23 | const arena = arena_instance.allocator(); | 23 | return cmdObjCopy(arena, init.io, args[1..]); |
| 24 | |||
| 25 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | ||
| 26 | const gpa = general_purpose_allocator.allocator(); | ||
| 27 | |||
| 28 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 29 | defer threaded.deinit(); | ||
| 30 | const io = threaded.io(); | ||
| 31 | |||
| 32 | const args = try std.process.argsAlloc(arena); | ||
| 33 | return cmdObjCopy(arena, io, args[1..]); | ||
| 34 | } | 24 | } |
| 35 | 25 | ||
| 36 | fn cmdObjCopy(arena: Allocator, io: Io, args: []const []const u8) !void { | 26 | fn cmdObjCopy(arena: Allocator, io: Io, args: []const []const u8) !void { |
lib/compiler/reduce.zig+17-24| ... | @@ -47,19 +47,11 @@ const Interestingness = enum { interesting, unknown, boring }; | ... | @@ -47,19 +47,11 @@ const Interestingness = enum { interesting, unknown, boring }; |
| 47 | // - reduce flags sent to the compiler | 47 | // - reduce flags sent to the compiler |
| 48 | // - integrate with the build system? | 48 | // - integrate with the build system? |
| 49 | 49 | ||
| 50 | pub fn main() !void { | 50 | pub fn main(init: std.process.Init) !void { |
| 51 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 51 | const arena = init.arena.allocator(); |
| 52 | defer arena_instance.deinit(); | 52 | const gpa = init.gpa; |
| 53 | const arena = arena_instance.allocator(); | 53 | const io = init.io; |
| 54 | 54 | const args = try init.minimal.args.toSlice(arena); | |
| 55 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | ||
| 56 | const gpa = general_purpose_allocator.allocator(); | ||
| 57 | |||
| 58 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 59 | defer threaded.deinit(); | ||
| 60 | const io = threaded.io(); | ||
| 61 | |||
| 62 | const args = try std.process.argsAlloc(arena); | ||
| 63 | 55 | ||
| 64 | var opt_checker_path: ?[]const u8 = null; | 56 | var opt_checker_path: ?[]const u8 = null; |
| 65 | var opt_root_source_file_path: ?[]const u8 = null; | 57 | var opt_root_source_file_path: ?[]const u8 = null; |
| ... | @@ -73,8 +65,7 @@ pub fn main() !void { | ... | @@ -73,8 +65,7 @@ pub fn main() !void { |
| 73 | const arg = args[i]; | 65 | const arg = args[i]; |
| 74 | if (mem.startsWith(u8, arg, "-")) { | 66 | if (mem.startsWith(u8, arg, "-")) { |
| 75 | if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { | 67 | if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { |
| 76 | const stdout = Io.File.stdout(); | 68 | try Io.File.stdout().writeStreamingAll(io, usage); |
| 77 | try stdout.writeAll(usage); | ||
| 78 | return std.process.cleanExit(io); | 69 | return std.process.cleanExit(io); |
| 79 | } else if (mem.eql(u8, arg, "--")) { | 70 | } else if (mem.eql(u8, arg, "--")) { |
| 80 | argv = args[i + 1 ..]; | 71 | argv = args[i + 1 ..]; |
| ... | @@ -131,12 +122,10 @@ pub fn main() !void { | ... | @@ -131,12 +122,10 @@ pub fn main() !void { |
| 131 | 122 | ||
| 132 | if (!skip_smoke_test) { | 123 | if (!skip_smoke_test) { |
| 133 | std.debug.print("smoke testing the interestingness check...\n", .{}); | 124 | std.debug.print("smoke testing the interestingness check...\n", .{}); |
| 134 | switch (try runCheck(arena, interestingness_argv.items)) { | 125 | switch (try runCheck(arena, io, interestingness_argv.items)) { |
| 135 | .interesting => {}, | 126 | .interesting => {}, |
| 136 | .boring, .unknown => |t| { | 127 | .boring, .unknown => |t| { |
| 137 | fatal("interestingness check returned {s} for unmodified input\n", .{ | 128 | fatal("interestingness check returned {t} for unmodified input\n", .{t}); |
| 138 | @tagName(t), | ||
| 139 | }); | ||
| 140 | }, | 129 | }, |
| 141 | } | 130 | } |
| 142 | } | 131 | } |
| ... | @@ -238,7 +227,7 @@ pub fn main() !void { | ... | @@ -238,7 +227,7 @@ pub fn main() !void { |
| 238 | try Io.Dir.cwd().writeFile(io, .{ .sub_path = root_source_file_path, .data = rendered.written() }); | 227 | try Io.Dir.cwd().writeFile(io, .{ .sub_path = root_source_file_path, .data = rendered.written() }); |
| 239 | // std.debug.print("trying this code:\n{s}\n", .{rendered.items}); | 228 | // std.debug.print("trying this code:\n{s}\n", .{rendered.items}); |
| 240 | 229 | ||
| 241 | const interestingness = try runCheck(arena, interestingness_argv.items); | 230 | const interestingness = try runCheck(arena, io, interestingness_argv.items); |
| 242 | std.debug.print("{d} random transformations: {t}. {d}/{d}\n", .{ | 231 | std.debug.print("{d} random transformations: {t}. {d}/{d}\n", .{ |
| 243 | subset_size, interestingness, start_index, transformations.items.len, | 232 | subset_size, interestingness, start_index, transformations.items.len, |
| 244 | }); | 233 | }); |
| ... | @@ -293,20 +282,24 @@ fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random) | ... | @@ -293,20 +282,24 @@ fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random) |
| 293 | 282 | ||
| 294 | fn termToInteresting(term: std.process.Child.Term) Interestingness { | 283 | fn termToInteresting(term: std.process.Child.Term) Interestingness { |
| 295 | return switch (term) { | 284 | return switch (term) { |
| 296 | .Exited => |code| switch (code) { | 285 | .exited => |code| switch (code) { |
| 297 | 0 => .interesting, | 286 | 0 => .interesting, |
| 298 | 1 => .unknown, | 287 | 1 => .unknown, |
| 299 | else => .boring, | 288 | else => .boring, |
| 300 | }, | 289 | }, |
| 301 | else => b: { | 290 | .signal => |sig| { |
| 291 | std.debug.print("interestingness check terminated with signal {t}\n", .{sig}); | ||
| 292 | return .boring; | ||
| 293 | }, | ||
| 294 | else => { | ||
| 302 | std.debug.print("interestingness check aborted unexpectedly\n", .{}); | 295 | std.debug.print("interestingness check aborted unexpectedly\n", .{}); |
| 303 | break :b .boring; | 296 | return .boring; |
| 304 | }, | 297 | }, |
| 305 | }; | 298 | }; |
| 306 | } | 299 | } |
| 307 | 300 | ||
| 308 | fn runCheck(arena: Allocator, io: Io, argv: []const []const u8) !Interestingness { | 301 | fn runCheck(arena: Allocator, io: Io, argv: []const []const u8) !Interestingness { |
| 309 | const result = try std.process.Child.run(arena, io, .{ .argv = argv }); | 302 | const result = try std.process.run(arena, io, .{ .argv = argv }); |
| 310 | if (result.stderr.len != 0) | 303 | if (result.stderr.len != 0) |
| 311 | std.debug.print("{s}", .{result.stderr}); | 304 | std.debug.print("{s}", .{result.stderr}); |
| 312 | return termToInteresting(result.term); | 305 | return termToInteresting(result.term); |
lib/compiler/resinator/compile.zig+2-3| ... | @@ -80,7 +80,7 @@ pub const Dependencies = struct { | ... | @@ -80,7 +80,7 @@ pub const Dependencies = struct { |
| 80 | } | 80 | } |
| 81 | }; | 81 | }; |
| 82 | 82 | ||
| 83 | pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io.Writer, options: CompileOptions) !void { | 83 | pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io.Writer, options: CompileOptions, environ_map: *const std.process.Environ.Map) !void { |
| 84 | var lexer = lex.Lexer.init(source, .{ | 84 | var lexer = lex.Lexer.init(source, .{ |
| 85 | .default_code_page = options.default_code_page, | 85 | .default_code_page = options.default_code_page, |
| 86 | .source_mappings = options.source_mappings, | 86 | .source_mappings = options.source_mappings, |
| ... | @@ -148,8 +148,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io | ... | @@ -148,8 +148,7 @@ pub fn compile(allocator: Allocator, io: Io, source: []const u8, writer: *std.Io |
| 148 | try search_dirs.append(allocator, .{ .dir = dir, .path = try allocator.dupe(u8, system_include_path) }); | 148 | try search_dirs.append(allocator, .{ .dir = dir, .path = try allocator.dupe(u8, system_include_path) }); |
| 149 | } | 149 | } |
| 150 | if (!options.ignore_include_env_var) { | 150 | if (!options.ignore_include_env_var) { |
| 151 | const INCLUDE = std.process.getEnvVarOwned(allocator, "INCLUDE") catch ""; | 151 | const INCLUDE = environ_map.get("INCLUDE") orelse ""; |
| 152 | defer allocator.free(INCLUDE); | ||
| 153 | 152 | ||
| 154 | // The only precedence here is llvm-rc which also uses the platform-specific | 153 | // The only precedence here is llvm-rc which also uses the platform-specific |
| 155 | // delimiter. There's no precedence set by `rc.exe` since it's Windows-only. | 154 | // delimiter. There's no precedence set by `rc.exe` since it's Windows-only. |
lib/compiler/resinator/main.zig+38-11| ... | @@ -19,12 +19,18 @@ const fmtResourceType = @import("res.zig").NameOrOrdinal.fmtResourceType; | ... | @@ -19,12 +19,18 @@ const fmtResourceType = @import("res.zig").NameOrOrdinal.fmtResourceType; |
| 19 | const aro = @import("aro"); | 19 | const aro = @import("aro"); |
| 20 | const compiler_util = @import("../util.zig"); | 20 | const compiler_util = @import("../util.zig"); |
| 21 | 21 | ||
| 22 | pub fn main() !void { | 22 | pub fn main(init: std.process.Init.Minimal) !void { |
| 23 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | 23 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; |
| 24 | defer std.debug.assert(debug_allocator.deinit() == .ok); | 24 | defer std.debug.assert(debug_allocator.deinit() == .ok); |
| 25 | const gpa = debug_allocator.allocator(); | 25 | const gpa = debug_allocator.allocator(); |
| 26 | 26 | ||
| 27 | var threaded: std.Io.Threaded = .init(gpa, .{}); | 27 | var environ_map = try init.environ.createMap(gpa); |
| 28 | defer environ_map.deinit(); | ||
| 29 | |||
| 30 | var threaded: std.Io.Threaded = .init(gpa, .{ | ||
| 31 | .environ = init.environ, | ||
| 32 | .argv0 = .init(init.args), | ||
| 33 | }); | ||
| 28 | defer threaded.deinit(); | 34 | defer threaded.deinit(); |
| 29 | const io = threaded.io(); | 35 | const io = threaded.io(); |
| 30 | 36 | ||
| ... | @@ -32,7 +38,7 @@ pub fn main() !void { | ... | @@ -32,7 +38,7 @@ pub fn main() !void { |
| 32 | defer arena_state.deinit(); | 38 | defer arena_state.deinit(); |
| 33 | const arena = arena_state.allocator(); | 39 | const arena = arena_state.allocator(); |
| 34 | 40 | ||
| 35 | const args = try std.process.argsAlloc(arena); | 41 | const args = try init.args.toSlice(arena); |
| 36 | 42 | ||
| 37 | if (args.len < 2) { | 43 | if (args.len < 2) { |
| 38 | const stderr = try io.lockStderr(&.{}, null); | 44 | const stderr = try io.lockStderr(&.{}, null); |
| ... | @@ -145,8 +151,8 @@ pub fn main() !void { | ... | @@ -145,8 +151,8 @@ pub fn main() !void { |
| 145 | defer argv.deinit(aro_arena); | 151 | defer argv.deinit(aro_arena); |
| 146 | 152 | ||
| 147 | try argv.append(aro_arena, "arocc"); // dummy command name | 153 | try argv.append(aro_arena, "arocc"); // dummy command name |
| 148 | const resolved_include_paths = try include_paths.get(&error_handler); | 154 | const resolved_include_paths = try include_paths.get(&error_handler, &environ_map); |
| 149 | try preprocess.appendAroArgs(aro_arena, &argv, options, resolved_include_paths); | 155 | try preprocess.appendAroArgs(aro_arena, &argv, options, resolved_include_paths, &environ_map); |
| 150 | try argv.append(aro_arena, switch (options.input_source) { | 156 | try argv.append(aro_arena, switch (options.input_source) { |
| 151 | .stdio => "-", | 157 | .stdio => "-", |
| 152 | .filename => |filename| filename, | 158 | .filename => |filename| filename, |
| ... | @@ -280,7 +286,7 @@ pub fn main() !void { | ... | @@ -280,7 +286,7 @@ pub fn main() !void { |
| 280 | .dependencies = maybe_dependencies, | 286 | .dependencies = maybe_dependencies, |
| 281 | .ignore_include_env_var = options.ignore_include_env_var, | 287 | .ignore_include_env_var = options.ignore_include_env_var, |
| 282 | .extra_include_paths = options.extra_include_paths.items, | 288 | .extra_include_paths = options.extra_include_paths.items, |
| 283 | .system_include_paths = try include_paths.get(&error_handler), | 289 | .system_include_paths = try include_paths.get(&error_handler, &environ_map), |
| 284 | .default_language_id = options.default_language_id, | 290 | .default_language_id = options.default_language_id, |
| 285 | .default_code_page = default_code_page, | 291 | .default_code_page = default_code_page, |
| 286 | .disjoint_code_page = has_disjoint_code_page, | 292 | .disjoint_code_page = has_disjoint_code_page, |
| ... | @@ -289,7 +295,7 @@ pub fn main() !void { | ... | @@ -289,7 +295,7 @@ pub fn main() !void { |
| 289 | .max_string_literal_codepoints = options.max_string_literal_codepoints, | 295 | .max_string_literal_codepoints = options.max_string_literal_codepoints, |
| 290 | .silent_duplicate_control_ids = options.silent_duplicate_control_ids, | 296 | .silent_duplicate_control_ids = options.silent_duplicate_control_ids, |
| 291 | .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page, | 297 | .warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page, |
| 292 | }) catch |err| switch (err) { | 298 | }, &environ_map) catch |err| switch (err) { |
| 293 | error.ParseError, error.CompileError => { | 299 | error.ParseError, error.CompileError => { |
| 294 | try error_handler.emitDiagnostics(gpa, Io.Dir.cwd(), final_input, &diagnostics, mapping_results.mappings); | 300 | try error_handler.emitDiagnostics(gpa, Io.Dir.cwd(), final_input, &diagnostics, mapping_results.mappings); |
| 295 | // Delete the output file on error | 301 | // Delete the output file on error |
| ... | @@ -536,13 +542,24 @@ const LazyIncludePaths = struct { | ... | @@ -536,13 +542,24 @@ const LazyIncludePaths = struct { |
| 536 | target_machine_type: std.coff.IMAGE.FILE.MACHINE, | 542 | target_machine_type: std.coff.IMAGE.FILE.MACHINE, |
| 537 | resolved_include_paths: ?[]const []const u8 = null, | 543 | resolved_include_paths: ?[]const []const u8 = null, |
| 538 | 544 | ||
| 539 | pub fn get(self: *LazyIncludePaths, error_handler: *ErrorHandler) ![]const []const u8 { | 545 | pub fn get( |
| 546 | self: *LazyIncludePaths, | ||
| 547 | error_handler: *ErrorHandler, | ||
| 548 | environ_map: *const std.process.Environ.Map, | ||
| 549 | ) ![]const []const u8 { | ||
| 540 | const io = self.io; | 550 | const io = self.io; |
| 541 | 551 | ||
| 542 | if (self.resolved_include_paths) |include_paths| | 552 | if (self.resolved_include_paths) |include_paths| |
| 543 | return include_paths; | 553 | return include_paths; |
| 544 | 554 | ||
| 545 | return getIncludePaths(self.arena, io, self.auto_includes_option, self.zig_lib_dir, self.target_machine_type) catch |err| switch (err) { | 555 | return getIncludePaths( |
| 556 | self.arena, | ||
| 557 | io, | ||
| 558 | self.auto_includes_option, | ||
| 559 | self.zig_lib_dir, | ||
| 560 | self.target_machine_type, | ||
| 561 | environ_map, | ||
| 562 | ) catch |err| switch (err) { | ||
| 546 | error.OutOfMemory => |e| return e, | 563 | error.OutOfMemory => |e| return e, |
| 547 | else => |e| { | 564 | else => |e| { |
| 548 | switch (e) { | 565 | switch (e) { |
| ... | @@ -569,6 +586,7 @@ fn getIncludePaths( | ... | @@ -569,6 +586,7 @@ fn getIncludePaths( |
| 569 | auto_includes_option: cli.Options.AutoIncludes, | 586 | auto_includes_option: cli.Options.AutoIncludes, |
| 570 | zig_lib_dir: []const u8, | 587 | zig_lib_dir: []const u8, |
| 571 | target_machine_type: std.coff.IMAGE.FILE.MACHINE, | 588 | target_machine_type: std.coff.IMAGE.FILE.MACHINE, |
| 589 | environ_map: *const std.process.Environ.Map, | ||
| 572 | ) ![]const []const u8 { | 590 | ) ![]const []const u8 { |
| 573 | if (auto_includes_option == .none) return &[_][]const u8{}; | 591 | if (auto_includes_option == .none) return &[_][]const u8{}; |
| 574 | 592 | ||
| ... | @@ -615,7 +633,7 @@ fn getIncludePaths( | ... | @@ -615,7 +633,7 @@ fn getIncludePaths( |
| 615 | }; | 633 | }; |
| 616 | const target = std.zig.resolveTargetQueryOrFatal(io, target_query); | 634 | const target = std.zig.resolveTargetQueryOrFatal(io, target_query); |
| 617 | const is_native_abi = target_query.isNativeAbi(); | 635 | const is_native_abi = target_query.isNativeAbi(); |
| 618 | const detected_libc = std.zig.LibCDirs.detect(arena, io, zig_lib_dir, &target, is_native_abi, true, null) catch { | 636 | const detected_libc = std.zig.LibCDirs.detect(arena, io, zig_lib_dir, &target, is_native_abi, true, null, environ_map) catch { |
| 619 | if (includes == .any) { | 637 | if (includes == .any) { |
| 620 | // fall back to mingw | 638 | // fall back to mingw |
| 621 | includes = .gnu; | 639 | includes = .gnu; |
| ... | @@ -641,7 +659,16 @@ fn getIncludePaths( | ... | @@ -641,7 +659,16 @@ fn getIncludePaths( |
| 641 | }; | 659 | }; |
| 642 | const target = std.zig.resolveTargetQueryOrFatal(io, target_query); | 660 | const target = std.zig.resolveTargetQueryOrFatal(io, target_query); |
| 643 | const is_native_abi = target_query.isNativeAbi(); | 661 | const is_native_abi = target_query.isNativeAbi(); |
| 644 | const detected_libc = std.zig.LibCDirs.detect(arena, io, zig_lib_dir, &target, is_native_abi, true, null) catch |err| switch (err) { | 662 | const detected_libc = std.zig.LibCDirs.detect( |
| 663 | arena, | ||
| 664 | io, | ||
| 665 | zig_lib_dir, | ||
| 666 | &target, | ||
| 667 | is_native_abi, | ||
| 668 | true, | ||
| 669 | null, | ||
| 670 | environ_map, | ||
| 671 | ) catch |err| switch (err) { | ||
| 645 | error.OutOfMemory => |e| return e, | 672 | error.OutOfMemory => |e| return e, |
| 646 | else => return error.MingwIncludesNotFound, | 673 | else => return error.MingwIncludesNotFound, |
| 647 | }; | 674 | }; |
lib/compiler/resinator/preprocess.zig+2-2| ... | @@ -86,7 +86,7 @@ fn hasAnyErrors(comp: *aro.Compilation) bool { | ... | @@ -86,7 +86,7 @@ fn hasAnyErrors(comp: *aro.Compilation) bool { |
| 86 | 86 | ||
| 87 | /// `arena` is used for temporary -D argument strings and the INCLUDE environment variable. | 87 | /// `arena` is used for temporary -D argument strings and the INCLUDE environment variable. |
| 88 | /// The arena should be kept alive at least as long as `argv`. | 88 | /// The arena should be kept alive at least as long as `argv`. |
| 89 | pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options: cli.Options, system_include_paths: []const []const u8) !void { | 89 | 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 { |
| 90 | try argv.appendSlice(arena, &.{ | 90 | try argv.appendSlice(arena, &.{ |
| 91 | "-E", | 91 | "-E", |
| 92 | "--comments", | 92 | "--comments", |
| ... | @@ -109,7 +109,7 @@ pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options | ... | @@ -109,7 +109,7 @@ pub fn appendAroArgs(arena: Allocator, argv: *std.ArrayList([]const u8), options |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | if (!options.ignore_include_env_var) { | 111 | if (!options.ignore_include_env_var) { |
| 112 | const INCLUDE = std.process.getEnvVarOwned(arena, "INCLUDE") catch ""; | 112 | const INCLUDE = environ_map.get("INCLUDE") orelse ""; |
| 113 | 113 | ||
| 114 | // The only precedence here is llvm-rc which also uses the platform-specific | 114 | // The only precedence here is llvm-rc which also uses the platform-specific |
| 115 | // delimiter. There's no precedence set by `rc.exe` since it's Windows-only. | 115 | // delimiter. There's no precedence set by `rc.exe` since it's Windows-only. |
lib/compiler/std-docs.zig+36-33| ... | @@ -21,19 +21,12 @@ fn usage(io: Io) noreturn { | ... | @@ -21,19 +21,12 @@ fn usage(io: Io) noreturn { |
| 21 | std.process.exit(1); | 21 | std.process.exit(1); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | pub fn main() !void { | 24 | pub fn main(init: std.process.Init) !void { |
| 25 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 25 | const arena = init.arena.allocator(); |
| 26 | defer arena_instance.deinit(); | 26 | const gpa = init.gpa; |
| 27 | const arena = arena_instance.allocator(); | 27 | const io = init.io; |
| 28 | |||
| 29 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | ||
| 30 | const gpa = general_purpose_allocator.allocator(); | ||
| 31 | 28 | ||
| 32 | var threaded: Io.Threaded = .init(gpa, .{}); | 29 | var argv = try init.minimal.args.iterateAllocator(arena); |
| 33 | defer threaded.deinit(); | ||
| 34 | const io = threaded.io(); | ||
| 35 | |||
| 36 | var argv = try std.process.argsWithAllocator(arena); | ||
| 37 | defer argv.deinit(); | 30 | defer argv.deinit(); |
| 38 | assert(argv.skip()); | 31 | assert(argv.skip()); |
| 39 | const zig_lib_directory = argv.next().?; | 32 | const zig_lib_directory = argv.next().?; |
| ... | @@ -72,7 +65,7 @@ pub fn main() !void { | ... | @@ -72,7 +65,7 @@ pub fn main() !void { |
| 72 | const url_with_newline = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/\n", .{port}); | 65 | const url_with_newline = try std.fmt.allocPrint(arena, "http://127.0.0.1:{d}/\n", .{port}); |
| 73 | Io.File.stdout().writeStreamingAll(io, url_with_newline) catch {}; | 66 | Io.File.stdout().writeStreamingAll(io, url_with_newline) catch {}; |
| 74 | if (should_open_browser) { | 67 | if (should_open_browser) { |
| 75 | openBrowserTab(gpa, io, url_with_newline[0 .. url_with_newline.len - 1 :'\n']) catch |err| { | 68 | openBrowserTab(io, url_with_newline[0 .. url_with_newline.len - 1 :'\n']) catch |err| { |
| 76 | std.log.err("unable to open browser: {t}", .{err}); | 69 | std.log.err("unable to open browser: {t}", .{err}); |
| 77 | }; | 70 | }; |
| 78 | } | 71 | } |
| ... | @@ -324,11 +317,12 @@ fn buildWasmBinary( | ... | @@ -324,11 +317,12 @@ fn buildWasmBinary( |
| 324 | "--listen=-", // | 317 | "--listen=-", // |
| 325 | }); | 318 | }); |
| 326 | 319 | ||
| 327 | var child = std.process.Child.init(argv.items, gpa); | 320 | var child = try std.process.spawn(io, .{ |
| 328 | child.stdin_behavior = .Pipe; | 321 | .argv = argv.items, |
| 329 | child.stdout_behavior = .Pipe; | 322 | .stdin = .pipe, |
| 330 | child.stderr_behavior = .Pipe; | 323 | .stdout = .pipe, |
| 331 | try child.spawn(io); | 324 | .stderr = .pipe, |
| 325 | }); | ||
| 332 | 326 | ||
| 333 | var poller = Io.poll(gpa, enum { stdout, stderr }, .{ | 327 | var poller = Io.poll(gpa, enum { stdout, stderr }, .{ |
| 334 | .stdout = child.stdout.?, | 328 | .stdout = child.stdout.?, |
| ... | @@ -388,19 +382,26 @@ fn buildWasmBinary( | ... | @@ -388,19 +382,26 @@ fn buildWasmBinary( |
| 388 | child.stdin = null; | 382 | child.stdin = null; |
| 389 | 383 | ||
| 390 | switch (try child.wait(io)) { | 384 | switch (try child.wait(io)) { |
| 391 | .Exited => |code| { | 385 | .exited => |code| { |
| 392 | if (code != 0) { | 386 | if (code != 0) { |
| 393 | std.log.err( | 387 | std.log.err( |
| 394 | "the following command exited with error code {d}:\n{s}", | 388 | "the following command exited with error code {d}:\n{s}", |
| 395 | .{ code, try std.Build.Step.allocPrintCmd(arena, null, argv.items) }, | 389 | .{ code, try std.Build.Step.allocPrintCmd(arena, null, null, argv.items) }, |
| 396 | ); | 390 | ); |
| 397 | return error.WasmCompilationFailed; | 391 | return error.WasmCompilationFailed; |
| 398 | } | 392 | } |
| 399 | }, | 393 | }, |
| 400 | .Signal, .Stopped, .Unknown => { | 394 | .signal => |sig| { |
| 395 | std.log.err( | ||
| 396 | "the following command terminated with signal {t}:\n{s}", | ||
| 397 | .{ sig, try std.Build.Step.allocPrintCmd(arena, null, null, argv.items) }, | ||
| 398 | ); | ||
| 399 | return error.WasmCompilationFailed; | ||
| 400 | }, | ||
| 401 | .stopped, .unknown => { | ||
| 401 | std.log.err( | 402 | std.log.err( |
| 402 | "the following command terminated unexpectedly:\n{s}", | 403 | "the following command terminated unexpectedly:\n{s}", |
| 403 | .{try std.Build.Step.allocPrintCmd(arena, null, argv.items)}, | 404 | .{try std.Build.Step.allocPrintCmd(arena, null, null, argv.items)}, |
| 404 | ); | 405 | ); |
| 405 | return error.WasmCompilationFailed; | 406 | return error.WasmCompilationFailed; |
| 406 | }, | 407 | }, |
| ... | @@ -410,14 +411,14 @@ fn buildWasmBinary( | ... | @@ -410,14 +411,14 @@ fn buildWasmBinary( |
| 410 | try result_error_bundle.renderToStderr(io, .{}, .auto); | 411 | try result_error_bundle.renderToStderr(io, .{}, .auto); |
| 411 | std.log.err("the following command failed with {d} compilation errors:\n{s}", .{ | 412 | std.log.err("the following command failed with {d} compilation errors:\n{s}", .{ |
| 412 | result_error_bundle.errorMessageCount(), | 413 | result_error_bundle.errorMessageCount(), |
| 413 | try std.Build.Step.allocPrintCmd(arena, null, argv.items), | 414 | try std.Build.Step.allocPrintCmd(arena, null, null, argv.items), |
| 414 | }); | 415 | }); |
| 415 | return error.WasmCompilationFailed; | 416 | return error.WasmCompilationFailed; |
| 416 | } | 417 | } |
| 417 | 418 | ||
| 418 | return result orelse { | 419 | return result orelse { |
| 419 | std.log.err("child process failed to report result\n{s}", .{ | 420 | std.log.err("child process failed to report result\n{s}", .{ |
| 420 | try std.Build.Step.allocPrintCmd(arena, null, argv.items), | 421 | try std.Build.Step.allocPrintCmd(arena, null, null, argv.items), |
| 421 | }); | 422 | }); |
| 422 | return error.WasmCompilationFailed; | 423 | return error.WasmCompilationFailed; |
| 423 | }; | 424 | }; |
| ... | @@ -434,22 +435,24 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { | ... | @@ -434,22 +435,24 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { |
| 434 | }; | 435 | }; |
| 435 | } | 436 | } |
| 436 | 437 | ||
| 437 | fn openBrowserTab(gpa: Allocator, io: Io, url: []const u8) !void { | 438 | fn openBrowserTab(io: Io, url: []const u8) !void { |
| 438 | // Until https://github.com/ziglang/zig/issues/19205 is implemented, we | 439 | // Until https://github.com/ziglang/zig/issues/19205 is implemented, we |
| 439 | // spawn a thread for this child process. | 440 | // spawn and then leak a concurrent task for this child process. |
| 440 | _ = try std.Thread.spawn(.{}, openBrowserTabThread, .{ gpa, io, url }); | 441 | const future = try io.concurrent(openBrowserTabTask, .{ io, url }); |
| 442 | _ = future; // leak it | ||
| 441 | } | 443 | } |
| 442 | 444 | ||
| 443 | fn openBrowserTabThread(gpa: Allocator, io: Io, url: []const u8) !void { | 445 | fn openBrowserTabTask(io: Io, url: []const u8) !void { |
| 444 | const main_exe = switch (builtin.os.tag) { | 446 | const main_exe = switch (builtin.os.tag) { |
| 445 | .windows => "explorer", | 447 | .windows => "explorer", |
| 446 | .macos => "open", | 448 | .macos => "open", |
| 447 | else => "xdg-open", | 449 | else => "xdg-open", |
| 448 | }; | 450 | }; |
| 449 | var child = std.process.Child.init(&.{ main_exe, url }, gpa); | 451 | var child = try std.process.spawn(io, .{ |
| 450 | child.stdin_behavior = .Ignore; | 452 | .argv = &.{ main_exe, url }, |
| 451 | child.stdout_behavior = .Ignore; | 453 | .stdin = .ignore, |
| 452 | child.stderr_behavior = .Ignore; | 454 | .stdout = .ignore, |
| 453 | try child.spawn(io); | 455 | .stderr = .ignore, |
| 456 | }); | ||
| 454 | _ = try child.wait(io); | 457 | _ = try child.wait(io); |
| 455 | } | 458 | } |
lib/compiler/test_runner.zig+11-10| ... | @@ -29,7 +29,7 @@ const need_simple = switch (builtin.zig_backend) { | ... | @@ -29,7 +29,7 @@ const need_simple = switch (builtin.zig_backend) { |
| 29 | else => false, | 29 | else => false, |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | pub fn main() void { | 32 | pub fn main(init: std.process.Init.Minimal) void { |
| 33 | @disableInstrumentation(); | 33 | @disableInstrumentation(); |
| 34 | 34 | ||
| 35 | if (builtin.cpu.arch.isSpirV()) { | 35 | if (builtin.cpu.arch.isSpirV()) { |
| ... | @@ -38,11 +38,10 @@ pub fn main() void { | ... | @@ -38,11 +38,10 @@ pub fn main() void { |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | if (need_simple) { | 40 | if (need_simple) { |
| 41 | return mainSimple() catch @panic("test failure\n"); | 41 | return mainSimple() catch @panic("test failure"); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | const args = std.process.argsAlloc(fba.allocator()) catch | 44 | const args = init.args.toSlice(fba.allocator()) catch @panic("unable to parse command line args"); |
| 45 | @panic("unable to parse command line args"); | ||
| 46 | 45 | ||
| 47 | var listen = false; | 46 | var listen = false; |
| 48 | var opt_cache_dir: ?[]const u8 = null; | 47 | var opt_cache_dir: ?[]const u8 = null; |
| ... | @@ -66,13 +65,13 @@ pub fn main() void { | ... | @@ -66,13 +65,13 @@ pub fn main() void { |
| 66 | } | 65 | } |
| 67 | 66 | ||
| 68 | if (listen) { | 67 | if (listen) { |
| 69 | return mainServer(args) catch @panic("internal test runner failure"); | 68 | return mainServer(init) catch @panic("internal test runner failure"); |
| 70 | } else { | 69 | } else { |
| 71 | return mainTerminal(args); | 70 | return mainTerminal(init); |
| 72 | } | 71 | } |
| 73 | } | 72 | } |
| 74 | 73 | ||
| 75 | fn mainServer(args: []const [:0]const u8) !void { | 74 | fn mainServer(init: std.process.Init.Minimal) !void { |
| 76 | @disableInstrumentation(); | 75 | @disableInstrumentation(); |
| 77 | var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io, &stdin_buffer); | 76 | var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io, &stdin_buffer); |
| 78 | var stdout_writer = Io.File.stdout().writerStreaming(runner_threaded_io, &stdout_buffer); | 77 | var stdout_writer = Io.File.stdout().writerStreaming(runner_threaded_io, &stdout_buffer); |
| ... | @@ -132,7 +131,8 @@ fn mainServer(args: []const [:0]const u8) !void { | ... | @@ -132,7 +131,8 @@ fn mainServer(args: []const [:0]const u8) !void { |
| 132 | .run_test => { | 131 | .run_test => { |
| 133 | testing.allocator_instance = .{}; | 132 | testing.allocator_instance = .{}; |
| 134 | testing.io_instance = .init(testing.allocator, .{ | 133 | testing.io_instance = .init(testing.allocator, .{ |
| 135 | .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{}, | 134 | .argv0 = .init(init.args), |
| 135 | .environ = init.environ, | ||
| 136 | }); | 136 | }); |
| 137 | log_err_count = 0; | 137 | log_err_count = 0; |
| 138 | const index = try server.receiveBody_u32(); | 138 | const index = try server.receiveBody_u32(); |
| ... | @@ -217,7 +217,7 @@ fn mainServer(args: []const [:0]const u8) !void { | ... | @@ -217,7 +217,7 @@ fn mainServer(args: []const [:0]const u8) !void { |
| 217 | } | 217 | } |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | fn mainTerminal(args: []const [:0]const u8) void { | 220 | fn mainTerminal(init: std.process.Init.Minimal) void { |
| 221 | @disableInstrumentation(); | 221 | @disableInstrumentation(); |
| 222 | if (builtin.fuzz) @panic("fuzz test requires server"); | 222 | if (builtin.fuzz) @panic("fuzz test requires server"); |
| 223 | 223 | ||
| ... | @@ -236,7 +236,8 @@ fn mainTerminal(args: []const [:0]const u8) void { | ... | @@ -236,7 +236,8 @@ fn mainTerminal(args: []const [:0]const u8) void { |
| 236 | for (test_fn_list, 0..) |test_fn, i| { | 236 | for (test_fn_list, 0..) |test_fn, i| { |
| 237 | testing.allocator_instance = .{}; | 237 | testing.allocator_instance = .{}; |
| 238 | testing.io_instance = .init(testing.allocator, .{ | 238 | testing.io_instance = .init(testing.allocator, .{ |
| 239 | .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{}, | 239 | .argv0 = .init(init.args), |
| 240 | .environ = init.environ, | ||
| 240 | }); | 241 | }); |
| 241 | defer { | 242 | defer { |
| 242 | testing.io_instance.deinit(); | 243 | testing.io_instance.deinit(); |
lib/compiler/translate-c/main.zig+34-44| ... | @@ -9,21 +9,13 @@ const Translator = @import("Translator.zig"); | ... | @@ -9,21 +9,13 @@ const Translator = @import("Translator.zig"); |
| 9 | 9 | ||
| 10 | const fast_exit = @import("builtin").mode != .Debug; | 10 | const fast_exit = @import("builtin").mode != .Debug; |
| 11 | 11 | ||
| 12 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | 12 | pub fn main(init: std.process.Init) u8 { |
| 13 | const gpa = init.gpa; | ||
| 14 | const arena = init.arena.allocator(); | ||
| 15 | const io = init.io; | ||
| 16 | const environ_map = init.environ_map; | ||
| 13 | 17 | ||
| 14 | pub fn main() u8 { | 18 | const args = init.minimal.args.toSlice(arena) catch { |
| 15 | const gpa = general_purpose_allocator.allocator(); | ||
| 16 | defer _ = general_purpose_allocator.deinit(); | ||
| 17 | |||
| 18 | var arena_instance = std.heap.ArenaAllocator.init(gpa); | ||
| 19 | defer arena_instance.deinit(); | ||
| 20 | const arena = arena_instance.allocator(); | ||
| 21 | |||
| 22 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 23 | defer threaded.deinit(); | ||
| 24 | const io = threaded.io(); | ||
| 25 | |||
| 26 | const args = process.argsAlloc(arena) catch { | ||
| 27 | std.debug.print("ran out of memory allocating arguments\n", .{}); | 19 | std.debug.print("ran out of memory allocating arguments\n", .{}); |
| 28 | if (fast_exit) process.exit(1); | 20 | if (fast_exit) process.exit(1); |
| 29 | return 1; | 21 | return 1; |
| ... | @@ -34,8 +26,8 @@ pub fn main() u8 { | ... | @@ -34,8 +26,8 @@ pub fn main() u8 { |
| 34 | zig_integration = true; | 26 | zig_integration = true; |
| 35 | } | 27 | } |
| 36 | 28 | ||
| 37 | const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(); | 29 | const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(environ_map); |
| 38 | const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(); | 30 | const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(environ_map); |
| 39 | 31 | ||
| 40 | var stderr_buf: [1024]u8 = undefined; | 32 | var stderr_buf: [1024]u8 = undefined; |
| 41 | var stderr = Io.File.stderr().writer(io, &stderr_buf); | 33 | var stderr = Io.File.stderr().writer(io, &stderr_buf); |
| ... | @@ -50,7 +42,7 @@ pub fn main() u8 { | ... | @@ -50,7 +42,7 @@ pub fn main() u8 { |
| 50 | }; | 42 | }; |
| 51 | defer diagnostics.deinit(); | 43 | defer diagnostics.deinit(); |
| 52 | 44 | ||
| 53 | var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, Io.Dir.cwd()) catch |err| switch (err) { | 45 | var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, .cwd(), environ_map) catch |err| switch (err) { |
| 54 | error.OutOfMemory => { | 46 | error.OutOfMemory => { |
| 55 | std.debug.print("ran out of memory initializing C compilation\n", .{}); | 47 | std.debug.print("ran out of memory initializing C compilation\n", .{}); |
| 56 | if (fast_exit) process.exit(1); | 48 | if (fast_exit) process.exit(1); |
| ... | @@ -123,43 +115,41 @@ pub const usage = | ... | @@ -123,43 +115,41 @@ pub const usage = |
| 123 | \\ | 115 | \\ |
| 124 | ; | 116 | ; |
| 125 | 117 | ||
| 126 | fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration: bool) !void { | 118 | fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig_integration: bool) !void { |
| 127 | const gpa = d.comp.gpa; | 119 | const gpa = d.comp.gpa; |
| 128 | const io = d.comp.io; | 120 | const io = d.comp.io; |
| 129 | 121 | ||
| 130 | const aro_args = args: { | 122 | var aro_args: std.ArrayList([:0]const u8) = .empty; |
| 131 | var i: usize = 0; | 123 | defer aro_args.deinit(gpa); |
| 132 | for (args) |arg| { | 124 | |
| 133 | args[i] = arg; | 125 | for (args, 0..) |arg, i| { |
| 134 | if (mem.eql(u8, arg, "--help")) { | 126 | if (mem.eql(u8, arg, "--help")) { |
| 135 | var stdout_buf: [512]u8 = undefined; | 127 | var stdout_buf: [512]u8 = undefined; |
| 136 | var stdout = Io.File.stdout().writer(io, &stdout_buf); | 128 | var stdout = Io.File.stdout().writer(io, &stdout_buf); |
| 137 | try stdout.interface.print(usage, .{args[0]}); | 129 | try stdout.interface.print(usage, .{args[0]}); |
| 138 | try stdout.interface.flush(); | 130 | try stdout.interface.flush(); |
| 139 | return; | 131 | return; |
| 140 | } else if (mem.eql(u8, arg, "--version")) { | 132 | } else if (mem.eql(u8, arg, "--version")) { |
| 141 | var stdout_buf: [512]u8 = undefined; | 133 | var stdout_buf: [512]u8 = undefined; |
| 142 | var stdout = Io.File.stdout().writer(io, &stdout_buf); | 134 | var stdout = Io.File.stdout().writer(io, &stdout_buf); |
| 143 | // TODO add version | 135 | // TODO add version |
| 144 | try stdout.interface.writeAll("0.0.0-dev\n"); | 136 | try stdout.interface.writeAll("0.0.0-dev\n"); |
| 145 | try stdout.interface.flush(); | 137 | try stdout.interface.flush(); |
| 146 | return; | 138 | return; |
| 147 | } else if (mem.eql(u8, arg, "--zig-integration")) { | 139 | } else if (mem.eql(u8, arg, "--zig-integration")) { |
| 148 | if (i != 1 or !zig_integration) | 140 | if (i != 1 or !zig_integration) |
| 149 | return d.fatal("--zig-integration must be the first argument", .{}); | 141 | return d.fatal("--zig-integration must be the first argument", .{}); |
| 150 | } else { | 142 | } else { |
| 151 | i += 1; | 143 | try aro_args.append(gpa, arg); |
| 152 | } | ||
| 153 | } | 144 | } |
| 154 | break :args args[0..i]; | 145 | } |
| 155 | }; | ||
| 156 | const user_macros = macros: { | 146 | const user_macros = macros: { |
| 157 | var macro_buf: std.ArrayList(u8) = .empty; | 147 | var macro_buf: std.ArrayList(u8) = .empty; |
| 158 | defer macro_buf.deinit(gpa); | 148 | defer macro_buf.deinit(gpa); |
| 159 | 149 | ||
| 160 | var discard_buf: [256]u8 = undefined; | 150 | var discard_buf: [256]u8 = undefined; |
| 161 | var discarding: std.Io.Writer.Discarding = .init(&discard_buf); | 151 | var discarding: std.Io.Writer.Discarding = .init(&discard_buf); |
| 162 | assert(!try d.parseArgs(&discarding.writer, &macro_buf, aro_args)); | 152 | assert(!try d.parseArgs(&discarding.writer, &macro_buf, aro_args.items)); |
| 163 | if (macro_buf.items.len > std.math.maxInt(u32)) { | 153 | if (macro_buf.items.len > std.math.maxInt(u32)) { |
| 164 | return d.fatal("user provided macro source exceeded max size", .{}); | 154 | return d.fatal("user provided macro source exceeded max size", .{}); |
| 165 | } | 155 | } |
lib/compiler_rt.zig+17-1| ... | @@ -1,7 +1,23 @@ | ... | @@ -1,7 +1,23 @@ |
| 1 | const std = @import("std"); | ||
| 1 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 2 | const common = @import("compiler_rt/common.zig"); | 3 | const common = @import("compiler_rt/common.zig"); |
| 3 | 4 | ||
| 4 | pub const panic = common.panic; | 5 | /// Avoid dragging in the runtime safety mechanisms into this .o file, unless |
| 6 | /// we're trying to test compiler-rt. | ||
| 7 | pub const panic = if (common.test_safety) | ||
| 8 | std.debug.FullPanic(std.debug.defaultPanic) | ||
| 9 | else | ||
| 10 | std.debug.no_panic; | ||
| 11 | |||
| 12 | pub const std_options_debug_threaded_io: ?*std.Io.Threaded = if (builtin.is_test) | ||
| 13 | std.Io.Threaded.global_single_threaded | ||
| 14 | else | ||
| 15 | null; | ||
| 16 | |||
| 17 | pub const std_options_debug_io: std.Io = if (builtin.is_test) | ||
| 18 | std.Io.Threaded.global_single_threaded.ioBasic() | ||
| 19 | else | ||
| 20 | unreachable; | ||
| 5 | 21 | ||
| 6 | comptime { | 22 | comptime { |
| 7 | // Integer routines | 23 | // Integer routines |
lib/compiler_rt/absvdi2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const absv = @import("./absv.zig").absv; | 2 | const absv = @import("./absv.zig").absv; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__absvdi2, .{ .name = "__absvdi2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__absvdi2, .{ .name = "__absvdi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/absvsi2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const absv = @import("./absv.zig").absv; | 2 | const absv = @import("./absv.zig").absv; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__absvsi2, .{ .name = "__absvsi2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__absvsi2, .{ .name = "__absvsi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/absvti2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const absv = @import("./absv.zig").absv; | 2 | const absv = @import("./absv.zig").absv; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__absvti2, .{ .name = "__absvti2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__absvti2, .{ .name = "__absvti2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/adddf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/addhf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__addhf3, .{ .name = "__addhf3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__addhf3, .{ .name = "__addhf3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/addsf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/addtf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__addtf3, .{ .name = "__addkf3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__addtf3, .{ .name = "__addkf3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/addvdi3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const testing = @import("std").testing; | 2 | const testing = @import("std").testing; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__addvdi3, .{ .name = "__addvdi3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__addvdi3, .{ .name = "__addvdi3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/addvsi3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const testing = @import("std").testing; | 2 | const testing = @import("std").testing; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__addvsi3, .{ .name = "__addvsi3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__addvsi3, .{ .name = "__addvsi3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/addxf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__addxf3, .{ .name = "__addxf3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__addxf3, .{ .name = "__addxf3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/arm.zig-2| ... | @@ -6,8 +6,6 @@ const target = builtin.target; | ... | @@ -6,8 +6,6 @@ const target = builtin.target; |
| 6 | const arch = builtin.cpu.arch; | 6 | const arch = builtin.cpu.arch; |
| 7 | const common = @import("common.zig"); | 7 | const common = @import("common.zig"); |
| 8 | 8 | ||
| 9 | pub const panic = common.panic; | ||
| 10 | |||
| 11 | comptime { | 9 | comptime { |
| 12 | if (!builtin.is_test) { | 10 | if (!builtin.is_test) { |
| 13 | if (arch.isArm()) { | 11 | if (arch.isArm()) { |
lib/compiler_rt/atomics.zig-1| ... | @@ -5,7 +5,6 @@ const cpu = builtin.cpu; | ... | @@ -5,7 +5,6 @@ const cpu = builtin.cpu; |
| 5 | const arch = cpu.arch; | 5 | const arch = cpu.arch; |
| 6 | const linkage = common.linkage; | 6 | const linkage = common.linkage; |
| 7 | const visibility = common.visibility; | 7 | const visibility = common.visibility; |
| 8 | pub const panic = common.panic; | ||
| 9 | 8 | ||
| 10 | // This parameter is true iff the target architecture supports the bare minimum | 9 | // This parameter is true iff the target architecture supports the bare minimum |
| 11 | // to implement the atomic load/store intrinsics. | 10 | // to implement the atomic load/store intrinsics. |
lib/compiler_rt/aulldiv.zig-2| ... | @@ -5,8 +5,6 @@ const os = builtin.os.tag; | ... | @@ -5,8 +5,6 @@ const os = builtin.os.tag; |
| 5 | const abi = builtin.abi; | 5 | const abi = builtin.abi; |
| 6 | const common = @import("common.zig"); | 6 | const common = @import("common.zig"); |
| 7 | 7 | ||
| 8 | pub const panic = common.panic; | ||
| 9 | |||
| 10 | comptime { | 8 | comptime { |
| 11 | if (common.want_windows_x86_msvc_abi) { | 9 | if (common.want_windows_x86_msvc_abi) { |
| 12 | // Don't let LLVM apply the stdcall name mangling on those MSVC builtins | 10 | // Don't let LLVM apply the stdcall name mangling on those MSVC builtins |
lib/compiler_rt/aullrem.zig-2| ... | @@ -5,8 +5,6 @@ const os = builtin.os.tag; | ... | @@ -5,8 +5,6 @@ const os = builtin.os.tag; |
| 5 | const abi = builtin.abi; | 5 | const abi = builtin.abi; |
| 6 | const common = @import("common.zig"); | 6 | const common = @import("common.zig"); |
| 7 | 7 | ||
| 8 | pub const panic = common.panic; | ||
| 9 | |||
| 10 | comptime { | 8 | comptime { |
| 11 | if (common.want_windows_x86_msvc_abi) { | 9 | if (common.want_windows_x86_msvc_abi) { |
| 12 | // Don't let LLVM apply the stdcall name mangling on those MSVC builtins | 10 | // Don't let LLVM apply the stdcall name mangling on those MSVC builtins |
lib/compiler_rt/bitreverse.zig-2| ... | @@ -2,8 +2,6 @@ const std = @import("std"); | ... | @@ -2,8 +2,6 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | @export(&__bitreversesi2, .{ .name = "__bitreversesi2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__bitreversesi2, .{ .name = "__bitreversesi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 9 | @export(&__bitreversedi2, .{ .name = "__bitreversedi2", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__bitreversedi2, .{ .name = "__bitreversedi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/bswap.zig-2| ... | @@ -2,8 +2,6 @@ const std = @import("std"); | ... | @@ -2,8 +2,6 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | @export(&__bswapsi2, .{ .name = "__bswapsi2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__bswapsi2, .{ .name = "__bswapsi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 9 | @export(&__bswapdi2, .{ .name = "__bswapdi2", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__bswapdi2, .{ .name = "__bswapdi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/ceil.zig-2| ... | @@ -13,8 +13,6 @@ const mem = std.mem; | ... | @@ -13,8 +13,6 @@ const mem = std.mem; |
| 13 | const expect = std.testing.expect; | 13 | const expect = std.testing.expect; |
| 14 | const common = @import("common.zig"); | 14 | const common = @import("common.zig"); |
| 15 | 15 | ||
| 16 | pub const panic = common.panic; | ||
| 17 | |||
| 18 | comptime { | 16 | comptime { |
| 19 | @export(&__ceilh, .{ .name = "__ceilh", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&__ceilh, .{ .name = "__ceilh", .linkage = common.linkage, .visibility = common.visibility }); |
| 20 | @export(&ceilf, .{ .name = "ceilf", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(&ceilf, .{ .name = "ceilf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/clear_cache.zig-1| ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const arch = builtin.cpu.arch; | 3 | const arch = builtin.cpu.arch; |
| 4 | const os = builtin.os.tag; | 4 | const os = builtin.os.tag; |
| 5 | const common = @import("common.zig"); | 5 | const common = @import("common.zig"); |
| 6 | pub const panic = common.panic; | ||
| 7 | 6 | ||
| 8 | // Ported from llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28 | 7 | // Ported from llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28 |
| 9 | 8 |
lib/compiler_rt/cmp.zig-2| ... | @@ -2,8 +2,6 @@ const std = @import("std"); | ... | @@ -2,8 +2,6 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | @export(&__cmpsi2, .{ .name = "__cmpsi2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__cmpsi2, .{ .name = "__cmpsi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 9 | @export(&__cmpdi2, .{ .name = "__cmpdi2", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__cmpdi2, .{ .name = "__cmpdi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/cmpdf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_aeabi) { | 7 | if (common.want_aeabi) { |
| 10 | @export(&__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/cmphf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__eqhf2, .{ .name = "__eqhf2", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__eqhf2, .{ .name = "__eqhf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | @export(&__nehf2, .{ .name = "__nehf2", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__nehf2, .{ .name = "__nehf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/cmpsf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_aeabi) { | 7 | if (common.want_aeabi) { |
| 10 | @export(&__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/cmptf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_ppc_abi) { | 7 | if (common.want_ppc_abi) { |
| 10 | @export(&__eqtf2, .{ .name = "__eqkf2", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__eqtf2, .{ .name = "__eqkf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/cmpxf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__eqxf2, .{ .name = "__eqxf2", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__eqxf2, .{ .name = "__eqxf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | @export(&__nexf2, .{ .name = "__nexf2", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__nexf2, .{ .name = "__nexf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/common.zig-4| ... | @@ -126,10 +126,6 @@ pub const test_safety = switch (builtin.zig_backend) { | ... | @@ -126,10 +126,6 @@ pub const test_safety = switch (builtin.zig_backend) { |
| 126 | else => builtin.is_test, | 126 | else => builtin.is_test, |
| 127 | }; | 127 | }; |
| 128 | 128 | ||
| 129 | // Avoid dragging in the runtime safety mechanisms into this .o file, unless | ||
| 130 | // we're trying to test compiler-rt. | ||
| 131 | pub const panic = if (test_safety) std.debug.FullPanic(std.debug.defaultPanic) else std.debug.no_panic; | ||
| 132 | |||
| 133 | /// This seems to mostly correspond to `clang::TargetInfo::HasFloat16`. | 129 | /// This seems to mostly correspond to `clang::TargetInfo::HasFloat16`. |
| 134 | pub fn F16T(comptime OtherType: type) type { | 130 | pub fn F16T(comptime OtherType: type) type { |
| 135 | return switch (builtin.cpu.arch) { | 131 | return switch (builtin.cpu.arch) { |
lib/compiler_rt/cos.zig-2| ... | @@ -4,8 +4,6 @@ const mem = std.mem; | ... | @@ -4,8 +4,6 @@ const mem = std.mem; |
| 4 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
| 5 | const common = @import("common.zig"); | 5 | const common = @import("common.zig"); |
| 6 | 6 | ||
| 7 | pub const panic = common.panic; | ||
| 8 | |||
| 9 | const trig = @import("trig.zig"); | 7 | const trig = @import("trig.zig"); |
| 10 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; | 8 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; |
| 11 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; | 9 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; |
lib/compiler_rt/count0bits.zig-2| ... | @@ -2,8 +2,6 @@ const std = @import("std"); | ... | @@ -2,8 +2,6 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | @export(&__clzsi2, .{ .name = "__clzsi2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__clzsi2, .{ .name = "__clzsi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 9 | @export(&__clzdi2, .{ .name = "__clzdi2", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__clzdi2, .{ .name = "__clzdi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/divdf3.zig-2| ... | @@ -10,8 +10,6 @@ const common = @import("common.zig"); | ... | @@ -10,8 +10,6 @@ const common = @import("common.zig"); |
| 10 | const normalize = common.normalize; | 10 | const normalize = common.normalize; |
| 11 | const wideMultiply = common.wideMultiply; | 11 | const wideMultiply = common.wideMultiply; |
| 12 | 12 | ||
| 13 | pub const panic = common.panic; | ||
| 14 | |||
| 15 | comptime { | 13 | comptime { |
| 16 | if (common.want_aeabi) { | 14 | if (common.want_aeabi) { |
| 17 | @export(&__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = common.linkage, .visibility = common.visibility }); | 15 | @export(&__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/divsf3.zig-2| ... | @@ -9,8 +9,6 @@ const arch = builtin.cpu.arch; | ... | @@ -9,8 +9,6 @@ const arch = builtin.cpu.arch; |
| 9 | const common = @import("common.zig"); | 9 | const common = @import("common.zig"); |
| 10 | const normalize = common.normalize; | 10 | const normalize = common.normalize; |
| 11 | 11 | ||
| 12 | pub const panic = common.panic; | ||
| 13 | |||
| 14 | comptime { | 12 | comptime { |
| 15 | if (common.want_aeabi) { | 13 | if (common.want_aeabi) { |
| 16 | @export(&__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = common.linkage, .visibility = common.visibility }); | 14 | @export(&__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/divtf3.zig-2| ... | @@ -5,8 +5,6 @@ const common = @import("common.zig"); | ... | @@ -5,8 +5,6 @@ const common = @import("common.zig"); |
| 5 | const normalize = common.normalize; | 5 | const normalize = common.normalize; |
| 6 | const wideMultiply = common.wideMultiply; | 6 | const wideMultiply = common.wideMultiply; |
| 7 | 7 | ||
| 8 | pub const panic = common.panic; | ||
| 9 | |||
| 10 | comptime { | 8 | comptime { |
| 11 | if (common.want_ppc_abi) { | 9 | if (common.want_ppc_abi) { |
| 12 | @export(&__divtf3, .{ .name = "__divkf3", .linkage = common.linkage, .visibility = common.visibility }); | 10 | @export(&__divtf3, .{ .name = "__divkf3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/divti3.zig-2| ... | @@ -4,8 +4,6 @@ const udivmod = @import("udivmod.zig").udivmod; | ... | @@ -4,8 +4,6 @@ const udivmod = @import("udivmod.zig").udivmod; |
| 4 | const arch = builtin.cpu.arch; | 4 | const arch = builtin.cpu.arch; |
| 5 | const common = @import("common.zig"); | 5 | const common = @import("common.zig"); |
| 6 | 6 | ||
| 7 | pub const panic = common.panic; | ||
| 8 | |||
| 9 | comptime { | 7 | comptime { |
| 10 | if (common.want_windows_v2u64_abi) { | 8 | if (common.want_windows_v2u64_abi) { |
| 11 | @export(&__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = common.linkage, .visibility = common.visibility }); | 9 | @export(&__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/divxf3.zig-2| ... | @@ -6,8 +6,6 @@ const common = @import("common.zig"); | ... | @@ -6,8 +6,6 @@ const common = @import("common.zig"); |
| 6 | const normalize = common.normalize; | 6 | const normalize = common.normalize; |
| 7 | const wideMultiply = common.wideMultiply; | 7 | const wideMultiply = common.wideMultiply; |
| 8 | 8 | ||
| 9 | pub const panic = common.panic; | ||
| 10 | |||
| 11 | comptime { | 9 | comptime { |
| 12 | @export(&__divxf3, .{ .name = "__divxf3", .linkage = common.linkage, .visibility = common.visibility }); | 10 | @export(&__divxf3, .{ .name = "__divxf3", .linkage = common.linkage, .visibility = common.visibility }); |
| 13 | } | 11 | } |
lib/compiler_rt/emutls.zig-2| ... | @@ -15,8 +15,6 @@ const expect = std.testing.expect; | ... | @@ -15,8 +15,6 @@ const expect = std.testing.expect; |
| 15 | /// typedef unsigned int gcc_word __attribute__((mode(word))); | 15 | /// typedef unsigned int gcc_word __attribute__((mode(word))); |
| 16 | const gcc_word = usize; | 16 | const gcc_word = usize; |
| 17 | 17 | ||
| 18 | pub const panic = common.panic; | ||
| 19 | |||
| 20 | comptime { | 18 | comptime { |
| 21 | if (builtin.link_libc and (builtin.abi.isAndroid() or builtin.abi.isOpenHarmony() or builtin.os.tag == .openbsd)) { | 19 | if (builtin.link_libc and (builtin.abi.isAndroid() or builtin.abi.isOpenHarmony() or builtin.os.tag == .openbsd)) { |
| 22 | @export(&__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage, .visibility = common.visibility }); | 20 | @export(&__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/exp.zig-2| ... | @@ -13,8 +13,6 @@ const expect = std.testing.expect; | ... | @@ -13,8 +13,6 @@ const expect = std.testing.expect; |
| 13 | const expectEqual = std.testing.expectEqual; | 13 | const expectEqual = std.testing.expectEqual; |
| 14 | const common = @import("common.zig"); | 14 | const common = @import("common.zig"); |
| 15 | 15 | ||
| 16 | pub const panic = common.panic; | ||
| 17 | |||
| 18 | comptime { | 16 | comptime { |
| 19 | @export(&__exph, .{ .name = "__exph", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&__exph, .{ .name = "__exph", .linkage = common.linkage, .visibility = common.visibility }); |
| 20 | @export(&expf, .{ .name = "expf", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(&expf, .{ .name = "expf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/exp2.zig-2| ... | @@ -13,8 +13,6 @@ const expect = std.testing.expect; | ... | @@ -13,8 +13,6 @@ const expect = std.testing.expect; |
| 13 | const expectEqual = std.testing.expectEqual; | 13 | const expectEqual = std.testing.expectEqual; |
| 14 | const common = @import("common.zig"); | 14 | const common = @import("common.zig"); |
| 15 | 15 | ||
| 16 | pub const panic = common.panic; | ||
| 17 | |||
| 18 | comptime { | 16 | comptime { |
| 19 | @export(&__exp2h, .{ .name = "__exp2h", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&__exp2h, .{ .name = "__exp2h", .linkage = common.linkage, .visibility = common.visibility }); |
| 20 | @export(&exp2f, .{ .name = "exp2f", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(&exp2f, .{ .name = "exp2f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/extenddftf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extendf = @import("./extendf.zig").extendf; | 2 | const extendf = @import("./extendf.zig").extendf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__extenddftf2, .{ .name = "__extenddfkf2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__extenddftf2, .{ .name = "__extenddfkf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/extenddfxf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extend_f80 = @import("./extendf.zig").extend_f80; | 2 | const extend_f80 = @import("./extendf.zig").extend_f80; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/extendhfdf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extendf = @import("./extendf.zig").extendf; | 2 | const extendf = @import("./extendf.zig").extendf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__extendhfdf2, .{ .name = "__extendhfdf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__extendhfdf2, .{ .name = "__extendhfdf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/extendhfsf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extendf = @import("./extendf.zig").extendf; | 2 | const extendf = @import("./extendf.zig").extendf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.gnu_f16_abi) { | 5 | if (common.gnu_f16_abi) { |
| 8 | @export(&__gnu_h2f_ieee, .{ .name = "__gnu_h2f_ieee", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__gnu_h2f_ieee, .{ .name = "__gnu_h2f_ieee", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/extendhftf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extendf = @import("./extendf.zig").extendf; | 2 | const extendf = @import("./extendf.zig").extendf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__extendhftf2, .{ .name = "__extendhftf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__extendhftf2, .{ .name = "__extendhftf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/extendhfxf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extend_f80 = @import("./extendf.zig").extend_f80; | 2 | const extend_f80 = @import("./extendf.zig").extend_f80; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/extendsfdf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extendf = @import("./extendf.zig").extendf; | 2 | const extendf = @import("./extendf.zig").extendf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/extendsftf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extendf = @import("./extendf.zig").extendf; | 2 | const extendf = @import("./extendf.zig").extendf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__extendsftf2, .{ .name = "__extendsfkf2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__extendsftf2, .{ .name = "__extendsfkf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/extendsfxf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const extend_f80 = @import("./extendf.zig").extend_f80; | 2 | const extend_f80 = @import("./extendf.zig").extend_f80; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__extendsfxf2, .{ .name = "__extendsfxf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__extendsfxf2, .{ .name = "__extendsfxf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/extendxftf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__extendxftf2, .{ .name = "__extendxftf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__extendxftf2, .{ .name = "__extendxftf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fabs.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const arch = builtin.cpu.arch; | 3 | const arch = builtin.cpu.arch; |
| 4 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fabsh, .{ .name = "__fabsh", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fabsh, .{ .name = "__fabsh", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | @export(&fabsf, .{ .name = "fabsf", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&fabsf, .{ .name = "fabsf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixdfdi.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixdfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixdfei, .{ .name = "__fixdfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixdfei, .{ .name = "__fixdfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixdfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixdfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixdfti_windows_x86_64, .{ .name = "__fixdfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixdfti_windows_x86_64, .{ .name = "__fixdfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixhfdi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixhfdi, .{ .name = "__fixhfdi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixhfdi, .{ .name = "__fixhfdi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixhfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixhfei, .{ .name = "__fixhfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixhfei, .{ .name = "__fixhfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixhfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixhfsi, .{ .name = "__fixhfsi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixhfsi, .{ .name = "__fixhfsi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixhfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixhfti_windows_x86_64, .{ .name = "__fixhfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixhfti_windows_x86_64, .{ .name = "__fixhfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixsfdi.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixsfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixsfei, .{ .name = "__fixsfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixsfei, .{ .name = "__fixsfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixsfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixsfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixsfti_windows_x86_64, .{ .name = "__fixsfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixsfti_windows_x86_64, .{ .name = "__fixsfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixtfdi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__fixtfdi, .{ .name = "__fixkfdi", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__fixtfdi, .{ .name = "__fixkfdi", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixtfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixtfei, .{ .name = "__fixtfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixtfei, .{ .name = "__fixtfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixtfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__fixtfsi, .{ .name = "__fixkfsi", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__fixtfsi, .{ .name = "__fixkfsi", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixtfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixtfti_windows_x86_64, .{ .name = "__fixtfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixtfti_windows_x86_64, .{ .name = "__fixtfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunsdfdi.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunsdfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixunsdfei, .{ .name = "__fixunsdfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunsdfei, .{ .name = "__fixunsdfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixunsdfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunsdfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixunsdfti_windows_x86_64, .{ .name = "__fixunsdfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunsdfti_windows_x86_64, .{ .name = "__fixunsdfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunshfdi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixunshfdi, .{ .name = "__fixunshfdi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixunshfdi, .{ .name = "__fixunshfdi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixunshfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixunshfei, .{ .name = "__fixunshfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunshfei, .{ .name = "__fixunshfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixunshfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixunshfsi, .{ .name = "__fixunshfsi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixunshfsi, .{ .name = "__fixunshfsi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixunshfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixunshfti_windows_x86_64, .{ .name = "__fixunshfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunshfti_windows_x86_64, .{ .name = "__fixunshfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunssfdi.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunssfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixunssfei, .{ .name = "__fixunssfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunssfei, .{ .name = "__fixunssfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixunssfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunssfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixunssfti_windows_x86_64, .{ .name = "__fixunssfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunssfti_windows_x86_64, .{ .name = "__fixunssfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunstfdi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunstfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixunstfei, .{ .name = "__fixunstfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunstfei, .{ .name = "__fixunstfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixunstfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunstfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixunstfti_windows_x86_64, .{ .name = "__fixunstfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunstfti_windows_x86_64, .{ .name = "__fixunstfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixunsxfdi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixunsxfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixunsxfei, .{ .name = "__fixunsxfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunsxfei, .{ .name = "__fixunsxfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixunsxfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixunsxfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixunsxfti_windows_x86_64, .{ .name = "__fixunsxfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixunsxfti_windows_x86_64, .{ .name = "__fixunsxfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fixxfdi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixxfdi, .{ .name = "__fixxfdi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixxfdi, .{ .name = "__fixxfdi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixxfei.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; | 4 | const bigIntFromFloat = @import("int_from_float.zig").bigIntFromFloat; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__fixxfei, .{ .name = "__fixxfei", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixxfei, .{ .name = "__fixxfei", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/fixxfsi.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 2 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__fixxfsi, .{ .name = "__fixxfsi", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__fixxfsi, .{ .name = "__fixxfsi", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/fixxfti.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; | 3 | const intFromFloat = @import("./int_from_float.zig").intFromFloat; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__fixxfti_windows_x86_64, .{ .name = "__fixxfti", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__fixxfti_windows_x86_64, .{ .name = "__fixxfti", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatdidf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatdihf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatdihf, .{ .name = "__floatdihf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatdihf, .{ .name = "__floatdihf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floatdisf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatditf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__floatditf, .{ .name = "__floatdikf", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__floatditf, .{ .name = "__floatdikf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatdixf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatdixf, .{ .name = "__floatdixf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatdixf, .{ .name = "__floatdixf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floateidf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floateidf, .{ .name = "__floateidf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floateidf, .{ .name = "__floateidf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floateihf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floateihf, .{ .name = "__floateihf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floateihf, .{ .name = "__floateihf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floateisf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floateisf, .{ .name = "__floateisf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floateisf, .{ .name = "__floateisf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floateitf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floateitf, .{ .name = "__floateitf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floateitf, .{ .name = "__floateitf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floateixf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floateixf, .{ .name = "__floateixf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floateixf, .{ .name = "__floateixf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floatsidf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatsihf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatsihf, .{ .name = "__floatsihf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatsihf, .{ .name = "__floatsihf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floatsisf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatsitf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__floatsitf, .{ .name = "__floatsikf", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__floatsitf, .{ .name = "__floatsikf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatsixf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatsixf, .{ .name = "__floatsixf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatsixf, .{ .name = "__floatsixf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floattidf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floattidf_windows_x86_64, .{ .name = "__floattidf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floattidf_windows_x86_64, .{ .name = "__floattidf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floattihf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floattihf_windows_x86_64, .{ .name = "__floattihf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floattihf_windows_x86_64, .{ .name = "__floattihf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floattisf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floattisf_windows_x86_64, .{ .name = "__floattisf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floattisf_windows_x86_64, .{ .name = "__floattisf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floattitf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floattitf_windows_x86_64, .{ .name = "__floattitf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floattitf_windows_x86_64, .{ .name = "__floattitf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floattixf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floattixf_windows_x86_64, .{ .name = "__floattixf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floattixf_windows_x86_64, .{ .name = "__floattixf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatundidf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatundihf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatundihf, .{ .name = "__floatundihf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatundihf, .{ .name = "__floatundihf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floatundisf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_aeabi) { | 6 | if (common.want_aeabi) { |
| 9 | @export(&__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatunditf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__floatunditf, .{ .name = "__floatundikf", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__floatunditf, .{ .name = "__floatundikf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatundixf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatundixf, .{ .name = "__floatundixf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatundixf, .{ .name = "__floatundixf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floatuneidf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floatuneidf, .{ .name = "__floatuneidf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuneidf, .{ .name = "__floatuneidf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floatuneihf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floatuneihf, .{ .name = "__floatuneihf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuneihf, .{ .name = "__floatuneihf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floatuneisf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floatuneisf, .{ .name = "__floatuneisf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuneisf, .{ .name = "__floatuneisf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floatuneitf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floatuneitf, .{ .name = "__floatuneitf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuneitf, .{ .name = "__floatuneitf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floatuneixf.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const common = @import("common.zig"); | 3 | const common = @import("common.zig"); |
| 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; | 4 | const floatFromBigInt = @import("float_from_int.zig").floatFromBigInt; |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__floatuneixf, .{ .name = "__floatuneixf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuneixf, .{ .name = "__floatuneixf", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | } | 8 | } |
lib/compiler_rt/floatunsidf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatunsihf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatunsihf, .{ .name = "__floatunsihf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatunsihf, .{ .name = "__floatunsihf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floatunsisf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatunsitf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__floatunsitf, .{ .name = "__floatunsikf", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__floatunsitf, .{ .name = "__floatunsikf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatunsixf.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 2 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__floatunsixf, .{ .name = "__floatunsixf", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__floatunsixf, .{ .name = "__floatunsixf", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/floatuntidf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floatuntidf_windows_x86_64, .{ .name = "__floatuntidf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuntidf_windows_x86_64, .{ .name = "__floatuntidf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatuntihf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floatuntihf_windows_x86_64, .{ .name = "__floatuntihf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuntihf_windows_x86_64, .{ .name = "__floatuntihf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatuntisf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floatuntisf_windows_x86_64, .{ .name = "__floatuntisf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuntisf_windows_x86_64, .{ .name = "__floatuntisf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatuntitf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floatuntitf_windows_x86_64, .{ .name = "__floatuntitf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuntitf_windows_x86_64, .{ .name = "__floatuntitf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floatuntixf.zig-2| ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); | ... | @@ -2,8 +2,6 @@ const builtin = @import("builtin"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; | 3 | const floatFromInt = @import("./float_from_int.zig").floatFromInt; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | if (common.want_windows_v2u64_abi) { | 6 | if (common.want_windows_v2u64_abi) { |
| 9 | @export(&__floatuntixf_windows_x86_64, .{ .name = "__floatuntixf", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__floatuntixf_windows_x86_64, .{ .name = "__floatuntixf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/floor.zig-2| ... | @@ -13,8 +13,6 @@ const expect = std.testing.expect; | ... | @@ -13,8 +13,6 @@ const expect = std.testing.expect; |
| 13 | const arch = builtin.cpu.arch; | 13 | const arch = builtin.cpu.arch; |
| 14 | const common = @import("common.zig"); | 14 | const common = @import("common.zig"); |
| 15 | 15 | ||
| 16 | pub const panic = common.panic; | ||
| 17 | |||
| 18 | comptime { | 16 | comptime { |
| 19 | @export(&__floorh, .{ .name = "__floorh", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&__floorh, .{ .name = "__floorh", .linkage = common.linkage, .visibility = common.visibility }); |
| 20 | @export(&floorf, .{ .name = "floorf", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(&floorf, .{ .name = "floorf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fma.zig-2| ... | @@ -10,8 +10,6 @@ const math = std.math; | ... | @@ -10,8 +10,6 @@ const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | const common = @import("common.zig"); | 11 | const common = @import("common.zig"); |
| 12 | 12 | ||
| 13 | pub const panic = common.panic; | ||
| 14 | |||
| 15 | comptime { | 13 | comptime { |
| 16 | @export(&__fmah, .{ .name = "__fmah", .linkage = common.linkage, .visibility = common.visibility }); | 14 | @export(&__fmah, .{ .name = "__fmah", .linkage = common.linkage, .visibility = common.visibility }); |
| 17 | @export(&fmaf, .{ .name = "fmaf", .linkage = common.linkage, .visibility = common.visibility }); | 15 | @export(&fmaf, .{ .name = "fmaf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fmax.zig-2| ... | @@ -4,8 +4,6 @@ const math = std.math; | ... | @@ -4,8 +4,6 @@ const math = std.math; |
| 4 | const arch = builtin.cpu.arch; | 4 | const arch = builtin.cpu.arch; |
| 5 | const common = @import("common.zig"); | 5 | const common = @import("common.zig"); |
| 6 | 6 | ||
| 7 | pub const panic = common.panic; | ||
| 8 | |||
| 9 | comptime { | 7 | comptime { |
| 10 | @export(&__fmaxh, .{ .name = "__fmaxh", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__fmaxh, .{ .name = "__fmaxh", .linkage = common.linkage, .visibility = common.visibility }); |
| 11 | @export(&fmaxf, .{ .name = "fmaxf", .linkage = common.linkage, .visibility = common.visibility }); | 9 | @export(&fmaxf, .{ .name = "fmaxf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fmin.zig-2| ... | @@ -4,8 +4,6 @@ const math = std.math; | ... | @@ -4,8 +4,6 @@ const math = std.math; |
| 4 | const arch = builtin.cpu.arch; | 4 | const arch = builtin.cpu.arch; |
| 5 | const common = @import("common.zig"); | 5 | const common = @import("common.zig"); |
| 6 | 6 | ||
| 7 | pub const panic = common.panic; | ||
| 8 | |||
| 9 | comptime { | 7 | comptime { |
| 10 | @export(&__fminh, .{ .name = "__fminh", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__fminh, .{ .name = "__fminh", .linkage = common.linkage, .visibility = common.visibility }); |
| 11 | @export(&fminf, .{ .name = "fminf", .linkage = common.linkage, .visibility = common.visibility }); | 9 | @export(&fminf, .{ .name = "fminf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/fmod.zig-2| ... | @@ -6,8 +6,6 @@ const arch = builtin.cpu.arch; | ... | @@ -6,8 +6,6 @@ const arch = builtin.cpu.arch; |
| 6 | const common = @import("common.zig"); | 6 | const common = @import("common.zig"); |
| 7 | const normalize = common.normalize; | 7 | const normalize = common.normalize; |
| 8 | 8 | ||
| 9 | pub const panic = common.panic; | ||
| 10 | |||
| 11 | comptime { | 9 | comptime { |
| 12 | @export(&__fmodh, .{ .name = "__fmodh", .linkage = common.linkage, .visibility = common.visibility }); | 10 | @export(&__fmodh, .{ .name = "__fmodh", .linkage = common.linkage, .visibility = common.visibility }); |
| 13 | @export(&fmodf, .{ .name = "fmodf", .linkage = common.linkage, .visibility = common.visibility }); | 11 | @export(&fmodf, .{ .name = "fmodf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/gedf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_aeabi) { | 7 | if (common.want_aeabi) { |
| 10 | @export(&__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/gehf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__gehf2, .{ .name = "__gehf2", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__gehf2, .{ .name = "__gehf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | @export(&__gthf2, .{ .name = "__gthf2", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__gthf2, .{ .name = "__gthf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/gesf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_aeabi) { | 7 | if (common.want_aeabi) { |
| 10 | @export(&__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/getf2.zig-2| ... | @@ -3,8 +3,6 @@ | ... | @@ -3,8 +3,6 @@ |
| 3 | const common = @import("./common.zig"); | 3 | const common = @import("./common.zig"); |
| 4 | const comparef = @import("./comparef.zig"); | 4 | const comparef = @import("./comparef.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_ppc_abi) { | 7 | if (common.want_ppc_abi) { |
| 10 | @export(&__getf2, .{ .name = "__gekf2", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__getf2, .{ .name = "__gekf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/gexf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const comparef = @import("./comparef.zig"); | 2 | const comparef = @import("./comparef.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__gexf2, .{ .name = "__gexf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__gexf2, .{ .name = "__gexf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | @export(&__gtxf2, .{ .name = "__gtxf2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__gtxf2, .{ .name = "__gtxf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/int.zig-2| ... | @@ -10,8 +10,6 @@ const common = @import("common.zig"); | ... | @@ -10,8 +10,6 @@ const common = @import("common.zig"); |
| 10 | const udivmod = @import("udivmod.zig").udivmod; | 10 | const udivmod = @import("udivmod.zig").udivmod; |
| 11 | const __divti3 = @import("divti3.zig").__divti3; | 11 | const __divti3 = @import("divti3.zig").__divti3; |
| 12 | 12 | ||
| 13 | pub const panic = common.panic; | ||
| 14 | |||
| 15 | comptime { | 13 | comptime { |
| 16 | @export(&__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility }); | 14 | @export(&__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility }); |
| 17 | @export(&__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility }); | 15 | @export(&__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/log.zig-2| ... | @@ -12,8 +12,6 @@ const expectEqual = std.testing.expectEqual; | ... | @@ -12,8 +12,6 @@ const expectEqual = std.testing.expectEqual; |
| 12 | const arch = builtin.cpu.arch; | 12 | const arch = builtin.cpu.arch; |
| 13 | const common = @import("common.zig"); | 13 | const common = @import("common.zig"); |
| 14 | 14 | ||
| 15 | pub const panic = common.panic; | ||
| 16 | |||
| 17 | comptime { | 15 | comptime { |
| 18 | @export(&__logh, .{ .name = "__logh", .linkage = common.linkage, .visibility = common.visibility }); | 16 | @export(&__logh, .{ .name = "__logh", .linkage = common.linkage, .visibility = common.visibility }); |
| 19 | @export(&logf, .{ .name = "logf", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&logf, .{ .name = "logf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/log10.zig-2| ... | @@ -13,8 +13,6 @@ const maxInt = std.math.maxInt; | ... | @@ -13,8 +13,6 @@ const maxInt = std.math.maxInt; |
| 13 | const arch = builtin.cpu.arch; | 13 | const arch = builtin.cpu.arch; |
| 14 | const common = @import("common.zig"); | 14 | const common = @import("common.zig"); |
| 15 | 15 | ||
| 16 | pub const panic = common.panic; | ||
| 17 | |||
| 18 | comptime { | 16 | comptime { |
| 19 | @export(&__log10h, .{ .name = "__log10h", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&__log10h, .{ .name = "__log10h", .linkage = common.linkage, .visibility = common.visibility }); |
| 20 | @export(&log10f, .{ .name = "log10f", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(&log10f, .{ .name = "log10f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/log2.zig-2| ... | @@ -13,8 +13,6 @@ const maxInt = std.math.maxInt; | ... | @@ -13,8 +13,6 @@ const maxInt = std.math.maxInt; |
| 13 | const arch = builtin.cpu.arch; | 13 | const arch = builtin.cpu.arch; |
| 14 | const common = @import("common.zig"); | 14 | const common = @import("common.zig"); |
| 15 | 15 | ||
| 16 | pub const panic = common.panic; | ||
| 17 | |||
| 18 | comptime { | 16 | comptime { |
| 19 | @export(&__log2h, .{ .name = "__log2h", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&__log2h, .{ .name = "__log2h", .linkage = common.linkage, .visibility = common.visibility }); |
| 20 | @export(&log2f, .{ .name = "log2f", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(&log2f, .{ .name = "log2f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/modti3.zig-2| ... | @@ -7,8 +7,6 @@ const builtin = @import("builtin"); | ... | @@ -7,8 +7,6 @@ const builtin = @import("builtin"); |
| 7 | const udivmod = @import("udivmod.zig").udivmod; | 7 | const udivmod = @import("udivmod.zig").udivmod; |
| 8 | const common = @import("common.zig"); | 8 | const common = @import("common.zig"); |
| 9 | 9 | ||
| 10 | pub const panic = common.panic; | ||
| 11 | |||
| 12 | comptime { | 10 | comptime { |
| 13 | if (common.want_windows_v2u64_abi) { | 11 | if (common.want_windows_v2u64_abi) { |
| 14 | @export(&__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = common.linkage, .visibility = common.visibility }); | 12 | @export(&__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/mulXi3.zig-2| ... | @@ -4,8 +4,6 @@ const testing = std.testing; | ... | @@ -4,8 +4,6 @@ const testing = std.testing; |
| 4 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 5 | const native_endian = builtin.cpu.arch.endian(); | 5 | const native_endian = builtin.cpu.arch.endian(); |
| 6 | 6 | ||
| 7 | pub const panic = common.panic; | ||
| 8 | |||
| 9 | comptime { | 7 | comptime { |
| 10 | @export(&__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); |
| 11 | if (common.want_aeabi) { | 9 | if (common.want_aeabi) { |
lib/compiler_rt/muldc3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulc3 = @import("./mulc3.zig"); | 2 | const mulc3 = @import("./mulc3.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (@import("builtin").zig_backend != .stage2_c) { | 5 | if (@import("builtin").zig_backend != .stage2_c) { |
| 8 | @export(&__muldc3, .{ .name = "__muldc3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__muldc3, .{ .name = "__muldc3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/muldf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulf3 = @import("./mulf3.zig").mulf3; | 2 | const mulf3 = @import("./mulf3.zig").mulf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/mulhc3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulc3 = @import("./mulc3.zig"); | 2 | const mulc3 = @import("./mulc3.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (@import("builtin").zig_backend != .stage2_c) { | 5 | if (@import("builtin").zig_backend != .stage2_c) { |
| 8 | @export(&__mulhc3, .{ .name = "__mulhc3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__mulhc3, .{ .name = "__mulhc3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/mulhf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulf3 = @import("./mulf3.zig").mulf3; | 2 | const mulf3 = @import("./mulf3.zig").mulf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__mulhf3, .{ .name = "__mulhf3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__mulhf3, .{ .name = "__mulhf3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/mulo.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | @export(&__mulosi4, .{ .name = "__mulosi4", .linkage = common.linkage, .visibility = common.visibility }); | 7 | @export(&__mulosi4, .{ .name = "__mulosi4", .linkage = common.linkage, .visibility = common.visibility }); |
| 10 | @export(&__mulodi4, .{ .name = "__mulodi4", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__mulodi4, .{ .name = "__mulodi4", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/mulsc3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulc3 = @import("./mulc3.zig"); | 2 | const mulc3 = @import("./mulc3.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (@import("builtin").zig_backend != .stage2_c) { | 5 | if (@import("builtin").zig_backend != .stage2_c) { |
| 8 | @export(&__mulsc3, .{ .name = "__mulsc3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__mulsc3, .{ .name = "__mulsc3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/mulsf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulf3 = @import("./mulf3.zig").mulf3; | 2 | const mulf3 = @import("./mulf3.zig").mulf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/multc3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulc3 = @import("./mulc3.zig"); | 2 | const mulc3 = @import("./mulc3.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (@import("builtin").zig_backend != .stage2_c) { | 5 | if (@import("builtin").zig_backend != .stage2_c) { |
| 8 | if (common.want_ppc_abi) | 6 | if (common.want_ppc_abi) |
lib/compiler_rt/multf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulf3 = @import("./mulf3.zig").mulf3; | 2 | const mulf3 = @import("./mulf3.zig").mulf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__multf3, .{ .name = "__mulkf3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__multf3, .{ .name = "__mulkf3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/mulvsi3.zig-2| ... | @@ -2,8 +2,6 @@ const mulv = @import("mulo.zig"); | ... | @@ -2,8 +2,6 @@ const mulv = @import("mulo.zig"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const testing = @import("std").testing; | 3 | const testing = @import("std").testing; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | @export(&__mulvsi3, .{ .name = "__mulvsi3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__mulvsi3, .{ .name = "__mulvsi3", .linkage = common.linkage, .visibility = common.visibility }); |
| 9 | } | 7 | } |
lib/compiler_rt/mulxc3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulc3 = @import("./mulc3.zig"); | 2 | const mulc3 = @import("./mulc3.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (@import("builtin").zig_backend != .stage2_c) { | 5 | if (@import("builtin").zig_backend != .stage2_c) { |
| 8 | @export(&__mulxc3, .{ .name = "__mulxc3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__mulxc3, .{ .name = "__mulxc3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/mulxf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const mulf3 = @import("./mulf3.zig").mulf3; | 2 | const mulf3 = @import("./mulf3.zig").mulf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__mulxf3, .{ .name = "__mulxf3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__mulxf3, .{ .name = "__mulxf3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/negXi2.zig-2| ... | @@ -10,8 +10,6 @@ const std = @import("std"); | ... | @@ -10,8 +10,6 @@ const std = @import("std"); |
| 10 | const builtin = @import("builtin"); | 10 | const builtin = @import("builtin"); |
| 11 | const common = @import("common.zig"); | 11 | const common = @import("common.zig"); |
| 12 | 12 | ||
| 13 | pub const panic = common.panic; | ||
| 14 | |||
| 15 | comptime { | 13 | comptime { |
| 16 | @export(&__negsi2, .{ .name = "__negsi2", .linkage = common.linkage, .visibility = common.visibility }); | 14 | @export(&__negsi2, .{ .name = "__negsi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 17 | @export(&__negdi2, .{ .name = "__negdi2", .linkage = common.linkage, .visibility = common.visibility }); | 15 | @export(&__negdi2, .{ .name = "__negdi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/negdf2.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | 2 | ||
| 3 | pub const panic = common.panic; | ||
| 4 | |||
| 5 | comptime { | 3 | comptime { |
| 6 | if (common.want_aeabi) { | 4 | if (common.want_aeabi) { |
| 7 | @export(&__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/neghf2.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | 2 | ||
| 3 | pub const panic = common.panic; | ||
| 4 | |||
| 5 | comptime { | 3 | comptime { |
| 6 | @export(&__neghf2, .{ .name = "__neghf2", .linkage = common.linkage, .visibility = common.visibility }); | 4 | @export(&__neghf2, .{ .name = "__neghf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 7 | } | 5 | } |
lib/compiler_rt/negsf2.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | 2 | ||
| 3 | pub const panic = common.panic; | ||
| 4 | |||
| 5 | comptime { | 3 | comptime { |
| 6 | if (common.want_aeabi) { | 4 | if (common.want_aeabi) { |
| 7 | @export(&__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/negtf2.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | 2 | ||
| 3 | pub const panic = common.panic; | ||
| 4 | |||
| 5 | comptime { | 3 | comptime { |
| 6 | if (common.want_ppc_abi) | 4 | if (common.want_ppc_abi) |
| 7 | @export(&__negtf2, .{ .name = "__negkf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__negtf2, .{ .name = "__negkf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/negv.zig-2| ... | @@ -5,8 +5,6 @@ const std = @import("std"); | ... | @@ -5,8 +5,6 @@ const std = @import("std"); |
| 5 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 6 | const common = @import("common.zig"); | 6 | const common = @import("common.zig"); |
| 7 | 7 | ||
| 8 | pub const panic = common.panic; | ||
| 9 | |||
| 10 | comptime { | 8 | comptime { |
| 11 | @export(&__negvsi2, .{ .name = "__negvsi2", .linkage = common.linkage, .visibility = common.visibility }); | 9 | @export(&__negvsi2, .{ .name = "__negvsi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 12 | @export(&__negvdi2, .{ .name = "__negvdi2", .linkage = common.linkage, .visibility = common.visibility }); | 10 | @export(&__negvdi2, .{ .name = "__negvdi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/negxf2.zig-2| ... | @@ -1,7 +1,5 @@ | ... | @@ -1,7 +1,5 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | 2 | ||
| 3 | pub const panic = common.panic; | ||
| 4 | |||
| 5 | comptime { | 3 | comptime { |
| 6 | @export(&__negxf2, .{ .name = "__negxf2", .linkage = common.linkage, .visibility = common.visibility }); | 4 | @export(&__negxf2, .{ .name = "__negxf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 7 | } | 5 | } |
lib/compiler_rt/parity.zig-2| ... | @@ -5,8 +5,6 @@ const std = @import("std"); | ... | @@ -5,8 +5,6 @@ const std = @import("std"); |
| 5 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 6 | const common = @import("common.zig"); | 6 | const common = @import("common.zig"); |
| 7 | 7 | ||
| 8 | pub const panic = common.panic; | ||
| 9 | |||
| 10 | comptime { | 8 | comptime { |
| 11 | @export(&__paritysi2, .{ .name = "__paritysi2", .linkage = common.linkage, .visibility = common.visibility }); | 9 | @export(&__paritysi2, .{ .name = "__paritysi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 12 | @export(&__paritydi2, .{ .name = "__paritydi2", .linkage = common.linkage, .visibility = common.visibility }); | 10 | @export(&__paritydi2, .{ .name = "__paritydi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/popcount.zig-2| ... | @@ -10,8 +10,6 @@ const builtin = @import("builtin"); | ... | @@ -10,8 +10,6 @@ const builtin = @import("builtin"); |
| 10 | const std = @import("std"); | 10 | const std = @import("std"); |
| 11 | const common = @import("common.zig"); | 11 | const common = @import("common.zig"); |
| 12 | 12 | ||
| 13 | pub const panic = common.panic; | ||
| 14 | |||
| 15 | comptime { | 13 | comptime { |
| 16 | @export(&__popcountsi2, .{ .name = "__popcountsi2", .linkage = common.linkage, .visibility = common.visibility }); | 14 | @export(&__popcountsi2, .{ .name = "__popcountsi2", .linkage = common.linkage, .visibility = common.visibility }); |
| 17 | @export(&__popcountdi2, .{ .name = "__popcountdi2", .linkage = common.linkage, .visibility = common.visibility }); | 15 | @export(&__popcountdi2, .{ .name = "__popcountdi2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/powiXf2.zig-2| ... | @@ -7,8 +7,6 @@ const builtin = @import("builtin"); | ... | @@ -7,8 +7,6 @@ const builtin = @import("builtin"); |
| 7 | const common = @import("common.zig"); | 7 | const common = @import("common.zig"); |
| 8 | const std = @import("std"); | 8 | const std = @import("std"); |
| 9 | 9 | ||
| 10 | pub const panic = common.panic; | ||
| 11 | |||
| 12 | comptime { | 10 | comptime { |
| 13 | @export(&__powihf2, .{ .name = "__powihf2", .linkage = common.linkage, .visibility = common.visibility }); | 11 | @export(&__powihf2, .{ .name = "__powihf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 14 | @export(&__powisf2, .{ .name = "__powisf2", .linkage = common.linkage, .visibility = common.visibility }); | 12 | @export(&__powisf2, .{ .name = "__powisf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/round.zig-2| ... | @@ -12,8 +12,6 @@ const expect = std.testing.expect; | ... | @@ -12,8 +12,6 @@ const expect = std.testing.expect; |
| 12 | const arch = builtin.cpu.arch; | 12 | const arch = builtin.cpu.arch; |
| 13 | const common = @import("common.zig"); | 13 | const common = @import("common.zig"); |
| 14 | 14 | ||
| 15 | pub const panic = common.panic; | ||
| 16 | |||
| 17 | comptime { | 15 | comptime { |
| 18 | @export(&__roundh, .{ .name = "__roundh", .linkage = common.linkage, .visibility = common.visibility }); | 16 | @export(&__roundh, .{ .name = "__roundh", .linkage = common.linkage, .visibility = common.visibility }); |
| 19 | @export(&roundf, .{ .name = "roundf", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&roundf, .{ .name = "roundf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/shift.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const Log2Int = std.math.Log2Int; | 3 | const Log2Int = std.math.Log2Int; |
| 4 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | // symbol compatibility with libgcc | 7 | // symbol compatibility with libgcc |
| 10 | @export(&__ashlsi3, .{ .name = "__ashlsi3", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__ashlsi3, .{ .name = "__ashlsi3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/sin.zig-2| ... | @@ -16,8 +16,6 @@ const trig = @import("trig.zig"); | ... | @@ -16,8 +16,6 @@ const trig = @import("trig.zig"); |
| 16 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; | 16 | const rem_pio2 = @import("rem_pio2.zig").rem_pio2; |
| 17 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; | 17 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; |
| 18 | 18 | ||
| 19 | pub const panic = common.panic; | ||
| 20 | |||
| 21 | comptime { | 19 | comptime { |
| 22 | @export(&__sinh, .{ .name = "__sinh", .linkage = common.linkage, .visibility = common.visibility }); | 20 | @export(&__sinh, .{ .name = "__sinh", .linkage = common.linkage, .visibility = common.visibility }); |
| 23 | @export(&sinf, .{ .name = "sinf", .linkage = common.linkage, .visibility = common.visibility }); | 21 | @export(&sinf, .{ .name = "sinf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/sincos.zig-2| ... | @@ -8,8 +8,6 @@ const rem_pio2 = @import("rem_pio2.zig").rem_pio2; | ... | @@ -8,8 +8,6 @@ const rem_pio2 = @import("rem_pio2.zig").rem_pio2; |
| 8 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; | 8 | const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; |
| 9 | const common = @import("common.zig"); | 9 | const common = @import("common.zig"); |
| 10 | 10 | ||
| 11 | pub const panic = common.panic; | ||
| 12 | |||
| 13 | comptime { | 11 | comptime { |
| 14 | @export(&__sincosh, .{ .name = "__sincosh", .linkage = common.linkage, .visibility = common.visibility }); | 12 | @export(&__sincosh, .{ .name = "__sincosh", .linkage = common.linkage, .visibility = common.visibility }); |
| 15 | @export(&sincosf, .{ .name = "sincosf", .linkage = common.linkage, .visibility = common.visibility }); | 13 | @export(&sincosf, .{ .name = "sincosf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/sqrt.zig-2| ... | @@ -11,8 +11,6 @@ const arch = builtin.cpu.arch; | ... | @@ -11,8 +11,6 @@ const arch = builtin.cpu.arch; |
| 11 | const math = std.math; | 11 | const math = std.math; |
| 12 | const common = @import("common.zig"); | 12 | const common = @import("common.zig"); |
| 13 | 13 | ||
| 14 | pub const panic = common.panic; | ||
| 15 | |||
| 16 | comptime { | 14 | comptime { |
| 17 | @export(&__sqrth, .{ .name = "__sqrth", .linkage = common.linkage, .visibility = common.visibility }); | 15 | @export(&__sqrth, .{ .name = "__sqrth", .linkage = common.linkage, .visibility = common.visibility }); |
| 18 | @export(&sqrtf, .{ .name = "sqrtf", .linkage = common.linkage, .visibility = common.visibility }); | 16 | @export(&sqrtf, .{ .name = "sqrtf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/stack_probe.zig-2| ... | @@ -5,8 +5,6 @@ const os_tag = builtin.os.tag; | ... | @@ -5,8 +5,6 @@ const os_tag = builtin.os.tag; |
| 5 | const arch = builtin.cpu.arch; | 5 | const arch = builtin.cpu.arch; |
| 6 | const abi = builtin.abi; | 6 | const abi = builtin.abi; |
| 7 | 7 | ||
| 8 | pub const panic = common.panic; | ||
| 9 | |||
| 10 | comptime { | 8 | comptime { |
| 11 | if (builtin.os.tag == .windows) { | 9 | if (builtin.os.tag == .windows) { |
| 12 | // Default stack-probe functions emitted by LLVM | 10 | // Default stack-probe functions emitted by LLVM |
lib/compiler_rt/subdf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/subhf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__subhf3, .{ .name = "__subhf3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__subhf3, .{ .name = "__subhf3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/subsf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/subtf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const addf3 = @import("./addf3.zig").addf3; | 2 | const addf3 = @import("./addf3.zig").addf3; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__subtf3, .{ .name = "__subkf3", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__subtf3, .{ .name = "__subkf3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/subvdi3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const testing = @import("std").testing; | 2 | const testing = @import("std").testing; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__subvdi3, .{ .name = "__subvdi3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__subvdi3, .{ .name = "__subvdi3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/subvsi3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const testing = @import("std").testing; | 2 | const testing = @import("std").testing; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__subvsi3, .{ .name = "__subvsi3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__subvsi3, .{ .name = "__subvsi3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/subxf3.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__subxf3, .{ .name = "__subxf3", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__subxf3, .{ .name = "__subxf3", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/tan.zig-2| ... | @@ -18,8 +18,6 @@ const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; | ... | @@ -18,8 +18,6 @@ const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; |
| 18 | const arch = builtin.cpu.arch; | 18 | const arch = builtin.cpu.arch; |
| 19 | const common = @import("common.zig"); | 19 | const common = @import("common.zig"); |
| 20 | 20 | ||
| 21 | pub const panic = common.panic; | ||
| 22 | |||
| 23 | comptime { | 21 | comptime { |
| 24 | @export(&__tanh, .{ .name = "__tanh", .linkage = common.linkage, .visibility = common.visibility }); | 22 | @export(&__tanh, .{ .name = "__tanh", .linkage = common.linkage, .visibility = common.visibility }); |
| 25 | @export(&tanf, .{ .name = "tanf", .linkage = common.linkage, .visibility = common.visibility }); | 23 | @export(&tanf, .{ .name = "tanf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/trunc.zig-2| ... | @@ -12,8 +12,6 @@ const mem = std.mem; | ... | @@ -12,8 +12,6 @@ const mem = std.mem; |
| 12 | const expect = std.testing.expect; | 12 | const expect = std.testing.expect; |
| 13 | const common = @import("common.zig"); | 13 | const common = @import("common.zig"); |
| 14 | 14 | ||
| 15 | pub const panic = common.panic; | ||
| 16 | |||
| 17 | comptime { | 15 | comptime { |
| 18 | @export(&__trunch, .{ .name = "__trunch", .linkage = common.linkage, .visibility = common.visibility }); | 16 | @export(&__trunch, .{ .name = "__trunch", .linkage = common.linkage, .visibility = common.visibility }); |
| 19 | @export(&truncf, .{ .name = "truncf", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(&truncf, .{ .name = "truncf", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/truncdfhf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const truncf = @import("./truncf.zig").truncf; | 2 | const truncf = @import("./truncf.zig").truncf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/truncdfsf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const truncf = @import("./truncf.zig").truncf; | 2 | const truncf = @import("./truncf.zig").truncf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/truncsfhf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const truncf = @import("./truncf.zig").truncf; | 2 | const truncf = @import("./truncf.zig").truncf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.gnu_f16_abi) { | 5 | if (common.gnu_f16_abi) { |
| 8 | @export(&__gnu_f2h_ieee, .{ .name = "__gnu_f2h_ieee", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__gnu_f2h_ieee, .{ .name = "__gnu_f2h_ieee", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/trunctfdf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const truncf = @import("./truncf.zig").truncf; | 2 | const truncf = @import("./truncf.zig").truncf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/trunctfhf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const truncf = @import("./truncf.zig").truncf; | 2 | const truncf = @import("./truncf.zig").truncf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/trunctfsf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const truncf = @import("./truncf.zig").truncf; | 2 | const truncf = @import("./truncf.zig").truncf; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/trunctfxf2.zig-2| ... | @@ -2,8 +2,6 @@ const math = @import("std").math; | ... | @@ -2,8 +2,6 @@ const math = @import("std").math; |
| 2 | const common = @import("./common.zig"); | 2 | const common = @import("./common.zig"); |
| 3 | const trunc_f80 = @import("./truncf.zig").trunc_f80; | 3 | const trunc_f80 = @import("./truncf.zig").trunc_f80; |
| 4 | 4 | ||
| 5 | pub const panic = common.panic; | ||
| 6 | |||
| 7 | comptime { | 5 | comptime { |
| 8 | @export(&__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 9 | } | 7 | } |
lib/compiler_rt/truncxfdf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const trunc_f80 = @import("./truncf.zig").trunc_f80; | 2 | const trunc_f80 = @import("./truncf.zig").trunc_f80; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/truncxfhf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const trunc_f80 = @import("./truncf.zig").trunc_f80; | 2 | const trunc_f80 = @import("./truncf.zig").trunc_f80; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/truncxfsf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const trunc_f80 = @import("./truncf.zig").trunc_f80; | 2 | const trunc_f80 = @import("./truncf.zig").trunc_f80; |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__truncxfsf2, .{ .name = "__truncxfsf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__truncxfsf2, .{ .name = "__truncxfsf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/udivmodti4.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const udivmod = @import("udivmod.zig").udivmod; | 3 | const udivmod = @import("udivmod.zig").udivmod; |
| 4 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_windows_v2u64_abi) { | 7 | if (common.want_windows_v2u64_abi) { |
| 10 | @export(&__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/udivti3.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const udivmod = @import("udivmod.zig").udivmod; | 3 | const udivmod = @import("udivmod.zig").udivmod; |
| 4 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_windows_v2u64_abi) { | 7 | if (common.want_windows_v2u64_abi) { |
| 10 | @export(&__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/umodti3.zig-2| ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,8 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const udivmod = @import("udivmod.zig").udivmod; | 3 | const udivmod = @import("udivmod.zig").udivmod; |
| 4 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 5 | 5 | ||
| 6 | pub const panic = common.panic; | ||
| 7 | |||
| 8 | comptime { | 6 | comptime { |
| 9 | if (common.want_windows_v2u64_abi) { | 7 | if (common.want_windows_v2u64_abi) { |
| 10 | @export(&__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = common.linkage, .visibility = common.visibility }); | 8 | @export(&__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/unorddf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const comparef = @import("./comparef.zig"); | 2 | const comparef = @import("./comparef.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/unordhf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const comparef = @import("./comparef.zig"); | 2 | const comparef = @import("./comparef.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__unordhf2, .{ .name = "__unordhf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__unordhf2, .{ .name = "__unordhf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/compiler_rt/unordsf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const comparef = @import("./comparef.zig"); | 2 | const comparef = @import("./comparef.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_aeabi) { | 5 | if (common.want_aeabi) { |
| 8 | @export(&__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/unordtf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const comparef = @import("./comparef.zig"); | 2 | const comparef = @import("./comparef.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | if (common.want_ppc_abi) { | 5 | if (common.want_ppc_abi) { |
| 8 | @export(&__unordtf2, .{ .name = "__unordkf2", .linkage = common.linkage, .visibility = common.visibility }); | 6 | @export(&__unordtf2, .{ .name = "__unordkf2", .linkage = common.linkage, .visibility = common.visibility }); |
lib/compiler_rt/unordxf2.zig-2| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const common = @import("./common.zig"); | 1 | const common = @import("./common.zig"); |
| 2 | const comparef = @import("./comparef.zig"); | 2 | const comparef = @import("./comparef.zig"); |
| 3 | 3 | ||
| 4 | pub const panic = common.panic; | ||
| 5 | |||
| 6 | comptime { | 4 | comptime { |
| 7 | @export(&__unordxf2, .{ .name = "__unordxf2", .linkage = common.linkage, .visibility = common.visibility }); | 5 | @export(&__unordxf2, .{ .name = "__unordxf2", .linkage = common.linkage, .visibility = common.visibility }); |
| 8 | } | 6 | } |
lib/init/src/main.zig+11-9| ... | @@ -3,19 +3,21 @@ const Io = std.Io; | ... | @@ -3,19 +3,21 @@ const Io = std.Io; |
| 3 | 3 | ||
| 4 | const _NAME = @import(".NAME"); | 4 | const _NAME = @import(".NAME"); |
| 5 | 5 | ||
| 6 | pub fn main() !void { | 6 | pub fn main(init: std.process.Init) !void { |
| 7 | // Prints to stderr, unbuffered, ignoring potential errors. | 7 | // Prints to stderr, unbuffered, ignoring potential errors. |
| 8 | std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); | 8 | std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); |
| 9 | 9 | ||
| 10 | // In order to allocate memory we must construct an `Allocator` instance. | 10 | // This is appropriate for anything that lives as long as the process. |
| 11 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | 11 | const arena: std.mem.Allocator = init.arena.allocator(); |
| 12 | defer _ = debug_allocator.deinit(); // This checks for leaks. | ||
| 13 | const gpa = debug_allocator.allocator(); | ||
| 14 | 12 | ||
| 15 | // In order to do I/O operations we must construct an `Io` instance. | 13 | // Accessing command line arguments: |
| 16 | var threaded: std.Io.Threaded = .init(gpa, .{}); | 14 | const args = try init.minimal.args.toSlice(arena); |
| 17 | defer threaded.deinit(); | 15 | for (args) |arg| { |
| 18 | const io = threaded.io(); | 16 | std.log.info("arg: {s}", .{arg}); |
| 17 | } | ||
| 18 | |||
| 19 | // In order to do I/O operations need an `Io` instance. | ||
| 20 | const io = init.io; | ||
| 19 | 21 | ||
| 20 | // Stdout is for the actual output of your application, for example if you | 22 | // Stdout is for the actual output of your application, for example if you |
| 21 | // are implementing gzip, then only the compressed bytes should be sent to | 23 | // are implementing gzip, then only the compressed bytes should be sent to |
lib/std/Build.zig+29-35| ... | @@ -12,7 +12,6 @@ const StringHashMap = std.StringHashMap; | ... | @@ -12,7 +12,6 @@ const StringHashMap = std.StringHashMap; |
| 12 | const Allocator = std.mem.Allocator; | 12 | const Allocator = std.mem.Allocator; |
| 13 | const Target = std.Target; | 13 | const Target = std.Target; |
| 14 | const process = std.process; | 14 | const process = std.process; |
| 15 | const EnvMap = std.process.EnvMap; | ||
| 16 | const File = std.Io.File; | 15 | const File = std.Io.File; |
| 17 | const Sha256 = std.crypto.hash.sha2.Sha256; | 16 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 18 | const ArrayList = std.ArrayList; | 17 | const ArrayList = std.ArrayList; |
| ... | @@ -118,7 +117,7 @@ pub const Graph = struct { | ... | @@ -118,7 +117,7 @@ pub const Graph = struct { |
| 118 | debug_compiler_runtime_libs: bool = false, | 117 | debug_compiler_runtime_libs: bool = false, |
| 119 | cache: Cache, | 118 | cache: Cache, |
| 120 | zig_exe: [:0]const u8, | 119 | zig_exe: [:0]const u8, |
| 121 | env_map: EnvMap, | 120 | environ_map: process.Environ.Map, |
| 122 | global_cache_root: Cache.Directory, | 121 | global_cache_root: Cache.Directory, |
| 123 | zig_lib_directory: Cache.Directory, | 122 | zig_lib_directory: Cache.Directory, |
| 124 | needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .empty, | 123 | needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .empty, |
| ... | @@ -190,7 +189,7 @@ pub const RunError = error{ | ... | @@ -190,7 +189,7 @@ pub const RunError = error{ |
| 190 | ExitCodeFailure, | 189 | ExitCodeFailure, |
| 191 | ProcessTerminated, | 190 | ProcessTerminated, |
| 192 | ExecNotSupported, | 191 | ExecNotSupported, |
| 193 | } || std.process.Child.SpawnError; | 192 | } || std.process.SpawnError; |
| 194 | 193 | ||
| 195 | pub const PkgConfigError = error{ | 194 | pub const PkgConfigError = error{ |
| 196 | PkgConfigCrashed, | 195 | PkgConfigCrashed, |
| ... | @@ -289,7 +288,7 @@ pub fn create( | ... | @@ -289,7 +288,7 @@ pub fn create( |
| 289 | .lib_dir = undefined, | 288 | .lib_dir = undefined, |
| 290 | .exe_dir = undefined, | 289 | .exe_dir = undefined, |
| 291 | .h_dir = undefined, | 290 | .h_dir = undefined, |
| 292 | .dest_dir = graph.env_map.get("DESTDIR"), | 291 | .dest_dir = graph.environ_map.get("DESTDIR"), |
| 293 | .install_tls = .{ | 292 | .install_tls = .{ |
| 294 | .step = .init(.{ | 293 | .step = .init(.{ |
| 295 | .id = TopLevelStep.base_id, | 294 | .id = TopLevelStep.base_id, |
| ... | @@ -1738,8 +1737,7 @@ pub fn pathFromRoot(b: *Build, sub_path: []const u8) []u8 { | ... | @@ -1738,8 +1737,7 @@ pub fn pathFromRoot(b: *Build, sub_path: []const u8) []u8 { |
| 1738 | } | 1737 | } |
| 1739 | 1738 | ||
| 1740 | fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 { | 1739 | fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 { |
| 1741 | const cwd = process.getCwdAlloc(b.allocator) catch @panic("OOM"); | 1740 | return b.pathResolve(&.{ b.graph.cache.cwd, sub_path }); |
| 1742 | return b.pathResolve(&.{ cwd, sub_path }); | ||
| 1743 | } | 1741 | } |
| 1744 | 1742 | ||
| 1745 | pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 { | 1743 | 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 { | ... | @@ -1755,7 +1753,7 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 { |
| 1755 | } | 1753 | } |
| 1756 | 1754 | ||
| 1757 | fn supportedWindowsProgramExtension(ext: []const u8) bool { | 1755 | fn supportedWindowsProgramExtension(ext: []const u8) bool { |
| 1758 | inline for (@typeInfo(std.process.Child.WindowsExtension).@"enum".fields) |field| { | 1756 | inline for (@typeInfo(std.process.WindowsExtension).@"enum".fields) |field| { |
| 1759 | if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true; | 1757 | if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true; |
| 1760 | } | 1758 | } |
| 1761 | return false; | 1759 | return false; |
| ... | @@ -1773,7 +1771,7 @@ fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 { | ... | @@ -1773,7 +1771,7 @@ fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 { |
| 1773 | } | 1771 | } |
| 1774 | 1772 | ||
| 1775 | if (builtin.os.tag == .windows) { | 1773 | if (builtin.os.tag == .windows) { |
| 1776 | if (b.graph.env_map.get("PATHEXT")) |PATHEXT| { | 1774 | if (b.graph.environ_map.get("PATHEXT")) |PATHEXT| { |
| 1777 | var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter); | 1775 | var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter); |
| 1778 | 1776 | ||
| 1779 | while (it.next()) |ext| { | 1777 | while (it.next()) |ext| { |
| ... | @@ -1804,7 +1802,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const | ... | @@ -1804,7 +1802,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const |
| 1804 | return tryFindProgram(b, b.pathJoin(&.{ search_prefix, "bin", name })) orelse continue; | 1802 | return tryFindProgram(b, b.pathJoin(&.{ search_prefix, "bin", name })) orelse continue; |
| 1805 | } | 1803 | } |
| 1806 | } | 1804 | } |
| 1807 | if (b.graph.env_map.get("PATH")) |PATH| { | 1805 | if (b.graph.environ_map.get("PATH")) |PATH| { |
| 1808 | for (names) |name| { | 1806 | for (names) |name| { |
| 1809 | if (fs.path.isAbsolute(name)) { | 1807 | if (fs.path.isAbsolute(name)) { |
| 1810 | return name; | 1808 | return name; |
| ... | @@ -1830,24 +1828,26 @@ pub fn runAllowFail( | ... | @@ -1830,24 +1828,26 @@ pub fn runAllowFail( |
| 1830 | b: *Build, | 1828 | b: *Build, |
| 1831 | argv: []const []const u8, | 1829 | argv: []const []const u8, |
| 1832 | out_code: *u8, | 1830 | out_code: *u8, |
| 1833 | stderr_behavior: std.process.Child.StdIo, | 1831 | stderr_behavior: std.process.SpawnOptions.StdIo, |
| 1834 | ) RunError![]u8 { | 1832 | ) RunError![]u8 { |
| 1835 | assert(argv.len != 0); | 1833 | assert(argv.len != 0); |
| 1836 | 1834 | ||
| 1837 | if (!process.can_spawn) | 1835 | if (!process.can_spawn) |
| 1838 | return error.ExecNotSupported; | 1836 | return error.ExecNotSupported; |
| 1839 | 1837 | ||
| 1840 | const io = b.graph.io; | 1838 | const graph = b.graph; |
| 1839 | const io = graph.io; | ||
| 1841 | 1840 | ||
| 1842 | const max_output_size = 400 * 1024; | 1841 | const max_output_size = 400 * 1024; |
| 1843 | var child = std.process.Child.init(argv, b.allocator); | 1842 | try Step.handleVerbose2(b, null, &graph.environ_map, argv); |
| 1844 | child.stdin_behavior = .Ignore; | 1843 | |
| 1845 | child.stdout_behavior = .Pipe; | 1844 | var child = try std.process.spawn(io, .{ |
| 1846 | child.stderr_behavior = stderr_behavior; | 1845 | .argv = argv, |
| 1847 | child.env_map = &b.graph.env_map; | 1846 | .environ_map = &graph.environ_map, |
| 1848 | 1847 | .stdin = .ignore, | |
| 1849 | try Step.handleVerbose2(b, null, child.env_map, argv); | 1848 | .stdout = .pipe, |
| 1850 | try child.spawn(io); | 1849 | .stderr = stderr_behavior, |
| 1850 | }); | ||
| 1851 | 1851 | ||
| 1852 | var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); | 1852 | var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); |
| 1853 | const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch { | 1853 | const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch { |
| ... | @@ -1857,14 +1857,18 @@ pub fn runAllowFail( | ... | @@ -1857,14 +1857,18 @@ pub fn runAllowFail( |
| 1857 | 1857 | ||
| 1858 | const term = try child.wait(io); | 1858 | const term = try child.wait(io); |
| 1859 | switch (term) { | 1859 | switch (term) { |
| 1860 | .Exited => |code| { | 1860 | .exited => |code| { |
| 1861 | if (code != 0) { | 1861 | if (code != 0) { |
| 1862 | out_code.* = @as(u8, @truncate(code)); | 1862 | out_code.* = @as(u8, @truncate(code)); |
| 1863 | return error.ExitCodeFailure; | 1863 | return error.ExitCodeFailure; |
| 1864 | } | 1864 | } |
| 1865 | return stdout; | 1865 | return stdout; |
| 1866 | }, | 1866 | }, |
| 1867 | .Signal, .Stopped, .Unknown => |code| { | 1867 | .signal => |sig| { |
| 1868 | out_code.* = @as(u8, @truncate(@intFromEnum(sig))); | ||
| 1869 | return error.ProcessTerminated; | ||
| 1870 | }, | ||
| 1871 | .stopped, .unknown => |code| { | ||
| 1868 | out_code.* = @as(u8, @truncate(code)); | 1872 | out_code.* = @as(u8, @truncate(code)); |
| 1869 | return error.ProcessTerminated; | 1873 | return error.ProcessTerminated; |
| 1870 | }, | 1874 | }, |
| ... | @@ -1875,21 +1879,11 @@ pub fn runAllowFail( | ... | @@ -1875,21 +1879,11 @@ pub fn runAllowFail( |
| 1875 | /// inside step make() functions. If any errors occur, it fails the build with | 1879 | /// inside step make() functions. If any errors occur, it fails the build with |
| 1876 | /// a helpful message. | 1880 | /// a helpful message. |
| 1877 | pub fn run(b: *Build, argv: []const []const u8) []u8 { | 1881 | pub fn run(b: *Build, argv: []const []const u8) []u8 { |
| 1878 | if (!process.can_spawn) { | ||
| 1879 | std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{ | ||
| 1880 | try Step.allocPrintCmd(b.allocator, null, argv), | ||
| 1881 | }); | ||
| 1882 | process.exit(1); | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | var code: u8 = undefined; | 1882 | var code: u8 = undefined; |
| 1886 | return b.runAllowFail(argv, &code, .Inherit) catch |err| { | 1883 | return b.runAllowFail(argv, &code, .inherit) catch |err| process.fatal( |
| 1887 | const printed_cmd = Step.allocPrintCmd(b.allocator, null, argv) catch @panic("OOM"); | 1884 | "the following command failed with {t}:\n{s}", |
| 1888 | std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{ | 1885 | .{ err, Step.allocPrintCmd(b.allocator, null, null, argv) catch @panic("OOM") }, |
| 1889 | @errorName(err), printed_cmd, | 1886 | ); |
| 1890 | }); | ||
| 1891 | process.exit(1); | ||
| 1892 | }; | ||
| 1893 | } | 1887 | } |
| 1894 | 1888 | ||
| 1895 | pub fn addSearchPrefix(b: *Build, search_prefix: []const u8) void { | 1889 | pub fn addSearchPrefix(b: *Build, search_prefix: []const u8) void { |
lib/std/Build/Cache.zig+27-8| ... | @@ -30,6 +30,8 @@ mutex: Io.Mutex = .init, | ... | @@ -30,6 +30,8 @@ mutex: Io.Mutex = .init, |
| 30 | /// and usefulness of the cache for advanced use cases. | 30 | /// and usefulness of the cache for advanced use cases. |
| 31 | prefixes_buffer: [4]Directory = undefined, | 31 | prefixes_buffer: [4]Directory = undefined, |
| 32 | prefixes_len: usize = 0, | 32 | prefixes_len: usize = 0, |
| 33 | /// Used to identify prefixes. References external memory. | ||
| 34 | cwd: []const u8, | ||
| 33 | 35 | ||
| 34 | pub const Path = @import("Cache/Path.zig"); | 36 | pub const Path = @import("Cache/Path.zig"); |
| 35 | pub const Directory = @import("Cache/Directory.zig"); | 37 | pub const Directory = @import("Cache/Directory.zig"); |
| ... | @@ -78,11 +80,12 @@ fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { | ... | @@ -78,11 +80,12 @@ fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { |
| 78 | /// Takes ownership of `resolved_path` on success. | 80 | /// Takes ownership of `resolved_path` on success. |
| 79 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { | 81 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { |
| 80 | const gpa = cache.gpa; | 82 | const gpa = cache.gpa; |
| 83 | const cwd = cache.cwd; | ||
| 81 | const prefixes_slice = cache.prefixes(); | 84 | const prefixes_slice = cache.prefixes(); |
| 82 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. | 85 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. |
| 83 | while (i < prefixes_slice.len) : (i += 1) { | 86 | while (i < prefixes_slice.len) : (i += 1) { |
| 84 | const p = prefixes_slice[i].path.?; | 87 | const p = prefixes_slice[i].path.?; |
| 85 | const sub_path = getPrefixSubpath(gpa, p, resolved_path) catch |err| switch (err) { | 88 | const sub_path = getPrefixSubpath(gpa, cwd, p, resolved_path) catch |err| switch (err) { |
| 86 | error.NotASubPath => continue, | 89 | error.NotASubPath => continue, |
| 87 | else => |e| return e, | 90 | else => |e| return e, |
| 88 | }; | 91 | }; |
| ... | @@ -100,10 +103,10 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { | ... | @@ -100,10 +103,10 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { |
| 100 | }; | 103 | }; |
| 101 | } | 104 | } |
| 102 | 105 | ||
| 103 | fn getPrefixSubpath(allocator: Allocator, prefix: []const u8, path: []u8) ![]u8 { | 106 | fn getPrefixSubpath(gpa: Allocator, cwd: []const u8, prefix: []const u8, path: []u8) ![]u8 { |
| 104 | const relative = try std.fs.path.relative(allocator, prefix, path); | 107 | const relative = try std.fs.path.relative(gpa, cwd, null, prefix, path); |
| 105 | errdefer allocator.free(relative); | 108 | errdefer gpa.free(relative); |
| 106 | var component_iterator = std.fs.path.NativeComponentIterator.init(relative); | 109 | var component_iterator: std.fs.path.NativeComponentIterator = .init(relative); |
| 107 | if (component_iterator.root() != null) { | 110 | if (component_iterator.root() != null) { |
| 108 | return error.NotASubPath; | 111 | return error.NotASubPath; |
| 109 | } | 112 | } |
| ... | @@ -1307,11 +1310,14 @@ fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp { | ... | @@ -1307,11 +1310,14 @@ fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp { |
| 1307 | } | 1310 | } |
| 1308 | 1311 | ||
| 1309 | test "cache file and then recall it" { | 1312 | test "cache file and then recall it" { |
| 1310 | const io = std.testing.io; | 1313 | const io = testing.io; |
| 1311 | 1314 | ||
| 1312 | var tmp = testing.tmpDir(.{}); | 1315 | var tmp = testing.tmpDir(.{}); |
| 1313 | defer tmp.cleanup(); | 1316 | defer tmp.cleanup(); |
| 1314 | 1317 | ||
| 1318 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1319 | defer testing.allocator.free(cwd); | ||
| 1320 | |||
| 1315 | const temp_file = "test.txt"; | 1321 | const temp_file = "test.txt"; |
| 1316 | const temp_manifest_dir = "temp_manifest_dir"; | 1322 | const temp_manifest_dir = "temp_manifest_dir"; |
| 1317 | 1323 | ||
| ... | @@ -1331,6 +1337,7 @@ test "cache file and then recall it" { | ... | @@ -1331,6 +1337,7 @@ test "cache file and then recall it" { |
| 1331 | .io = io, | 1337 | .io = io, |
| 1332 | .gpa = testing.allocator, | 1338 | .gpa = testing.allocator, |
| 1333 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1339 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1340 | .cwd = cwd, | ||
| 1334 | }; | 1341 | }; |
| 1335 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1342 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1336 | defer cache.manifest_dir.close(io); | 1343 | defer cache.manifest_dir.close(io); |
| ... | @@ -1371,11 +1378,14 @@ test "cache file and then recall it" { | ... | @@ -1371,11 +1378,14 @@ test "cache file and then recall it" { |
| 1371 | } | 1378 | } |
| 1372 | 1379 | ||
| 1373 | test "check that changing a file makes cache fail" { | 1380 | test "check that changing a file makes cache fail" { |
| 1374 | const io = std.testing.io; | 1381 | const io = testing.io; |
| 1375 | 1382 | ||
| 1376 | var tmp = testing.tmpDir(.{}); | 1383 | var tmp = testing.tmpDir(.{}); |
| 1377 | defer tmp.cleanup(); | 1384 | defer tmp.cleanup(); |
| 1378 | 1385 | ||
| 1386 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1387 | defer testing.allocator.free(cwd); | ||
| 1388 | |||
| 1379 | const temp_file = "cache_hash_change_file_test.txt"; | 1389 | const temp_file = "cache_hash_change_file_test.txt"; |
| 1380 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; | 1390 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; |
| 1381 | const original_temp_file_contents = "Hello, world!\n"; | 1391 | const original_temp_file_contents = "Hello, world!\n"; |
| ... | @@ -1397,6 +1407,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -1397,6 +1407,7 @@ test "check that changing a file makes cache fail" { |
| 1397 | .io = io, | 1407 | .io = io, |
| 1398 | .gpa = testing.allocator, | 1408 | .gpa = testing.allocator, |
| 1399 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1409 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1410 | .cwd = cwd, | ||
| 1400 | }; | 1411 | }; |
| 1401 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1412 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1402 | defer cache.manifest_dir.close(io); | 1413 | defer cache.manifest_dir.close(io); |
| ... | @@ -1448,6 +1459,9 @@ test "no file inputs" { | ... | @@ -1448,6 +1459,9 @@ test "no file inputs" { |
| 1448 | var tmp = testing.tmpDir(.{}); | 1459 | var tmp = testing.tmpDir(.{}); |
| 1449 | defer tmp.cleanup(); | 1460 | defer tmp.cleanup(); |
| 1450 | 1461 | ||
| 1462 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1463 | defer testing.allocator.free(cwd); | ||
| 1464 | |||
| 1451 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; | 1465 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; |
| 1452 | 1466 | ||
| 1453 | var digest1: HexDigest = undefined; | 1467 | var digest1: HexDigest = undefined; |
| ... | @@ -1457,6 +1471,7 @@ test "no file inputs" { | ... | @@ -1457,6 +1471,7 @@ test "no file inputs" { |
| 1457 | .io = io, | 1471 | .io = io, |
| 1458 | .gpa = testing.allocator, | 1472 | .gpa = testing.allocator, |
| 1459 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1473 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1474 | .cwd = cwd, | ||
| 1460 | }; | 1475 | }; |
| 1461 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1476 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1462 | defer cache.manifest_dir.close(io); | 1477 | defer cache.manifest_dir.close(io); |
| ... | @@ -1489,11 +1504,14 @@ test "no file inputs" { | ... | @@ -1489,11 +1504,14 @@ test "no file inputs" { |
| 1489 | } | 1504 | } |
| 1490 | 1505 | ||
| 1491 | test "Manifest with files added after initial hash work" { | 1506 | test "Manifest with files added after initial hash work" { |
| 1492 | const io = std.testing.io; | 1507 | const io = testing.io; |
| 1493 | 1508 | ||
| 1494 | var tmp = testing.tmpDir(.{}); | 1509 | var tmp = testing.tmpDir(.{}); |
| 1495 | defer tmp.cleanup(); | 1510 | defer tmp.cleanup(); |
| 1496 | 1511 | ||
| 1512 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1513 | defer testing.allocator.free(cwd); | ||
| 1514 | |||
| 1497 | const temp_file1 = "cache_hash_post_file_test1.txt"; | 1515 | const temp_file1 = "cache_hash_post_file_test1.txt"; |
| 1498 | const temp_file2 = "cache_hash_post_file_test2.txt"; | 1516 | const temp_file2 = "cache_hash_post_file_test2.txt"; |
| 1499 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; | 1517 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; |
| ... | @@ -1516,6 +1534,7 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1516,6 +1534,7 @@ test "Manifest with files added after initial hash work" { |
| 1516 | .io = io, | 1534 | .io = io, |
| 1517 | .gpa = testing.allocator, | 1535 | .gpa = testing.allocator, |
| 1518 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1536 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1537 | .cwd = cwd, | ||
| 1519 | }; | 1538 | }; |
| 1520 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1539 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1521 | defer cache.manifest_dir.close(io); | 1540 | defer cache.manifest_dir.close(io); |
lib/std/Build/Step.zig+43-42| ... | @@ -348,19 +348,21 @@ pub fn captureChildProcess( | ... | @@ -348,19 +348,21 @@ pub fn captureChildProcess( |
| 348 | gpa: Allocator, | 348 | gpa: Allocator, |
| 349 | progress_node: std.Progress.Node, | 349 | progress_node: std.Progress.Node, |
| 350 | argv: []const []const u8, | 350 | argv: []const []const u8, |
| 351 | ) !std.process.Child.RunResult { | 351 | ) !std.process.RunResult { |
| 352 | const arena = s.owner.allocator; | 352 | const graph = s.owner.graph; |
| 353 | const io = s.owner.graph.io; | 353 | const arena = graph.arena; |
| 354 | const io = graph.io; | ||
| 354 | 355 | ||
| 355 | // If an error occurs, it's happened in this command: | 356 | // If an error occurs, it's happened in this command: |
| 356 | assert(s.result_failed_command == null); | 357 | assert(s.result_failed_command == null); |
| 357 | s.result_failed_command = try allocPrintCmd(gpa, null, argv); | 358 | s.result_failed_command = try allocPrintCmd(gpa, null, null, argv); |
| 358 | 359 | ||
| 359 | try handleChildProcUnsupported(s); | 360 | try handleChildProcUnsupported(s); |
| 360 | try handleVerbose(s.owner, null, argv); | 361 | try handleVerbose(s.owner, null, argv); |
| 361 | 362 | ||
| 362 | const result = std.process.Child.run(arena, io, .{ | 363 | const result = std.process.run(arena, io, .{ |
| 363 | .argv = argv, | 364 | .argv = argv, |
| 365 | .environ_map = &graph.environ_map, | ||
| 364 | .progress_node = progress_node, | 366 | .progress_node = progress_node, |
| 365 | }) catch |err| return s.fail("failed to run {s}: {t}", .{ argv[0], err }); | 367 | }) catch |err| return s.fail("failed to run {s}: {t}", .{ argv[0], err }); |
| 366 | 368 | ||
| ... | @@ -406,7 +408,7 @@ pub fn evalZigProcess( | ... | @@ -406,7 +408,7 @@ pub fn evalZigProcess( |
| 406 | 408 | ||
| 407 | // If an error occurs, it's happened in this command: | 409 | // If an error occurs, it's happened in this command: |
| 408 | assert(s.result_failed_command == null); | 410 | assert(s.result_failed_command == null); |
| 409 | s.result_failed_command = try allocPrintCmd(gpa, null, argv); | 411 | s.result_failed_command = try allocPrintCmd(gpa, null, null, argv); |
| 410 | 412 | ||
| 411 | if (s.getZigProcess()) |zp| update: { | 413 | if (s.getZigProcess()) |zp| update: { |
| 412 | assert(watch); | 414 | assert(watch); |
| ... | @@ -442,35 +444,34 @@ pub fn evalZigProcess( | ... | @@ -442,35 +444,34 @@ pub fn evalZigProcess( |
| 442 | return result; | 444 | return result; |
| 443 | } | 445 | } |
| 444 | assert(argv.len != 0); | 446 | assert(argv.len != 0); |
| 445 | const arena = b.allocator; | ||
| 446 | 447 | ||
| 447 | try handleChildProcUnsupported(s); | 448 | try handleChildProcUnsupported(s); |
| 448 | try handleVerbose(s.owner, null, argv); | 449 | try handleVerbose(s.owner, null, argv); |
| 449 | 450 | ||
| 450 | var child = std.process.Child.init(argv, arena); | 451 | const zp = try gpa.create(ZigProcess); |
| 451 | child.env_map = &b.graph.env_map; | 452 | defer if (!watch) gpa.destroy(zp); |
| 452 | child.stdin_behavior = .Pipe; | ||
| 453 | child.stdout_behavior = .Pipe; | ||
| 454 | child.stderr_behavior = .Pipe; | ||
| 455 | child.request_resource_usage_statistics = true; | ||
| 456 | child.progress_node = prog_node; | ||
| 457 | 453 | ||
| 458 | child.spawn(io) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err }); | 454 | zp.child = std.process.spawn(io, .{ |
| 455 | .argv = argv, | ||
| 456 | .environ_map = &b.graph.environ_map, | ||
| 457 | .stdin = .pipe, | ||
| 458 | .stdout = .pipe, | ||
| 459 | .stderr = .pipe, | ||
| 460 | .request_resource_usage_statistics = true, | ||
| 461 | .progress_node = prog_node, | ||
| 462 | }) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err }); | ||
| 463 | defer if (!watch) zp.child.kill(io); | ||
| 459 | 464 | ||
| 460 | const zp = try gpa.create(ZigProcess); | ||
| 461 | zp.* = .{ | 465 | zp.* = .{ |
| 462 | .child = child, | 466 | .child = zp.child, |
| 463 | .poller = Io.poll(gpa, ZigProcess.StreamEnum, .{ | 467 | .poller = Io.poll(gpa, ZigProcess.StreamEnum, .{ |
| 464 | .stdout = child.stdout.?, | 468 | .stdout = zp.child.stdout.?, |
| 465 | .stderr = child.stderr.?, | 469 | .stderr = zp.child.stderr.?, |
| 466 | }), | 470 | }), |
| 467 | .progress_ipc_fd = if (std.Progress.have_ipc) child.progress_node.getIpcFd() else {}, | 471 | .progress_ipc_fd = if (std.Progress.have_ipc) prog_node.getIpcFd() else {}, |
| 468 | }; | 472 | }; |
| 469 | if (watch) s.setZigProcess(zp); | 473 | if (watch) s.setZigProcess(zp); |
| 470 | defer if (!watch) { | 474 | defer if (!watch) zp.poller.deinit(); |
| 471 | zp.poller.deinit(); | ||
| 472 | gpa.destroy(zp); | ||
| 473 | }; | ||
| 474 | 475 | ||
| 475 | const result = try zigProcessUpdate(s, zp, watch, web_server, gpa); | 476 | const result = try zigProcessUpdate(s, zp, watch, web_server, gpa); |
| 476 | 477 | ||
| ... | @@ -486,7 +487,7 @@ pub fn evalZigProcess( | ... | @@ -486,7 +487,7 @@ pub fn evalZigProcess( |
| 486 | 487 | ||
| 487 | // Special handling for Compile step that is expecting compile errors. | 488 | // Special handling for Compile step that is expecting compile errors. |
| 488 | if (s.cast(Compile)) |compile| switch (term) { | 489 | if (s.cast(Compile)) |compile| switch (term) { |
| 489 | .Exited => { | 490 | .exited => { |
| 490 | // Note that the exit code may be 0 in this case due to the | 491 | // Note that the exit code may be 0 in this case due to the |
| 491 | // compiler server protocol. | 492 | // compiler server protocol. |
| 492 | if (compile.expect_errors != null) { | 493 | if (compile.expect_errors != null) { |
| ... | @@ -692,13 +693,17 @@ pub fn handleVerbose( | ... | @@ -692,13 +693,17 @@ pub fn handleVerbose( |
| 692 | pub fn handleVerbose2( | 693 | pub fn handleVerbose2( |
| 693 | b: *Build, | 694 | b: *Build, |
| 694 | opt_cwd: ?[]const u8, | 695 | opt_cwd: ?[]const u8, |
| 695 | opt_env: ?*const std.process.EnvMap, | 696 | opt_env: ?*const std.process.Environ.Map, |
| 696 | argv: []const []const u8, | 697 | argv: []const []const u8, |
| 697 | ) error{OutOfMemory}!void { | 698 | ) error{OutOfMemory}!void { |
| 698 | if (b.verbose) { | 699 | if (b.verbose) { |
| 700 | const graph = b.graph; | ||
| 699 | // Intention of verbose is to print all sub-process command lines to | 701 | // Intention of verbose is to print all sub-process command lines to |
| 700 | // stderr before spawning them. | 702 | // stderr before spawning them. |
| 701 | const text = try allocPrintCmd2(b.allocator, opt_cwd, opt_env, argv); | 703 | const text = try allocPrintCmd(b.allocator, opt_cwd, if (opt_env) |env| .{ |
| 704 | .child = env, | ||
| 705 | .parent = &graph.environ_map, | ||
| 706 | } else null, argv); | ||
| 702 | std.debug.print("{s}\n", .{text}); | 707 | std.debug.print("{s}\n", .{text}); |
| 703 | } | 708 | } |
| 704 | } | 709 | } |
| ... | @@ -714,12 +719,15 @@ pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFaile | ... | @@ -714,12 +719,15 @@ pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFaile |
| 714 | pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void { | 719 | pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void { |
| 715 | assert(s.result_failed_command != null); | 720 | assert(s.result_failed_command != null); |
| 716 | switch (term) { | 721 | switch (term) { |
| 717 | .Exited => |code| { | 722 | .exited => |code| { |
| 718 | if (code != 0) { | 723 | if (code != 0) { |
| 719 | return s.fail("process exited with error code {d}", .{code}); | 724 | return s.fail("process exited with error code {d}", .{code}); |
| 720 | } | 725 | } |
| 721 | }, | 726 | }, |
| 722 | .Signal, .Stopped, .Unknown => { | 727 | .signal => |sig| { |
| 728 | return s.fail("process terminated with signal {t}", .{sig}); | ||
| 729 | }, | ||
| 730 | .stopped, .unknown => { | ||
| 723 | return s.fail("process terminated unexpectedly", .{}); | 731 | return s.fail("process terminated unexpectedly", .{}); |
| 724 | }, | 732 | }, |
| 725 | } | 733 | } |
| ... | @@ -728,15 +736,10 @@ pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ Mak | ... | @@ -728,15 +736,10 @@ pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ Mak |
| 728 | pub fn allocPrintCmd( | 736 | pub fn allocPrintCmd( |
| 729 | gpa: Allocator, | 737 | gpa: Allocator, |
| 730 | opt_cwd: ?[]const u8, | 738 | opt_cwd: ?[]const u8, |
| 731 | argv: []const []const u8, | 739 | opt_env: ?struct { |
| 732 | ) Allocator.Error![]u8 { | 740 | child: *const std.process.Environ.Map, |
| 733 | return allocPrintCmd2(gpa, opt_cwd, null, argv); | 741 | parent: *const std.process.Environ.Map, |
| 734 | } | 742 | }, |
| 735 | |||
| 736 | pub fn allocPrintCmd2( | ||
| 737 | gpa: Allocator, | ||
| 738 | opt_cwd: ?[]const u8, | ||
| 739 | opt_env: ?*const std.process.EnvMap, | ||
| 740 | argv: []const []const u8, | 743 | argv: []const []const u8, |
| 741 | ) Allocator.Error![]u8 { | 744 | ) Allocator.Error![]u8 { |
| 742 | const shell = struct { | 745 | const shell = struct { |
| ... | @@ -779,13 +782,11 @@ pub fn allocPrintCmd2( | ... | @@ -779,13 +782,11 @@ pub fn allocPrintCmd2( |
| 779 | const writer = &aw.writer; | 782 | const writer = &aw.writer; |
| 780 | if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory; | 783 | if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory; |
| 781 | if (opt_env) |env| { | 784 | if (opt_env) |env| { |
| 782 | var process_env_map = std.process.getEnvMap(gpa) catch std.process.EnvMap.init(gpa); | 785 | var it = env.child.iterator(); |
| 783 | defer process_env_map.deinit(); | ||
| 784 | var it = env.iterator(); | ||
| 785 | while (it.next()) |entry| { | 786 | while (it.next()) |entry| { |
| 786 | const key = entry.key_ptr.*; | 787 | const key = entry.key_ptr.*; |
| 787 | const value = entry.value_ptr.*; | 788 | const value = entry.value_ptr.*; |
| 788 | if (process_env_map.get(key)) |process_value| { | 789 | if (env.parent.get(key)) |process_value| { |
| 789 | if (std.mem.eql(u8, value, process_value)) continue; | 790 | if (std.mem.eql(u8, value, process_value)) continue; |
| 790 | } | 791 | } |
| 791 | writer.print("{s}=", .{key}) catch return error.OutOfMemory; | 792 | writer.print("{s}=", .{key}) catch return error.OutOfMemory; |
lib/std/Build/Step/Compile.zig+4-4| ... | @@ -741,13 +741,13 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult { | ... | @@ -741,13 +741,13 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult { |
| 741 | }; | 741 | }; |
| 742 | 742 | ||
| 743 | var code: u8 = undefined; | 743 | var code: u8 = undefined; |
| 744 | const pkg_config_exe = b.graph.env_map.get("PKG_CONFIG") orelse "pkg-config"; | 744 | const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config"; |
| 745 | const stdout = if (b.runAllowFail(&[_][]const u8{ | 745 | const stdout = if (b.runAllowFail(&[_][]const u8{ |
| 746 | pkg_config_exe, | 746 | pkg_config_exe, |
| 747 | pkg_name, | 747 | pkg_name, |
| 748 | "--cflags", | 748 | "--cflags", |
| 749 | "--libs", | 749 | "--libs", |
| 750 | }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { | 750 | }, &code, .ignore)) |stdout| stdout else |err| switch (err) { |
| 751 | error.ProcessTerminated => return error.PkgConfigCrashed, | 751 | error.ProcessTerminated => return error.PkgConfigCrashed, |
| 752 | error.ExecNotSupported => return error.PkgConfigFailed, | 752 | error.ExecNotSupported => return error.PkgConfigFailed, |
| 753 | error.ExitCodeFailure => return error.PkgConfigFailed, | 753 | error.ExitCodeFailure => return error.PkgConfigFailed, |
| ... | @@ -1846,8 +1846,8 @@ pub fn doAtomicSymLinks( | ... | @@ -1846,8 +1846,8 @@ pub fn doAtomicSymLinks( |
| 1846 | } | 1846 | } |
| 1847 | 1847 | ||
| 1848 | fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg { | 1848 | fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg { |
| 1849 | const pkg_config_exe = b.graph.env_map.get("PKG_CONFIG") orelse "pkg-config"; | 1849 | const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config"; |
| 1850 | const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .Ignore); | 1850 | const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .ignore); |
| 1851 | var list = std.array_list.Managed(PkgConfigPkg).init(b.allocator); | 1851 | var list = std.array_list.Managed(PkgConfigPkg).init(b.allocator); |
| 1852 | errdefer list.deinit(); | 1852 | errdefer list.deinit(); |
| 1853 | var line_it = mem.tokenizeAny(u8, stdout, "\r\n"); | 1853 | var line_it = mem.tokenizeAny(u8, stdout, "\r\n"); |
lib/std/Build/Step/Fmt.zig+1-1| ... | @@ -69,7 +69,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -69,7 +69,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 69 | 69 | ||
| 70 | const run_result = try step.captureChildProcess(options.gpa, prog_node, argv.items); | 70 | const run_result = try step.captureChildProcess(options.gpa, prog_node, argv.items); |
| 71 | if (fmt.check) switch (run_result.term) { | 71 | if (fmt.check) switch (run_result.term) { |
| 72 | .Exited => |code| if (code != 0 and run_result.stdout.len != 0) { | 72 | .exited => |code| if (code != 0 and run_result.stdout.len != 0) { |
| 73 | var it = std.mem.tokenizeScalar(u8, run_result.stdout, '\n'); | 73 | var it = std.mem.tokenizeScalar(u8, run_result.stdout, '\n'); |
| 74 | while (it.next()) |bad_file_name| { | 74 | while (it.next()) |bad_file_name| { |
| 75 | try step.addError("{s}: non-conforming formatting", .{bad_file_name}); | 75 | try step.addError("{s}: non-conforming formatting", .{bad_file_name}); |
lib/std/Build/Step/Options.zig+5-1| ... | @@ -537,6 +537,9 @@ test Options { | ... | @@ -537,6 +537,9 @@ test Options { |
| 537 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | 537 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); |
| 538 | defer arena.deinit(); | 538 | defer arena.deinit(); |
| 539 | 539 | ||
| 540 | const cwd = try std.process.getCwdAlloc(std.testing.allocator); | ||
| 541 | defer std.testing.allocator.free(cwd); | ||
| 542 | |||
| 540 | var graph: std.Build.Graph = .{ | 543 | var graph: std.Build.Graph = .{ |
| 541 | .io = io, | 544 | .io = io, |
| 542 | .arena = arena.allocator(), | 545 | .arena = arena.allocator(), |
| ... | @@ -544,9 +547,10 @@ test Options { | ... | @@ -544,9 +547,10 @@ test Options { |
| 544 | .io = io, | 547 | .io = io, |
| 545 | .gpa = arena.allocator(), | 548 | .gpa = arena.allocator(), |
| 546 | .manifest_dir = Io.Dir.cwd(), | 549 | .manifest_dir = Io.Dir.cwd(), |
| 550 | .cwd = cwd, | ||
| 547 | }, | 551 | }, |
| 548 | .zig_exe = "test", | 552 | .zig_exe = "test", |
| 549 | .env_map = std.process.EnvMap.init(arena.allocator()), | 553 | .environ_map = std.process.Environ.Map.init(arena.allocator()), |
| 550 | .global_cache_root = .{ .path = "test", .handle = Io.Dir.cwd() }, | 554 | .global_cache_root = .{ .path = "test", .handle = Io.Dir.cwd() }, |
| 551 | .host = .{ | 555 | .host = .{ |
| 552 | .query = .{}, | 556 | .query = .{}, |
lib/std/Build/Step/Run.zig+132-148| ... | @@ -8,7 +8,7 @@ const Step = std.Build.Step; | ... | @@ -8,7 +8,7 @@ const Step = std.Build.Step; |
| 8 | const Dir = std.Io.Dir; | 8 | const Dir = std.Io.Dir; |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | const process = std.process; | 10 | const process = std.process; |
| 11 | const EnvMap = std.process.EnvMap; | 11 | const EnvMap = std.process.Environ.Map; |
| 12 | const assert = std.debug.assert; | 12 | const assert = std.debug.assert; |
| 13 | const Path = std.Build.Cache.Path; | 13 | const Path = std.Build.Cache.Path; |
| 14 | 14 | ||
| ... | @@ -23,7 +23,7 @@ argv: std.ArrayList(Arg), | ... | @@ -23,7 +23,7 @@ argv: std.ArrayList(Arg), |
| 23 | cwd: ?Build.LazyPath, | 23 | cwd: ?Build.LazyPath, |
| 24 | 24 | ||
| 25 | /// Override this field to modify the environment, or use setEnvironmentVariable | 25 | /// Override this field to modify the environment, or use setEnvironmentVariable |
| 26 | env_map: ?*EnvMap, | 26 | environ_map: ?*EnvMap, |
| 27 | 27 | ||
| 28 | /// Controls the `NO_COLOR` and `CLICOLOR_FORCE` environment variables. | 28 | /// Controls the `NO_COLOR` and `CLICOLOR_FORCE` environment variables. |
| 29 | color: Color = .auto, | 29 | color: Color = .auto, |
| ... | @@ -149,7 +149,7 @@ pub const StdIo = union(enum) { | ... | @@ -149,7 +149,7 @@ pub const StdIo = union(enum) { |
| 149 | expect_stderr_match: []const u8, | 149 | expect_stderr_match: []const u8, |
| 150 | expect_stdout_exact: []const u8, | 150 | expect_stdout_exact: []const u8, |
| 151 | expect_stdout_match: []const u8, | 151 | expect_stdout_match: []const u8, |
| 152 | expect_term: std.process.Child.Term, | 152 | expect_term: process.Child.Term, |
| 153 | }; | 153 | }; |
| 154 | }; | 154 | }; |
| 155 | 155 | ||
| ... | @@ -215,7 +215,7 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { | ... | @@ -215,7 +215,7 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { |
| 215 | }), | 215 | }), |
| 216 | .argv = .{}, | 216 | .argv = .{}, |
| 217 | .cwd = null, | 217 | .cwd = null, |
| 218 | .env_map = null, | 218 | .environ_map = null, |
| 219 | .disable_zig_progress = false, | 219 | .disable_zig_progress = false, |
| 220 | .stdio = .infer_from_args, | 220 | .stdio = .infer_from_args, |
| 221 | .stdin = .none, | 221 | .stdin = .none, |
| ... | @@ -540,12 +540,12 @@ pub fn clearEnvironment(run: *Run) void { | ... | @@ -540,12 +540,12 @@ pub fn clearEnvironment(run: *Run) void { |
| 540 | const b = run.step.owner; | 540 | const b = run.step.owner; |
| 541 | const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM"); | 541 | const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM"); |
| 542 | new_env_map.* = .init(b.allocator); | 542 | new_env_map.* = .init(b.allocator); |
| 543 | run.env_map = new_env_map; | 543 | run.environ_map = new_env_map; |
| 544 | } | 544 | } |
| 545 | 545 | ||
| 546 | pub fn addPathDir(run: *Run, search_path: []const u8) void { | 546 | pub fn addPathDir(run: *Run, search_path: []const u8) void { |
| 547 | const b = run.step.owner; | 547 | const b = run.step.owner; |
| 548 | const env_map = getEnvMapInternal(run); | 548 | const environ_map = getEnvMapInternal(run); |
| 549 | 549 | ||
| 550 | const use_wine = b.enable_wine and b.graph.host.result.os.tag != .windows and use_wine: switch (run.argv.items[0]) { | 550 | const use_wine = b.enable_wine and b.graph.host.result.os.tag != .windows and use_wine: switch (run.argv.items[0]) { |
| 551 | .artifact => |p| p.artifact.rootModuleTarget().os.tag == .windows, | 551 | .artifact => |p| p.artifact.rootModuleTarget().os.tag == .windows, |
| ... | @@ -562,7 +562,7 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void { | ... | @@ -562,7 +562,7 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void { |
| 562 | .output_file, .output_directory => false, | 562 | .output_file, .output_directory => false, |
| 563 | }; | 563 | }; |
| 564 | const key = if (use_wine) "WINEPATH" else "PATH"; | 564 | const key = if (use_wine) "WINEPATH" else "PATH"; |
| 565 | const prev_path = env_map.get(key); | 565 | const prev_path = environ_map.get(key); |
| 566 | 566 | ||
| 567 | if (prev_path) |pp| { | 567 | if (prev_path) |pp| { |
| 568 | const new_path = b.fmt("{s}{c}{s}", .{ | 568 | const new_path = b.fmt("{s}{c}{s}", .{ |
| ... | @@ -570,9 +570,9 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void { | ... | @@ -570,9 +570,9 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void { |
| 570 | if (use_wine) Dir.path.delimiter_windows else Dir.path.delimiter, | 570 | if (use_wine) Dir.path.delimiter_windows else Dir.path.delimiter, |
| 571 | search_path, | 571 | search_path, |
| 572 | }); | 572 | }); |
| 573 | env_map.put(key, new_path) catch @panic("OOM"); | 573 | environ_map.put(key, new_path) catch @panic("OOM"); |
| 574 | } else { | 574 | } else { |
| 575 | env_map.put(key, b.dupePath(search_path)) catch @panic("OOM"); | 575 | environ_map.put(key, b.dupePath(search_path)) catch @panic("OOM"); |
| 576 | } | 576 | } |
| 577 | } | 577 | } |
| 578 | 578 | ||
| ... | @@ -581,43 +581,51 @@ pub fn getEnvMap(run: *Run) *EnvMap { | ... | @@ -581,43 +581,51 @@ pub fn getEnvMap(run: *Run) *EnvMap { |
| 581 | } | 581 | } |
| 582 | 582 | ||
| 583 | fn getEnvMapInternal(run: *Run) *EnvMap { | 583 | fn getEnvMapInternal(run: *Run) *EnvMap { |
| 584 | const arena = run.step.owner.allocator; | 584 | const graph = run.step.owner.graph; |
| 585 | return run.env_map orelse { | 585 | const arena = graph.arena; |
| 586 | const env_map = arena.create(EnvMap) catch @panic("OOM"); | 586 | return run.environ_map orelse { |
| 587 | env_map.* = process.getEnvMap(arena) catch @panic("unhandled error"); | 587 | const cloned_map = arena.create(EnvMap) catch @panic("OOM"); |
| 588 | run.env_map = env_map; | 588 | cloned_map.* = graph.environ_map.clone(arena) catch @panic("OOM"); |
| 589 | return env_map; | 589 | run.environ_map = cloned_map; |
| 590 | return cloned_map; | ||
| 590 | }; | 591 | }; |
| 591 | } | 592 | } |
| 592 | 593 | ||
| 593 | pub fn setEnvironmentVariable(run: *Run, key: []const u8, value: []const u8) void { | 594 | pub fn setEnvironmentVariable(run: *Run, key: []const u8, value: []const u8) void { |
| 594 | const b = run.step.owner; | 595 | const environ_map = run.getEnvMap(); |
| 595 | const env_map = run.getEnvMap(); | 596 | // This data structure already dupes keys and values. |
| 596 | env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); | 597 | environ_map.put(key, value) catch @panic("OOM"); |
| 597 | } | 598 | } |
| 598 | 599 | ||
| 599 | pub fn removeEnvironmentVariable(run: *Run, key: []const u8) void { | 600 | pub fn removeEnvironmentVariable(run: *Run, key: []const u8) void { |
| 600 | run.getEnvMap().remove(key); | 601 | _ = run.getEnvMap().swapRemove(key); |
| 601 | } | 602 | } |
| 602 | 603 | ||
| 603 | /// Adds a check for exact stderr match. Does not add any other checks. | 604 | /// Adds a check for exact stderr match. Does not add any other checks. |
| 604 | pub fn expectStdErrEqual(run: *Run, bytes: []const u8) void { | 605 | pub fn expectStdErrEqual(run: *Run, bytes: []const u8) void { |
| 605 | const new_check: StdIo.Check = .{ .expect_stderr_exact = run.step.owner.dupe(bytes) }; | 606 | run.addCheck(.{ .expect_stderr_exact = run.step.owner.dupe(bytes) }); |
| 606 | run.addCheck(new_check); | 607 | } |
| 608 | |||
| 609 | pub fn expectStdErrMatch(run: *Run, bytes: []const u8) void { | ||
| 610 | run.addCheck(.{ .expect_stderr_match = run.step.owner.dupe(bytes) }); | ||
| 607 | } | 611 | } |
| 608 | 612 | ||
| 609 | /// Adds a check for exact stdout match as well as a check for exit code 0, if | 613 | /// Adds a check for exact stdout match as well as a check for exit code 0, if |
| 610 | /// there is not already an expected termination check. | 614 | /// there is not already an expected termination check. |
| 611 | pub fn expectStdOutEqual(run: *Run, bytes: []const u8) void { | 615 | pub fn expectStdOutEqual(run: *Run, bytes: []const u8) void { |
| 612 | const new_check: StdIo.Check = .{ .expect_stdout_exact = run.step.owner.dupe(bytes) }; | 616 | run.addCheck(.{ .expect_stdout_exact = run.step.owner.dupe(bytes) }); |
| 613 | run.addCheck(new_check); | 617 | if (!run.hasTermCheck()) run.expectExitCode(0); |
| 614 | if (!run.hasTermCheck()) { | 618 | } |
| 615 | run.expectExitCode(0); | 619 | |
| 616 | } | 620 | /// Adds a check for stdout match as well as a check for exit code 0, if there |
| 621 | /// is not already an expected termination check. | ||
| 622 | pub fn expectStdOutMatch(run: *Run, bytes: []const u8) void { | ||
| 623 | run.addCheck(.{ .expect_stdout_match = run.step.owner.dupe(bytes) }); | ||
| 624 | if (!run.hasTermCheck()) run.expectExitCode(0); | ||
| 617 | } | 625 | } |
| 618 | 626 | ||
| 619 | pub fn expectExitCode(run: *Run, code: u8) void { | 627 | pub fn expectExitCode(run: *Run, code: u8) void { |
| 620 | const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } }; | 628 | const new_check: StdIo.Check = .{ .expect_term = .{ .exited = code } }; |
| 621 | run.addCheck(new_check); | 629 | run.addCheck(new_check); |
| 622 | } | 630 | } |
| 623 | 631 | ||
| ... | @@ -749,28 +757,30 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { | ... | @@ -749,28 +757,30 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { |
| 749 | /// to make sure the child doesn't see paths relative to a cwd other than its own. | 757 | /// to make sure the child doesn't see paths relative to a cwd other than its own. |
| 750 | fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { | 758 | fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { |
| 751 | const b = run.step.owner; | 759 | const b = run.step.owner; |
| 752 | const path_str = path.toString(b.graph.arena) catch @panic("OOM"); | 760 | const graph = b.graph; |
| 761 | const arena = graph.arena; | ||
| 762 | |||
| 763 | const path_str = path.toString(arena) catch @panic("OOM"); | ||
| 753 | if (Dir.path.isAbsolute(path_str)) { | 764 | if (Dir.path.isAbsolute(path_str)) { |
| 754 | // Absolute paths don't need changing. | 765 | // Absolute paths don't need changing. |
| 755 | return path_str; | 766 | return path_str; |
| 756 | } | 767 | } |
| 757 | const child_cwd_rel: []const u8 = rel: { | 768 | const child_cwd_rel: []const u8 = rel: { |
| 758 | const child_lazy_cwd = run.cwd orelse break :rel path_str; | 769 | const child_lazy_cwd = run.cwd orelse break :rel path_str; |
| 759 | const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(b.graph.arena) catch @panic("OOM"); | 770 | const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(arena) catch @panic("OOM"); |
| 760 | // Convert it from relative to *our* cwd, to relative to the *child's* cwd. | 771 | // Convert it from relative to *our* cwd, to relative to the *child's* cwd. |
| 761 | break :rel Dir.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM"); | 772 | break :rel Dir.path.relative(arena, graph.cache.cwd, &graph.environ_map, child_cwd, path_str) catch @panic("OOM"); |
| 762 | }; | 773 | }; |
| 763 | // Not every path can be made relative, e.g. if the path and the child cwd are on different | 774 | // Not every path can be made relative, e.g. if the path and the child cwd are on different |
| 764 | // disk designators on Windows. In that case, `relative` will return an absolute path which we can | 775 | // disk designators on Windows. In that case, `relative` will return an absolute path which we can |
| 765 | // just return. | 776 | // just return. |
| 766 | if (Dir.path.isAbsolute(child_cwd_rel)) { | 777 | if (Dir.path.isAbsolute(child_cwd_rel)) return child_cwd_rel; |
| 767 | return child_cwd_rel; | 778 | |
| 768 | } | ||
| 769 | // We're not done yet. In some cases this path must be prefixed with './': | 779 | // We're not done yet. In some cases this path must be prefixed with './': |
| 770 | // * On POSIX, the executable name cannot be a single component like 'foo' | 780 | // * On POSIX, the executable name cannot be a single component like 'foo' |
| 771 | // * Some executables might treat a leading '-' like a flag, which we must avoid | 781 | // * Some executables might treat a leading '-' like a flag, which we must avoid |
| 772 | // There's no harm in it, so just *always* apply this prefix. | 782 | // There's no harm in it, so just *always* apply this prefix. |
| 773 | return Dir.path.join(b.graph.arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); | 783 | return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); |
| 774 | } | 784 | } |
| 775 | 785 | ||
| 776 | const IndexedOutput = struct { | 786 | const IndexedOutput = struct { |
| ... | @@ -791,32 +801,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -791,32 +801,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 791 | var man = b.graph.cache.obtain(); | 801 | var man = b.graph.cache.obtain(); |
| 792 | defer man.deinit(); | 802 | defer man.deinit(); |
| 793 | 803 | ||
| 794 | if (run.env_map) |env_map| { | 804 | if (run.environ_map) |environ_map| { |
| 795 | const KV = struct { []const u8, []const u8 }; | 805 | for (environ_map.keys(), environ_map.values()) |key, value| { |
| 796 | var kv_pairs = try std.array_list.Managed(KV).initCapacity(arena, env_map.count()); | 806 | man.hash.addBytes(key); |
| 797 | var iter = env_map.iterator(); | 807 | man.hash.addBytes(value); |
| 798 | while (iter.next()) |entry| { | ||
| 799 | kv_pairs.appendAssumeCapacity(.{ entry.key_ptr.*, entry.value_ptr.* }); | ||
| 800 | } | ||
| 801 | |||
| 802 | std.mem.sortUnstable(KV, kv_pairs.items, {}, struct { | ||
| 803 | fn lessThan(_: void, kv1: KV, kv2: KV) bool { | ||
| 804 | const k1 = kv1[0]; | ||
| 805 | const k2 = kv2[0]; | ||
| 806 | |||
| 807 | if (k1.len != k2.len) return k1.len < k2.len; | ||
| 808 | |||
| 809 | for (k1, k2) |c1, c2| { | ||
| 810 | if (c1 == c2) continue; | ||
| 811 | return c1 < c2; | ||
| 812 | } | ||
| 813 | unreachable; // two keys cannot be equal | ||
| 814 | } | ||
| 815 | }.lessThan); | ||
| 816 | |||
| 817 | for (kv_pairs.items) |kv| { | ||
| 818 | man.hash.addBytes(kv[0]); | ||
| 819 | man.hash.addBytes(kv[1]); | ||
| 820 | } | 808 | } |
| 821 | } | 809 | } |
| 822 | 810 | ||
| ... | @@ -1181,40 +1169,40 @@ fn populateGeneratedPaths( | ... | @@ -1181,40 +1169,40 @@ fn populateGeneratedPaths( |
| 1181 | } | 1169 | } |
| 1182 | } | 1170 | } |
| 1183 | 1171 | ||
| 1184 | fn formatTerm(term: ?std.process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error!void { | 1172 | fn formatTerm(term: ?process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error!void { |
| 1185 | if (term) |t| switch (t) { | 1173 | if (term) |t| switch (t) { |
| 1186 | .Exited => |code| try w.print("exited with code {d}", .{code}), | 1174 | .exited => |code| try w.print("exited with code {d}", .{code}), |
| 1187 | .Signal => |sig| try w.print("terminated with signal {d}", .{sig}), | 1175 | .signal => |sig| try w.print("terminated with signal {t}", .{sig}), |
| 1188 | .Stopped => |sig| try w.print("stopped with signal {d}", .{sig}), | 1176 | .stopped => |sig| try w.print("stopped with signal {d}", .{sig}), |
| 1189 | .Unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}), | 1177 | .unknown => |code| try w.print("terminated for unknown reason with code {d}", .{code}), |
| 1190 | } else { | 1178 | } else { |
| 1191 | try w.writeAll("exited with any code"); | 1179 | try w.writeAll("exited with any code"); |
| 1192 | } | 1180 | } |
| 1193 | } | 1181 | } |
| 1194 | fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Alt(?std.process.Child.Term, formatTerm) { | 1182 | fn fmtTerm(term: ?process.Child.Term) std.fmt.Alt(?process.Child.Term, formatTerm) { |
| 1195 | return .{ .data = term }; | 1183 | return .{ .data = term }; |
| 1196 | } | 1184 | } |
| 1197 | 1185 | ||
| 1198 | fn termMatches(expected: ?std.process.Child.Term, actual: std.process.Child.Term) bool { | 1186 | fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool { |
| 1199 | return if (expected) |e| switch (e) { | 1187 | return if (expected) |e| switch (e) { |
| 1200 | .Exited => |expected_code| switch (actual) { | 1188 | .exited => |expected_code| switch (actual) { |
| 1201 | .Exited => |actual_code| expected_code == actual_code, | 1189 | .exited => |actual_code| expected_code == actual_code, |
| 1202 | else => false, | 1190 | else => false, |
| 1203 | }, | 1191 | }, |
| 1204 | .Signal => |expected_sig| switch (actual) { | 1192 | .signal => |expected_sig| switch (actual) { |
| 1205 | .Signal => |actual_sig| expected_sig == actual_sig, | 1193 | .signal => |actual_sig| expected_sig == actual_sig, |
| 1206 | else => false, | 1194 | else => false, |
| 1207 | }, | 1195 | }, |
| 1208 | .Stopped => |expected_sig| switch (actual) { | 1196 | .stopped => |expected_sig| switch (actual) { |
| 1209 | .Stopped => |actual_sig| expected_sig == actual_sig, | 1197 | .stopped => |actual_sig| expected_sig == actual_sig, |
| 1210 | else => false, | 1198 | else => false, |
| 1211 | }, | 1199 | }, |
| 1212 | .Unknown => |expected_code| switch (actual) { | 1200 | .unknown => |expected_code| switch (actual) { |
| 1213 | .Unknown => |actual_code| expected_code == actual_code, | 1201 | .unknown => |actual_code| expected_code == actual_code, |
| 1214 | else => false, | 1202 | else => false, |
| 1215 | }, | 1203 | }, |
| 1216 | } else switch (actual) { | 1204 | } else switch (actual) { |
| 1217 | .Exited => true, | 1205 | .exited => true, |
| 1218 | else => false, | 1206 | else => false, |
| 1219 | }; | 1207 | }; |
| 1220 | } | 1208 | } |
| ... | @@ -1241,7 +1229,7 @@ fn runCommand( | ... | @@ -1241,7 +1229,7 @@ fn runCommand( |
| 1241 | const cwd: ?[]const u8 = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, step) else null; | 1229 | const cwd: ?[]const u8 = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, step) else null; |
| 1242 | 1230 | ||
| 1243 | try step.handleChildProcUnsupported(); | 1231 | try step.handleChildProcUnsupported(); |
| 1244 | try Step.handleVerbose2(step.owner, cwd, run.env_map, argv); | 1232 | try Step.handleVerbose2(step.owner, cwd, run.environ_map, argv); |
| 1245 | 1233 | ||
| 1246 | const allow_skip = switch (run.stdio) { | 1234 | const allow_skip = switch (run.stdio) { |
| 1247 | .check, .zig_test => run.skip_foreign_checks, | 1235 | .check, .zig_test => run.skip_foreign_checks, |
| ... | @@ -1251,13 +1239,13 @@ fn runCommand( | ... | @@ -1251,13 +1239,13 @@ fn runCommand( |
| 1251 | var interp_argv = std.array_list.Managed([]const u8).init(b.allocator); | 1239 | var interp_argv = std.array_list.Managed([]const u8).init(b.allocator); |
| 1252 | defer interp_argv.deinit(); | 1240 | defer interp_argv.deinit(); |
| 1253 | 1241 | ||
| 1254 | var env_map: EnvMap = env: { | 1242 | var environ_map: EnvMap = env: { |
| 1255 | const orig = run.env_map orelse &b.graph.env_map; | 1243 | const orig = run.environ_map orelse &b.graph.environ_map; |
| 1256 | break :env try orig.clone(gpa); | 1244 | break :env try orig.clone(gpa); |
| 1257 | }; | 1245 | }; |
| 1258 | defer env_map.deinit(); | 1246 | defer environ_map.deinit(); |
| 1259 | 1247 | ||
| 1260 | const opt_generic_result = spawnChildAndCollect(run, argv, &env_map, has_side_effects, options, fuzz_context) catch |err| term: { | 1248 | const opt_generic_result = spawnChildAndCollect(run, argv, &environ_map, has_side_effects, options, fuzz_context) catch |err| term: { |
| 1261 | // InvalidExe: cpu arch mismatch | 1249 | // InvalidExe: cpu arch mismatch |
| 1262 | // FileNotFound: can happen with a wrong dynamic linker path | 1250 | // FileNotFound: can happen with a wrong dynamic linker path |
| 1263 | if (err == error.InvalidExe or err == error.FileNotFound) interpret: { | 1251 | if (err == error.InvalidExe or err == error.FileNotFound) interpret: { |
| ... | @@ -1293,8 +1281,8 @@ fn runCommand( | ... | @@ -1293,8 +1281,8 @@ fn runCommand( |
| 1293 | 1281 | ||
| 1294 | // Wine's excessive stderr logging is only situationally helpful. Disable it by default, but | 1282 | // Wine's excessive stderr logging is only situationally helpful. Disable it by default, but |
| 1295 | // allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired. | 1283 | // allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired. |
| 1296 | if (env_map.get("WINEDEBUG") == null) { | 1284 | if (environ_map.get("WINEDEBUG") == null) { |
| 1297 | try env_map.put("WINEDEBUG", "-all"); | 1285 | try environ_map.put("WINEDEBUG", "-all"); |
| 1298 | } | 1286 | } |
| 1299 | } else { | 1287 | } else { |
| 1300 | return failForeign(run, "-fwine", argv[0], exe); | 1288 | return failForeign(run, "-fwine", argv[0], exe); |
| ... | @@ -1391,9 +1379,9 @@ fn runCommand( | ... | @@ -1391,9 +1379,9 @@ fn runCommand( |
| 1391 | 1379 | ||
| 1392 | gpa.free(step.result_failed_command.?); | 1380 | gpa.free(step.result_failed_command.?); |
| 1393 | step.result_failed_command = null; | 1381 | step.result_failed_command = null; |
| 1394 | try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items); | 1382 | try Step.handleVerbose2(step.owner, cwd, run.environ_map, interp_argv.items); |
| 1395 | 1383 | ||
| 1396 | break :term spawnChildAndCollect(run, interp_argv.items, &env_map, has_side_effects, options, fuzz_context) catch |e| { | 1384 | break :term spawnChildAndCollect(run, interp_argv.items, &environ_map, has_side_effects, options, fuzz_context) catch |e| { |
| 1397 | if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped; | 1385 | if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped; |
| 1398 | if (e == error.MakeFailed) return error.MakeFailed; // error already reported | 1386 | if (e == error.MakeFailed) return error.MakeFailed; // error already reported |
| 1399 | return step.fail("unable to spawn interpreter {s}: {s}", .{ | 1387 | return step.fail("unable to spawn interpreter {s}: {s}", .{ |
| ... | @@ -1525,8 +1513,8 @@ fn runCommand( | ... | @@ -1525,8 +1513,8 @@ fn runCommand( |
| 1525 | else => { | 1513 | else => { |
| 1526 | // On failure, report captured stderr like normal standard error output. | 1514 | // On failure, report captured stderr like normal standard error output. |
| 1527 | const bad_exit = switch (generic_result.term) { | 1515 | const bad_exit = switch (generic_result.term) { |
| 1528 | .Exited => |code| code != 0, | 1516 | .exited => |code| code != 0, |
| 1529 | .Signal, .Stopped, .Unknown => true, | 1517 | .signal, .stopped, .unknown => true, |
| 1530 | }; | 1518 | }; |
| 1531 | if (bad_exit) { | 1519 | if (bad_exit) { |
| 1532 | if (generic_result.stderr) |bytes| { | 1520 | if (generic_result.stderr) |bytes| { |
| ... | @@ -1540,7 +1528,7 @@ fn runCommand( | ... | @@ -1540,7 +1528,7 @@ fn runCommand( |
| 1540 | } | 1528 | } |
| 1541 | 1529 | ||
| 1542 | const EvalGenericResult = struct { | 1530 | const EvalGenericResult = struct { |
| 1543 | term: std.process.Child.Term, | 1531 | term: process.Child.Term, |
| 1544 | stdout: ?[]const u8, | 1532 | stdout: ?[]const u8, |
| 1545 | stderr: ?[]const u8, | 1533 | stderr: ?[]const u8, |
| 1546 | }; | 1534 | }; |
| ... | @@ -1548,13 +1536,12 @@ const EvalGenericResult = struct { | ... | @@ -1548,13 +1536,12 @@ const EvalGenericResult = struct { |
| 1548 | fn spawnChildAndCollect( | 1536 | fn spawnChildAndCollect( |
| 1549 | run: *Run, | 1537 | run: *Run, |
| 1550 | argv: []const []const u8, | 1538 | argv: []const []const u8, |
| 1551 | env_map: *EnvMap, | 1539 | environ_map: *EnvMap, |
| 1552 | has_side_effects: bool, | 1540 | has_side_effects: bool, |
| 1553 | options: Step.MakeOptions, | 1541 | options: Step.MakeOptions, |
| 1554 | fuzz_context: ?FuzzContext, | 1542 | fuzz_context: ?FuzzContext, |
| 1555 | ) !?EvalGenericResult { | 1543 | ) !?EvalGenericResult { |
| 1556 | const b = run.step.owner; | 1544 | const b = run.step.owner; |
| 1557 | const arena = b.allocator; | ||
| 1558 | const graph = b.graph; | 1545 | const graph = b.graph; |
| 1559 | const io = graph.io; | 1546 | const io = graph.io; |
| 1560 | 1547 | ||
| ... | @@ -1563,75 +1550,76 @@ fn spawnChildAndCollect( | ... | @@ -1563,75 +1550,76 @@ fn spawnChildAndCollect( |
| 1563 | assert(run.stdio == .zig_test); | 1550 | assert(run.stdio == .zig_test); |
| 1564 | } | 1551 | } |
| 1565 | 1552 | ||
| 1566 | var child = std.process.Child.init(argv, arena); | 1553 | const child_cwd = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, &run.step) else null; |
| 1567 | if (run.cwd) |lazy_cwd| { | ||
| 1568 | child.cwd = lazy_cwd.getPath2(b, &run.step); | ||
| 1569 | } | ||
| 1570 | child.env_map = env_map; | ||
| 1571 | child.request_resource_usage_statistics = true; | ||
| 1572 | |||
| 1573 | child.stdin_behavior = switch (run.stdio) { | ||
| 1574 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 1575 | .inherit => .Inherit, | ||
| 1576 | .check => .Ignore, | ||
| 1577 | .zig_test => .Pipe, | ||
| 1578 | }; | ||
| 1579 | child.stdout_behavior = switch (run.stdio) { | ||
| 1580 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 1581 | .inherit => .Inherit, | ||
| 1582 | .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore, | ||
| 1583 | .zig_test => .Pipe, | ||
| 1584 | }; | ||
| 1585 | child.stderr_behavior = switch (run.stdio) { | ||
| 1586 | .infer_from_args => if (has_side_effects) .Inherit else .Pipe, | ||
| 1587 | .inherit => .Inherit, | ||
| 1588 | .check => .Pipe, | ||
| 1589 | .zig_test => .Pipe, | ||
| 1590 | }; | ||
| 1591 | if (run.captured_stdout != null) child.stdout_behavior = .Pipe; | ||
| 1592 | if (run.captured_stderr != null) child.stderr_behavior = .Pipe; | ||
| 1593 | if (run.stdin != .none) { | ||
| 1594 | assert(run.stdio != .inherit); | ||
| 1595 | child.stdin_behavior = .Pipe; | ||
| 1596 | } | ||
| 1597 | 1554 | ||
| 1598 | // If an error occurs, it's caused by this command: | 1555 | // If an error occurs, it's caused by this command: |
| 1599 | assert(run.step.result_failed_command == null); | 1556 | assert(run.step.result_failed_command == null); |
| 1600 | run.step.result_failed_command = try Step.allocPrintCmd(options.gpa, child.cwd, argv); | 1557 | run.step.result_failed_command = try Step.allocPrintCmd(options.gpa, child_cwd, .{ |
| 1558 | .child = environ_map, | ||
| 1559 | .parent = &graph.environ_map, | ||
| 1560 | }, argv); | ||
| 1561 | |||
| 1562 | var spawn_options: process.SpawnOptions = .{ | ||
| 1563 | .argv = argv, | ||
| 1564 | .cwd = child_cwd, | ||
| 1565 | .environ_map = environ_map, | ||
| 1566 | .request_resource_usage_statistics = true, | ||
| 1567 | .stdin = if (run.stdin != .none) s: { | ||
| 1568 | assert(run.stdio != .inherit); | ||
| 1569 | break :s .pipe; | ||
| 1570 | } else switch (run.stdio) { | ||
| 1571 | .infer_from_args => if (has_side_effects) .inherit else .ignore, | ||
| 1572 | .inherit => .inherit, | ||
| 1573 | .check => .ignore, | ||
| 1574 | .zig_test => .pipe, | ||
| 1575 | }, | ||
| 1576 | .stdout = if (run.captured_stdout != null) .pipe else switch (run.stdio) { | ||
| 1577 | .infer_from_args => if (has_side_effects) .inherit else .ignore, | ||
| 1578 | .inherit => .inherit, | ||
| 1579 | .check => |checks| if (checksContainStdout(checks.items)) .pipe else .ignore, | ||
| 1580 | .zig_test => .pipe, | ||
| 1581 | }, | ||
| 1582 | .stderr = if (run.captured_stderr != null) .pipe else switch (run.stdio) { | ||
| 1583 | .infer_from_args => if (has_side_effects) .inherit else .pipe, | ||
| 1584 | .inherit => .inherit, | ||
| 1585 | .check => .pipe, | ||
| 1586 | .zig_test => .pipe, | ||
| 1587 | }, | ||
| 1588 | }; | ||
| 1601 | 1589 | ||
| 1602 | if (run.stdio == .zig_test) { | 1590 | if (run.stdio == .zig_test) { |
| 1603 | var timer = try std.time.Timer.start(); | 1591 | var timer = try std.time.Timer.start(); |
| 1604 | defer run.step.result_duration_ns = timer.read(); | 1592 | defer run.step.result_duration_ns = timer.read(); |
| 1605 | try evalZigTest(run, &child, options, fuzz_context); | 1593 | try evalZigTest(run, spawn_options, options, fuzz_context); |
| 1606 | return null; | 1594 | return null; |
| 1607 | } else { | 1595 | } else { |
| 1608 | const inherit = child.stdout_behavior == .Inherit or child.stderr_behavior == .Inherit; | 1596 | const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit; |
| 1609 | if (!run.disable_zig_progress and !inherit) { | 1597 | if (!run.disable_zig_progress and !inherit) { |
| 1610 | child.progress_node = options.progress_node; | 1598 | spawn_options.progress_node = options.progress_node; |
| 1611 | } | 1599 | } |
| 1612 | const terminal_mode: Io.Terminal.Mode = if (inherit) m: { | 1600 | const terminal_mode: Io.Terminal.Mode = if (inherit) m: { |
| 1613 | const stderr = try io.lockStderr(&.{}, graph.stderr_mode); | 1601 | const stderr = try io.lockStderr(&.{}, graph.stderr_mode); |
| 1614 | break :m stderr.terminal_mode; | 1602 | break :m stderr.terminal_mode; |
| 1615 | } else .no_color; | 1603 | } else .no_color; |
| 1616 | defer if (inherit) io.unlockStderr(); | 1604 | defer if (inherit) io.unlockStderr(); |
| 1617 | try setColorEnvironmentVariables(run, env_map, terminal_mode); | 1605 | try setColorEnvironmentVariables(run, environ_map, terminal_mode); |
| 1618 | var timer = try std.time.Timer.start(); | 1606 | var timer = try std.time.Timer.start(); |
| 1619 | const res = try evalGeneric(run, &child); | 1607 | const res = try evalGeneric(run, spawn_options); |
| 1620 | run.step.result_duration_ns = timer.read(); | 1608 | run.step.result_duration_ns = timer.read(); |
| 1621 | return .{ .term = res.term, .stdout = res.stdout, .stderr = res.stderr }; | 1609 | return .{ .term = res.term, .stdout = res.stdout, .stderr = res.stderr }; |
| 1622 | } | 1610 | } |
| 1623 | } | 1611 | } |
| 1624 | 1612 | ||
| 1625 | fn setColorEnvironmentVariables(run: *Run, env_map: *EnvMap, terminal_mode: Io.Terminal.Mode) !void { | 1613 | fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode: Io.Terminal.Mode) !void { |
| 1626 | color: switch (run.color) { | 1614 | color: switch (run.color) { |
| 1627 | .manual => {}, | 1615 | .manual => {}, |
| 1628 | .enable => { | 1616 | .enable => { |
| 1629 | try env_map.put("CLICOLOR_FORCE", "1"); | 1617 | try environ_map.put("CLICOLOR_FORCE", "1"); |
| 1630 | env_map.remove("NO_COLOR"); | 1618 | _ = environ_map.swapRemove("NO_COLOR"); |
| 1631 | }, | 1619 | }, |
| 1632 | .disable => { | 1620 | .disable => { |
| 1633 | try env_map.put("NO_COLOR", "1"); | 1621 | try environ_map.put("NO_COLOR", "1"); |
| 1634 | env_map.remove("CLICOLOR_FORCE"); | 1622 | _ = environ_map.swapRemove("CLICOLOR_FORCE"); |
| 1635 | }, | 1623 | }, |
| 1636 | .inherit => switch (terminal_mode) { | 1624 | .inherit => switch (terminal_mode) { |
| 1637 | .no_color, .windows_api => continue :color .disable, | 1625 | .no_color, .windows_api => continue :color .disable, |
| ... | @@ -1655,7 +1643,7 @@ const StdioPollEnum = enum { stdout, stderr }; | ... | @@ -1655,7 +1643,7 @@ const StdioPollEnum = enum { stdout, stderr }; |
| 1655 | 1643 | ||
| 1656 | fn evalZigTest( | 1644 | fn evalZigTest( |
| 1657 | run: *Run, | 1645 | run: *Run, |
| 1658 | child: *std.process.Child, | 1646 | spawn_options: process.SpawnOptions, |
| 1659 | options: Step.MakeOptions, | 1647 | options: Step.MakeOptions, |
| 1660 | fuzz_context: ?FuzzContext, | 1648 | fuzz_context: ?FuzzContext, |
| 1661 | ) !void { | 1649 | ) !void { |
| ... | @@ -1679,14 +1667,14 @@ fn evalZigTest( | ... | @@ -1679,14 +1667,14 @@ fn evalZigTest( |
| 1679 | var test_metadata: ?TestMetadata = null; | 1667 | var test_metadata: ?TestMetadata = null; |
| 1680 | 1668 | ||
| 1681 | while (true) { | 1669 | while (true) { |
| 1682 | try child.spawn(io); | 1670 | var child = try process.spawn(io, spawn_options); |
| 1683 | var poller = std.Io.poll(gpa, StdioPollEnum, .{ | 1671 | var poller = std.Io.poll(gpa, StdioPollEnum, .{ |
| 1684 | .stdout = child.stdout.?, | 1672 | .stdout = child.stdout.?, |
| 1685 | .stderr = child.stderr.?, | 1673 | .stderr = child.stderr.?, |
| 1686 | }); | 1674 | }); |
| 1687 | var child_killed = false; | 1675 | var child_killed = false; |
| 1688 | defer if (!child_killed) { | 1676 | defer if (!child_killed) { |
| 1689 | _ = child.kill(io) catch {}; | 1677 | child.kill(io); |
| 1690 | poller.deinit(); | 1678 | poller.deinit(); |
| 1691 | run.step.result_peak_rss = @max( | 1679 | run.step.result_peak_rss = @max( |
| 1692 | run.step.result_peak_rss, | 1680 | run.step.result_peak_rss, |
| ... | @@ -1694,11 +1682,9 @@ fn evalZigTest( | ... | @@ -1694,11 +1682,9 @@ fn evalZigTest( |
| 1694 | ); | 1682 | ); |
| 1695 | }; | 1683 | }; |
| 1696 | 1684 | ||
| 1697 | try child.waitForSpawn(); | ||
| 1698 | |||
| 1699 | switch (try pollZigTest( | 1685 | switch (try pollZigTest( |
| 1700 | run, | 1686 | run, |
| 1701 | child, | 1687 | &child, |
| 1702 | options, | 1688 | options, |
| 1703 | fuzz_context, | 1689 | fuzz_context, |
| 1704 | &poller, | 1690 | &poller, |
| ... | @@ -1760,7 +1746,7 @@ fn evalZigTest( | ... | @@ -1760,7 +1746,7 @@ fn evalZigTest( |
| 1760 | // Report an error if the child terminated uncleanly or if we were still trying to run more tests. | 1746 | // Report an error if the child terminated uncleanly or if we were still trying to run more tests. |
| 1761 | run.step.result_stderr = stderr_owned; | 1747 | run.step.result_stderr = stderr_owned; |
| 1762 | const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32); | 1748 | const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32); |
| 1763 | if (!tests_done or !termMatches(.{ .Exited = 0 }, term)) { | 1749 | if (!tests_done or !termMatches(.{ .exited = 0 }, term)) { |
| 1764 | // The individual unit test results are irrelevant: the test runner itself broke! | 1750 | // The individual unit test results are irrelevant: the test runner itself broke! |
| 1765 | // Fail immediately without populating `s.test_results`. | 1751 | // Fail immediately without populating `s.test_results`. |
| 1766 | return run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)}); | 1752 | return run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)}); |
| ... | @@ -1815,7 +1801,7 @@ fn evalZigTest( | ... | @@ -1815,7 +1801,7 @@ fn evalZigTest( |
| 1815 | /// * `poll` fails, indicating the child closed stdout and stderr | 1801 | /// * `poll` fails, indicating the child closed stdout and stderr |
| 1816 | fn pollZigTest( | 1802 | fn pollZigTest( |
| 1817 | run: *Run, | 1803 | run: *Run, |
| 1818 | child: *std.process.Child, | 1804 | child: *process.Child, |
| 1819 | options: Step.MakeOptions, | 1805 | options: Step.MakeOptions, |
| 1820 | fuzz_context: ?FuzzContext, | 1806 | fuzz_context: ?FuzzContext, |
| 1821 | poller: *std.Io.Poller(StdioPollEnum), | 1807 | poller: *std.Io.Poller(StdioPollEnum), |
| ... | @@ -2173,15 +2159,13 @@ fn sendRunFuzzTestMessage( | ... | @@ -2173,15 +2159,13 @@ fn sendRunFuzzTestMessage( |
| 2173 | }; | 2159 | }; |
| 2174 | } | 2160 | } |
| 2175 | 2161 | ||
| 2176 | fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { | 2162 | fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResult { |
| 2177 | const b = run.step.owner; | 2163 | const b = run.step.owner; |
| 2178 | const io = b.graph.io; | 2164 | const io = b.graph.io; |
| 2179 | const arena = b.allocator; | 2165 | const arena = b.allocator; |
| 2180 | 2166 | ||
| 2181 | try child.spawn(io); | 2167 | var child = try process.spawn(io, spawn_options); |
| 2182 | errdefer _ = child.kill(io) catch {}; | 2168 | defer child.kill(io); |
| 2183 | |||
| 2184 | try child.waitForSpawn(); | ||
| 2185 | 2169 | ||
| 2186 | switch (run.stdin) { | 2170 | switch (run.stdin) { |
| 2187 | .bytes => |bytes| { | 2171 | .bytes => |bytes| { |
| ... | @@ -2331,10 +2315,10 @@ fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { | ... | @@ -2331,10 +2315,10 @@ fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { |
| 2331 | => |s| hh.addBytes(s), | 2315 | => |s| hh.addBytes(s), |
| 2332 | 2316 | ||
| 2333 | .expect_term => |term| { | 2317 | .expect_term => |term| { |
| 2334 | hh.add(@as(std.meta.Tag(std.process.Child.Term), term)); | 2318 | hh.add(@as(std.meta.Tag(process.Child.Term), term)); |
| 2335 | switch (term) { | 2319 | switch (term) { |
| 2336 | .Exited => |x| hh.add(x), | 2320 | inline .exited, .signal => |x| hh.add(x), |
| 2337 | .Signal, .Stopped, .Unknown => |x| hh.add(x), | 2321 | .stopped, .unknown => |x| hh.add(x), |
| 2338 | } | 2322 | } |
| 2339 | }, | 2323 | }, |
| 2340 | } | 2324 | } |
lib/std/Build/Watch.zig+11-10| ... | @@ -99,7 +99,8 @@ const Os = switch (builtin.os.tag) { | ... | @@ -99,7 +99,8 @@ const Os = switch (builtin.os.tag) { |
| 99 | }; | 99 | }; |
| 100 | }; | 100 | }; |
| 101 | 101 | ||
| 102 | fn init() !Watch { | 102 | fn init(cwd_path: []const u8) !Watch { |
| 103 | _ = cwd_path; | ||
| 103 | return .{ | 104 | return .{ |
| 104 | .dir_table = .{}, | 105 | .dir_table = .{}, |
| 105 | .dir_count = 0, | 106 | .dir_count = 0, |
| ... | @@ -427,7 +428,8 @@ const Os = switch (builtin.os.tag) { | ... | @@ -427,7 +428,8 @@ const Os = switch (builtin.os.tag) { |
| 427 | } | 428 | } |
| 428 | }; | 429 | }; |
| 429 | 430 | ||
| 430 | fn init() !Watch { | 431 | fn init(cwd_path: []const u8) !Watch { |
| 432 | _ = cwd_path; | ||
| 431 | return .{ | 433 | return .{ |
| 432 | .dir_table = .{}, | 434 | .dir_table = .{}, |
| 433 | .dir_count = 0, | 435 | .dir_count = 0, |
| ... | @@ -658,14 +660,13 @@ const Os = switch (builtin.os.tag) { | ... | @@ -658,14 +660,13 @@ const Os = switch (builtin.os.tag) { |
| 658 | const EV = std.c.EV; | 660 | const EV = std.c.EV; |
| 659 | const NOTE = std.c.NOTE; | 661 | const NOTE = std.c.NOTE; |
| 660 | 662 | ||
| 661 | fn init() !Watch { | 663 | fn init(cwd_path: []const u8) !Watch { |
| 662 | const kq_fd = try posix.kqueue(); | 664 | _ = cwd_path; |
| 663 | errdefer posix.close(kq_fd); | ||
| 664 | return .{ | 665 | return .{ |
| 665 | .dir_table = .{}, | 666 | .dir_table = .{}, |
| 666 | .dir_count = 0, | 667 | .dir_count = 0, |
| 667 | .os = .{ | 668 | .os = .{ |
| 668 | .kq_fd = kq_fd, | 669 | .kq_fd = try posix.kqueue(), |
| 669 | .handles = .empty, | 670 | .handles = .empty, |
| 670 | }, | 671 | }, |
| 671 | .generation = 0, | 672 | .generation = 0, |
| ... | @@ -841,9 +842,9 @@ const Os = switch (builtin.os.tag) { | ... | @@ -841,9 +842,9 @@ const Os = switch (builtin.os.tag) { |
| 841 | .macos => struct { | 842 | .macos => struct { |
| 842 | fse: FsEvents, | 843 | fse: FsEvents, |
| 843 | 844 | ||
| 844 | fn init() !Watch { | 845 | fn init(cwd_path: []const u8) !Watch { |
| 845 | return .{ | 846 | return .{ |
| 846 | .os = .{ .fse = try .init() }, | 847 | .os = .{ .fse = try .init(cwd_path) }, |
| 847 | .dir_count = 0, | 848 | .dir_count = 0, |
| 848 | .dir_table = undefined, | 849 | .dir_table = undefined, |
| 849 | .generation = undefined, | 850 | .generation = undefined, |
| ... | @@ -863,8 +864,8 @@ const Os = switch (builtin.os.tag) { | ... | @@ -863,8 +864,8 @@ const Os = switch (builtin.os.tag) { |
| 863 | else => void, | 864 | else => void, |
| 864 | }; | 865 | }; |
| 865 | 866 | ||
| 866 | pub fn init() !Watch { | 867 | pub fn init(cwd_path: []const u8) !Watch { |
| 867 | return Os.init(); | 868 | return Os.init(cwd_path); |
| 868 | } | 869 | } |
| 869 | 870 | ||
| 870 | pub const Match = struct { | 871 | pub const Match = struct { |
lib/std/Build/Watch/FsEvents.zig+7-5| ... | @@ -43,6 +43,8 @@ dispatch_queue: dispatch_queue_t, | ... | @@ -43,6 +43,8 @@ dispatch_queue: dispatch_queue_t, |
| 43 | /// of writing. See the comment at the start of `wait` for details. | 43 | /// of writing. See the comment at the start of `wait` for details. |
| 44 | since_event: FSEventStreamEventId, | 44 | since_event: FSEventStreamEventId, |
| 45 | 45 | ||
| 46 | cwd_path: []const u8, | ||
| 47 | |||
| 46 | /// All of the symbols we pull from the `dlopen`ed CoreServices framework. If any of these symbols | 48 | /// All of the symbols we pull from the `dlopen`ed CoreServices framework. If any of these symbols |
| 47 | /// is not present, `init` will close the framework and return an error. | 49 | /// is not present, `init` will close the framework and return an error. |
| 48 | const ResolvedSymbols = struct { | 50 | const ResolvedSymbols = struct { |
| ... | @@ -78,7 +80,7 @@ const ResolvedSymbols = struct { | ... | @@ -78,7 +80,7 @@ const ResolvedSymbols = struct { |
| 78 | kCFAllocatorUseContext: *const CFAllocatorRef, | 80 | kCFAllocatorUseContext: *const CFAllocatorRef, |
| 79 | }; | 81 | }; |
| 80 | 82 | ||
| 81 | pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { | 83 | pub fn init(cwd_path: []const u8) error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { |
| 82 | var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch | 84 | var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch |
| 83 | return error.OpenFrameworkFailed; | 85 | return error.OpenFrameworkFailed; |
| 84 | errdefer core_services.close(); | 86 | errdefer core_services.close(); |
| ... | @@ -99,6 +101,7 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { | ... | @@ -99,6 +101,7 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { |
| 99 | // Not `.since_now`, because this means we can init `FsEvents` *before* we do work in order | 101 | // Not `.since_now`, because this means we can init `FsEvents` *before* we do work in order |
| 100 | // to notice any changes which happened during said work. | 102 | // to notice any changes which happened during said work. |
| 101 | .since_event = resolved_symbols.FSEventsGetCurrentEventId(), | 103 | .since_event = resolved_symbols.FSEventsGetCurrentEventId(), |
| 104 | .cwd_path = cwd_path, | ||
| 102 | }; | 105 | }; |
| 103 | } | 106 | } |
| 104 | 107 | ||
| ... | @@ -120,9 +123,6 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) | ... | @@ -120,9 +123,6 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) |
| 120 | defer fse.paths_arena = paths_arena_instance.state; | 123 | defer fse.paths_arena = paths_arena_instance.state; |
| 121 | const paths_arena = paths_arena_instance.allocator(); | 124 | const paths_arena = paths_arena_instance.allocator(); |
| 122 | 125 | ||
| 123 | const cwd_path = try std.process.getCwdAlloc(gpa); | ||
| 124 | defer gpa.free(cwd_path); | ||
| 125 | |||
| 126 | var need_dirs: std.StringArrayHashMapUnmanaged(void) = .empty; | 126 | var need_dirs: std.StringArrayHashMapUnmanaged(void) = .empty; |
| 127 | defer need_dirs.deinit(gpa); | 127 | defer need_dirs.deinit(gpa); |
| 128 | 128 | ||
| ... | @@ -131,7 +131,9 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) | ... | @@ -131,7 +131,9 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) |
| 131 | // We take `step` by pointer for a slight memory optimization in a moment. | 131 | // We take `step` by pointer for a slight memory optimization in a moment. |
| 132 | for (steps) |*step| { | 132 | for (steps) |*step| { |
| 133 | for (step.*.inputs.table.keys(), step.*.inputs.table.values()) |path, *files| { | 133 | for (step.*.inputs.table.keys(), step.*.inputs.table.values()) |path, *files| { |
| 134 | const resolved_dir = try std.fs.path.resolvePosix(paths_arena, &.{ cwd_path, path.root_dir.path orelse ".", path.sub_path }); | 134 | const resolved_dir = try std.fs.path.resolvePosix(paths_arena, &.{ |
| 135 | fse.cwd_path, path.root_dir.path orelse ".", path.sub_path, | ||
| 136 | }); | ||
| 135 | try need_dirs.put(gpa, resolved_dir, {}); | 137 | try need_dirs.put(gpa, resolved_dir, {}); |
| 136 | for (files.items) |file_name| { | 138 | for (files.items) |file_name| { |
| 137 | const watch_path = if (std.mem.eql(u8, file_name, ".")) | 139 | const watch_path = if (std.mem.eql(u8, file_name, ".")) |
lib/std/Build/WebServer.zig+25-21| ... | @@ -482,8 +482,8 @@ pub fn serveFile( | ... | @@ -482,8 +482,8 @@ pub fn serveFile( |
| 482 | }); | 482 | }); |
| 483 | } | 483 | } |
| 484 | pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { | 484 | pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { |
| 485 | const gpa = ws.gpa; | 485 | const graph = ws.graph; |
| 486 | const io = ws.graph.io; | 486 | const io = graph.io; |
| 487 | 487 | ||
| 488 | var send_buffer: [0x4000]u8 = undefined; | 488 | var send_buffer: [0x4000]u8 = undefined; |
| 489 | var response = try request.respondStreaming(&send_buffer, .{ | 489 | var response = try request.respondStreaming(&send_buffer, .{ |
| ... | @@ -495,9 +495,6 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons | ... | @@ -495,9 +495,6 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons |
| 495 | }, | 495 | }, |
| 496 | }); | 496 | }); |
| 497 | 497 | ||
| 498 | var cached_cwd_path: ?[]const u8 = null; | ||
| 499 | defer if (cached_cwd_path) |p| gpa.free(p); | ||
| 500 | |||
| 501 | var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer }; | 498 | var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer }; |
| 502 | 499 | ||
| 503 | for (paths) |path| { | 500 | for (paths) |path| { |
| ... | @@ -516,10 +513,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons | ... | @@ -516,10 +513,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons |
| 516 | // resulting in modules named "" and "src". The compiler needs to tell the build system | 513 | // resulting in modules named "" and "src". The compiler needs to tell the build system |
| 517 | // about the module graph so that the build system can correctly encode this information in | 514 | // about the module graph so that the build system can correctly encode this information in |
| 518 | // the tar file. | 515 | // the tar file. |
| 519 | archiver.prefix = path.root_dir.path orelse cwd: { | 516 | archiver.prefix = path.root_dir.path orelse graph.cache.cwd; |
| 520 | if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa); | ||
| 521 | break :cwd cached_cwd_path.?; | ||
| 522 | }; | ||
| 523 | try archiver.writeFile(path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds())); | 517 | try archiver.writeFile(path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds())); |
| 524 | } | 518 | } |
| 525 | 519 | ||
| ... | @@ -528,13 +522,13 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons | ... | @@ -528,13 +522,13 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons |
| 528 | } | 522 | } |
| 529 | 523 | ||
| 530 | fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path { | 524 | fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.OptimizeMode) !Cache.Path { |
| 531 | const io = ws.graph.io; | ||
| 532 | const root_name = "build-web"; | 525 | const root_name = "build-web"; |
| 533 | const arch_os_abi = "wasm32-freestanding"; | 526 | const arch_os_abi = "wasm32-freestanding"; |
| 534 | const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; | 527 | const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; |
| 535 | 528 | ||
| 536 | const gpa = ws.gpa; | 529 | const gpa = ws.gpa; |
| 537 | const graph = ws.graph; | 530 | const graph = ws.graph; |
| 531 | const io = graph.io; | ||
| 538 | 532 | ||
| 539 | const main_src_path: Cache.Path = .{ | 533 | const main_src_path: Cache.Path = .{ |
| 540 | .root_dir = graph.zig_lib_directory, | 534 | .root_dir = graph.zig_lib_directory, |
| ... | @@ -572,11 +566,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim | ... | @@ -572,11 +566,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim |
| 572 | "--listen=-", | 566 | "--listen=-", |
| 573 | }); | 567 | }); |
| 574 | 568 | ||
| 575 | var child: std.process.Child = .init(argv.items, gpa); | 569 | var child = try std.process.spawn(io, .{ |
| 576 | child.stdin_behavior = .Pipe; | 570 | .argv = argv.items, |
| 577 | child.stdout_behavior = .Pipe; | 571 | .environ_map = &graph.environ_map, |
| 578 | child.stderr_behavior = .Pipe; | 572 | .stdin = .pipe, |
| 579 | try child.spawn(io); | 573 | .stdout = .pipe, |
| 574 | .stderr = .pipe, | ||
| 575 | }); | ||
| 576 | defer child.kill(io); | ||
| 580 | 577 | ||
| 581 | var poller = Io.poll(gpa, enum { stdout, stderr }, .{ | 578 | var poller = Io.poll(gpa, enum { stdout, stderr }, .{ |
| 582 | .stdout = child.stdout.?, | 579 | .stdout = child.stdout.?, |
| ... | @@ -636,19 +633,26 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim | ... | @@ -636,19 +633,26 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim |
| 636 | child.stdin = null; | 633 | child.stdin = null; |
| 637 | 634 | ||
| 638 | switch (try child.wait(io)) { | 635 | switch (try child.wait(io)) { |
| 639 | .Exited => |code| { | 636 | .exited => |code| { |
| 640 | if (code != 0) { | 637 | if (code != 0) { |
| 641 | log.err( | 638 | log.err( |
| 642 | "the following command exited with error code {d}:\n{s}", | 639 | "the following command exited with error code {d}:\n{s}", |
| 643 | .{ code, try Build.Step.allocPrintCmd(arena, null, argv.items) }, | 640 | .{ code, try Build.Step.allocPrintCmd(arena, null, null, argv.items) }, |
| 644 | ); | 641 | ); |
| 645 | return error.WasmCompilationFailed; | 642 | return error.WasmCompilationFailed; |
| 646 | } | 643 | } |
| 647 | }, | 644 | }, |
| 648 | .Signal, .Stopped, .Unknown => { | 645 | .signal => |sig| { |
| 646 | log.err( | ||
| 647 | "the following command terminated with signal {t}:\n{s}", | ||
| 648 | .{ sig, try Build.Step.allocPrintCmd(arena, null, null, argv.items) }, | ||
| 649 | ); | ||
| 650 | return error.WasmCompilationFailed; | ||
| 651 | }, | ||
| 652 | .stopped, .unknown => { | ||
| 649 | log.err( | 653 | log.err( |
| 650 | "the following command terminated unexpectedly:\n{s}", | 654 | "the following command terminated unexpectedly:\n{s}", |
| 651 | .{try Build.Step.allocPrintCmd(arena, null, argv.items)}, | 655 | .{try Build.Step.allocPrintCmd(arena, null, null, argv.items)}, |
| 652 | ); | 656 | ); |
| 653 | return error.WasmCompilationFailed; | 657 | return error.WasmCompilationFailed; |
| 654 | }, | 658 | }, |
| ... | @@ -658,14 +662,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim | ... | @@ -658,14 +662,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim |
| 658 | try result_error_bundle.renderToStderr(io, .{}, .auto); | 662 | try result_error_bundle.renderToStderr(io, .{}, .auto); |
| 659 | log.err("the following command failed with {d} compilation errors:\n{s}", .{ | 663 | log.err("the following command failed with {d} compilation errors:\n{s}", .{ |
| 660 | result_error_bundle.errorMessageCount(), | 664 | result_error_bundle.errorMessageCount(), |
| 661 | try Build.Step.allocPrintCmd(arena, null, argv.items), | 665 | try Build.Step.allocPrintCmd(arena, null, null, argv.items), |
| 662 | }); | 666 | }); |
| 663 | return error.WasmCompilationFailed; | 667 | return error.WasmCompilationFailed; |
| 664 | } | 668 | } |
| 665 | 669 | ||
| 666 | const base_path = result orelse { | 670 | const base_path = result orelse { |
| 667 | log.err("child process failed to report result\n{s}", .{ | 671 | log.err("child process failed to report result\n{s}", .{ |
| 668 | try Build.Step.allocPrintCmd(arena, null, argv.items), | 672 | try Build.Step.allocPrintCmd(arena, null, null, argv.items), |
| 669 | }); | 673 | }); |
| 670 | return error.WasmCompilationFailed; | 674 | return error.WasmCompilationFailed; |
| 671 | }; | 675 | }; |
lib/std/Io.zig+8| ... | @@ -717,6 +717,14 @@ pub const VTable = struct { | ... | @@ -717,6 +717,14 @@ pub const VTable = struct { |
| 717 | tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr, | 717 | tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr, |
| 718 | unlockStderr: *const fn (?*anyopaque) void, | 718 | unlockStderr: *const fn (?*anyopaque) void, |
| 719 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, | 719 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, |
| 720 | processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError, | ||
| 721 | processReplacePath: *const fn (?*anyopaque, Dir, std.process.ReplaceOptions) std.process.ReplaceError, | ||
| 722 | processSpawn: *const fn (?*anyopaque, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, | ||
| 723 | processSpawnPath: *const fn (?*anyopaque, Dir, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, | ||
| 724 | childWait: *const fn (?*anyopaque, *std.process.Child) std.process.Child.WaitError!std.process.Child.Term, | ||
| 725 | childKill: *const fn (?*anyopaque, *std.process.Child) void, | ||
| 726 | |||
| 727 | progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File, | ||
| 720 | 728 | ||
| 721 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, | 729 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 722 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, | 730 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
lib/std/Io/Dir.zig+4-3| ... | @@ -82,13 +82,14 @@ pub const Entry = struct { | ... | @@ -82,13 +82,14 @@ pub const Entry = struct { |
| 82 | /// | 82 | /// |
| 83 | /// On POSIX targets, this function is comptime-callable. | 83 | /// On POSIX targets, this function is comptime-callable. |
| 84 | /// | 84 | /// |
| 85 | /// On WASI, the value this returns is application-configurable. | 85 | /// This function is overridable via `std.Options.cwd`. |
| 86 | pub fn cwd() Dir { | 86 | pub fn cwd() Dir { |
| 87 | return switch (native_os) { | 87 | const cwdFn = std.Options.cwd orelse return switch (native_os) { |
| 88 | .windows => .{ .handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle }, | 88 | .windows => .{ .handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle }, |
| 89 | .wasi => .{ .handle = std.options.wasiCwd() }, | 89 | .wasi => .{ .handle = 3 }, // Expect the first preopen to be current working directory. |
| 90 | else => .{ .handle = std.posix.AT.FDCWD }, | 90 | else => .{ .handle = std.posix.AT.FDCWD }, |
| 91 | }; | 91 | }; |
| 92 | return cwdFn(); | ||
| 92 | } | 93 | } |
| 93 | 94 | ||
| 94 | pub const Reader = struct { | 95 | pub const Reader = struct { |
lib/std/Io/Threaded.zig+2410-218| ... | @@ -13,6 +13,7 @@ const File = std.Io.File; | ... | @@ -13,6 +13,7 @@ const File = std.Io.File; |
| 13 | const Dir = std.Io.Dir; | 13 | const Dir = std.Io.Dir; |
| 14 | const HostName = std.Io.net.HostName; | 14 | const HostName = std.Io.net.HostName; |
| 15 | const IpAddress = std.Io.net.IpAddress; | 15 | const IpAddress = std.Io.net.IpAddress; |
| 16 | const process = std.process; | ||
| 16 | const Allocator = std.mem.Allocator; | 17 | const Allocator = std.mem.Allocator; |
| 17 | const Alignment = std.mem.Alignment; | 18 | const Alignment = std.mem.Alignment; |
| 18 | const assert = std.debug.assert; | 19 | const assert = std.debug.assert; |
| ... | @@ -63,24 +64,41 @@ stderr_writer_initialized: bool = false, | ... | @@ -63,24 +64,41 @@ stderr_writer_initialized: bool = false, |
| 63 | argv0: Argv0, | 64 | argv0: Argv0, |
| 64 | environ: Environ, | 65 | environ: Environ, |
| 65 | 66 | ||
| 67 | null_file: NullFile = .{}, | ||
| 68 | |||
| 66 | pub const Argv0 = switch (native_os) { | 69 | pub const Argv0 = switch (native_os) { |
| 67 | .openbsd, .haiku => struct { | 70 | .openbsd, .haiku => struct { |
| 68 | value: ?[*:0]const u8 = null, | 71 | value: ?[*:0]const u8, |
| 72 | |||
| 73 | pub const empty: Argv0 = .{ .value = null }; | ||
| 74 | |||
| 75 | pub fn init(args: process.Args) Argv0 { | ||
| 76 | return .{ .value = args.value[0] }; | ||
| 77 | } | ||
| 78 | }, | ||
| 79 | else => struct { | ||
| 80 | pub const empty: Argv0 = .{}; | ||
| 81 | |||
| 82 | pub fn init(args: process.Args) Argv0 { | ||
| 83 | _ = args; | ||
| 84 | return .{}; | ||
| 85 | } | ||
| 69 | }, | 86 | }, |
| 70 | else => struct {}, | ||
| 71 | }; | 87 | }; |
| 72 | 88 | ||
| 73 | pub const Environ = struct { | 89 | const Environ = struct { |
| 74 | /// Unmodified data directly from the OS. | 90 | /// Unmodified data directly from the OS. |
| 75 | block: Block = &.{}, | 91 | process_environ: process.Environ = .empty, |
| 76 | /// Protected by `mutex`. Determines whether the other fields have been | 92 | /// Protected by `mutex`. Determines whether the other fields have been |
| 77 | /// memoized based on `block`. | 93 | /// memoized based on `process_environ`. |
| 78 | initialized: bool = false, | 94 | initialized: bool = false, |
| 79 | /// Protected by `mutex`. Memoized based on `block`. Tracks whether the | 95 | /// Protected by `mutex`. Memoized based on `process_environ`. Tracks whether the |
| 80 | /// environment variables are present, ignoring their value. | 96 | /// environment variables are present, ignoring their value. |
| 81 | exist: Exist = .{}, | 97 | exist: Exist = .{}, |
| 82 | /// Protected by `mutex`. Memoized based on `block`. | 98 | /// Protected by `mutex`. Memoized based on `process_environ`. |
| 83 | string: String = .{}, | 99 | string: String = .{}, |
| 100 | /// ZIG_PROGRESS | ||
| 101 | zig_progress_handle: std.Progress.ParentFileError!u31 = error.EnvironmentVariableMissing, | ||
| 84 | /// Protected by `mutex`. Tracks the problem, if any, that occurred when | 102 | /// Protected by `mutex`. Tracks the problem, if any, that occurred when |
| 85 | /// trying to scan environment variables. | 103 | /// trying to scan environment variables. |
| 86 | /// | 104 | /// |
| ... | @@ -89,21 +107,50 @@ pub const Environ = struct { | ... | @@ -89,21 +107,50 @@ pub const Environ = struct { |
| 89 | 107 | ||
| 90 | pub const Error = Allocator.Error || Io.UnexpectedError; | 108 | pub const Error = Allocator.Error || Io.UnexpectedError; |
| 91 | 109 | ||
| 92 | pub const Block = []const [*:0]const u8; | ||
| 93 | |||
| 94 | pub const Exist = struct { | 110 | pub const Exist = struct { |
| 95 | NO_COLOR: bool = false, | 111 | NO_COLOR: bool = false, |
| 96 | CLICOLOR_FORCE: bool = false, | 112 | CLICOLOR_FORCE: bool = false, |
| 97 | }; | 113 | }; |
| 98 | 114 | ||
| 99 | pub const String = switch (native_os) { | 115 | pub const String = switch (native_os) { |
| 100 | .openbsd, .haiku => struct { | 116 | .windows, .wasi => struct {}, |
| 117 | else => struct { | ||
| 101 | PATH: ?[:0]const u8 = null, | 118 | PATH: ?[:0]const u8 = null, |
| 119 | DEBUGINFOD_CACHE_PATH: ?[:0]const u8 = null, | ||
| 120 | XDG_CACHE_HOME: ?[:0]const u8 = null, | ||
| 121 | HOME: ?[:0]const u8 = null, | ||
| 102 | }, | 122 | }, |
| 103 | else => struct {}, | ||
| 104 | }; | 123 | }; |
| 105 | }; | 124 | }; |
| 106 | 125 | ||
| 126 | pub const NullFile = switch (native_os) { | ||
| 127 | .windows => struct { | ||
| 128 | handle: ?windows.HANDLE = null, | ||
| 129 | |||
| 130 | fn deinit(this: *@This()) void { | ||
| 131 | if (this.handle) |handle| { | ||
| 132 | windows.CloseHandle(handle); | ||
| 133 | this.handle = null; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | }, | ||
| 137 | .wasi, .ios, .tvos, .visionos, .watchos => struct { | ||
| 138 | fn deinit(this: @This()) void { | ||
| 139 | _ = this; | ||
| 140 | } | ||
| 141 | }, | ||
| 142 | else => struct { | ||
| 143 | fd: posix.fd_t = -1, | ||
| 144 | |||
| 145 | fn deinit(this: *@This()) void { | ||
| 146 | if (this.fd >= 0) { | ||
| 147 | posix.close(this.fd); | ||
| 148 | this.fd = -1; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | }, | ||
| 152 | }; | ||
| 153 | |||
| 107 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { | 154 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { |
| 108 | unknown = 0, | 155 | unknown = 0, |
| 109 | _, | 156 | _, |
| ... | @@ -1105,6 +1152,7 @@ const Syscall = struct { | ... | @@ -1105,6 +1152,7 @@ const Syscall = struct { |
| 1105 | 1152 | ||
| 1106 | const max_iovecs_len = 8; | 1153 | const max_iovecs_len = 8; |
| 1107 | const splat_buffer_size = 64; | 1154 | const splat_buffer_size = 64; |
| 1155 | const default_PATH = "/usr/local/bin:/bin/:/usr/bin"; | ||
| 1108 | 1156 | ||
| 1109 | comptime { | 1157 | comptime { |
| 1110 | if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX); | 1158 | if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX); |
| ... | @@ -1137,7 +1185,8 @@ pub const InitOptions = struct { | ... | @@ -1137,7 +1185,8 @@ pub const InitOptions = struct { |
| 1137 | /// Affects the following operations: | 1185 | /// Affects the following operations: |
| 1138 | /// * `fileIsTty` | 1186 | /// * `fileIsTty` |
| 1139 | /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH"). | 1187 | /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH"). |
| 1140 | environ: Environ = .{}, | 1188 | /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath` |
| 1189 | environ: process.Environ, | ||
| 1141 | }; | 1190 | }; |
| 1142 | 1191 | ||
| 1143 | /// Related: | 1192 | /// Related: |
| ... | @@ -1167,8 +1216,8 @@ pub fn init( | ... | @@ -1167,8 +1216,8 @@ pub fn init( |
| 1167 | .old_sig_pipe = undefined, | 1216 | .old_sig_pipe = undefined, |
| 1168 | .have_signal_handler = false, | 1217 | .have_signal_handler = false, |
| 1169 | .argv0 = options.argv0, | 1218 | .argv0 = options.argv0, |
| 1170 | .environ = options.environ, | ||
| 1171 | .worker_threads = .init(null), | 1219 | .worker_threads = .init(null), |
| 1220 | .environ = .{ .process_environ = options.environ }, | ||
| 1172 | }; | 1221 | }; |
| 1173 | 1222 | ||
| 1174 | if (posix.Sigaction != void) { | 1223 | if (posix.Sigaction != void) { |
| ... | @@ -1235,6 +1284,7 @@ pub fn deinit(t: *Threaded) void { | ... | @@ -1235,6 +1284,7 @@ pub fn deinit(t: *Threaded) void { |
| 1235 | if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); | 1284 | if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); |
| 1236 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); | 1285 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); |
| 1237 | } | 1286 | } |
| 1287 | t.null_file.deinit(); | ||
| 1238 | t.* = undefined; | 1288 | t.* = undefined; |
| 1239 | } | 1289 | } |
| 1240 | 1290 | ||
| ... | @@ -1402,6 +1452,14 @@ pub fn io(t: *Threaded) Io { | ... | @@ -1402,6 +1452,14 @@ pub fn io(t: *Threaded) Io { |
| 1402 | .tryLockStderr = tryLockStderr, | 1452 | .tryLockStderr = tryLockStderr, |
| 1403 | .unlockStderr = unlockStderr, | 1453 | .unlockStderr = unlockStderr, |
| 1404 | .processSetCurrentDir = processSetCurrentDir, | 1454 | .processSetCurrentDir = processSetCurrentDir, |
| 1455 | .processReplace = processReplace, | ||
| 1456 | .processReplacePath = processReplacePath, | ||
| 1457 | .processSpawn = processSpawn, | ||
| 1458 | .processSpawnPath = processSpawnPath, | ||
| 1459 | .childWait = childWait, | ||
| 1460 | .childKill = childKill, | ||
| 1461 | |||
| 1462 | .progressParentFile = progressParentFile, | ||
| 1405 | 1463 | ||
| 1406 | .now = now, | 1464 | .now = now, |
| 1407 | .sleep = sleep, | 1465 | .sleep = sleep, |
| ... | @@ -1540,6 +1598,14 @@ pub fn ioBasic(t: *Threaded) Io { | ... | @@ -1540,6 +1598,14 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1540 | .tryLockStderr = tryLockStderr, | 1598 | .tryLockStderr = tryLockStderr, |
| 1541 | .unlockStderr = unlockStderr, | 1599 | .unlockStderr = unlockStderr, |
| 1542 | .processSetCurrentDir = processSetCurrentDir, | 1600 | .processSetCurrentDir = processSetCurrentDir, |
| 1601 | .processReplace = processReplace, | ||
| 1602 | .processReplacePath = processReplacePath, | ||
| 1603 | .processSpawn = processSpawn, | ||
| 1604 | .processSpawnPath = processSpawnPath, | ||
| 1605 | .childWait = childWait, | ||
| 1606 | .childKill = childKill, | ||
| 1607 | |||
| 1608 | .progressParentFile = progressParentFile, | ||
| 1543 | 1609 | ||
| 1544 | .now = now, | 1610 | .now = now, |
| 1545 | .sleep = sleep, | 1611 | .sleep = sleep, |
| ... | @@ -1603,6 +1669,18 @@ const have_fchmod = switch (native_os) { | ... | @@ -1603,6 +1669,18 @@ const have_fchmod = switch (native_os) { |
| 1603 | else => true, | 1669 | else => true, |
| 1604 | }; | 1670 | }; |
| 1605 | 1671 | ||
| 1672 | const have_waitid = switch (native_os) { | ||
| 1673 | .linux => @hasField(std.os.linux.SYS, "waitid"), | ||
| 1674 | else => false, | ||
| 1675 | }; | ||
| 1676 | |||
| 1677 | const have_wait4 = switch (native_os) { | ||
| 1678 | .linux => @hasField(std.os.linux.SYS, "wait4"), | ||
| 1679 | .dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .serenity, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => true, | ||
| 1680 | else => false, | ||
| 1681 | }; | ||
| 1682 | |||
| 1683 | const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open; | ||
| 1606 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; | 1684 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| 1607 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; | 1685 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; |
| 1608 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; | 1686 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; |
| ... | @@ -2249,7 +2327,7 @@ fn dirCreateDirPath( | ... | @@ -2249,7 +2327,7 @@ fn dirCreateDirPath( |
| 2249 | ) Dir.CreateDirPathError!Dir.CreatePathStatus { | 2327 | ) Dir.CreateDirPathError!Dir.CreatePathStatus { |
| 2250 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 2328 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2251 | 2329 | ||
| 2252 | var it = std.fs.path.componentIterator(sub_path); | 2330 | var it = Dir.path.componentIterator(sub_path); |
| 2253 | var status: Dir.CreatePathStatus = .existed; | 2331 | var status: Dir.CreatePathStatus = .existed; |
| 2254 | var component = it.last() orelse return error.BadPathName; | 2332 | var component = it.last() orelse return error.BadPathName; |
| 2255 | while (true) { | 2333 | while (true) { |
| ... | @@ -2309,9 +2387,9 @@ fn dirCreateDirPathOpenWindows( | ... | @@ -2309,9 +2387,9 @@ fn dirCreateDirPathOpenWindows( |
| 2309 | 2387 | ||
| 2310 | _ = permissions; // TODO apply these permissions | 2388 | _ = permissions; // TODO apply these permissions |
| 2311 | 2389 | ||
| 2312 | var it = std.fs.path.componentIterator(sub_path); | 2390 | var it = Dir.path.componentIterator(sub_path); |
| 2313 | // If there are no components in the path, then create a dummy component with the full path. | 2391 | // If there are no components in the path, then create a dummy component with the full path. |
| 2314 | var component: std.fs.path.NativeComponentIterator.Component = it.last() orelse .{ | 2392 | var component: Dir.path.NativeComponentIterator.Component = it.last() orelse .{ |
| 2315 | .name = "", | 2393 | .name = "", |
| 2316 | .path = sub_path, | 2394 | .path = sub_path, |
| 2317 | }; | 2395 | }; |
| ... | @@ -2349,7 +2427,7 @@ fn dirCreateDirPathOpenWindows( | ... | @@ -2349,7 +2427,7 @@ fn dirCreateDirPathOpenWindows( |
| 2349 | }, | 2427 | }, |
| 2350 | &.{ | 2428 | &.{ |
| 2351 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | 2429 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
| 2352 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 2430 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 2353 | .Attributes = .{}, | 2431 | .Attributes = .{}, |
| 2354 | .ObjectName = &nt_name, | 2432 | .ObjectName = &nt_name, |
| 2355 | .SecurityDescriptor = null, | 2433 | .SecurityDescriptor = null, |
| ... | @@ -2988,7 +3066,7 @@ fn dirAccessWindows( | ... | @@ -2988,7 +3066,7 @@ fn dirAccessWindows( |
| 2988 | }; | 3066 | }; |
| 2989 | var attr: windows.OBJECT_ATTRIBUTES = .{ | 3067 | var attr: windows.OBJECT_ATTRIBUTES = .{ |
| 2990 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | 3068 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), |
| 2991 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 3069 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 2992 | .Attributes = .{}, | 3070 | .Attributes = .{}, |
| 2993 | .ObjectName = &nt_name, | 3071 | .ObjectName = &nt_name, |
| 2994 | .SecurityDescriptor = null, | 3072 | .SecurityDescriptor = null, |
| ... | @@ -3537,7 +3615,7 @@ fn dirOpenFileWindows( | ... | @@ -3537,7 +3615,7 @@ fn dirOpenFileWindows( |
| 3537 | _ = t; | 3615 | _ = t; |
| 3538 | const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path); | 3616 | const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path); |
| 3539 | const sub_path_w = sub_path_w_array.span(); | 3617 | const sub_path_w = sub_path_w_array.span(); |
| 3540 | const dir_handle = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle; | 3618 | const dir_handle = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle; |
| 3541 | return dirOpenFileWtf16(dir_handle, sub_path_w, flags); | 3619 | return dirOpenFileWtf16(dir_handle, sub_path_w, flags); |
| 3542 | } | 3620 | } |
| 3543 | 3621 | ||
| ... | @@ -3620,7 +3698,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -3620,7 +3698,10 @@ pub fn dirOpenFileWtf16( |
| 3620 | // kernel bug with retry attempts. | 3698 | // kernel bug with retry attempts. |
| 3621 | syscall.finish(); | 3699 | syscall.finish(); |
| 3622 | if (max_attempts - attempt == 0) return error.SharingViolation; | 3700 | if (max_attempts - attempt == 0) return error.SharingViolation; |
| 3623 | _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE); | 3701 | try parking_sleep.sleep(.{ .duration = .{ |
| 3702 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), | ||
| 3703 | .clock = .awake, | ||
| 3704 | } }); | ||
| 3624 | attempt += 1; | 3705 | attempt += 1; |
| 3625 | syscall = try .start(); | 3706 | syscall = try .start(); |
| 3626 | continue; | 3707 | continue; |
| ... | @@ -3642,7 +3723,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -3642,7 +3723,10 @@ pub fn dirOpenFileWtf16( |
| 3642 | // fixed by sleeping and retrying until the error goes away. | 3723 | // fixed by sleeping and retrying until the error goes away. |
| 3643 | syscall.finish(); | 3724 | syscall.finish(); |
| 3644 | if (max_attempts - attempt == 0) return error.SharingViolation; | 3725 | if (max_attempts - attempt == 0) return error.SharingViolation; |
| 3645 | _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE); | 3726 | try parking_sleep.sleep(.{ .duration = .{ |
| 3727 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), | ||
| 3728 | .clock = .awake, | ||
| 3729 | } }); | ||
| 3646 | attempt += 1; | 3730 | attempt += 1; |
| 3647 | syscall = try .start(); | 3731 | syscall = try .start(); |
| 3648 | continue; | 3732 | continue; |
| ... | @@ -3935,7 +4019,7 @@ pub fn dirOpenDirWindows( | ... | @@ -3935,7 +4019,7 @@ pub fn dirOpenDirWindows( |
| 3935 | }, | 4019 | }, |
| 3936 | &.{ | 4020 | &.{ |
| 3937 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | 4021 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
| 3938 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 4022 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 3939 | .Attributes = .{}, | 4023 | .Attributes = .{}, |
| 3940 | .ObjectName = &nt_name, | 4024 | .ObjectName = &nt_name, |
| 3941 | .SecurityDescriptor = null, | 4025 | .SecurityDescriptor = null, |
| ... | @@ -5083,7 +5167,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov | ... | @@ -5083,7 +5167,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 5083 | } }, | 5167 | } }, |
| 5084 | &.{ | 5168 | &.{ |
| 5085 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | 5169 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
| 5086 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 5170 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 5087 | .Attributes = .{}, | 5171 | .Attributes = .{}, |
| 5088 | .ObjectName = &nt_name, | 5172 | .ObjectName = &nt_name, |
| 5089 | .SecurityDescriptor = null, | 5173 | .SecurityDescriptor = null, |
| ... | @@ -5360,7 +5444,7 @@ fn dirRenameWindows( | ... | @@ -5360,7 +5444,7 @@ fn dirRenameWindows( |
| 5360 | .POSIX_SEMANTICS = true, | 5444 | .POSIX_SEMANTICS = true, |
| 5361 | .IGNORE_READONLY_ATTRIBUTE = true, | 5445 | .IGNORE_READONLY_ATTRIBUTE = true, |
| 5362 | }, | 5446 | }, |
| 5363 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle, | 5447 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle, |
| 5364 | .FileName = new_path_w, | 5448 | .FileName = new_path_w, |
| 5365 | }); | 5449 | }); |
| 5366 | var io_status_block: w.IO_STATUS_BLOCK = undefined; | 5450 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| ... | @@ -5389,7 +5473,7 @@ fn dirRenameWindows( | ... | @@ -5389,7 +5473,7 @@ fn dirRenameWindows( |
| 5389 | if (need_fallback) { | 5473 | if (need_fallback) { |
| 5390 | const rename_info: w.FILE.RENAME_INFORMATION = .init(.{ | 5474 | const rename_info: w.FILE.RENAME_INFORMATION = .init(.{ |
| 5391 | .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists }, | 5475 | .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists }, |
| 5392 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle, | 5476 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle, |
| 5393 | .FileName = new_path_w, | 5477 | .FileName = new_path_w, |
| 5394 | }); | 5478 | }); |
| 5395 | var io_status_block: w.IO_STATUS_BLOCK = undefined; | 5479 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| ... | @@ -5614,7 +5698,7 @@ fn dirSymLinkWindows( | ... | @@ -5614,7 +5698,7 @@ fn dirSymLinkWindows( |
| 5614 | // Already an NT path, no need to do anything to it | 5698 | // Already an NT path, no need to do anything to it |
| 5615 | break :target_path target_path_w.span(); | 5699 | break :target_path target_path_w.span(); |
| 5616 | } else { | 5700 | } else { |
| 5617 | switch (w.getWin32PathType(u16, target_path_w.span())) { | 5701 | switch (Dir.path.getWin32PathType(u16, target_path_w.span())) { |
| 5618 | // Rooted paths need to avoid getting put through wToPrefixedFileW | 5702 | // Rooted paths need to avoid getting put through wToPrefixedFileW |
| 5619 | // (and they are treated as relative in this context) | 5703 | // (and they are treated as relative in this context) |
| 5620 | // Note: It seems that rooted paths in symbolic links are relative to | 5704 | // Note: It seems that rooted paths in symbolic links are relative to |
| ... | @@ -5624,13 +5708,13 @@ fn dirSymLinkWindows( | ... | @@ -5624,13 +5708,13 @@ fn dirSymLinkWindows( |
| 5624 | // the C:\ drive. | 5708 | // the C:\ drive. |
| 5625 | .rooted => break :target_path target_path_w.span(), | 5709 | .rooted => break :target_path target_path_w.span(), |
| 5626 | // Keep relative paths relative, but anything else needs to get NT-prefixed. | 5710 | // Keep relative paths relative, but anything else needs to get NT-prefixed. |
| 5627 | else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path_w.span())) | 5711 | else => if (!Dir.path.isAbsoluteWindowsWtf16(target_path_w.span())) |
| 5628 | break :target_path target_path_w.span(), | 5712 | break :target_path target_path_w.span(), |
| 5629 | } | 5713 | } |
| 5630 | } | 5714 | } |
| 5631 | var prefixed_target_path = try w.wToPrefixedFileW(dir.handle, target_path_w.span()); | 5715 | var prefixed_target_path = try w.wToPrefixedFileW(dir.handle, target_path_w.span()); |
| 5632 | // We do this after prefixing to ensure that drive-relative paths are treated as absolute | 5716 | // We do this after prefixing to ensure that drive-relative paths are treated as absolute |
| 5633 | is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span()); | 5717 | is_target_absolute = Dir.path.isAbsoluteWindowsWtf16(prefixed_target_path.span()); |
| 5634 | break :target_path prefixed_target_path.span(); | 5718 | break :target_path prefixed_target_path.span(); |
| 5635 | }; | 5719 | }; |
| 5636 | 5720 | ||
| ... | @@ -5638,8 +5722,8 @@ fn dirSymLinkWindows( | ... | @@ -5638,8 +5722,8 @@ fn dirSymLinkWindows( |
| 5638 | var buffer: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; | 5722 | var buffer: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; |
| 5639 | const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4; | 5723 | const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4; |
| 5640 | const header_len = @sizeOf(w.ULONG) + @sizeOf(w.USHORT) * 2; | 5724 | const header_len = @sizeOf(w.ULONG) + @sizeOf(w.USHORT) * 2; |
| 5641 | const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path); | 5725 | const target_is_absolute = Dir.path.isAbsoluteWindowsWtf16(final_target_path); |
| 5642 | const symlink_data = SYMLINK_DATA{ | 5726 | const symlink_data: SYMLINK_DATA = .{ |
| 5643 | .ReparseTag = .SYMLINK, | 5727 | .ReparseTag = .SYMLINK, |
| 5644 | .ReparseDataLength = @intCast(buf_len - header_len), | 5728 | .ReparseDataLength = @intCast(buf_len - header_len), |
| 5645 | .Reserved = 0, | 5729 | .Reserved = 0, |
| ... | @@ -7892,7 +7976,7 @@ fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void { | ... | @@ -7892,7 +7976,7 @@ fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void { |
| 7892 | } | 7976 | } |
| 7893 | } | 7977 | } |
| 7894 | 7978 | ||
| 7895 | fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.process.OpenExecutableError!File { | 7979 | fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.OpenExecutableError!File { |
| 7896 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 7980 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 7897 | switch (native_os) { | 7981 | switch (native_os) { |
| 7898 | .wasi => return error.OperationUnsupported, | 7982 | .wasi => return error.OperationUnsupported, |
| ... | @@ -7933,7 +8017,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.proce | ... | @@ -7933,7 +8017,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.proce |
| 7933 | } | 8017 | } |
| 7934 | } | 8018 | } |
| 7935 | 8019 | ||
| 7936 | fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.ExecutablePathError!usize { | 8020 | fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.ExecutablePathError!usize { |
| 7937 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 8021 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 7938 | 8022 | ||
| 7939 | switch (native_os) { | 8023 | switch (native_os) { |
| ... | @@ -11693,14 +11777,14 @@ fn netLookupFallible( | ... | @@ -11693,14 +11777,14 @@ fn netLookupFallible( |
| 11693 | fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { | 11777 | fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { |
| 11694 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 11778 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 11695 | // Only global mutex since this is Threaded. | 11779 | // Only global mutex since this is Threaded. |
| 11696 | std.process.stderr_thread_mutex.lock(); | 11780 | process.stderr_thread_mutex.lock(); |
| 11697 | return initLockedStderr(t, terminal_mode); | 11781 | return initLockedStderr(t, terminal_mode); |
| 11698 | } | 11782 | } |
| 11699 | 11783 | ||
| 11700 | fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr { | 11784 | fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr { |
| 11701 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 11785 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 11702 | // Only global mutex since this is Threaded. | 11786 | // Only global mutex since this is Threaded. |
| 11703 | if (!std.process.stderr_thread_mutex.tryLock()) return null; | 11787 | if (!process.stderr_thread_mutex.tryLock()) return null; |
| 11704 | return try initLockedStderr(t, terminal_mode); | 11788 | return try initLockedStderr(t, terminal_mode); |
| 11705 | } | 11789 | } |
| 11706 | 11790 | ||
| ... | @@ -11731,10 +11815,10 @@ fn unlockStderr(userdata: ?*anyopaque) void { | ... | @@ -11731,10 +11815,10 @@ fn unlockStderr(userdata: ?*anyopaque) void { |
| 11731 | }; | 11815 | }; |
| 11732 | t.stderr_writer.interface.end = 0; | 11816 | t.stderr_writer.interface.end = 0; |
| 11733 | t.stderr_writer.interface.buffer = &.{}; | 11817 | t.stderr_writer.interface.buffer = &.{}; |
| 11734 | std.process.stderr_thread_mutex.unlock(); | 11818 | process.stderr_thread_mutex.unlock(); |
| 11735 | } | 11819 | } |
| 11736 | 11820 | ||
| 11737 | fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void { | 11821 | fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void { |
| 11738 | if (native_os == .wasi) return error.OperationUnsupported; | 11822 | if (native_os == .wasi) return error.OperationUnsupported; |
| 11739 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 11823 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 11740 | _ = t; | 11824 | _ = t; |
| ... | @@ -11769,38 +11853,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentD | ... | @@ -11769,38 +11853,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentD |
| 11769 | }; | 11853 | }; |
| 11770 | } | 11854 | } |
| 11771 | 11855 | ||
| 11772 | if (dir.handle == posix.AT.FDCWD) return; | 11856 | return fchdir(dir.handle); |
| 11773 | |||
| 11774 | const syscall: Syscall = try .start(); | ||
| 11775 | while (true) { | ||
| 11776 | switch (posix.errno(posix.system.fchdir(dir.handle))) { | ||
| 11777 | .SUCCESS => return syscall.finish(), | ||
| 11778 | .INTR => { | ||
| 11779 | try syscall.checkCancel(); | ||
| 11780 | continue; | ||
| 11781 | }, | ||
| 11782 | .ACCES => { | ||
| 11783 | syscall.finish(); | ||
| 11784 | return error.AccessDenied; | ||
| 11785 | }, | ||
| 11786 | .BADF => |err| { | ||
| 11787 | syscall.finish(); | ||
| 11788 | return errnoBug(err); | ||
| 11789 | }, | ||
| 11790 | .NOTDIR => { | ||
| 11791 | syscall.finish(); | ||
| 11792 | return error.NotDir; | ||
| 11793 | }, | ||
| 11794 | .IO => { | ||
| 11795 | syscall.finish(); | ||
| 11796 | return error.FileSystem; | ||
| 11797 | }, | ||
| 11798 | else => |err| { | ||
| 11799 | syscall.finish(); | ||
| 11800 | return posix.unexpectedErrno(err); | ||
| 11801 | }, | ||
| 11802 | } | ||
| 11803 | } | ||
| 11804 | } | 11857 | } |
| 11805 | 11858 | ||
| 11806 | pub const PosixAddress = extern union { | 11859 | pub const PosixAddress = extern union { |
| ... | @@ -12577,6 +12630,45 @@ fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void { | ... | @@ -12577,6 +12630,45 @@ fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void { |
| 12577 | 12630 | ||
| 12578 | fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {} | 12631 | fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {} |
| 12579 | 12632 | ||
| 12633 | const WindowsEnvironStrings = struct { | ||
| 12634 | PATH: ?[:0]const u16 = null, | ||
| 12635 | PATHEXT: ?[:0]const u16 = null, | ||
| 12636 | |||
| 12637 | fn scan() WindowsEnvironStrings { | ||
| 12638 | const ptr = windows.peb().ProcessParameters.Environment; | ||
| 12639 | |||
| 12640 | var result: WindowsEnvironStrings = .{}; | ||
| 12641 | var i: usize = 0; | ||
| 12642 | while (ptr[i] != 0) { | ||
| 12643 | const key_start = i; | ||
| 12644 | |||
| 12645 | // There are some special environment variables that start with =, | ||
| 12646 | // so we need a special case to not treat = as a key/value separator | ||
| 12647 | // if it's the first character. | ||
| 12648 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 12649 | if (ptr[key_start] == '=') i += 1; | ||
| 12650 | |||
| 12651 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | ||
| 12652 | const key_w = ptr[key_start..i]; | ||
| 12653 | |||
| 12654 | if (ptr[i] == '=') i += 1; | ||
| 12655 | |||
| 12656 | const value_start = i; | ||
| 12657 | while (ptr[i] != 0) : (i += 1) {} | ||
| 12658 | const value_w = ptr[value_start..i :0]; | ||
| 12659 | |||
| 12660 | i += 1; // skip over null byte | ||
| 12661 | |||
| 12662 | inline for (@typeInfo(WindowsEnvironStrings).@"struct".fields) |field| { | ||
| 12663 | const field_name_w = comptime std.unicode.wtf8ToWtf16LeStringLiteral(field.name); | ||
| 12664 | if (std.mem.eql(u16, key_w, field_name_w)) @field(result, field.name) = value_w; | ||
| 12665 | } | ||
| 12666 | } | ||
| 12667 | |||
| 12668 | return result; | ||
| 12669 | } | ||
| 12670 | }; | ||
| 12671 | |||
| 12580 | fn scanEnviron(t: *Threaded) void { | 12672 | fn scanEnviron(t: *Threaded) void { |
| 12581 | t.mutex.lock(); | 12673 | t.mutex.lock(); |
| 12582 | defer t.mutex.unlock(); | 12674 | defer t.mutex.unlock(); |
| ... | @@ -12585,6 +12677,9 @@ fn scanEnviron(t: *Threaded) void { | ... | @@ -12585,6 +12677,9 @@ fn scanEnviron(t: *Threaded) void { |
| 12585 | t.environ.initialized = true; | 12677 | t.environ.initialized = true; |
| 12586 | 12678 | ||
| 12587 | if (is_windows) { | 12679 | if (is_windows) { |
| 12680 | // This value expires with any call that modifies the environment, | ||
| 12681 | // which is outside of this Io implementation's control, so references | ||
| 12682 | // must be short-lived. | ||
| 12588 | const ptr = windows.peb().ProcessParameters.Environment; | 12683 | const ptr = windows.peb().ProcessParameters.Environment; |
| 12589 | 12684 | ||
| 12590 | var i: usize = 0; | 12685 | var i: usize = 0; |
| ... | @@ -12652,9 +12747,9 @@ fn scanEnviron(t: *Threaded) void { | ... | @@ -12652,9 +12747,9 @@ fn scanEnviron(t: *Threaded) void { |
| 12652 | } | 12747 | } |
| 12653 | comptime assert(@sizeOf(Environ.String) == 0); | 12748 | comptime assert(@sizeOf(Environ.String) == 0); |
| 12654 | } | 12749 | } |
| 12655 | } else if (builtin.link_libc) { | 12750 | } else { |
| 12656 | var ptr = std.c.environ; | 12751 | for (t.environ.process_environ.block) |opt_line| { |
| 12657 | while (ptr[0]) |line| : (ptr += 1) { | 12752 | const line = opt_line.?; |
| 12658 | var line_i: usize = 0; | 12753 | var line_i: usize = 0; |
| 12659 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | 12754 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} |
| 12660 | const key = line[0..line_i]; | 12755 | const key = line[0..line_i]; |
| ... | @@ -12667,190 +12762,2150 @@ fn scanEnviron(t: *Threaded) void { | ... | @@ -12667,190 +12762,2150 @@ fn scanEnviron(t: *Threaded) void { |
| 12667 | t.environ.exist.NO_COLOR = true; | 12762 | t.environ.exist.NO_COLOR = true; |
| 12668 | } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) { | 12763 | } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) { |
| 12669 | t.environ.exist.CLICOLOR_FORCE = true; | 12764 | t.environ.exist.CLICOLOR_FORCE = true; |
| 12670 | } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) { | 12765 | } else if (std.mem.eql(u8, key, "ZIG_PROGRESS")) { |
| 12671 | t.environ.string.PATH = value; | 12766 | t.environ.zig_progress_handle = std.fmt.parseInt(u31, value, 10) catch error.UnrecognizedFormat; |
| 12767 | } else inline for (@typeInfo(Environ.String).@"struct".fields) |field| { | ||
| 12768 | if (std.mem.eql(u8, key, field.name)) @field(t.environ.string, field.name) = value; | ||
| 12672 | } | 12769 | } |
| 12673 | } | 12770 | } |
| 12674 | } else { | 12771 | } |
| 12675 | for (t.environ.block) |line| { | 12772 | } |
| 12676 | var line_i: usize = 0; | ||
| 12677 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | ||
| 12678 | const key = line[0..line_i]; | ||
| 12679 | 12773 | ||
| 12680 | var end_i: usize = line_i; | 12774 | fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) process.ReplaceError { |
| 12681 | while (line[end_i] != 0) : (end_i += 1) {} | 12775 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 12682 | const value = line[line_i + 1 .. end_i :0]; | ||
| 12683 | 12776 | ||
| 12684 | if (std.mem.eql(u8, key, "NO_COLOR")) { | 12777 | if (!process.can_replace) return error.OperationUnsupported; |
| 12685 | t.environ.exist.NO_COLOR = true; | 12778 | |
| 12686 | } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) { | 12779 | t.scanEnviron(); // for PATH |
| 12687 | t.environ.exist.CLICOLOR_FORCE = true; | 12780 | const PATH = t.environ.string.PATH orelse default_PATH; |
| 12688 | } else if (@hasField(Environ.String, "PATH") and std.mem.eql(u8, key, "PATH")) { | 12781 | |
| 12689 | t.environ.string.PATH = value; | 12782 | var arena_allocator = std.heap.ArenaAllocator.init(t.allocator); |
| 12690 | } | 12783 | defer arena_allocator.deinit(); |
| 12784 | const arena = arena_allocator.allocator(); | ||
| 12785 | |||
| 12786 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); | ||
| 12787 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | ||
| 12788 | |||
| 12789 | const envp: [*:null]const ?[*:0]const u8 = m: { | ||
| 12790 | const prog_fd: i32 = -1; | ||
| 12791 | if (options.environ_map) |environ_map| { | ||
| 12792 | break :m (try environ_map.createBlockPosix(arena, .{ | ||
| 12793 | .zig_progress_fd = prog_fd, | ||
| 12794 | })).ptr; | ||
| 12691 | } | 12795 | } |
| 12692 | } | 12796 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ |
| 12797 | .zig_progress_fd = prog_fd, | ||
| 12798 | })).ptr; | ||
| 12799 | }; | ||
| 12800 | |||
| 12801 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | ||
| 12693 | } | 12802 | } |
| 12694 | 12803 | ||
| 12695 | test { | 12804 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { |
| 12696 | _ = @import("Threaded/test.zig"); | 12805 | if (!process.can_replace) return error.OperationUnsupported; |
| 12806 | _ = userdata; | ||
| 12807 | _ = dir; | ||
| 12808 | _ = options; | ||
| 12809 | @panic("TODO processReplacePath"); | ||
| 12697 | } | 12810 | } |
| 12698 | 12811 | ||
| 12699 | const use_parking_futex = switch (builtin.target.os.tag) { | 12812 | fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 12700 | .windows => true, // RtlWaitOnAddress is a userland implementation anyway | 12813 | if (!process.can_spawn) return error.OperationUnsupported; |
| 12701 | .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now. | 12814 | _ = userdata; |
| 12702 | .illumos => true, // Illumos has no futex mechanism | 12815 | _ = dir; |
| 12703 | else => false, | 12816 | _ = options; |
| 12817 | @panic("TODO processSpawnPath"); | ||
| 12818 | } | ||
| 12819 | |||
| 12820 | const processSpawn = switch (native_os) { | ||
| 12821 | .wasi, .ios, .tvos, .visionos, .watchos => processSpawnUnsupported, | ||
| 12822 | .windows => processSpawnWindows, | ||
| 12823 | else => processSpawnPosix, | ||
| 12704 | }; | 12824 | }; |
| 12705 | const use_parking_sleep = switch (builtin.target.os.tag) { | ||
| 12706 | // On Windows, we can implement sleep either with `NtDelayExecution` (which is how `SleepEx` in | ||
| 12707 | // kernel32 works) or `NtWaitForAlertByThreadId` (thread parking). We're already using the | ||
| 12708 | // latter for futex, so we may as well use it for sleeping too, to maximise code reuse. I'm | ||
| 12709 | // also more confident that it will always correctly handle the cancelation race (so "unpark" | ||
| 12710 | // before "park" causes "park" to return immediately): it *seems* like alertable sleeps paired | ||
| 12711 | // with `NtAlertThread` do actually do this too, but there could be some caveat (e.g. it might | ||
| 12712 | // fail under some specific condition), whereas `NtWaitForAlertByThreadId` must reliably trigger | ||
| 12713 | // this behavior because `RtlWaitOnAddress` relies on it. | ||
| 12714 | .windows => true, | ||
| 12715 | 12825 | ||
| 12716 | // These targets have `_lwp_park`, which is superior to POSIX nanosleep because it has a better | 12826 | fn processSpawnUnsupported(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 12717 | // cancelation mechanism. | 12827 | _ = userdata; |
| 12718 | .netbsd, | 12828 | _ = options; |
| 12719 | .illumos, | 12829 | return error.OperationUnsupported; |
| 12720 | => true, | 12830 | } |
| 12721 | 12831 | ||
| 12722 | else => false, | 12832 | const Spawned = struct { |
| 12833 | pid: posix.pid_t, | ||
| 12834 | err_fd: posix.fd_t, | ||
| 12835 | stdin: ?File, | ||
| 12836 | stdout: ?File, | ||
| 12837 | stderr: ?File, | ||
| 12723 | }; | 12838 | }; |
| 12724 | 12839 | ||
| 12725 | const parking_futex = struct { | 12840 | fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Spawned { |
| 12726 | comptime { | 12841 | // The child process does need to access (one end of) these pipes. However, |
| 12727 | assert(use_parking_futex); | 12842 | // we must initially set CLOEXEC to avoid a race condition. If another thread |
| 12728 | } | 12843 | // is racing to spawn a different child process, we don't want it to inherit |
| 12844 | // these FDs in any scenario; that would mean that, for instance, calls to | ||
| 12845 | // `poll` from the parent would not report the child's stdout as closing when | ||
| 12846 | // expected, since the other child may retain a reference to the write end of | ||
| 12847 | // the pipe. So, we create the pipes with CLOEXEC initially. After fork, we | ||
| 12848 | // need to do something in the new child to make sure we preserve the reference | ||
| 12849 | // we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it | ||
| 12850 | // turns out, we `dup2` everything anyway, so there's no need! | ||
| 12851 | const pipe_flags: posix.O = .{ .CLOEXEC = true }; | ||
| 12729 | 12852 | ||
| 12730 | const Bucket = struct { | 12853 | const stdin_pipe = if (options.stdin == .pipe) try pipe2(pipe_flags) else undefined; |
| 12731 | /// Used as a fast check for `wake` to avoid having to acquire `mutex` to discover there are no | 12854 | errdefer if (options.stdin == .pipe) { |
| 12732 | /// waiters. It is important for `wait` to increment this *before* checking the futex value to | 12855 | destroyPipe(stdin_pipe); |
| 12733 | /// avoid a race. | 12856 | }; |
| 12734 | num_waiters: std.atomic.Value(u32), | ||
| 12735 | /// Protects `waiters`. | ||
| 12736 | mutex: std.Thread.Mutex, | ||
| 12737 | waiters: std.DoublyLinkedList, | ||
| 12738 | 12857 | ||
| 12739 | /// Prevent false sharing between buckets. | 12858 | const stdout_pipe = if (options.stdout == .pipe) try pipe2(pipe_flags) else undefined; |
| 12740 | _: void align(std.atomic.cache_line) = {}, | 12859 | errdefer if (options.stdout == .pipe) { |
| 12860 | destroyPipe(stdout_pipe); | ||
| 12861 | }; | ||
| 12741 | 12862 | ||
| 12742 | const init: Bucket = .{ .num_waiters = .init(0), .mutex = .{}, .waiters = .{} }; | 12863 | const stderr_pipe = if (options.stderr == .pipe) try pipe2(pipe_flags) else undefined; |
| 12864 | errdefer if (options.stderr == .pipe) { | ||
| 12865 | destroyPipe(stderr_pipe); | ||
| 12743 | }; | 12866 | }; |
| 12744 | 12867 | ||
| 12745 | const Waiter = struct { | 12868 | const any_ignore = (options.stdin == .ignore or options.stdout == .ignore or options.stderr == .ignore); |
| 12746 | node: std.DoublyLinkedList.Node, | 12869 | const dev_null_fd = if (any_ignore) try getDevNullFd(t) else undefined; |
| 12747 | address: usize, | 12870 | |
| 12748 | tid: std.Thread.Id, | 12871 | const prog_pipe: [2]posix.fd_t = p: { |
| 12749 | /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread | 12872 | if (options.progress_node.index == .none) { |
| 12750 | /// which atomically updates it (to `.none` or `.canceling`) is responsible for: | 12873 | break :p .{ -1, -1 }; |
| 12751 | /// | 12874 | } else { |
| 12752 | /// * Removing the `Waiter` from `Bucket.waiters` | 12875 | // We use CLOEXEC for the same reason as in `pipe_flags`. |
| 12753 | /// * Decrementing `Bucket.num_waiters` | 12876 | break :p try pipe2(.{ .NONBLOCK = true, .CLOEXEC = true }); |
| 12754 | /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope | 12877 | } |
| 12755 | /// while it is still in the `Bucket`). | ||
| 12756 | thread_status: *std.atomic.Value(Thread.Status), | ||
| 12757 | }; | 12878 | }; |
| 12879 | errdefer destroyPipe(prog_pipe); | ||
| 12758 | 12880 | ||
| 12759 | fn bucketForAddress(address: usize) *Bucket { | 12881 | var arena_allocator = std.heap.ArenaAllocator.init(t.allocator); |
| 12760 | const global = struct { | 12882 | defer arena_allocator.deinit(); |
| 12761 | /// Length must be a power of two. The longer this array, the less likely contention is | 12883 | const arena = arena_allocator.allocator(); |
| 12762 | /// between different futexes. This length seems like it'll provide a reasonable balance | ||
| 12763 | /// between contention and memory usage: assuming a 128-byte `Bucket` (due to cache line | ||
| 12764 | /// alignment), this uses 32 KiB of memory. | ||
| 12765 | var buckets: [256]Bucket = @splat(.init); | ||
| 12766 | }; | ||
| 12767 | 12884 | ||
| 12768 | // Here we use Fibonacci hashing: the golden ratio can be used to evenly redistribute input | 12885 | // The POSIX standard does not allow malloc() between fork() and execve(), |
| 12769 | // values across a range, giving a poor, but extremely quick to compute, hash. | 12886 | // and this allocator may be a libc allocator. |
| 12887 | // I have personally observed the child process deadlocking when it tries | ||
| 12888 | // to call malloc() due to a heap allocation between fork() and execve(), | ||
| 12889 | // in musl v1.1.24. | ||
| 12890 | // Additionally, we want to reduce the number of possible ways things | ||
| 12891 | // can fail between fork() and execve(). | ||
| 12892 | // Therefore, we do all the allocation for the execve() before the fork(). | ||
| 12893 | // This means we must do the null-termination of argv and env vars here. | ||
| 12894 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); | ||
| 12895 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | ||
| 12770 | 12896 | ||
| 12771 | // This literal is the rounded value of '2^64 / phi' (where 'phi' is the golden ratio). The | 12897 | const prog_fileno = 3; |
| 12772 | // shift then converts it to '2^b / phi', where 'b' is the pointer bit width. | 12898 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); |
| 12773 | const fibonacci_multiplier = 0x9E3779B97F4A7C15 >> (64 - @bitSizeOf(usize)); | ||
| 12774 | const hashed = address *% fibonacci_multiplier; | ||
| 12775 | 12899 | ||
| 12776 | comptime assert(std.math.isPowerOfTwo(global.buckets.len)); | 12900 | const envp: [*:null]const ?[*:0]const u8 = m: { |
| 12777 | // The high bits of `hashed` have better entropy than the low bits. | 12901 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; |
| 12778 | const index = hashed >> (@bitSizeOf(usize) - @ctz(global.buckets.len)); | 12902 | if (options.environ_map) |environ_map| { |
| 12903 | break :m (try environ_map.createBlockPosix(arena, .{ | ||
| 12904 | .zig_progress_fd = prog_fd, | ||
| 12905 | })).ptr; | ||
| 12906 | } | ||
| 12907 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ | ||
| 12908 | .zig_progress_fd = prog_fd, | ||
| 12909 | })).ptr; | ||
| 12910 | }; | ||
| 12779 | 12911 | ||
| 12780 | return &global.buckets[index]; | 12912 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. |
| 12781 | } | 12913 | // It is closed by the child (via CLOEXEC) without writing if `execvpe` succeeds. |
| 12914 | const err_pipe: [2]posix.fd_t = try pipe2(.{ .CLOEXEC = true }); | ||
| 12915 | errdefer destroyPipe(err_pipe); | ||
| 12782 | 12916 | ||
| 12783 | fn wait(ptr: *const u32, expect: u32, uncancelable: bool, timeout: Io.Timeout) Io.Cancelable!void { | 12917 | t.scanEnviron(); // for PATH |
| 12784 | const bucket = bucketForAddress(@intFromPtr(ptr)); | 12918 | const PATH = t.environ.string.PATH orelse default_PATH; |
| 12785 | 12919 | ||
| 12786 | // Put the threadlocal access outside of the critical section. | 12920 | const pid_result: posix.pid_t = fork: { |
| 12787 | const opt_thread = Thread.current; | 12921 | const rc = posix.system.fork(); |
| 12788 | const self_tid = if (opt_thread) |thread| thread.id else std.Thread.getCurrentId(); | 12922 | switch (posix.errno(rc)) { |
| 12923 | .SUCCESS => break :fork @intCast(rc), | ||
| 12924 | .AGAIN => return error.SystemResources, | ||
| 12925 | .NOMEM => return error.SystemResources, | ||
| 12926 | .NOSYS => return error.OperationUnsupported, | ||
| 12927 | else => |err| return posix.unexpectedErrno(err), | ||
| 12928 | } | ||
| 12929 | }; | ||
| 12789 | 12930 | ||
| 12790 | var waiter: Waiter = .{ | 12931 | if (pid_result == 0) { |
| 12791 | .node = undefined, // populated by list append | 12932 | defer comptime unreachable; // We are the child. |
| 12792 | .address = @intFromPtr(ptr), | 12933 | if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked; |
| 12793 | .tid = self_tid, | 12934 | const ep1 = err_pipe[1]; |
| 12794 | .thread_status = undefined, // populated in critical section | ||
| 12795 | }; | ||
| 12796 | 12935 | ||
| 12797 | var status_buf: std.atomic.Value(Thread.Status) = undefined; | 12936 | setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); |
| 12937 | setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); | ||
| 12938 | setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err); | ||
| 12798 | 12939 | ||
| 12799 | { | 12940 | if (options.cwd_dir) |cwd| { |
| 12800 | bucket.mutex.lock(); | 12941 | fchdir(cwd.handle) catch |err| forkBail(ep1, err); |
| 12801 | defer bucket.mutex.unlock(); | 12942 | } else if (options.cwd) |cwd| { |
| 12943 | chdir(cwd) catch |err| forkBail(ep1, err); | ||
| 12944 | } | ||
| 12802 | 12945 | ||
| 12803 | _ = bucket.num_waiters.fetchAdd(1, .acquire); | 12946 | // Must happen after fchdir above, the cwd file descriptor might be |
| 12947 | // equal to prog_fileno and be clobbered by this dup2 call. | ||
| 12948 | if (prog_pipe[1] != -1) dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(ep1, err); | ||
| 12804 | 12949 | ||
| 12805 | if (@atomicLoad(u32, ptr, .monotonic) != expect) { | 12950 | if (options.gid) |gid| { |
| 12806 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | 12951 | switch (posix.errno(posix.system.setregid(gid, gid))) { |
| 12807 | return; | 12952 | .SUCCESS => {}, |
| 12953 | .AGAIN => forkBail(ep1, error.ResourceLimitReached), | ||
| 12954 | .INVAL => forkBail(ep1, error.InvalidUserId), | ||
| 12955 | .PERM => forkBail(ep1, error.PermissionDenied), | ||
| 12956 | else => forkBail(ep1, error.Unexpected), | ||
| 12808 | } | 12957 | } |
| 12958 | } | ||
| 12809 | 12959 | ||
| 12810 | // This is in the critical section to avoid marking the thread as parked until we're | 12960 | if (options.uid) |uid| { |
| 12811 | // certain that we're actually going to park. | 12961 | switch (posix.errno(posix.system.setreuid(uid, uid))) { |
| 12812 | waiter.thread_status = status: { | 12962 | .SUCCESS => {}, |
| 12813 | cancelable: { | 12963 | .AGAIN => forkBail(ep1, error.ResourceLimitReached), |
| 12814 | if (uncancelable) break :cancelable; | 12964 | .INVAL => forkBail(ep1, error.InvalidUserId), |
| 12815 | const thread = opt_thread orelse break :cancelable; | 12965 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 12816 | switch (thread.cancel_protection) { | 12966 | else => forkBail(ep1, error.Unexpected), |
| 12817 | .blocked => break :cancelable, | 12967 | } |
| 12818 | .unblocked => {}, | 12968 | } |
| 12819 | } | ||
| 12820 | thread.futex_waiter = &waiter; | ||
| 12821 | const old_status = thread.status.fetchOr( | ||
| 12822 | .{ .cancelation = @enumFromInt(0b001), .awaitable = .null }, | ||
| 12823 | .release, // release `thread.futex_waiter` | ||
| 12824 | ); | ||
| 12825 | switch (old_status.cancelation) { | ||
| 12826 | .none => {}, // status is now `.parked` | ||
| 12827 | .canceling => { | ||
| 12828 | // status is now `.canceled` | ||
| 12829 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | ||
| 12830 | return error.Canceled; | ||
| 12831 | }, | ||
| 12832 | .canceled => break :cancelable, // status is still `.canceled` | ||
| 12833 | .parked => unreachable, | ||
| 12834 | .blocked => unreachable, | ||
| 12835 | .blocked_windows_dns => unreachable, | ||
| 12836 | .blocked_canceling => unreachable, | ||
| 12837 | } | ||
| 12838 | // We could now be unparked for a cancelation at any time! | ||
| 12839 | break :status &thread.status; | ||
| 12840 | } | ||
| 12841 | // This is an uncancelable wait, so just use `status_buf`. Note that the value of | ||
| 12842 | // `status_buf.awaitable` is irrelevant because this is only visible to futex code, | ||
| 12843 | // while only cancelation cares about `awaitable`. | ||
| 12844 | status_buf.raw = .{ .cancelation = .parked, .awaitable = .null }; | ||
| 12845 | break :status &status_buf; | ||
| 12846 | }; | ||
| 12847 | 12969 | ||
| 12848 | bucket.waiters.append(&waiter.node); | 12970 | if (options.pgid) |pid| { |
| 12971 | switch (posix.errno(posix.system.setpgid(0, pid))) { | ||
| 12972 | .SUCCESS => {}, | ||
| 12973 | .ACCES => forkBail(ep1, error.ProcessAlreadyExec), | ||
| 12974 | .INVAL => forkBail(ep1, error.InvalidProcessGroupId), | ||
| 12975 | .PERM => forkBail(ep1, error.PermissionDenied), | ||
| 12976 | else => forkBail(ep1, error.Unexpected), | ||
| 12977 | } | ||
| 12849 | } | 12978 | } |
| 12850 | 12979 | ||
| 12851 | if (park(timeout, ptr)) { | 12980 | if (options.start_suspended) { |
| 12852 | // We were unparked by either `wake` or cancelation, so our current status is either | 12981 | switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) { |
| 12853 | // `.none` or `.canceling`. In either case, they've already removed `waiter` from | 12982 | .SUCCESS => {}, |
| 12983 | .PERM => forkBail(ep1, error.PermissionDenied), | ||
| 12984 | else => forkBail(ep1, error.Unexpected), | ||
| 12985 | } | ||
| 12986 | } | ||
| 12987 | |||
| 12988 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | ||
| 12989 | forkBail(ep1, err); | ||
| 12990 | } | ||
| 12991 | |||
| 12992 | const pid: posix.pid_t = @intCast(pid_result); // We are the parent. | ||
| 12993 | errdefer comptime unreachable; // The child is forked; we must not error from now on | ||
| 12994 | |||
| 12995 | posix.close(err_pipe[1]); // make sure only the child holds the write end open | ||
| 12996 | |||
| 12997 | if (options.stdin == .pipe) posix.close(stdin_pipe[0]); | ||
| 12998 | if (options.stdout == .pipe) posix.close(stdout_pipe[1]); | ||
| 12999 | if (options.stderr == .pipe) posix.close(stderr_pipe[1]); | ||
| 13000 | |||
| 13001 | if (prog_pipe[1] != -1) posix.close(prog_pipe[1]); | ||
| 13002 | |||
| 13003 | options.progress_node.setIpcFd(prog_pipe[0]); | ||
| 13004 | |||
| 13005 | return .{ | ||
| 13006 | .pid = pid, | ||
| 13007 | .err_fd = err_pipe[0], | ||
| 13008 | .stdin = switch (options.stdin) { | ||
| 13009 | .pipe => .{ .handle = stdin_pipe[1] }, | ||
| 13010 | else => null, | ||
| 13011 | }, | ||
| 13012 | .stdout = switch (options.stdout) { | ||
| 13013 | .pipe => .{ .handle = stdout_pipe[0] }, | ||
| 13014 | else => null, | ||
| 13015 | }, | ||
| 13016 | .stderr = switch (options.stderr) { | ||
| 13017 | .pipe => .{ .handle = stderr_pipe[0] }, | ||
| 13018 | else => null, | ||
| 13019 | }, | ||
| 13020 | }; | ||
| 13021 | } | ||
| 13022 | |||
| 13023 | fn getDevNullFd(t: *Threaded) !posix.fd_t { | ||
| 13024 | { | ||
| 13025 | t.mutex.lock(); | ||
| 13026 | defer t.mutex.unlock(); | ||
| 13027 | if (t.null_file.fd != -1) return t.null_file.fd; | ||
| 13028 | } | ||
| 13029 | const mode: u32 = 0; | ||
| 13030 | const syscall: Syscall = try .start(); | ||
| 13031 | while (true) { | ||
| 13032 | const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, mode); | ||
| 13033 | switch (posix.errno(rc)) { | ||
| 13034 | .SUCCESS => { | ||
| 13035 | syscall.finish(); | ||
| 13036 | const fresh_fd: posix.fd_t = @intCast(rc); | ||
| 13037 | t.mutex.lock(); // Another thread might have won the race. | ||
| 13038 | defer t.mutex.unlock(); | ||
| 13039 | if (t.null_file.fd != -1) { | ||
| 13040 | posix.close(fresh_fd); | ||
| 13041 | return t.null_file.fd; | ||
| 13042 | } else { | ||
| 13043 | t.null_file.fd = fresh_fd; | ||
| 13044 | return fresh_fd; | ||
| 13045 | } | ||
| 13046 | }, | ||
| 13047 | .INTR => { | ||
| 13048 | try syscall.checkCancel(); | ||
| 13049 | continue; | ||
| 13050 | }, | ||
| 13051 | .ACCES => return syscall.fail(error.AccessDenied), | ||
| 13052 | .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded), | ||
| 13053 | .NFILE => return syscall.fail(error.SystemFdQuotaExceeded), | ||
| 13054 | .NODEV => return syscall.fail(error.NoDevice), | ||
| 13055 | .NOENT => return syscall.fail(error.FileNotFound), | ||
| 13056 | .NOMEM => return syscall.fail(error.SystemResources), | ||
| 13057 | .PERM => return syscall.fail(error.PermissionDenied), | ||
| 13058 | else => |err| return syscall.unexpectedErrno(err), | ||
| 13059 | } | ||
| 13060 | } | ||
| 13061 | } | ||
| 13062 | |||
| 13063 | fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { | ||
| 13064 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 13065 | const spawned = try spawnPosix(t, options); | ||
| 13066 | defer posix.close(spawned.err_fd); | ||
| 13067 | |||
| 13068 | // Wait for the child to report any errors in or before `execvpe`. | ||
| 13069 | if (readIntFd(spawned.err_fd)) |child_err_int| { | ||
| 13070 | const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int)); | ||
| 13071 | return child_err; | ||
| 13072 | } else |read_err| switch (read_err) { | ||
| 13073 | error.EndOfStream => { | ||
| 13074 | // Write end closed by CLOEXEC at the time of the `execvpe` call, | ||
| 13075 | // indicating success. | ||
| 13076 | }, | ||
| 13077 | else => { | ||
| 13078 | // Problem reading the error from the error reporting pipe. We | ||
| 13079 | // don't know if the child is alive or dead. Better to assume it is | ||
| 13080 | // alive so the resource does not risk being leaked. | ||
| 13081 | }, | ||
| 13082 | } | ||
| 13083 | |||
| 13084 | return .{ | ||
| 13085 | .id = spawned.pid, | ||
| 13086 | .thread_handle = {}, | ||
| 13087 | .stdin = spawned.stdin, | ||
| 13088 | .stdout = spawned.stdout, | ||
| 13089 | .stderr = spawned.stderr, | ||
| 13090 | .request_resource_usage_statistics = options.request_resource_usage_statistics, | ||
| 13091 | }; | ||
| 13092 | } | ||
| 13093 | |||
| 13094 | fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitError!process.Child.Term { | ||
| 13095 | if (native_os == .wasi) unreachable; | ||
| 13096 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 13097 | _ = t; | ||
| 13098 | switch (native_os) { | ||
| 13099 | .windows => return childWaitWindows(child), | ||
| 13100 | else => return childWaitPosix(child), | ||
| 13101 | } | ||
| 13102 | } | ||
| 13103 | |||
| 13104 | fn childKill(userdata: ?*anyopaque, child: *process.Child) void { | ||
| 13105 | if (native_os == .wasi) unreachable; | ||
| 13106 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 13107 | if (is_windows) { | ||
| 13108 | childKillWindows(t, child, 1) catch childCleanupWindows(child); | ||
| 13109 | } else { | ||
| 13110 | childKillPosix(child) catch {}; | ||
| 13111 | childCleanupPosix(child); | ||
| 13112 | } | ||
| 13113 | } | ||
| 13114 | |||
| 13115 | fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT) !void { | ||
| 13116 | _ = t; // TODO cancelation | ||
| 13117 | const handle = child.id.?; | ||
| 13118 | if (windows.kernel32.TerminateProcess(handle, exit_code) == 0) { | ||
| 13119 | switch (windows.GetLastError()) { | ||
| 13120 | .ACCESS_DENIED => { | ||
| 13121 | // Usually when TerminateProcess triggers a ACCESS_DENIED error, it | ||
| 13122 | // indicates that the process has already exited, but there may be | ||
| 13123 | // some rare edge cases where our process handle no longer has the | ||
| 13124 | // PROCESS_TERMINATE access right, so let's do another check to make | ||
| 13125 | // sure the process is really no longer running: | ||
| 13126 | windows.WaitForSingleObjectEx(handle, 0, false) catch return error.AccessDenied; | ||
| 13127 | return error.AlreadyTerminated; | ||
| 13128 | }, | ||
| 13129 | else => |err| return windows.unexpectedError(err), | ||
| 13130 | } | ||
| 13131 | } | ||
| 13132 | _ = windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE); | ||
| 13133 | childCleanupWindows(child); | ||
| 13134 | } | ||
| 13135 | |||
| 13136 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { | ||
| 13137 | const handle = child.id.?; | ||
| 13138 | |||
| 13139 | const syscall: Syscall = try .start(); | ||
| 13140 | while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) { | ||
| 13141 | windows.WAIT_OBJECT_0 => break syscall.finish(), | ||
| 13142 | windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => { | ||
| 13143 | try syscall.checkCancel(); | ||
| 13144 | continue; | ||
| 13145 | }, | ||
| 13146 | windows.WAIT_FAILED => { | ||
| 13147 | syscall.finish(); | ||
| 13148 | switch (windows.GetLastError()) { | ||
| 13149 | else => |err| return windows.unexpectedError(err), | ||
| 13150 | } | ||
| 13151 | }, | ||
| 13152 | else => return syscall.fail(error.Unexpected), | ||
| 13153 | }; | ||
| 13154 | |||
| 13155 | const term: process.Child.Term = x: { | ||
| 13156 | var exit_code: windows.DWORD = undefined; | ||
| 13157 | if (windows.kernel32.GetExitCodeProcess(handle, &exit_code) == 0) { | ||
| 13158 | break :x .{ .unknown = 0 }; | ||
| 13159 | } else { | ||
| 13160 | break :x .{ .exited = @as(u8, @truncate(exit_code)) }; | ||
| 13161 | } | ||
| 13162 | }; | ||
| 13163 | |||
| 13164 | childCleanupWindows(child); | ||
| 13165 | return term; | ||
| 13166 | } | ||
| 13167 | |||
| 13168 | fn childCleanupWindows(child: *process.Child) void { | ||
| 13169 | const handle = child.id orelse return; | ||
| 13170 | |||
| 13171 | if (child.request_resource_usage_statistics) | ||
| 13172 | child.resource_usage_statistics.rusage = windows.GetProcessMemoryInfo(handle) catch null; | ||
| 13173 | |||
| 13174 | windows.CloseHandle(handle); | ||
| 13175 | child.id = null; | ||
| 13176 | |||
| 13177 | windows.CloseHandle(child.thread_handle); | ||
| 13178 | child.thread_handle = undefined; | ||
| 13179 | |||
| 13180 | if (child.stdin) |*stdin| { | ||
| 13181 | windows.CloseHandle(stdin.handle); | ||
| 13182 | child.stdin = null; | ||
| 13183 | } | ||
| 13184 | if (child.stdout) |*stdout| { | ||
| 13185 | windows.CloseHandle(stdout.handle); | ||
| 13186 | child.stdout = null; | ||
| 13187 | } | ||
| 13188 | if (child.stderr) |*stderr| { | ||
| 13189 | windows.CloseHandle(stderr.handle); | ||
| 13190 | child.stderr = null; | ||
| 13191 | } | ||
| 13192 | } | ||
| 13193 | |||
| 13194 | fn childWaitPosix(child: *process.Child) process.Child.WaitError!process.Child.Term { | ||
| 13195 | defer childCleanupPosix(child); | ||
| 13196 | |||
| 13197 | const pid = child.id.?; | ||
| 13198 | |||
| 13199 | var ru: posix.rusage = undefined; | ||
| 13200 | const ru_ptr = if (child.request_resource_usage_statistics) &ru else null; | ||
| 13201 | |||
| 13202 | if (have_wait4) { | ||
| 13203 | var status: if (builtin.link_libc) c_int else u32 = undefined; | ||
| 13204 | const syscall: Syscall = try .start(); | ||
| 13205 | while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, ru_ptr))) { | ||
| 13206 | .SUCCESS => { | ||
| 13207 | syscall.finish(); | ||
| 13208 | if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*; | ||
| 13209 | return statusToTerm(@bitCast(status)); | ||
| 13210 | }, | ||
| 13211 | .INTR => { | ||
| 13212 | try syscall.checkCancel(); | ||
| 13213 | continue; | ||
| 13214 | }, | ||
| 13215 | .CHILD => |err| return syscall.errnoBug(err), // Double-free. | ||
| 13216 | else => |err| return syscall.unexpectedErrno(err), | ||
| 13217 | }; | ||
| 13218 | } | ||
| 13219 | |||
| 13220 | if (have_waitid) { | ||
| 13221 | const linux = std.os.linux; // Bypass libc which has the wrong signature. | ||
| 13222 | var info: linux.siginfo_t = undefined; | ||
| 13223 | const syscall: Syscall = try .start(); | ||
| 13224 | while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, ru_ptr))) { | ||
| 13225 | .SUCCESS => { | ||
| 13226 | syscall.finish(); | ||
| 13227 | if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*; | ||
| 13228 | const status: u32 = @bitCast(info.fields.common.second.sigchld.status); | ||
| 13229 | const code: linux.CLD = @enumFromInt(info.code); | ||
| 13230 | return switch (code) { | ||
| 13231 | .EXITED => .{ .exited = @truncate(status) }, | ||
| 13232 | .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) }, | ||
| 13233 | .TRAPPED, .STOPPED => .{ .stopped = status }, | ||
| 13234 | _, .CONTINUED => .{ .unknown = status }, | ||
| 13235 | }; | ||
| 13236 | }, | ||
| 13237 | .INTR => { | ||
| 13238 | try syscall.checkCancel(); | ||
| 13239 | continue; | ||
| 13240 | }, | ||
| 13241 | .CHILD => |err| return syscall.errnoBug(err), // Double-free. | ||
| 13242 | else => |err| return syscall.unexpectedErrno(err), | ||
| 13243 | }; | ||
| 13244 | } | ||
| 13245 | |||
| 13246 | var status: if (builtin.link_libc) c_int else u32 = undefined; | ||
| 13247 | const syscall: Syscall = try .start(); | ||
| 13248 | while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) { | ||
| 13249 | .SUCCESS => { | ||
| 13250 | syscall.finish(); | ||
| 13251 | return statusToTerm(@bitCast(status)); | ||
| 13252 | }, | ||
| 13253 | .INTR => { | ||
| 13254 | try syscall.checkCancel(); | ||
| 13255 | continue; | ||
| 13256 | }, | ||
| 13257 | .CHILD => |err| return syscall.errnoBug(err), // Double-free. | ||
| 13258 | else => |err| return syscall.unexpectedErrno(err), | ||
| 13259 | }; | ||
| 13260 | } | ||
| 13261 | |||
| 13262 | fn statusToTerm(status: u32) process.Child.Term { | ||
| 13263 | return if (posix.W.IFEXITED(status)) | ||
| 13264 | .{ .exited = posix.W.EXITSTATUS(status) } | ||
| 13265 | else if (posix.W.IFSIGNALED(status)) | ||
| 13266 | .{ .signal = posix.W.TERMSIG(status) } | ||
| 13267 | else if (posix.W.IFSTOPPED(status)) | ||
| 13268 | .{ .stopped = posix.W.STOPSIG(status) } | ||
| 13269 | else | ||
| 13270 | .{ .unknown = status }; | ||
| 13271 | } | ||
| 13272 | |||
| 13273 | fn childKillPosix(child: *process.Child) !void { | ||
| 13274 | // Entire function body is intentionally uncancelable. | ||
| 13275 | |||
| 13276 | const pid = child.id.?; | ||
| 13277 | |||
| 13278 | while (true) switch (posix.errno(posix.system.kill(pid, .TERM))) { | ||
| 13279 | .SUCCESS => break, | ||
| 13280 | .INTR => continue, | ||
| 13281 | .PERM => return error.PermissionDenied, | ||
| 13282 | .INVAL => |err| return errnoBug(err), | ||
| 13283 | .SRCH => |err| return errnoBug(err), | ||
| 13284 | else => |err| return posix.unexpectedErrno(err), | ||
| 13285 | }; | ||
| 13286 | |||
| 13287 | if (have_wait4) { | ||
| 13288 | var status: if (builtin.link_libc) c_int else u32 = undefined; | ||
| 13289 | while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, null))) { | ||
| 13290 | .SUCCESS => return, | ||
| 13291 | .INTR => continue, | ||
| 13292 | .CHILD => |err| return errnoBug(err), // Double-free. | ||
| 13293 | else => |err| return posix.unexpectedErrno(err), | ||
| 13294 | }; | ||
| 13295 | } | ||
| 13296 | |||
| 13297 | if (have_waitid) { | ||
| 13298 | const linux = std.os.linux; // Bypass libc which has the wrong signature. | ||
| 13299 | var info: linux.siginfo_t = undefined; | ||
| 13300 | while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, null))) { | ||
| 13301 | .SUCCESS => return, | ||
| 13302 | .INTR => continue, | ||
| 13303 | .CHILD => |err| return errnoBug(err), // Double-free. | ||
| 13304 | else => |err| return posix.unexpectedErrno(err), | ||
| 13305 | }; | ||
| 13306 | } | ||
| 13307 | |||
| 13308 | var status: if (builtin.link_libc) c_int else u32 = undefined; | ||
| 13309 | while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) { | ||
| 13310 | .SUCCESS => return, | ||
| 13311 | .INTR => continue, | ||
| 13312 | .CHILD => |err| return errnoBug(err), // Double-free. | ||
| 13313 | else => |err| return posix.unexpectedErrno(err), | ||
| 13314 | }; | ||
| 13315 | } | ||
| 13316 | |||
| 13317 | fn childCleanupPosix(child: *process.Child) void { | ||
| 13318 | if (child.stdin) |*stdin| { | ||
| 13319 | posix.close(stdin.handle); | ||
| 13320 | child.stdin = null; | ||
| 13321 | } | ||
| 13322 | if (child.stdout) |*stdout| { | ||
| 13323 | posix.close(stdout.handle); | ||
| 13324 | child.stdout = null; | ||
| 13325 | } | ||
| 13326 | if (child.stderr) |*stderr| { | ||
| 13327 | posix.close(stderr.handle); | ||
| 13328 | child.stderr = null; | ||
| 13329 | } | ||
| 13330 | child.id = null; | ||
| 13331 | } | ||
| 13332 | |||
| 13333 | /// Errors that can occur between fork() and execv() | ||
| 13334 | const ForkBailError = process.SpawnError || process.ReplaceError; | ||
| 13335 | |||
| 13336 | /// Child of fork calls this to report an error to the fork parent. Then the | ||
| 13337 | /// child exits. | ||
| 13338 | fn forkBail(fd: posix.fd_t, err: ForkBailError) noreturn { | ||
| 13339 | writeIntFd(fd, @as(ErrInt, @intFromError(err))) catch {}; | ||
| 13340 | // If we're linking libc, some naughty applications may have registered atexit handlers | ||
| 13341 | // which we really do not want to run in the fork child. I caught LLVM doing this and | ||
| 13342 | // it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne, | ||
| 13343 | // "Why'd you have to go and make things so complicated?" | ||
| 13344 | if (builtin.link_libc) { | ||
| 13345 | // The `_exit` function does nothing but make the exit syscall, unlike `exit`. | ||
| 13346 | std.c._exit(1); | ||
| 13347 | } else if (native_os == .linux and !builtin.single_threaded) { | ||
| 13348 | std.os.linux.exit_group(1); | ||
| 13349 | } else { | ||
| 13350 | posix.system.exit(1); | ||
| 13351 | } | ||
| 13352 | } | ||
| 13353 | |||
| 13354 | fn writeIntFd(fd: posix.fd_t, value: ErrInt) !void { | ||
| 13355 | var buffer: [8]u8 = undefined; | ||
| 13356 | std.mem.writeInt(u64, &buffer, value, .little); | ||
| 13357 | // Skip the cancel mechanism. | ||
| 13358 | var i: usize = 0; | ||
| 13359 | while (true) { | ||
| 13360 | const rc = posix.system.write(fd, buffer[i..].ptr, buffer.len - i); | ||
| 13361 | switch (posix.errno(rc)) { | ||
| 13362 | .SUCCESS => { | ||
| 13363 | const n: usize = @intCast(rc); | ||
| 13364 | i += n; | ||
| 13365 | if (buffer.len - i == 0) return; | ||
| 13366 | }, | ||
| 13367 | .INTR => continue, | ||
| 13368 | else => return error.SystemResources, | ||
| 13369 | } | ||
| 13370 | } | ||
| 13371 | } | ||
| 13372 | |||
| 13373 | fn readIntFd(fd: posix.fd_t) !ErrInt { | ||
| 13374 | var buffer: [8]u8 = undefined; | ||
| 13375 | var i: usize = 0; | ||
| 13376 | while (true) { | ||
| 13377 | const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i); | ||
| 13378 | switch (posix.errno(rc)) { | ||
| 13379 | .SUCCESS => { | ||
| 13380 | const n: usize = @intCast(rc); | ||
| 13381 | if (n == 0) break; | ||
| 13382 | i += n; | ||
| 13383 | continue; | ||
| 13384 | }, | ||
| 13385 | .INTR => continue, | ||
| 13386 | else => |err| return posix.unexpectedErrno(err), | ||
| 13387 | } | ||
| 13388 | } | ||
| 13389 | if (buffer.len - i != 0) return error.EndOfStream; | ||
| 13390 | return @intCast(std.mem.readInt(u64, &buffer, .little)); | ||
| 13391 | } | ||
| 13392 | |||
| 13393 | const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); | ||
| 13394 | |||
| 13395 | fn destroyPipe(pipe: [2]posix.fd_t) void { | ||
| 13396 | if (pipe[0] != -1) posix.close(pipe[0]); | ||
| 13397 | if (pipe[0] != pipe[1]) posix.close(pipe[1]); | ||
| 13398 | } | ||
| 13399 | |||
| 13400 | fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { | ||
| 13401 | switch (stdio) { | ||
| 13402 | .pipe => try dup2(pipe_fd, std_fileno), | ||
| 13403 | .close => posix.close(std_fileno), | ||
| 13404 | .inherit => {}, | ||
| 13405 | .ignore => try dup2(dev_null_fd, std_fileno), | ||
| 13406 | .file => @panic("TODO implement setUpChildIo when file is used"), | ||
| 13407 | } | ||
| 13408 | } | ||
| 13409 | |||
| 13410 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { | ||
| 13411 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 13412 | |||
| 13413 | var saAttr: windows.SECURITY_ATTRIBUTES = .{ | ||
| 13414 | .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), | ||
| 13415 | .bInheritHandle = windows.TRUE, | ||
| 13416 | .lpSecurityDescriptor = null, | ||
| 13417 | }; | ||
| 13418 | |||
| 13419 | const any_ignore = | ||
| 13420 | options.stdin == .ignore or | ||
| 13421 | options.stdout == .ignore or | ||
| 13422 | options.stderr == .ignore; | ||
| 13423 | |||
| 13424 | const nul_handle = if (any_ignore) try getNulHandle(t) else undefined; | ||
| 13425 | |||
| 13426 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; | ||
| 13427 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; | ||
| 13428 | switch (options.stdin) { | ||
| 13429 | .pipe => { | ||
| 13430 | try windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); | ||
| 13431 | }, | ||
| 13432 | .ignore => { | ||
| 13433 | g_hChildStd_IN_Rd = nul_handle; | ||
| 13434 | }, | ||
| 13435 | .inherit => { | ||
| 13436 | g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null; | ||
| 13437 | }, | ||
| 13438 | .close => { | ||
| 13439 | g_hChildStd_IN_Rd = null; | ||
| 13440 | }, | ||
| 13441 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | ||
| 13442 | } | ||
| 13443 | errdefer if (options.stdin == .pipe) { | ||
| 13444 | windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); | ||
| 13445 | }; | ||
| 13446 | |||
| 13447 | var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; | ||
| 13448 | var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; | ||
| 13449 | switch (options.stdout) { | ||
| 13450 | .pipe => { | ||
| 13451 | try windowsMakeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); | ||
| 13452 | }, | ||
| 13453 | .ignore => { | ||
| 13454 | g_hChildStd_OUT_Wr = nul_handle; | ||
| 13455 | }, | ||
| 13456 | .inherit => { | ||
| 13457 | g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null; | ||
| 13458 | }, | ||
| 13459 | .close => { | ||
| 13460 | g_hChildStd_OUT_Wr = null; | ||
| 13461 | }, | ||
| 13462 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | ||
| 13463 | } | ||
| 13464 | errdefer if (options.stdout == .pipe) { | ||
| 13465 | windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); | ||
| 13466 | }; | ||
| 13467 | |||
| 13468 | var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; | ||
| 13469 | var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; | ||
| 13470 | switch (options.stderr) { | ||
| 13471 | .pipe => { | ||
| 13472 | try windowsMakeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); | ||
| 13473 | }, | ||
| 13474 | .ignore => { | ||
| 13475 | g_hChildStd_ERR_Wr = nul_handle; | ||
| 13476 | }, | ||
| 13477 | .inherit => { | ||
| 13478 | g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null; | ||
| 13479 | }, | ||
| 13480 | .close => { | ||
| 13481 | g_hChildStd_ERR_Wr = null; | ||
| 13482 | }, | ||
| 13483 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | ||
| 13484 | } | ||
| 13485 | errdefer if (options.stderr == .pipe) { | ||
| 13486 | windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); | ||
| 13487 | }; | ||
| 13488 | |||
| 13489 | var siStartInfo: windows.STARTUPINFOW = .{ | ||
| 13490 | .cb = @sizeOf(windows.STARTUPINFOW), | ||
| 13491 | .hStdError = g_hChildStd_ERR_Wr, | ||
| 13492 | .hStdOutput = g_hChildStd_OUT_Wr, | ||
| 13493 | .hStdInput = g_hChildStd_IN_Rd, | ||
| 13494 | .dwFlags = windows.STARTF_USESTDHANDLES, | ||
| 13495 | |||
| 13496 | .lpReserved = null, | ||
| 13497 | .lpDesktop = null, | ||
| 13498 | .lpTitle = null, | ||
| 13499 | .dwX = 0, | ||
| 13500 | .dwY = 0, | ||
| 13501 | .dwXSize = 0, | ||
| 13502 | .dwYSize = 0, | ||
| 13503 | .dwXCountChars = 0, | ||
| 13504 | .dwYCountChars = 0, | ||
| 13505 | .dwFillAttribute = 0, | ||
| 13506 | .wShowWindow = 0, | ||
| 13507 | .cbReserved2 = 0, | ||
| 13508 | .lpReserved2 = null, | ||
| 13509 | }; | ||
| 13510 | var piProcInfo: windows.PROCESS_INFORMATION = undefined; | ||
| 13511 | |||
| 13512 | var arena_allocator = std.heap.ArenaAllocator.init(t.allocator); | ||
| 13513 | defer arena_allocator.deinit(); | ||
| 13514 | const arena = arena_allocator.allocator(); | ||
| 13515 | |||
| 13516 | const cwd_w = if (options.cwd) |cwd| try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd) else null; | ||
| 13517 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; | ||
| 13518 | |||
| 13519 | const maybe_envp_buf = if (options.environ_map) |environ_map| try environ_map.createBlockWindows(arena) else null; | ||
| 13520 | const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; | ||
| 13521 | |||
| 13522 | const app_name_wtf8 = options.argv[0]; | ||
| 13523 | const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8); | ||
| 13524 | |||
| 13525 | // The cwd provided by options is in effect when choosing the executable | ||
| 13526 | // path to match POSIX semantics. | ||
| 13527 | var cwd_path_w_needs_free = false; | ||
| 13528 | const cwd_path_w = x: { | ||
| 13529 | // If the app name is absolute, then we need to use its dirname as the cwd | ||
| 13530 | if (app_name_is_absolute) { | ||
| 13531 | cwd_path_w_needs_free = true; | ||
| 13532 | const dir = Dir.path.dirname(app_name_wtf8).?; | ||
| 13533 | break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, dir); | ||
| 13534 | } else if (options.cwd) |cwd| { | ||
| 13535 | cwd_path_w_needs_free = true; | ||
| 13536 | break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd); | ||
| 13537 | } else { | ||
| 13538 | break :x &[_:0]u16{}; // empty for cwd | ||
| 13539 | } | ||
| 13540 | }; | ||
| 13541 | |||
| 13542 | // If the app name has more than just a filename, then we need to separate | ||
| 13543 | // that into the basename and dirname and use the dirname as an addition to | ||
| 13544 | // the cwd path. This is because NtQueryDirectoryFile cannot accept | ||
| 13545 | // FileName params with path separators. | ||
| 13546 | const app_basename_wtf8 = Dir.path.basename(app_name_wtf8); | ||
| 13547 | // If the app name is absolute, then the cwd will already have the app's dirname in it, | ||
| 13548 | // so only populate app_dirname if app name is a relative path with > 0 path separators. | ||
| 13549 | const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) Dir.path.dirname(app_name_wtf8) else null; | ||
| 13550 | const app_dirname_w: ?[:0]u16 = x: { | ||
| 13551 | if (maybe_app_dirname_wtf8) |app_dirname_wtf8| { | ||
| 13552 | break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_dirname_wtf8); | ||
| 13553 | } | ||
| 13554 | break :x null; | ||
| 13555 | }; | ||
| 13556 | const app_name_w = try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_basename_wtf8); | ||
| 13557 | |||
| 13558 | const flags: windows.CreateProcessFlags = .{ | ||
| 13559 | .create_suspended = options.start_suspended, | ||
| 13560 | .create_unicode_environment = true, | ||
| 13561 | .create_no_window = options.create_no_window, | ||
| 13562 | }; | ||
| 13563 | |||
| 13564 | run: { | ||
| 13565 | // We have to scan each time because the PEB environment pointer is not stable. | ||
| 13566 | const env_strings: WindowsEnvironStrings = .scan(); | ||
| 13567 | const PATH = env_strings.PATH orelse &[_:0]u16{}; | ||
| 13568 | const PATHEXT = env_strings.PATHEXT orelse &[_:0]u16{}; | ||
| 13569 | |||
| 13570 | // In case the command ends up being a .bat/.cmd script, we need to escape things using the cmd.exe rules | ||
| 13571 | // and invoke cmd.exe ourselves in order to mitigate arbitrary command execution from maliciously | ||
| 13572 | // constructed arguments. | ||
| 13573 | // | ||
| 13574 | // We'll need to wait until we're actually trying to run the command to know for sure | ||
| 13575 | // if the resolved command has the `.bat` or `.cmd` extension, so we defer actually | ||
| 13576 | // serializing the command line until we determine how it should be serialized. | ||
| 13577 | var cmd_line_cache = WindowsCommandLineCache.init(arena, options.argv); | ||
| 13578 | |||
| 13579 | var app_buf: std.ArrayList(u16) = .empty; | ||
| 13580 | try app_buf.appendSlice(arena, app_name_w); | ||
| 13581 | |||
| 13582 | var dir_buf: std.ArrayList(u16) = .empty; | ||
| 13583 | |||
| 13584 | if (cwd_path_w.len > 0) { | ||
| 13585 | try dir_buf.appendSlice(arena, cwd_path_w); | ||
| 13586 | } | ||
| 13587 | if (app_dirname_w) |app_dir| { | ||
| 13588 | if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep); | ||
| 13589 | try dir_buf.appendSlice(arena, app_dir); | ||
| 13590 | } | ||
| 13591 | |||
| 13592 | windowsCreateProcessPathExt( | ||
| 13593 | arena, | ||
| 13594 | &dir_buf, | ||
| 13595 | &app_buf, | ||
| 13596 | PATHEXT, | ||
| 13597 | &cmd_line_cache, | ||
| 13598 | envp_ptr, | ||
| 13599 | cwd_w_ptr, | ||
| 13600 | flags, | ||
| 13601 | &siStartInfo, | ||
| 13602 | &piProcInfo, | ||
| 13603 | ) catch |no_path_err| { | ||
| 13604 | const original_err = switch (no_path_err) { | ||
| 13605 | // argv[0] contains unsupported characters that will never resolve to a valid exe. | ||
| 13606 | error.InvalidArg0 => return error.FileNotFound, | ||
| 13607 | error.FileNotFound, error.InvalidExe, error.AccessDenied => |e| e, | ||
| 13608 | error.UnrecoverableInvalidExe => return error.InvalidExe, | ||
| 13609 | else => |e| return e, | ||
| 13610 | }; | ||
| 13611 | |||
| 13612 | // If the app name had path separators, that disallows PATH searching, | ||
| 13613 | // and there's no need to search the PATH if the app name is absolute. | ||
| 13614 | // We still search the path if the cwd is absolute because of the | ||
| 13615 | // "cwd provided by options is in effect when choosing the executable path | ||
| 13616 | // to match posix semantics" behavior--we don't want to skip searching | ||
| 13617 | // the PATH just because we were trying to set the cwd of the child process. | ||
| 13618 | if (app_dirname_w != null or app_name_is_absolute) { | ||
| 13619 | return original_err; | ||
| 13620 | } | ||
| 13621 | |||
| 13622 | var it = std.mem.tokenizeScalar(u16, PATH, ';'); | ||
| 13623 | while (it.next()) |search_path| { | ||
| 13624 | dir_buf.clearRetainingCapacity(); | ||
| 13625 | try dir_buf.appendSlice(arena, search_path); | ||
| 13626 | |||
| 13627 | if (windowsCreateProcessPathExt( | ||
| 13628 | arena, | ||
| 13629 | &dir_buf, | ||
| 13630 | &app_buf, | ||
| 13631 | PATHEXT, | ||
| 13632 | &cmd_line_cache, | ||
| 13633 | envp_ptr, | ||
| 13634 | cwd_w_ptr, | ||
| 13635 | flags, | ||
| 13636 | &siStartInfo, | ||
| 13637 | &piProcInfo, | ||
| 13638 | )) { | ||
| 13639 | break :run; | ||
| 13640 | } else |err| switch (err) { | ||
| 13641 | // argv[0] contains unsupported characters that will never resolve to a valid exe. | ||
| 13642 | error.InvalidArg0 => return error.FileNotFound, | ||
| 13643 | error.FileNotFound, error.AccessDenied, error.InvalidExe => continue, | ||
| 13644 | error.UnrecoverableInvalidExe => return error.InvalidExe, | ||
| 13645 | else => |e| return e, | ||
| 13646 | } | ||
| 13647 | } else { | ||
| 13648 | return original_err; | ||
| 13649 | } | ||
| 13650 | }; | ||
| 13651 | } | ||
| 13652 | |||
| 13653 | if (options.stdin == .pipe) windows.CloseHandle(g_hChildStd_IN_Rd.?); | ||
| 13654 | if (options.stderr == .pipe) windows.CloseHandle(g_hChildStd_ERR_Wr.?); | ||
| 13655 | if (options.stdout == .pipe) windows.CloseHandle(g_hChildStd_OUT_Wr.?); | ||
| 13656 | |||
| 13657 | return .{ | ||
| 13658 | .id = piProcInfo.hProcess, | ||
| 13659 | .thread_handle = piProcInfo.hThread, | ||
| 13660 | .stdin = if (g_hChildStd_IN_Wr) |h| .{ .handle = h } else null, | ||
| 13661 | .stdout = if (g_hChildStd_OUT_Rd) |h| .{ .handle = h } else null, | ||
| 13662 | .stderr = if (g_hChildStd_ERR_Rd) |h| .{ .handle = h } else null, | ||
| 13663 | .request_resource_usage_statistics = options.request_resource_usage_statistics, | ||
| 13664 | }; | ||
| 13665 | } | ||
| 13666 | |||
| 13667 | fn getNulHandle(t: *Threaded) !windows.HANDLE { | ||
| 13668 | { | ||
| 13669 | t.mutex.lock(); | ||
| 13670 | defer t.mutex.unlock(); | ||
| 13671 | if (t.null_file.handle) |handle| return handle; | ||
| 13672 | } | ||
| 13673 | |||
| 13674 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; | ||
| 13675 | var nt_name: windows.UNICODE_STRING = .{ | ||
| 13676 | .Length = device_path.len * 2, | ||
| 13677 | .MaximumLength = device_path.len * 2, | ||
| 13678 | .Buffer = @constCast(&device_path), | ||
| 13679 | }; | ||
| 13680 | const attr: windows.OBJECT_ATTRIBUTES = .{ | ||
| 13681 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 13682 | .RootDirectory = null, | ||
| 13683 | .Attributes = .{ | ||
| 13684 | .INHERIT = true, | ||
| 13685 | }, | ||
| 13686 | .ObjectName = &nt_name, | ||
| 13687 | .SecurityDescriptor = null, | ||
| 13688 | .SecurityQualityOfService = null, | ||
| 13689 | }; | ||
| 13690 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 13691 | var fresh_handle: windows.HANDLE = undefined; | ||
| 13692 | var syscall: Syscall = try .start(); | ||
| 13693 | while (true) switch (windows.ntdll.NtCreateFile( | ||
| 13694 | &fresh_handle, | ||
| 13695 | .{ | ||
| 13696 | .STANDARD = .{ .SYNCHRONIZE = true }, | ||
| 13697 | .GENERIC = .{ .WRITE = true, .READ = true }, | ||
| 13698 | }, | ||
| 13699 | &attr, | ||
| 13700 | &io_status_block, | ||
| 13701 | null, | ||
| 13702 | .{ .NORMAL = true }, | ||
| 13703 | .VALID_FLAGS, | ||
| 13704 | .OPEN, | ||
| 13705 | .{ | ||
| 13706 | .DIRECTORY_FILE = false, | ||
| 13707 | .NON_DIRECTORY_FILE = true, | ||
| 13708 | .IO = .SYNCHRONOUS_NONALERT, | ||
| 13709 | .OPEN_REPARSE_POINT = false, | ||
| 13710 | }, | ||
| 13711 | null, | ||
| 13712 | 0, | ||
| 13713 | )) { | ||
| 13714 | .SUCCESS => { | ||
| 13715 | syscall.finish(); | ||
| 13716 | t.mutex.lock(); // Another thread might have won the race. | ||
| 13717 | defer t.mutex.unlock(); | ||
| 13718 | if (t.null_file.handle) |prev_handle| { | ||
| 13719 | windows.CloseHandle(fresh_handle); | ||
| 13720 | return prev_handle; | ||
| 13721 | } else { | ||
| 13722 | t.null_file.handle = fresh_handle; | ||
| 13723 | return fresh_handle; | ||
| 13724 | } | ||
| 13725 | }, | ||
| 13726 | .DELETE_PENDING => { | ||
| 13727 | // This error means that there *was* a file in this location on | ||
| 13728 | // the file system, but it was deleted. However, the OS is not | ||
| 13729 | // finished with the deletion operation, and so this CreateFile | ||
| 13730 | // call has failed. There is not really a sane way to handle | ||
| 13731 | // this other than retrying the creation after the OS finishes | ||
| 13732 | // the deletion. | ||
| 13733 | syscall.finish(); | ||
| 13734 | try parking_sleep.sleep(.{ .duration = .{ | ||
| 13735 | .raw = .fromMilliseconds(1), | ||
| 13736 | .clock = .awake, | ||
| 13737 | } }); | ||
| 13738 | syscall = try .start(); | ||
| 13739 | continue; | ||
| 13740 | }, | ||
| 13741 | .CANCELLED => { | ||
| 13742 | try syscall.checkCancel(); | ||
| 13743 | continue; | ||
| 13744 | }, | ||
| 13745 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | ||
| 13746 | .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status), | ||
| 13747 | .INVALID_HANDLE => |status| return syscall.ntstatusBug(status), | ||
| 13748 | .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName), | ||
| 13749 | .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 13750 | .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 13751 | .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice), | ||
| 13752 | .SHARING_VIOLATION => return syscall.fail(error.AccessDenied), | ||
| 13753 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | ||
| 13754 | .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice), | ||
| 13755 | .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir), | ||
| 13756 | .NOT_A_DIRECTORY => return syscall.fail(error.NotDir), | ||
| 13757 | .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied), | ||
| 13758 | else => |status| return syscall.unexpectedNtstatus(status), | ||
| 13759 | }; | ||
| 13760 | } | ||
| 13761 | |||
| 13762 | /// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path. | ||
| 13763 | /// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path. | ||
| 13764 | /// Note: `app_buf` should not contain any leading path separators. | ||
| 13765 | /// Note: If the dir is the cwd, dir_buf should be empty (len = 0). | ||
| 13766 | fn windowsCreateProcessPathExt( | ||
| 13767 | arena: Allocator, | ||
| 13768 | dir_buf: *std.ArrayList(u16), | ||
| 13769 | app_buf: *std.ArrayList(u16), | ||
| 13770 | pathext: [:0]const u16, | ||
| 13771 | cmd_line_cache: *WindowsCommandLineCache, | ||
| 13772 | envp_ptr: ?[*:0]const u16, | ||
| 13773 | cwd_ptr: ?[*:0]u16, | ||
| 13774 | flags: windows.CreateProcessFlags, | ||
| 13775 | lpStartupInfo: *windows.STARTUPINFOW, | ||
| 13776 | lpProcessInformation: *windows.PROCESS_INFORMATION, | ||
| 13777 | ) !void { | ||
| 13778 | const app_name_len = app_buf.items.len; | ||
| 13779 | const dir_path_len = dir_buf.items.len; | ||
| 13780 | |||
| 13781 | if (app_name_len == 0) return error.FileNotFound; | ||
| 13782 | |||
| 13783 | defer app_buf.shrinkRetainingCapacity(app_name_len); | ||
| 13784 | defer dir_buf.shrinkRetainingCapacity(dir_path_len); | ||
| 13785 | |||
| 13786 | // The name of the game here is to avoid CreateProcessW calls at all costs, | ||
| 13787 | // and only ever try calling it when we have a real candidate for execution. | ||
| 13788 | // Secondarily, we want to minimize the number of syscalls used when checking | ||
| 13789 | // for each PATHEXT-appended version of the app name. | ||
| 13790 | // | ||
| 13791 | // An overview of the technique used: | ||
| 13792 | // - Open the search directory for iteration (either cwd or a path from PATH) | ||
| 13793 | // - Use NtQueryDirectoryFile with a wildcard filename of `<app name>*` to | ||
| 13794 | // check if anything that could possibly match either the unappended version | ||
| 13795 | // of the app name or any of the versions with a PATHEXT value appended exists. | ||
| 13796 | // - If the wildcard NtQueryDirectoryFile call found nothing, we can exit early | ||
| 13797 | // without needing to use PATHEXT at all. | ||
| 13798 | // | ||
| 13799 | // This allows us to use a <open dir, NtQueryDirectoryFile, close dir> sequence | ||
| 13800 | // for any directory that doesn't contain any possible matches, instead of having | ||
| 13801 | // to use a separate look up for each individual filename combination (unappended + | ||
| 13802 | // each PATHEXT appended). For directories where the wildcard *does* match something, | ||
| 13803 | // we iterate the matches and take note of any that are either the unappended version, | ||
| 13804 | // or a version with a supported PATHEXT appended. We then try calling CreateProcessW | ||
| 13805 | // with the found versions in the appropriate order. | ||
| 13806 | var dir = dir: { | ||
| 13807 | // needs to be null-terminated | ||
| 13808 | try dir_buf.append(arena, 0); | ||
| 13809 | defer dir_buf.shrinkRetainingCapacity(dir_path_len); | ||
| 13810 | const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; | ||
| 13811 | const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z); | ||
| 13812 | break :dir dirOpenDirWindows(.cwd(), prefixed_path.span(), .{ | ||
| 13813 | .iterate = true, | ||
| 13814 | }) catch |err| switch (err) { | ||
| 13815 | // These errors must not be ignored because they should not be able | ||
| 13816 | // to affect which file is chosen to execute. Also `error.Canceled` | ||
| 13817 | // must never be swallowed. | ||
| 13818 | error.Canceled, | ||
| 13819 | error.SystemResources, | ||
| 13820 | error.Unexpected, | ||
| 13821 | error.ProcessFdQuotaExceeded, | ||
| 13822 | error.SystemFdQuotaExceeded, | ||
| 13823 | => |e| return e, | ||
| 13824 | |||
| 13825 | error.AccessDenied, | ||
| 13826 | error.PermissionDenied, | ||
| 13827 | error.SymLinkLoop, | ||
| 13828 | error.FileNotFound, | ||
| 13829 | error.NotDir, | ||
| 13830 | error.NoDevice, | ||
| 13831 | error.NetworkNotFound, | ||
| 13832 | error.NameTooLong, | ||
| 13833 | error.BadPathName, | ||
| 13834 | error.DeviceBusy, | ||
| 13835 | => return error.FileNotFound, | ||
| 13836 | }; | ||
| 13837 | }; | ||
| 13838 | defer windows.CloseHandle(dir.handle); | ||
| 13839 | |||
| 13840 | // Add wildcard and null-terminator | ||
| 13841 | try app_buf.append(arena, '*'); | ||
| 13842 | try app_buf.append(arena, 0); | ||
| 13843 | const app_name_wildcard = app_buf.items[0 .. app_buf.items.len - 1 :0]; | ||
| 13844 | |||
| 13845 | // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries | ||
| 13846 | // returned per NtQueryDirectoryFile call. | ||
| 13847 | var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined; | ||
| 13848 | const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2); | ||
| 13849 | if (file_information_buf.len < file_info_maximum_single_entry_size) { | ||
| 13850 | @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry"); | ||
| 13851 | } | ||
| 13852 | var io_status: windows.IO_STATUS_BLOCK = undefined; | ||
| 13853 | |||
| 13854 | const num_supported_pathext = @typeInfo(process.WindowsExtension).@"enum".fields.len; | ||
| 13855 | var pathext_seen = [_]bool{false} ** num_supported_pathext; | ||
| 13856 | var any_pathext_seen = false; | ||
| 13857 | var unappended_exists = false; | ||
| 13858 | |||
| 13859 | // Fully iterate the wildcard matches via NtQueryDirectoryFile and take note of all versions | ||
| 13860 | // of the app_name we should try to spawn. | ||
| 13861 | // Note: This is necessary because the order of the files returned is filesystem-dependent: | ||
| 13862 | // On NTFS, `blah.exe*` will always return `blah.exe` first if it exists. | ||
| 13863 | // On FAT32, it's possible for something like `blah.exe.obj` to be returned first. | ||
| 13864 | while (true) { | ||
| 13865 | const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong; | ||
| 13866 | var app_name_unicode_string = windows.UNICODE_STRING{ | ||
| 13867 | .Length = app_name_len_bytes, | ||
| 13868 | .MaximumLength = app_name_len_bytes, | ||
| 13869 | .Buffer = @constCast(app_name_wildcard.ptr), | ||
| 13870 | }; | ||
| 13871 | const rc = windows.ntdll.NtQueryDirectoryFile( | ||
| 13872 | dir.handle, | ||
| 13873 | null, | ||
| 13874 | null, | ||
| 13875 | null, | ||
| 13876 | &io_status, | ||
| 13877 | &file_information_buf, | ||
| 13878 | file_information_buf.len, | ||
| 13879 | .Directory, | ||
| 13880 | windows.FALSE, // single result | ||
| 13881 | &app_name_unicode_string, | ||
| 13882 | windows.FALSE, // restart iteration | ||
| 13883 | ); | ||
| 13884 | |||
| 13885 | // If we get nothing with the wildcard, then we can just bail out | ||
| 13886 | // as we know appending PATHEXT will not yield anything. | ||
| 13887 | switch (rc) { | ||
| 13888 | .SUCCESS => {}, | ||
| 13889 | .NO_SUCH_FILE => return error.FileNotFound, | ||
| 13890 | .NO_MORE_FILES => break, | ||
| 13891 | .ACCESS_DENIED => return error.AccessDenied, | ||
| 13892 | else => return windows.unexpectedStatus(rc), | ||
| 13893 | } | ||
| 13894 | |||
| 13895 | // According to the docs, this can only happen if there is not enough room in the | ||
| 13896 | // buffer to write at least one complete FILE_DIRECTORY_INFORMATION entry. | ||
| 13897 | // Therefore, this condition should not be possible to hit with the buffer size we use. | ||
| 13898 | std.debug.assert(io_status.Information != 0); | ||
| 13899 | |||
| 13900 | var it = windows.FileInformationIterator(windows.FILE_DIRECTORY_INFORMATION){ .buf = &file_information_buf }; | ||
| 13901 | while (it.next()) |info| { | ||
| 13902 | // Skip directories | ||
| 13903 | if (info.FileAttributes.DIRECTORY) continue; | ||
| 13904 | const filename = @as([*]u16, @ptrCast(&info.FileName))[0 .. info.FileNameLength / 2]; | ||
| 13905 | // Because all results start with the app_name since we're using the wildcard `app_name*`, | ||
| 13906 | // if the length is equal to app_name then this is an exact match | ||
| 13907 | if (filename.len == app_name_len) { | ||
| 13908 | // Note: We can't break early here because it's possible that the unappended version | ||
| 13909 | // fails to spawn, in which case we still want to try the PATHEXT appended versions. | ||
| 13910 | unappended_exists = true; | ||
| 13911 | } else if (windowsCreateProcessSupportsExtension(filename[app_name_len..])) |pathext_ext| { | ||
| 13912 | pathext_seen[@intFromEnum(pathext_ext)] = true; | ||
| 13913 | any_pathext_seen = true; | ||
| 13914 | } | ||
| 13915 | } | ||
| 13916 | } | ||
| 13917 | |||
| 13918 | const unappended_err = unappended: { | ||
| 13919 | if (unappended_exists) { | ||
| 13920 | if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) { | ||
| 13921 | '/', '\\' => {}, | ||
| 13922 | else => try dir_buf.append(arena, Dir.path.sep), | ||
| 13923 | }; | ||
| 13924 | try dir_buf.appendSlice(arena, app_buf.items[0..app_name_len]); | ||
| 13925 | try dir_buf.append(arena, 0); | ||
| 13926 | const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; | ||
| 13927 | |||
| 13928 | const is_bat_or_cmd = bat_or_cmd: { | ||
| 13929 | const app_name = app_buf.items[0..app_name_len]; | ||
| 13930 | const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :bat_or_cmd false; | ||
| 13931 | const ext = app_name[ext_start..]; | ||
| 13932 | const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse break :bat_or_cmd false; | ||
| 13933 | switch (ext_enum) { | ||
| 13934 | .cmd, .bat => break :bat_or_cmd true, | ||
| 13935 | else => break :bat_or_cmd false, | ||
| 13936 | } | ||
| 13937 | }; | ||
| 13938 | const cmd_line_w = if (is_bat_or_cmd) | ||
| 13939 | try cmd_line_cache.scriptCommandLine(full_app_name) | ||
| 13940 | else | ||
| 13941 | try cmd_line_cache.commandLine(); | ||
| 13942 | const app_name_w = if (is_bat_or_cmd) | ||
| 13943 | try cmd_line_cache.cmdExePath() | ||
| 13944 | else | ||
| 13945 | full_app_name; | ||
| 13946 | |||
| 13947 | if (windowsCreateProcess( | ||
| 13948 | app_name_w.ptr, | ||
| 13949 | cmd_line_w.ptr, | ||
| 13950 | envp_ptr, | ||
| 13951 | cwd_ptr, | ||
| 13952 | flags, | ||
| 13953 | lpStartupInfo, | ||
| 13954 | lpProcessInformation, | ||
| 13955 | )) |_| { | ||
| 13956 | return; | ||
| 13957 | } else |err| switch (err) { | ||
| 13958 | error.FileNotFound, | ||
| 13959 | error.AccessDenied, | ||
| 13960 | => break :unappended err, | ||
| 13961 | error.InvalidExe => { | ||
| 13962 | // On InvalidExe, if the extension of the app name is .exe then | ||
| 13963 | // it's treated as an unrecoverable error. Otherwise, it'll be | ||
| 13964 | // skipped as normal. | ||
| 13965 | const app_name = app_buf.items[0..app_name_len]; | ||
| 13966 | const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :unappended err; | ||
| 13967 | const ext = app_name[ext_start..]; | ||
| 13968 | if (windows.eqlIgnoreCaseWtf16(ext, std.unicode.utf8ToUtf16LeStringLiteral(".EXE"))) { | ||
| 13969 | return error.UnrecoverableInvalidExe; | ||
| 13970 | } | ||
| 13971 | break :unappended err; | ||
| 13972 | }, | ||
| 13973 | else => return err, | ||
| 13974 | } | ||
| 13975 | } | ||
| 13976 | break :unappended error.FileNotFound; | ||
| 13977 | }; | ||
| 13978 | |||
| 13979 | if (!any_pathext_seen) return unappended_err; | ||
| 13980 | |||
| 13981 | // Now try any PATHEXT appended versions that we've seen | ||
| 13982 | var ext_it = std.mem.tokenizeScalar(u16, pathext, ';'); | ||
| 13983 | while (ext_it.next()) |ext| { | ||
| 13984 | const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse continue; | ||
| 13985 | if (!pathext_seen[@intFromEnum(ext_enum)]) continue; | ||
| 13986 | |||
| 13987 | dir_buf.shrinkRetainingCapacity(dir_path_len); | ||
| 13988 | if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) { | ||
| 13989 | '/', '\\' => {}, | ||
| 13990 | else => try dir_buf.append(arena, Dir.path.sep), | ||
| 13991 | }; | ||
| 13992 | try dir_buf.appendSlice(arena, app_buf.items[0..app_name_len]); | ||
| 13993 | try dir_buf.appendSlice(arena, ext); | ||
| 13994 | try dir_buf.append(arena, 0); | ||
| 13995 | const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; | ||
| 13996 | |||
| 13997 | const is_bat_or_cmd = switch (ext_enum) { | ||
| 13998 | .cmd, .bat => true, | ||
| 13999 | else => false, | ||
| 14000 | }; | ||
| 14001 | const cmd_line_w = if (is_bat_or_cmd) | ||
| 14002 | try cmd_line_cache.scriptCommandLine(full_app_name) | ||
| 14003 | else | ||
| 14004 | try cmd_line_cache.commandLine(); | ||
| 14005 | const app_name_w = if (is_bat_or_cmd) | ||
| 14006 | try cmd_line_cache.cmdExePath() | ||
| 14007 | else | ||
| 14008 | full_app_name; | ||
| 14009 | |||
| 14010 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | ||
| 14011 | return; | ||
| 14012 | } else |err| switch (err) { | ||
| 14013 | error.FileNotFound => continue, | ||
| 14014 | error.AccessDenied => continue, | ||
| 14015 | error.InvalidExe => { | ||
| 14016 | // On InvalidExe, if the extension of the app name is .exe then | ||
| 14017 | // it's treated as an unrecoverable error. Otherwise, it'll be | ||
| 14018 | // skipped as normal. | ||
| 14019 | if (windows.eqlIgnoreCaseWtf16(ext, std.unicode.utf8ToUtf16LeStringLiteral(".EXE"))) { | ||
| 14020 | return error.UnrecoverableInvalidExe; | ||
| 14021 | } | ||
| 14022 | continue; | ||
| 14023 | }, | ||
| 14024 | else => return err, | ||
| 14025 | } | ||
| 14026 | } | ||
| 14027 | |||
| 14028 | return unappended_err; | ||
| 14029 | } | ||
| 14030 | |||
| 14031 | fn windowsCreateProcess( | ||
| 14032 | app_name: [*:0]u16, | ||
| 14033 | cmd_line: [*:0]u16, | ||
| 14034 | env_ptr: ?[*:0]const u16, | ||
| 14035 | cwd_ptr: ?[*:0]u16, | ||
| 14036 | flags: windows.CreateProcessFlags, | ||
| 14037 | lpStartupInfo: *windows.STARTUPINFOW, | ||
| 14038 | lpProcessInformation: *windows.PROCESS_INFORMATION, | ||
| 14039 | ) !void { | ||
| 14040 | const syscall: Syscall = try .start(); | ||
| 14041 | while (true) { | ||
| 14042 | if (windows.kernel32.CreateProcessW( | ||
| 14043 | app_name, | ||
| 14044 | cmd_line, | ||
| 14045 | null, | ||
| 14046 | null, | ||
| 14047 | windows.TRUE, | ||
| 14048 | flags, | ||
| 14049 | env_ptr, | ||
| 14050 | cwd_ptr, | ||
| 14051 | lpStartupInfo, | ||
| 14052 | lpProcessInformation, | ||
| 14053 | ) != 0) { | ||
| 14054 | return syscall.finish(); | ||
| 14055 | } else switch (windows.GetLastError()) { | ||
| 14056 | .INVALID_PARAMETER => unreachable, | ||
| 14057 | .OPERATION_ABORTED => { | ||
| 14058 | try syscall.checkCancel(); | ||
| 14059 | continue; | ||
| 14060 | }, | ||
| 14061 | .FILE_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 14062 | .PATH_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 14063 | .DIRECTORY => return syscall.fail(error.FileNotFound), | ||
| 14064 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | ||
| 14065 | .INVALID_NAME => return syscall.fail(error.InvalidName), | ||
| 14066 | .FILENAME_EXCED_RANGE => return syscall.fail(error.NameTooLong), | ||
| 14067 | .SHARING_VIOLATION => return syscall.fail(error.FileBusy), | ||
| 14068 | .COMMITMENT_LIMIT => return syscall.fail(error.SystemResources), | ||
| 14069 | |||
| 14070 | // These are all the system errors that are mapped to ENOEXEC by | ||
| 14071 | // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error | ||
| 14072 | // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK) | ||
| 14073 | // or urt/misc/errno.cpp (newer SDK) in the Windows SDK. | ||
| 14074 | .BAD_FORMAT, | ||
| 14075 | .INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp | ||
| 14076 | .INVALID_STACKSEG, | ||
| 14077 | .INVALID_MODULETYPE, | ||
| 14078 | .INVALID_EXE_SIGNATURE, | ||
| 14079 | .EXE_MARKED_INVALID, | ||
| 14080 | .BAD_EXE_FORMAT, | ||
| 14081 | .ITERATED_DATA_EXCEEDS_64k, | ||
| 14082 | .INVALID_MINALLOCSIZE, | ||
| 14083 | .DYNLINK_FROM_INVALID_RING, | ||
| 14084 | .IOPL_NOT_ENABLED, | ||
| 14085 | .INVALID_SEGDPL, | ||
| 14086 | .AUTODATASEG_EXCEEDS_64k, | ||
| 14087 | .RING2SEG_MUST_BE_MOVABLE, | ||
| 14088 | .RELOC_CHAIN_XEEDS_SEGLIM, | ||
| 14089 | .INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp | ||
| 14090 | // This one is not mapped to ENOEXEC but it is possible, for example | ||
| 14091 | // when calling CreateProcessW on a plain text file with a .exe extension | ||
| 14092 | .EXE_MACHINE_TYPE_MISMATCH, | ||
| 14093 | => return syscall.fail(error.InvalidExe), | ||
| 14094 | |||
| 14095 | else => |err| { | ||
| 14096 | syscall.finish(); | ||
| 14097 | return windows.unexpectedError(err); | ||
| 14098 | }, | ||
| 14099 | } | ||
| 14100 | } | ||
| 14101 | } | ||
| 14102 | |||
| 14103 | /// Case-insensitive WTF-16 lookup | ||
| 14104 | fn windowsCreateProcessSupportsExtension(ext: []const u16) ?process.WindowsExtension { | ||
| 14105 | comptime { | ||
| 14106 | // Ensures keeping this function in sync with the enum. | ||
| 14107 | const fields = @typeInfo(process.WindowsExtension).@"enum".fields; | ||
| 14108 | assert(fields.len == 4); | ||
| 14109 | assert(@intFromEnum(process.WindowsExtension.bat) == 0); | ||
| 14110 | assert(@intFromEnum(process.WindowsExtension.cmd) == 1); | ||
| 14111 | assert(@intFromEnum(process.WindowsExtension.com) == 2); | ||
| 14112 | assert(@intFromEnum(process.WindowsExtension.exe) == 3); | ||
| 14113 | } | ||
| 14114 | |||
| 14115 | if (ext.len != 4) return null; | ||
| 14116 | const State = enum { | ||
| 14117 | start, | ||
| 14118 | dot, | ||
| 14119 | b, | ||
| 14120 | ba, | ||
| 14121 | c, | ||
| 14122 | cm, | ||
| 14123 | co, | ||
| 14124 | e, | ||
| 14125 | ex, | ||
| 14126 | }; | ||
| 14127 | var state: State = .start; | ||
| 14128 | for (ext) |c| switch (state) { | ||
| 14129 | .start => switch (c) { | ||
| 14130 | '.' => state = .dot, | ||
| 14131 | else => return null, | ||
| 14132 | }, | ||
| 14133 | .dot => switch (c) { | ||
| 14134 | 'b', 'B' => state = .b, | ||
| 14135 | 'c', 'C' => state = .c, | ||
| 14136 | 'e', 'E' => state = .e, | ||
| 14137 | else => return null, | ||
| 14138 | }, | ||
| 14139 | .b => switch (c) { | ||
| 14140 | 'a', 'A' => state = .ba, | ||
| 14141 | else => return null, | ||
| 14142 | }, | ||
| 14143 | .c => switch (c) { | ||
| 14144 | 'm', 'M' => state = .cm, | ||
| 14145 | 'o', 'O' => state = .co, | ||
| 14146 | else => return null, | ||
| 14147 | }, | ||
| 14148 | .e => switch (c) { | ||
| 14149 | 'x', 'X' => state = .ex, | ||
| 14150 | else => return null, | ||
| 14151 | }, | ||
| 14152 | .ba => switch (c) { | ||
| 14153 | 't', 'T' => return .bat, | ||
| 14154 | else => return null, | ||
| 14155 | }, | ||
| 14156 | .cm => switch (c) { | ||
| 14157 | 'd', 'D' => return .cmd, | ||
| 14158 | else => return null, | ||
| 14159 | }, | ||
| 14160 | .co => switch (c) { | ||
| 14161 | 'm', 'M' => return .com, | ||
| 14162 | else => return null, | ||
| 14163 | }, | ||
| 14164 | .ex => switch (c) { | ||
| 14165 | 'e', 'E' => return .exe, | ||
| 14166 | else => return null, | ||
| 14167 | }, | ||
| 14168 | }; | ||
| 14169 | return null; | ||
| 14170 | } | ||
| 14171 | |||
| 14172 | test windowsCreateProcessSupportsExtension { | ||
| 14173 | try std.testing.expectEqual(process.WindowsExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?); | ||
| 14174 | try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null); | ||
| 14175 | } | ||
| 14176 | |||
| 14177 | /// Serializes argv into a WTF-16 encoded command-line string for use with CreateProcessW. | ||
| 14178 | /// | ||
| 14179 | /// Serialization is done on-demand and the result is cached in order to allow for: | ||
| 14180 | /// - Only serializing the particular type of command line needed (`.bat`/`.cmd` | ||
| 14181 | /// command line serialization is different from `.exe`/etc) | ||
| 14182 | /// - Reusing the serialized command lines if necessary (i.e. if the execution | ||
| 14183 | /// of a command fails and the PATH is going to be continued to be searched | ||
| 14184 | /// for more candidates) | ||
| 14185 | const WindowsCommandLineCache = struct { | ||
| 14186 | cmd_line: ?[:0]u16 = null, | ||
| 14187 | script_cmd_line: ?[:0]u16 = null, | ||
| 14188 | cmd_exe_path: ?[:0]u16 = null, | ||
| 14189 | argv: []const []const u8, | ||
| 14190 | allocator: Allocator, | ||
| 14191 | |||
| 14192 | fn init(allocator: Allocator, argv: []const []const u8) WindowsCommandLineCache { | ||
| 14193 | return .{ | ||
| 14194 | .allocator = allocator, | ||
| 14195 | .argv = argv, | ||
| 14196 | }; | ||
| 14197 | } | ||
| 14198 | |||
| 14199 | fn deinit(self: *WindowsCommandLineCache) void { | ||
| 14200 | if (self.cmd_line) |cmd_line| self.allocator.free(cmd_line); | ||
| 14201 | if (self.script_cmd_line) |script_cmd_line| self.allocator.free(script_cmd_line); | ||
| 14202 | if (self.cmd_exe_path) |cmd_exe_path| self.allocator.free(cmd_exe_path); | ||
| 14203 | } | ||
| 14204 | |||
| 14205 | fn commandLine(self: *WindowsCommandLineCache) ![:0]u16 { | ||
| 14206 | if (self.cmd_line == null) { | ||
| 14207 | self.cmd_line = try argvToCommandLineWindows(self.allocator, self.argv); | ||
| 14208 | } | ||
| 14209 | return self.cmd_line.?; | ||
| 14210 | } | ||
| 14211 | |||
| 14212 | /// Not cached, since the path to the batch script will change during PATH searching. | ||
| 14213 | /// `script_path` should be as qualified as possible, e.g. if the PATH is being searched, | ||
| 14214 | /// then script_path should include both the search path and the script filename | ||
| 14215 | /// (this allows avoiding cmd.exe having to search the PATH again). | ||
| 14216 | fn scriptCommandLine(self: *WindowsCommandLineCache, script_path: []const u16) ![:0]u16 { | ||
| 14217 | if (self.script_cmd_line) |v| self.allocator.free(v); | ||
| 14218 | self.script_cmd_line = try argvToScriptCommandLineWindows( | ||
| 14219 | self.allocator, | ||
| 14220 | script_path, | ||
| 14221 | self.argv[1..], | ||
| 14222 | ); | ||
| 14223 | return self.script_cmd_line.?; | ||
| 14224 | } | ||
| 14225 | |||
| 14226 | fn cmdExePath(self: *WindowsCommandLineCache) ![:0]u16 { | ||
| 14227 | if (self.cmd_exe_path == null) { | ||
| 14228 | self.cmd_exe_path = try windowsCmdExePath(self.allocator); | ||
| 14229 | } | ||
| 14230 | return self.cmd_exe_path.?; | ||
| 14231 | } | ||
| 14232 | }; | ||
| 14233 | |||
| 14234 | /// Returns the absolute path of `cmd.exe` within the Windows system directory. | ||
| 14235 | /// The caller owns the returned slice. | ||
| 14236 | fn windowsCmdExePath(allocator: Allocator) error{ OutOfMemory, Unexpected }![:0]u16 { | ||
| 14237 | var buf = try std.ArrayList(u16).initCapacity(allocator, 128); | ||
| 14238 | errdefer buf.deinit(allocator); | ||
| 14239 | while (true) { | ||
| 14240 | const unused_slice = buf.unusedCapacitySlice(); | ||
| 14241 | // TODO: Get the system directory from PEB.ReadOnlyStaticServerData | ||
| 14242 | const len = windows.kernel32.GetSystemDirectoryW(@ptrCast(unused_slice), @intCast(unused_slice.len)); | ||
| 14243 | if (len == 0) { | ||
| 14244 | switch (windows.GetLastError()) { | ||
| 14245 | else => |err| return windows.unexpectedError(err), | ||
| 14246 | } | ||
| 14247 | } | ||
| 14248 | if (len > unused_slice.len) { | ||
| 14249 | try buf.ensureUnusedCapacity(allocator, len); | ||
| 14250 | } else { | ||
| 14251 | buf.items.len = len; | ||
| 14252 | break; | ||
| 14253 | } | ||
| 14254 | } | ||
| 14255 | switch (buf.items[buf.items.len - 1]) { | ||
| 14256 | '/', '\\' => {}, | ||
| 14257 | else => try buf.append(allocator, Dir.path.sep), | ||
| 14258 | } | ||
| 14259 | try buf.appendSlice(allocator, std.unicode.utf8ToUtf16LeStringLiteral("cmd.exe")); | ||
| 14260 | return try buf.toOwnedSliceSentinel(allocator, 0); | ||
| 14261 | } | ||
| 14262 | |||
| 14263 | const ArgvToScriptCommandLineError = error{ | ||
| 14264 | OutOfMemory, | ||
| 14265 | InvalidWtf8, | ||
| 14266 | /// NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed | ||
| 14267 | /// within arguments when executing a `.bat`/`.cmd` script. | ||
| 14268 | /// - NUL/LF signifiies end of arguments, so anything afterwards | ||
| 14269 | /// would be lost after execution. | ||
| 14270 | /// - CR is stripped by `cmd.exe`, so any CR codepoints | ||
| 14271 | /// would be lost after execution. | ||
| 14272 | InvalidBatchScriptArg, | ||
| 14273 | }; | ||
| 14274 | |||
| 14275 | /// Serializes `argv` to a Windows command-line string that uses `cmd.exe /c` and `cmd.exe`-specific | ||
| 14276 | /// escaping rules. The caller owns the returned slice. | ||
| 14277 | /// | ||
| 14278 | /// Escapes `argv` using the suggested mitigation against arbitrary command execution from: | ||
| 14279 | /// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/ | ||
| 14280 | /// | ||
| 14281 | /// The return of this function will look like | ||
| 14282 | /// `cmd.exe /d /e:ON /v:OFF /c "<escaped command line>"` | ||
| 14283 | /// and should be used as the `lpCommandLine` of `CreateProcessW`, while the | ||
| 14284 | /// return of `windowsCmdExePath` should be used as `lpApplicationName`. | ||
| 14285 | /// | ||
| 14286 | /// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise. | ||
| 14287 | /// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem. | ||
| 14288 | fn argvToScriptCommandLineWindows( | ||
| 14289 | allocator: Allocator, | ||
| 14290 | /// Path to the `.bat`/`.cmd` script. If this path is relative, it is assumed to be relative to the CWD. | ||
| 14291 | /// The script must have been verified to exist at this path before calling this function. | ||
| 14292 | script_path: []const u16, | ||
| 14293 | /// Arguments, not including the script name itself. Expected to be encoded as WTF-8. | ||
| 14294 | script_args: []const []const u8, | ||
| 14295 | ) ArgvToScriptCommandLineError![:0]u16 { | ||
| 14296 | var buf = try std.array_list.Managed(u8).initCapacity(allocator, 64); | ||
| 14297 | defer buf.deinit(); | ||
| 14298 | |||
| 14299 | // `/d` disables execution of AutoRun commands. | ||
| 14300 | // `/e:ON` and `/v:OFF` are needed for BatBadBut mitigation: | ||
| 14301 | // > If delayed expansion is enabled via the registry value DelayedExpansion, | ||
| 14302 | // > it must be disabled by explicitly calling cmd.exe with the /V:OFF option. | ||
| 14303 | // > Escaping for % requires the command extension to be enabled. | ||
| 14304 | // > If it’s disabled via the registry value EnableExtensions, it must be enabled with the /E:ON option. | ||
| 14305 | // https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/ | ||
| 14306 | buf.appendSliceAssumeCapacity("cmd.exe /d /e:ON /v:OFF /c \""); | ||
| 14307 | |||
| 14308 | // Always quote the path to the script arg | ||
| 14309 | buf.appendAssumeCapacity('"'); | ||
| 14310 | // We always want the path to the batch script to include a path separator in order to | ||
| 14311 | // avoid cmd.exe searching the PATH for the script. This is not part of the arbitrary | ||
| 14312 | // command execution mitigation, we just know exactly what script we want to execute | ||
| 14313 | // at this point, and potentially making cmd.exe re-find it is unnecessary. | ||
| 14314 | // | ||
| 14315 | // If the script path does not have a path separator, then we know its relative to CWD and | ||
| 14316 | // we can just put `.\` in the front. | ||
| 14317 | if (std.mem.findAny(u16, script_path, &[_]u16{ | ||
| 14318 | std.mem.nativeToLittle(u16, '\\'), std.mem.nativeToLittle(u16, '/'), | ||
| 14319 | }) == null) { | ||
| 14320 | try buf.appendSlice(".\\"); | ||
| 14321 | } | ||
| 14322 | // Note that we don't do any escaping/mitigations for this argument, since the relevant | ||
| 14323 | // characters (", %, etc) are illegal in file paths and this function should only be called | ||
| 14324 | // with script paths that have been verified to exist. | ||
| 14325 | try std.unicode.wtf16LeToWtf8ArrayList(&buf, script_path); | ||
| 14326 | buf.appendAssumeCapacity('"'); | ||
| 14327 | |||
| 14328 | for (script_args) |arg| { | ||
| 14329 | // Literal carriage returns get stripped when run through cmd.exe | ||
| 14330 | // and NUL/newlines act as 'end of command.' Because of this, it's basically | ||
| 14331 | // always a mistake to include these characters in argv, so it's | ||
| 14332 | // an error condition in order to ensure that the return of this | ||
| 14333 | // function can always roundtrip through cmd.exe. | ||
| 14334 | if (std.mem.findAny(u8, arg, "\x00\r\n") != null) { | ||
| 14335 | return error.InvalidBatchScriptArg; | ||
| 14336 | } | ||
| 14337 | |||
| 14338 | // Separate args with a space. | ||
| 14339 | try buf.append(' '); | ||
| 14340 | |||
| 14341 | // Need to quote if the argument is empty (otherwise the arg would just be lost) | ||
| 14342 | // or if the last character is a `\`, since then something like "%~2" in a .bat | ||
| 14343 | // script would cause the closing " to be escaped which we don't want. | ||
| 14344 | var needs_quotes = arg.len == 0 or arg[arg.len - 1] == '\\'; | ||
| 14345 | if (!needs_quotes) { | ||
| 14346 | for (arg) |c| { | ||
| 14347 | switch (c) { | ||
| 14348 | // Known good characters that don't need to be quoted | ||
| 14349 | 'A'...'Z', 'a'...'z', '0'...'9', '#', '$', '*', '+', '-', '.', '/', ':', '?', '@', '\\', '_' => {}, | ||
| 14350 | // When in doubt, quote | ||
| 14351 | else => { | ||
| 14352 | needs_quotes = true; | ||
| 14353 | break; | ||
| 14354 | }, | ||
| 14355 | } | ||
| 14356 | } | ||
| 14357 | } | ||
| 14358 | if (needs_quotes) { | ||
| 14359 | try buf.append('"'); | ||
| 14360 | } | ||
| 14361 | var backslashes: usize = 0; | ||
| 14362 | for (arg) |c| { | ||
| 14363 | switch (c) { | ||
| 14364 | '\\' => { | ||
| 14365 | backslashes += 1; | ||
| 14366 | }, | ||
| 14367 | '"' => { | ||
| 14368 | try buf.appendNTimes('\\', backslashes); | ||
| 14369 | try buf.append('"'); | ||
| 14370 | backslashes = 0; | ||
| 14371 | }, | ||
| 14372 | // Replace `%` with `%%cd:~,%`. | ||
| 14373 | // | ||
| 14374 | // cmd.exe allows extracting a substring from an environment | ||
| 14375 | // variable with the syntax: `%foo:~<start_index>,<end_index>%`. | ||
| 14376 | // Therefore, `%cd:~,%` will always expand to an empty string | ||
| 14377 | // since both the start and end index are blank, and it is assumed | ||
| 14378 | // that `%cd%` is always available since it is a built-in variable | ||
| 14379 | // that corresponds to the current directory. | ||
| 14380 | // | ||
| 14381 | // This means that replacing `%foo%` with `%%cd:~,%foo%%cd:~,%` | ||
| 14382 | // will stop `%foo%` from being expanded and *after* expansion | ||
| 14383 | // we'll still be left with `%foo%` (the literal string). | ||
| 14384 | '%' => { | ||
| 14385 | // the trailing `%` is appended outside the switch | ||
| 14386 | try buf.appendSlice("%%cd:~,"); | ||
| 14387 | backslashes = 0; | ||
| 14388 | }, | ||
| 14389 | else => { | ||
| 14390 | backslashes = 0; | ||
| 14391 | }, | ||
| 14392 | } | ||
| 14393 | try buf.append(c); | ||
| 14394 | } | ||
| 14395 | if (needs_quotes) { | ||
| 14396 | try buf.appendNTimes('\\', backslashes); | ||
| 14397 | try buf.append('"'); | ||
| 14398 | } | ||
| 14399 | } | ||
| 14400 | |||
| 14401 | try buf.append('"'); | ||
| 14402 | |||
| 14403 | return try std.unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items); | ||
| 14404 | } | ||
| 14405 | |||
| 14406 | const ArgvToCommandLineError = error{ OutOfMemory, InvalidWtf8, InvalidArg0 }; | ||
| 14407 | |||
| 14408 | /// Serializes `argv` to a Windows command-line string suitable for passing to a child process and | ||
| 14409 | /// parsing by the `CommandLineToArgvW` algorithm. The caller owns the returned slice. | ||
| 14410 | /// | ||
| 14411 | /// To avoid arbitrary command execution, this function should not be used when spawning `.bat`/`.cmd` scripts. | ||
| 14412 | /// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/ | ||
| 14413 | /// | ||
| 14414 | /// When executing `.bat`/`.cmd` scripts, use `argvToScriptCommandLineWindows` instead. | ||
| 14415 | fn argvToCommandLineWindows( | ||
| 14416 | allocator: Allocator, | ||
| 14417 | argv: []const []const u8, | ||
| 14418 | ) ArgvToCommandLineError![:0]u16 { | ||
| 14419 | var buf = std.array_list.Managed(u8).init(allocator); | ||
| 14420 | defer buf.deinit(); | ||
| 14421 | |||
| 14422 | if (argv.len != 0) { | ||
| 14423 | const arg0 = argv[0]; | ||
| 14424 | |||
| 14425 | // The first argument must be quoted if it contains spaces or ASCII control characters | ||
| 14426 | // (excluding DEL). It also follows special quoting rules where backslashes have no special | ||
| 14427 | // interpretation, which makes it impossible to pass certain first arguments containing | ||
| 14428 | // double quotes to a child process without characters from the first argument leaking into | ||
| 14429 | // subsequent ones (which could have security implications). | ||
| 14430 | // | ||
| 14431 | // Empty arguments technically don't need quotes, but we quote them anyway for maximum | ||
| 14432 | // compatibility with different implementations of the 'CommandLineToArgvW' algorithm. | ||
| 14433 | // | ||
| 14434 | // Double quotes are illegal in paths on Windows, so for the sake of simplicity we reject | ||
| 14435 | // all first arguments containing double quotes, even ones that we could theoretically | ||
| 14436 | // serialize in unquoted form. | ||
| 14437 | var needs_quotes = arg0.len == 0; | ||
| 14438 | for (arg0) |c| { | ||
| 14439 | if (c <= ' ') { | ||
| 14440 | needs_quotes = true; | ||
| 14441 | } else if (c == '"') { | ||
| 14442 | return error.InvalidArg0; | ||
| 14443 | } | ||
| 14444 | } | ||
| 14445 | if (needs_quotes) { | ||
| 14446 | try buf.append('"'); | ||
| 14447 | try buf.appendSlice(arg0); | ||
| 14448 | try buf.append('"'); | ||
| 14449 | } else { | ||
| 14450 | try buf.appendSlice(arg0); | ||
| 14451 | } | ||
| 14452 | |||
| 14453 | for (argv[1..]) |arg| { | ||
| 14454 | try buf.append(' '); | ||
| 14455 | |||
| 14456 | // Subsequent arguments must be quoted if they contain spaces, tabs or double quotes, | ||
| 14457 | // or if they are empty. For simplicity and for maximum compatibility with different | ||
| 14458 | // implementations of the 'CommandLineToArgvW' algorithm, we also quote all ASCII | ||
| 14459 | // control characters (again, excluding DEL). | ||
| 14460 | needs_quotes = for (arg) |c| { | ||
| 14461 | if (c <= ' ' or c == '"') { | ||
| 14462 | break true; | ||
| 14463 | } | ||
| 14464 | } else arg.len == 0; | ||
| 14465 | if (!needs_quotes) { | ||
| 14466 | try buf.appendSlice(arg); | ||
| 14467 | continue; | ||
| 14468 | } | ||
| 14469 | |||
| 14470 | try buf.append('"'); | ||
| 14471 | var backslash_count: usize = 0; | ||
| 14472 | for (arg) |byte| { | ||
| 14473 | switch (byte) { | ||
| 14474 | '\\' => { | ||
| 14475 | backslash_count += 1; | ||
| 14476 | }, | ||
| 14477 | '"' => { | ||
| 14478 | try buf.appendNTimes('\\', backslash_count * 2 + 1); | ||
| 14479 | try buf.append('"'); | ||
| 14480 | backslash_count = 0; | ||
| 14481 | }, | ||
| 14482 | else => { | ||
| 14483 | try buf.appendNTimes('\\', backslash_count); | ||
| 14484 | try buf.append(byte); | ||
| 14485 | backslash_count = 0; | ||
| 14486 | }, | ||
| 14487 | } | ||
| 14488 | } | ||
| 14489 | try buf.appendNTimes('\\', backslash_count * 2); | ||
| 14490 | try buf.append('"'); | ||
| 14491 | } | ||
| 14492 | } | ||
| 14493 | |||
| 14494 | return try std.unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items); | ||
| 14495 | } | ||
| 14496 | |||
| 14497 | test argvToCommandLineWindows { | ||
| 14498 | const t = testArgvToCommandLineWindows; | ||
| 14499 | |||
| 14500 | try t(&.{ | ||
| 14501 | \\C:\Program Files\zig\zig.exe | ||
| 14502 | , | ||
| 14503 | \\run | ||
| 14504 | , | ||
| 14505 | \\.\src\main.zig | ||
| 14506 | , | ||
| 14507 | \\-target | ||
| 14508 | , | ||
| 14509 | \\x86_64-windows-gnu | ||
| 14510 | , | ||
| 14511 | \\-O | ||
| 14512 | , | ||
| 14513 | \\ReleaseSafe | ||
| 14514 | , | ||
| 14515 | \\-- | ||
| 14516 | , | ||
| 14517 | \\--emoji=🗿 | ||
| 14518 | , | ||
| 14519 | \\--eval=new Regex("Dwayne \"The Rock\" Johnson") | ||
| 14520 | , | ||
| 14521 | }, | ||
| 14522 | \\"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\")" | ||
| 14523 | ); | ||
| 14524 | |||
| 14525 | try t(&.{}, ""); | ||
| 14526 | try t(&.{""}, "\"\""); | ||
| 14527 | try t(&.{" "}, "\" \""); | ||
| 14528 | try t(&.{"\t"}, "\"\t\""); | ||
| 14529 | try t(&.{"\x07"}, "\"\x07\""); | ||
| 14530 | try t(&.{"🦎"}, "🦎"); | ||
| 14531 | |||
| 14532 | try t( | ||
| 14533 | &.{ "zig", "aa aa", "bb\tbb", "cc\ncc", "dd\r\ndd", "ee\x7Fee" }, | ||
| 14534 | "zig \"aa aa\" \"bb\tbb\" \"cc\ncc\" \"dd\r\ndd\" ee\x7Fee", | ||
| 14535 | ); | ||
| 14536 | |||
| 14537 | try t( | ||
| 14538 | &.{ "\\\\foo bar\\foo bar\\", "\\\\zig zag\\zig zag\\" }, | ||
| 14539 | "\"\\\\foo bar\\foo bar\\\" \"\\\\zig zag\\zig zag\\\\\"", | ||
| 14540 | ); | ||
| 14541 | |||
| 14542 | try std.testing.expectError( | ||
| 14543 | error.InvalidArg0, | ||
| 14544 | argvToCommandLineWindows(std.testing.allocator, &.{"\"quotes\"quotes\""}), | ||
| 14545 | ); | ||
| 14546 | try std.testing.expectError( | ||
| 14547 | error.InvalidArg0, | ||
| 14548 | argvToCommandLineWindows(std.testing.allocator, &.{"quotes\"quotes"}), | ||
| 14549 | ); | ||
| 14550 | try std.testing.expectError( | ||
| 14551 | error.InvalidArg0, | ||
| 14552 | argvToCommandLineWindows(std.testing.allocator, &.{"q u o t e s \" q u o t e s"}), | ||
| 14553 | ); | ||
| 14554 | } | ||
| 14555 | |||
| 14556 | fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []const u8) !void { | ||
| 14557 | const cmd_line_w = try argvToCommandLineWindows(std.testing.allocator, argv); | ||
| 14558 | defer std.testing.allocator.free(cmd_line_w); | ||
| 14559 | |||
| 14560 | const cmd_line = try std.unicode.wtf16LeToWtf8Alloc(std.testing.allocator, cmd_line_w); | ||
| 14561 | defer std.testing.allocator.free(cmd_line); | ||
| 14562 | |||
| 14563 | try std.testing.expectEqualStrings(expected_cmd_line, cmd_line); | ||
| 14564 | } | ||
| 14565 | |||
| 14566 | fn posixExecv( | ||
| 14567 | arg0_expand: process.ArgExpansion, | ||
| 14568 | file: [*:0]const u8, | ||
| 14569 | child_argv: [*:null]?[*:0]const u8, | ||
| 14570 | envp: [*:null]const ?[*:0]const u8, | ||
| 14571 | PATH: []const u8, | ||
| 14572 | ) process.ReplaceError { | ||
| 14573 | const file_slice = std.mem.sliceTo(file, 0); | ||
| 14574 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, envp); | ||
| 14575 | |||
| 14576 | // Use of PATH_MAX here is valid as the path_buf will be passed | ||
| 14577 | // directly to the operating system in posixExecvPath. | ||
| 14578 | var path_buf: [posix.PATH_MAX]u8 = undefined; | ||
| 14579 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); | ||
| 14580 | var seen_eacces = false; | ||
| 14581 | var err: process.ReplaceError = error.FileNotFound; | ||
| 14582 | |||
| 14583 | // In case of expanding arg0 we must put it back if we return with an error. | ||
| 14584 | const prev_arg0 = child_argv[0]; | ||
| 14585 | defer switch (arg0_expand) { | ||
| 14586 | .expand => child_argv[0] = prev_arg0, | ||
| 14587 | .no_expand => {}, | ||
| 14588 | }; | ||
| 14589 | |||
| 14590 | while (it.next()) |search_path| { | ||
| 14591 | const path_len = search_path.len + file_slice.len + 1; | ||
| 14592 | if (path_buf.len < path_len + 1) return error.NameTooLong; | ||
| 14593 | @memcpy(path_buf[0..search_path.len], search_path); | ||
| 14594 | path_buf[search_path.len] = '/'; | ||
| 14595 | @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice); | ||
| 14596 | path_buf[path_len] = 0; | ||
| 14597 | const full_path = path_buf[0..path_len :0].ptr; | ||
| 14598 | switch (arg0_expand) { | ||
| 14599 | .expand => child_argv[0] = full_path, | ||
| 14600 | .no_expand => {}, | ||
| 14601 | } | ||
| 14602 | err = posixExecvPath(full_path, child_argv, envp); | ||
| 14603 | switch (err) { | ||
| 14604 | error.AccessDenied => seen_eacces = true, | ||
| 14605 | error.FileNotFound, error.NotDir => {}, | ||
| 14606 | else => |e| return e, | ||
| 14607 | } | ||
| 14608 | } | ||
| 14609 | if (seen_eacces) return error.AccessDenied; | ||
| 14610 | return err; | ||
| 14611 | } | ||
| 14612 | |||
| 14613 | /// This function ignores PATH environment variable. | ||
| 14614 | pub fn posixExecvPath( | ||
| 14615 | path: [*:0]const u8, | ||
| 14616 | child_argv: [*:null]const ?[*:0]const u8, | ||
| 14617 | envp: [*:null]const ?[*:0]const u8, | ||
| 14618 | ) process.ReplaceError { | ||
| 14619 | try Thread.checkCancel(); | ||
| 14620 | switch (posix.errno(posix.system.execve(path, child_argv, envp))) { | ||
| 14621 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. | ||
| 14622 | .@"2BIG" => return error.SystemResources, | ||
| 14623 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 14624 | .NAMETOOLONG => return error.NameTooLong, | ||
| 14625 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 14626 | .NOMEM => return error.SystemResources, | ||
| 14627 | .ACCES => return error.AccessDenied, | ||
| 14628 | .PERM => return error.PermissionDenied, | ||
| 14629 | .INVAL => return error.InvalidExe, | ||
| 14630 | .NOEXEC => return error.InvalidExe, | ||
| 14631 | .IO => return error.FileSystem, | ||
| 14632 | .LOOP => return error.FileSystem, | ||
| 14633 | .ISDIR => return error.IsDir, | ||
| 14634 | .NOENT => return error.FileNotFound, | ||
| 14635 | .NOTDIR => return error.NotDir, | ||
| 14636 | .TXTBSY => return error.FileBusy, | ||
| 14637 | else => |err| switch (native_os) { | ||
| 14638 | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (err) { | ||
| 14639 | .BADEXEC => return error.InvalidExe, | ||
| 14640 | .BADARCH => return error.InvalidExe, | ||
| 14641 | else => return posix.unexpectedErrno(err), | ||
| 14642 | }, | ||
| 14643 | .linux => switch (err) { | ||
| 14644 | .LIBBAD => return error.InvalidExe, | ||
| 14645 | else => return posix.unexpectedErrno(err), | ||
| 14646 | }, | ||
| 14647 | else => return posix.unexpectedErrno(err), | ||
| 14648 | }, | ||
| 14649 | } | ||
| 14650 | } | ||
| 14651 | |||
| 14652 | fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | ||
| 14653 | var rd_h: windows.HANDLE = undefined; | ||
| 14654 | var wr_h: windows.HANDLE = undefined; | ||
| 14655 | try windows.CreatePipe(&rd_h, &wr_h, sattr); | ||
| 14656 | errdefer windowsDestroyPipe(rd_h, wr_h); | ||
| 14657 | try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0); | ||
| 14658 | rd.* = rd_h; | ||
| 14659 | wr.* = wr_h; | ||
| 14660 | } | ||
| 14661 | |||
| 14662 | fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { | ||
| 14663 | if (rd) |h| posix.close(h); | ||
| 14664 | if (wr) |h| posix.close(h); | ||
| 14665 | } | ||
| 14666 | |||
| 14667 | fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | ||
| 14668 | var tmp_bufw: [128]u16 = undefined; | ||
| 14669 | |||
| 14670 | // Anonymous pipes are built upon Named pipes. | ||
| 14671 | // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe | ||
| 14672 | // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. | ||
| 14673 | // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations | ||
| 14674 | const pipe_path = blk: { | ||
| 14675 | var tmp_buf: [128]u8 = undefined; | ||
| 14676 | // Forge a random path for the pipe. | ||
| 14677 | const pipe_path = std.fmt.bufPrintSentinel( | ||
| 14678 | &tmp_buf, | ||
| 14679 | "\\\\.\\pipe\\zig-childprocess-{d}-{d}", | ||
| 14680 | .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) }, | ||
| 14681 | 0, | ||
| 14682 | ) catch unreachable; | ||
| 14683 | const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable; | ||
| 14684 | tmp_bufw[len] = 0; | ||
| 14685 | break :blk tmp_bufw[0..len :0]; | ||
| 14686 | }; | ||
| 14687 | |||
| 14688 | // Create the read handle that can be used with overlapped IO ops. | ||
| 14689 | const read_handle = windows.kernel32.CreateNamedPipeW( | ||
| 14690 | pipe_path.ptr, | ||
| 14691 | windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED, | ||
| 14692 | windows.PIPE_TYPE_BYTE, | ||
| 14693 | 1, | ||
| 14694 | 4096, | ||
| 14695 | 4096, | ||
| 14696 | 0, | ||
| 14697 | sattr, | ||
| 14698 | ); | ||
| 14699 | if (read_handle == windows.INVALID_HANDLE_VALUE) { | ||
| 14700 | switch (windows.GetLastError()) { | ||
| 14701 | else => |err| return windows.unexpectedError(err), | ||
| 14702 | } | ||
| 14703 | } | ||
| 14704 | errdefer posix.close(read_handle); | ||
| 14705 | |||
| 14706 | var sattr_copy = sattr.*; | ||
| 14707 | const write_handle = windows.kernel32.CreateFileW( | ||
| 14708 | pipe_path.ptr, | ||
| 14709 | .{ .GENERIC = .{ .WRITE = true } }, | ||
| 14710 | 0, | ||
| 14711 | &sattr_copy, | ||
| 14712 | windows.OPEN_EXISTING, | ||
| 14713 | @bitCast(windows.FILE.ATTRIBUTE{ .NORMAL = true }), | ||
| 14714 | null, | ||
| 14715 | ); | ||
| 14716 | if (write_handle == windows.INVALID_HANDLE_VALUE) { | ||
| 14717 | switch (windows.GetLastError()) { | ||
| 14718 | else => |err| return windows.unexpectedError(err), | ||
| 14719 | } | ||
| 14720 | } | ||
| 14721 | errdefer posix.close(write_handle); | ||
| 14722 | |||
| 14723 | try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0); | ||
| 14724 | |||
| 14725 | rd.* = read_handle; | ||
| 14726 | wr.* = write_handle; | ||
| 14727 | } | ||
| 14728 | |||
| 14729 | var pipe_name_counter = std.atomic.Value(u32).init(1); | ||
| 14730 | |||
| 14731 | fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { | ||
| 14732 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 14733 | |||
| 14734 | t.scanEnviron(); | ||
| 14735 | |||
| 14736 | const int = try t.environ.zig_progress_handle; | ||
| 14737 | |||
| 14738 | return .{ .handle = switch (@typeInfo(Io.File.Handle)) { | ||
| 14739 | .int => int, | ||
| 14740 | .pointer => @ptrFromInt(int), | ||
| 14741 | else => return error.UnsupportedOperation, | ||
| 14742 | } }; | ||
| 14743 | } | ||
| 14744 | |||
| 14745 | pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { | ||
| 14746 | t.scanEnviron(); | ||
| 14747 | return @field(t.environ.string, name); | ||
| 14748 | } | ||
| 14749 | |||
| 14750 | test { | ||
| 14751 | _ = @import("Threaded/test.zig"); | ||
| 14752 | } | ||
| 14753 | |||
| 14754 | const use_parking_futex = switch (builtin.target.os.tag) { | ||
| 14755 | .windows => true, // RtlWaitOnAddress is a userland implementation anyway | ||
| 14756 | .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now. | ||
| 14757 | .illumos => true, // Illumos has no futex mechanism | ||
| 14758 | else => false, | ||
| 14759 | }; | ||
| 14760 | const use_parking_sleep = switch (builtin.target.os.tag) { | ||
| 14761 | // On Windows, we can implement sleep either with `NtDelayExecution` (which is how `SleepEx` in | ||
| 14762 | // kernel32 works) or `NtWaitForAlertByThreadId` (thread parking). We're already using the | ||
| 14763 | // latter for futex, so we may as well use it for sleeping too, to maximise code reuse. I'm | ||
| 14764 | // also more confident that it will always correctly handle the cancelation race (so "unpark" | ||
| 14765 | // before "park" causes "park" to return immediately): it *seems* like alertable sleeps paired | ||
| 14766 | // with `NtAlertThread` do actually do this too, but there could be some caveat (e.g. it might | ||
| 14767 | // fail under some specific condition), whereas `NtWaitForAlertByThreadId` must reliably trigger | ||
| 14768 | // this behavior because `RtlWaitOnAddress` relies on it. | ||
| 14769 | .windows => true, | ||
| 14770 | |||
| 14771 | // These targets have `_lwp_park`, which is superior to POSIX nanosleep because it has a better | ||
| 14772 | // cancelation mechanism. | ||
| 14773 | .netbsd, | ||
| 14774 | .illumos, | ||
| 14775 | => true, | ||
| 14776 | |||
| 14777 | else => false, | ||
| 14778 | }; | ||
| 14779 | |||
| 14780 | const parking_futex = struct { | ||
| 14781 | comptime { | ||
| 14782 | assert(use_parking_futex); | ||
| 14783 | } | ||
| 14784 | |||
| 14785 | const Bucket = struct { | ||
| 14786 | /// Used as a fast check for `wake` to avoid having to acquire `mutex` to discover there are no | ||
| 14787 | /// waiters. It is important for `wait` to increment this *before* checking the futex value to | ||
| 14788 | /// avoid a race. | ||
| 14789 | num_waiters: std.atomic.Value(u32), | ||
| 14790 | /// Protects `waiters`. | ||
| 14791 | mutex: std.Thread.Mutex, | ||
| 14792 | waiters: std.DoublyLinkedList, | ||
| 14793 | |||
| 14794 | /// Prevent false sharing between buckets. | ||
| 14795 | _: void align(std.atomic.cache_line) = {}, | ||
| 14796 | |||
| 14797 | const init: Bucket = .{ .num_waiters = .init(0), .mutex = .{}, .waiters = .{} }; | ||
| 14798 | }; | ||
| 14799 | |||
| 14800 | const Waiter = struct { | ||
| 14801 | node: std.DoublyLinkedList.Node, | ||
| 14802 | address: usize, | ||
| 14803 | tid: std.Thread.Id, | ||
| 14804 | /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread | ||
| 14805 | /// which atomically updates it (to `.none` or `.canceling`) is responsible for: | ||
| 14806 | /// | ||
| 14807 | /// * Removing the `Waiter` from `Bucket.waiters` | ||
| 14808 | /// * Decrementing `Bucket.num_waiters` | ||
| 14809 | /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope | ||
| 14810 | /// while it is still in the `Bucket`). | ||
| 14811 | thread_status: *std.atomic.Value(Thread.Status), | ||
| 14812 | }; | ||
| 14813 | |||
| 14814 | fn bucketForAddress(address: usize) *Bucket { | ||
| 14815 | const global = struct { | ||
| 14816 | /// Length must be a power of two. The longer this array, the less likely contention is | ||
| 14817 | /// between different futexes. This length seems like it'll provide a reasonable balance | ||
| 14818 | /// between contention and memory usage: assuming a 128-byte `Bucket` (due to cache line | ||
| 14819 | /// alignment), this uses 32 KiB of memory. | ||
| 14820 | var buckets: [256]Bucket = @splat(.init); | ||
| 14821 | }; | ||
| 14822 | |||
| 14823 | // Here we use Fibonacci hashing: the golden ratio can be used to evenly redistribute input | ||
| 14824 | // values across a range, giving a poor, but extremely quick to compute, hash. | ||
| 14825 | |||
| 14826 | // This literal is the rounded value of '2^64 / phi' (where 'phi' is the golden ratio). The | ||
| 14827 | // shift then converts it to '2^b / phi', where 'b' is the pointer bit width. | ||
| 14828 | const fibonacci_multiplier = 0x9E3779B97F4A7C15 >> (64 - @bitSizeOf(usize)); | ||
| 14829 | const hashed = address *% fibonacci_multiplier; | ||
| 14830 | |||
| 14831 | comptime assert(std.math.isPowerOfTwo(global.buckets.len)); | ||
| 14832 | // The high bits of `hashed` have better entropy than the low bits. | ||
| 14833 | const index = hashed >> (@bitSizeOf(usize) - @ctz(global.buckets.len)); | ||
| 14834 | |||
| 14835 | return &global.buckets[index]; | ||
| 14836 | } | ||
| 14837 | |||
| 14838 | fn wait(ptr: *const u32, expect: u32, uncancelable: bool, timeout: Io.Timeout) Io.Cancelable!void { | ||
| 14839 | const bucket = bucketForAddress(@intFromPtr(ptr)); | ||
| 14840 | |||
| 14841 | // Put the threadlocal access outside of the critical section. | ||
| 14842 | const opt_thread = Thread.current; | ||
| 14843 | const self_tid = if (opt_thread) |thread| thread.id else std.Thread.getCurrentId(); | ||
| 14844 | |||
| 14845 | var waiter: Waiter = .{ | ||
| 14846 | .node = undefined, // populated by list append | ||
| 14847 | .address = @intFromPtr(ptr), | ||
| 14848 | .tid = self_tid, | ||
| 14849 | .thread_status = undefined, // populated in critical section | ||
| 14850 | }; | ||
| 14851 | |||
| 14852 | var status_buf: std.atomic.Value(Thread.Status) = undefined; | ||
| 14853 | |||
| 14854 | { | ||
| 14855 | bucket.mutex.lock(); | ||
| 14856 | defer bucket.mutex.unlock(); | ||
| 14857 | |||
| 14858 | _ = bucket.num_waiters.fetchAdd(1, .acquire); | ||
| 14859 | |||
| 14860 | if (@atomicLoad(u32, ptr, .monotonic) != expect) { | ||
| 14861 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | ||
| 14862 | return; | ||
| 14863 | } | ||
| 14864 | |||
| 14865 | // This is in the critical section to avoid marking the thread as parked until we're | ||
| 14866 | // certain that we're actually going to park. | ||
| 14867 | waiter.thread_status = status: { | ||
| 14868 | cancelable: { | ||
| 14869 | if (uncancelable) break :cancelable; | ||
| 14870 | const thread = opt_thread orelse break :cancelable; | ||
| 14871 | switch (thread.cancel_protection) { | ||
| 14872 | .blocked => break :cancelable, | ||
| 14873 | .unblocked => {}, | ||
| 14874 | } | ||
| 14875 | thread.futex_waiter = &waiter; | ||
| 14876 | const old_status = thread.status.fetchOr( | ||
| 14877 | .{ .cancelation = @enumFromInt(0b001), .awaitable = .null }, | ||
| 14878 | .release, // release `thread.futex_waiter` | ||
| 14879 | ); | ||
| 14880 | switch (old_status.cancelation) { | ||
| 14881 | .none => {}, // status is now `.parked` | ||
| 14882 | .canceling => { | ||
| 14883 | // status is now `.canceled` | ||
| 14884 | assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0); | ||
| 14885 | return error.Canceled; | ||
| 14886 | }, | ||
| 14887 | .canceled => break :cancelable, // status is still `.canceled` | ||
| 14888 | .parked => unreachable, | ||
| 14889 | .blocked => unreachable, | ||
| 14890 | .blocked_windows_dns => unreachable, | ||
| 14891 | .blocked_canceling => unreachable, | ||
| 14892 | } | ||
| 14893 | // We could now be unparked for a cancelation at any time! | ||
| 14894 | break :status &thread.status; | ||
| 14895 | } | ||
| 14896 | // This is an uncancelable wait, so just use `status_buf`. Note that the value of | ||
| 14897 | // `status_buf.awaitable` is irrelevant because this is only visible to futex code, | ||
| 14898 | // while only cancelation cares about `awaitable`. | ||
| 14899 | status_buf.raw = .{ .cancelation = .parked, .awaitable = .null }; | ||
| 14900 | break :status &status_buf; | ||
| 14901 | }; | ||
| 14902 | |||
| 14903 | bucket.waiters.append(&waiter.node); | ||
| 14904 | } | ||
| 14905 | |||
| 14906 | if (park(timeout, ptr)) { | ||
| 14907 | // We were unparked by either `wake` or cancelation, so our current status is either | ||
| 14908 | // `.none` or `.canceling`. In either case, they've already removed `waiter` from | ||
| 12854 | // `bucket`, so we have nothing more to do! | 14909 | // `bucket`, so we have nothing more to do! |
| 12855 | } else |err| switch (err) { | 14910 | } else |err| switch (err) { |
| 12856 | error.Timeout => { | 14911 | error.Timeout => { |
| ... | @@ -13151,3 +15206,140 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void { | ... | @@ -13151,3 +15206,140 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void { |
| 13151 | else => comptime unreachable, | 15206 | else => comptime unreachable, |
| 13152 | } | 15207 | } |
| 13153 | } | 15208 | } |
| 15209 | |||
| 15210 | pub const PipeError = error{ | ||
| 15211 | SystemFdQuotaExceeded, | ||
| 15212 | ProcessFdQuotaExceeded, | ||
| 15213 | } || Io.UnexpectedError; | ||
| 15214 | |||
| 15215 | pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t { | ||
| 15216 | var fds: [2]posix.fd_t = undefined; | ||
| 15217 | |||
| 15218 | if (@TypeOf(posix.system.pipe2) != void) { | ||
| 15219 | switch (posix.errno(posix.system.pipe2(&fds, flags))) { | ||
| 15220 | .SUCCESS => return fds, | ||
| 15221 | .INVAL => |err| return errnoBug(err), // Invalid flags | ||
| 15222 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 15223 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 15224 | else => |err| return posix.unexpectedErrno(err), | ||
| 15225 | } | ||
| 15226 | } | ||
| 15227 | |||
| 15228 | switch (posix.errno(posix.system.pipe(&fds))) { | ||
| 15229 | .SUCCESS => {}, | ||
| 15230 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 15231 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 15232 | else => |err| return posix.unexpectedErrno(err), | ||
| 15233 | } | ||
| 15234 | errdefer { | ||
| 15235 | posix.close(fds[0]); | ||
| 15236 | posix.close(fds[1]); | ||
| 15237 | } | ||
| 15238 | |||
| 15239 | // https://github.com/ziglang/zig/issues/18882 | ||
| 15240 | if (@as(u32, @bitCast(flags)) == 0) return fds; | ||
| 15241 | |||
| 15242 | // CLOEXEC is special, it's a file descriptor flag and must be set using | ||
| 15243 | // F.SETFD. | ||
| 15244 | if (flags.CLOEXEC) for (fds) |fd| { | ||
| 15245 | switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(u32, posix.FD_CLOEXEC)))) { | ||
| 15246 | .SUCCESS => {}, | ||
| 15247 | else => |err| return posix.unexpectedErrno(err), | ||
| 15248 | } | ||
| 15249 | }; | ||
| 15250 | |||
| 15251 | const new_flags: u32 = f: { | ||
| 15252 | var new_flags = flags; | ||
| 15253 | new_flags.CLOEXEC = false; | ||
| 15254 | break :f @bitCast(new_flags); | ||
| 15255 | }; | ||
| 15256 | |||
| 15257 | // Set every other flag affecting the file status using F.SETFL. | ||
| 15258 | if (new_flags != 0) for (fds) |fd| { | ||
| 15259 | switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, new_flags))) { | ||
| 15260 | .SUCCESS => {}, | ||
| 15261 | .INVAL => |err| return errnoBug(err), | ||
| 15262 | else => |err| return posix.unexpectedErrno(err), | ||
| 15263 | } | ||
| 15264 | }; | ||
| 15265 | |||
| 15266 | return fds; | ||
| 15267 | } | ||
| 15268 | |||
| 15269 | pub const DupError = error{ | ||
| 15270 | ProcessFdQuotaExceeded, | ||
| 15271 | SystemResources, | ||
| 15272 | } || Io.UnexpectedError || Io.Cancelable; | ||
| 15273 | |||
| 15274 | pub fn dup2(old_fd: posix.fd_t, new_fd: posix.fd_t) DupError!void { | ||
| 15275 | const syscall: Syscall = try .start(); | ||
| 15276 | while (true) switch (posix.errno(posix.system.dup2(old_fd, new_fd))) { | ||
| 15277 | .SUCCESS => return syscall.finish(), | ||
| 15278 | .BUSY, .INTR => { | ||
| 15279 | try syscall.checkCancel(); | ||
| 15280 | continue; | ||
| 15281 | }, | ||
| 15282 | .INVAL => |err| return syscall.errnoBug(err), // invalid parameters | ||
| 15283 | .BADF => |err| return syscall.errnoBug(err), // use after free | ||
| 15284 | .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded), | ||
| 15285 | .NOMEM => return syscall.fail(error.SystemResources), | ||
| 15286 | else => |err| return syscall.unexpectedErrno(err), | ||
| 15287 | }; | ||
| 15288 | } | ||
| 15289 | |||
| 15290 | pub const FchdirError = error{ | ||
| 15291 | AccessDenied, | ||
| 15292 | NotDir, | ||
| 15293 | FileSystem, | ||
| 15294 | } || Io.Cancelable || Io.UnexpectedError; | ||
| 15295 | |||
| 15296 | pub fn fchdir(fd: posix.fd_t) FchdirError!void { | ||
| 15297 | if (fd == posix.AT.FDCWD) return; | ||
| 15298 | const syscall: Syscall = try .start(); | ||
| 15299 | while (true) switch (posix.errno(posix.system.fchdir(fd))) { | ||
| 15300 | .SUCCESS => return syscall.finish(), | ||
| 15301 | .INTR => { | ||
| 15302 | try syscall.checkCancel(); | ||
| 15303 | continue; | ||
| 15304 | }, | ||
| 15305 | .ACCES => return syscall.fail(error.AccessDenied), | ||
| 15306 | .NOTDIR => return syscall.fail(error.NotDir), | ||
| 15307 | .IO => return syscall.fail(error.FileSystem), | ||
| 15308 | .BADF => |err| return syscall.errnoBug(err), | ||
| 15309 | else => |err| return syscall.unexpectedErrno(err), | ||
| 15310 | }; | ||
| 15311 | } | ||
| 15312 | |||
| 15313 | pub const ChdirError = error{ | ||
| 15314 | AccessDenied, | ||
| 15315 | FileSystem, | ||
| 15316 | SymLinkLoop, | ||
| 15317 | NameTooLong, | ||
| 15318 | FileNotFound, | ||
| 15319 | SystemResources, | ||
| 15320 | NotDir, | ||
| 15321 | BadPathName, | ||
| 15322 | } || Io.Cancelable || Io.UnexpectedError; | ||
| 15323 | |||
| 15324 | pub fn chdir(dir_path: []const u8) ChdirError!void { | ||
| 15325 | var path_buffer: [posix.PATH_MAX]u8 = undefined; | ||
| 15326 | const dir_path_posix = try pathToPosix(dir_path, &path_buffer); | ||
| 15327 | const syscall: Syscall = try .start(); | ||
| 15328 | while (true) switch (posix.errno(posix.system.chdir(dir_path_posix))) { | ||
| 15329 | .SUCCESS => return syscall.finish(), | ||
| 15330 | .INTR => { | ||
| 15331 | try syscall.checkCancel(); | ||
| 15332 | continue; | ||
| 15333 | }, | ||
| 15334 | .ACCES => return syscall.fail(error.AccessDenied), | ||
| 15335 | .IO => return syscall.fail(error.FileSystem), | ||
| 15336 | .LOOP => return syscall.fail(error.SymLinkLoop), | ||
| 15337 | .NAMETOOLONG => return syscall.fail(error.NameTooLong), | ||
| 15338 | .NOENT => return syscall.fail(error.FileNotFound), | ||
| 15339 | .NOMEM => return syscall.fail(error.SystemResources), | ||
| 15340 | .NOTDIR => return syscall.fail(error.NotDir), | ||
| 15341 | .ILSEQ => return syscall.fail(error.BadPathName), | ||
| 15342 | .FAULT => |err| return syscall.errnoBug(err), | ||
| 15343 | else => |err| return syscall.unexpectedErrno(err), | ||
| 15344 | }; | ||
| 15345 | } |
lib/std/Io/Threaded/test.zig+25-7| ... | @@ -13,7 +13,10 @@ test "concurrent vs main prevents deadlock via oversubscription" { | ... | @@ -13,7 +13,10 @@ test "concurrent vs main prevents deadlock via oversubscription" { |
| 13 | return error.SkipZigTest; | 13 | return error.SkipZigTest; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | var threaded: Io.Threaded = .init(std.testing.allocator, .{}); | 16 | var threaded: Io.Threaded = .init(std.testing.allocator, .{ |
| 17 | .argv0 = .empty, | ||
| 18 | .environ = .empty, | ||
| 19 | }); | ||
| 17 | defer threaded.deinit(); | 20 | defer threaded.deinit(); |
| 18 | const io = threaded.io(); | 21 | const io = threaded.io(); |
| 19 | 22 | ||
| ... | @@ -46,7 +49,10 @@ test "concurrent vs concurrent prevents deadlock via oversubscription" { | ... | @@ -46,7 +49,10 @@ test "concurrent vs concurrent prevents deadlock via oversubscription" { |
| 46 | return error.SkipZigTest; | 49 | return error.SkipZigTest; |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 49 | var threaded: Io.Threaded = .init(std.testing.allocator, .{}); | 52 | var threaded: Io.Threaded = .init(std.testing.allocator, .{ |
| 53 | .argv0 = .empty, | ||
| 54 | .environ = .empty, | ||
| 55 | }); | ||
| 50 | defer threaded.deinit(); | 56 | defer threaded.deinit(); |
| 51 | const io = threaded.io(); | 57 | const io = threaded.io(); |
| 52 | 58 | ||
| ... | @@ -80,7 +86,10 @@ test "async/concurrent context and result alignment" { | ... | @@ -80,7 +86,10 @@ test "async/concurrent context and result alignment" { |
| 80 | var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined; | 86 | var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined; |
| 81 | var fba: std.heap.FixedBufferAllocator = .init(&buffer); | 87 | var fba: std.heap.FixedBufferAllocator = .init(&buffer); |
| 82 | 88 | ||
| 83 | var threaded: std.Io.Threaded = .init(fba.allocator(), .{}); | 89 | var threaded: std.Io.Threaded = .init(fba.allocator(), .{ |
| 90 | .argv0 = .empty, | ||
| 91 | .environ = .empty, | ||
| 92 | }); | ||
| 84 | defer threaded.deinit(); | 93 | defer threaded.deinit(); |
| 85 | const io = threaded.io(); | 94 | const io = threaded.io(); |
| 86 | 95 | ||
| ... | @@ -113,7 +122,10 @@ test "Group.async context alignment" { | ... | @@ -113,7 +122,10 @@ test "Group.async context alignment" { |
| 113 | var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined; | 122 | var buffer: [2048]u8 align(@alignOf(ByteArray512)) = undefined; |
| 114 | var fba: std.heap.FixedBufferAllocator = .init(&buffer); | 123 | var fba: std.heap.FixedBufferAllocator = .init(&buffer); |
| 115 | 124 | ||
| 116 | var threaded: std.Io.Threaded = .init(fba.allocator(), .{}); | 125 | var threaded: std.Io.Threaded = .init(fba.allocator(), .{ |
| 126 | .argv0 = .empty, | ||
| 127 | .environ = .empty, | ||
| 128 | }); | ||
| 117 | defer threaded.deinit(); | 129 | defer threaded.deinit(); |
| 118 | const io = threaded.io(); | 130 | const io = threaded.io(); |
| 119 | 131 | ||
| ... | @@ -133,7 +145,10 @@ fn returnArray() [32]u8 { | ... | @@ -133,7 +145,10 @@ fn returnArray() [32]u8 { |
| 133 | } | 145 | } |
| 134 | 146 | ||
| 135 | test "async with array return type" { | 147 | test "async with array return type" { |
| 136 | var threaded: std.Io.Threaded = .init(std.testing.allocator, .{}); | 148 | var threaded: std.Io.Threaded = .init(std.testing.allocator, .{ |
| 149 | .argv0 = .empty, | ||
| 150 | .environ = .empty, | ||
| 151 | }); | ||
| 137 | defer threaded.deinit(); | 152 | defer threaded.deinit(); |
| 138 | const io = threaded.io(); | 153 | const io = threaded.io(); |
| 139 | 154 | ||
| ... | @@ -155,7 +170,10 @@ test "cancel blocked read from pipe" { | ... | @@ -155,7 +170,10 @@ test "cancel blocked read from pipe" { |
| 155 | } | 170 | } |
| 156 | }; | 171 | }; |
| 157 | 172 | ||
| 158 | var threaded: std.Io.Threaded = .init(std.testing.allocator, .{}); | 173 | var threaded: std.Io.Threaded = .init(std.testing.allocator, .{ |
| 174 | .argv0 = .empty, | ||
| 175 | .environ = .empty, | ||
| 176 | }); | ||
| 159 | defer threaded.deinit(); | 177 | defer threaded.deinit(); |
| 160 | const io = threaded.io(); | 178 | const io = threaded.io(); |
| 161 | 179 | ||
| ... | @@ -169,7 +187,7 @@ test "cancel blocked read from pipe" { | ... | @@ -169,7 +187,7 @@ test "cancel blocked read from pipe" { |
| 169 | .bInheritHandle = std.os.windows.FALSE, | 187 | .bInheritHandle = std.os.windows.FALSE, |
| 170 | }), | 188 | }), |
| 171 | else => { | 189 | else => { |
| 172 | const pipe = try std.posix.pipe(); | 190 | const pipe = try std.Io.Threaded.pipe2(.{}); |
| 173 | read_end = .{ .handle = pipe[0] }; | 191 | read_end = .{ .handle = pipe[0] }; |
| 174 | write_end = .{ .handle = pipe[1] }; | 192 | write_end = .{ .handle = pipe[1] }; |
| 175 | }, | 193 | }, |
lib/std/Progress.zig+11-12| ... | @@ -422,7 +422,7 @@ pub const StartFailure = union(enum) { | ... | @@ -422,7 +422,7 @@ pub const StartFailure = union(enum) { |
| 422 | unstarted, | 422 | unstarted, |
| 423 | spawn_ipc_worker: error{ConcurrencyUnavailable}, | 423 | spawn_ipc_worker: error{ConcurrencyUnavailable}, |
| 424 | spawn_update_worker: error{ConcurrencyUnavailable}, | 424 | spawn_update_worker: error{ConcurrencyUnavailable}, |
| 425 | parse_env_var: error{ InvalidCharacter, Overflow }, | 425 | parent_ipc: error{ UnsupportedOperation, UnrecognizedFormat }, |
| 426 | }; | 426 | }; |
| 427 | 427 | ||
| 428 | const node_storage_buffer_len = 83; | 428 | const node_storage_buffer_len = 83; |
| ... | @@ -446,6 +446,12 @@ const noop_impl = builtin.single_threaded or switch (builtin.os.tag) { | ... | @@ -446,6 +446,12 @@ const noop_impl = builtin.single_threaded or switch (builtin.os.tag) { |
| 446 | else => false, | 446 | else => false, |
| 447 | }; | 447 | }; |
| 448 | 448 | ||
| 449 | pub const ParentFileError = error{ | ||
| 450 | UnsupportedOperation, | ||
| 451 | EnvironmentVariableMissing, | ||
| 452 | UnrecognizedFormat, | ||
| 453 | }; | ||
| 454 | |||
| 449 | /// Initializes a global Progress instance. | 455 | /// Initializes a global Progress instance. |
| 450 | /// | 456 | /// |
| 451 | /// Asserts there is only one global Progress instance. | 457 | /// Asserts there is only one global Progress instance. |
| ... | @@ -476,20 +482,13 @@ pub fn start(io: Io, options: Options) Node { | ... | @@ -476,20 +482,13 @@ pub fn start(io: Io, options: Options) Node { |
| 476 | 482 | ||
| 477 | global_progress.io = io; | 483 | global_progress.io = io; |
| 478 | 484 | ||
| 479 | if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| { | 485 | if (io.vtable.progressParentFile(io.userdata)) |ipc_file| { |
| 480 | global_progress.update_worker = io.concurrent(ipcThreadRun, .{ | 486 | global_progress.update_worker = io.concurrent(ipcThreadRun, .{ io, ipc_file }) catch |err| { |
| 481 | io, | ||
| 482 | @as(Io.File, .{ .handle = switch (@typeInfo(Io.File.Handle)) { | ||
| 483 | .int => ipc_fd, | ||
| 484 | .pointer => @ptrFromInt(ipc_fd), | ||
| 485 | else => @compileError("unsupported fd_t of " ++ @typeName(Io.File.Handle)), | ||
| 486 | } }), | ||
| 487 | }) catch |err| { | ||
| 488 | global_progress.start_failure = .{ .spawn_ipc_worker = err }; | 487 | global_progress.start_failure = .{ .spawn_ipc_worker = err }; |
| 489 | return Node.none; | 488 | return Node.none; |
| 490 | }; | 489 | }; |
| 491 | } else |env_err| switch (env_err) { | 490 | } else |env_err| switch (env_err) { |
| 492 | error.EnvironmentVariableNotFound => { | 491 | error.EnvironmentVariableMissing => { |
| 493 | if (options.disable_printing) { | 492 | if (options.disable_printing) { |
| 494 | return Node.none; | 493 | return Node.none; |
| 495 | } | 494 | } |
| ... | @@ -535,7 +534,7 @@ pub fn start(io: Io, options: Options) Node { | ... | @@ -535,7 +534,7 @@ pub fn start(io: Io, options: Options) Node { |
| 535 | } | 534 | } |
| 536 | }, | 535 | }, |
| 537 | else => |e| { | 536 | else => |e| { |
| 538 | global_progress.start_failure = .{ .parse_env_var = e }; | 537 | global_progress.start_failure = .{ .parent_ipc = e }; |
| 539 | return Node.none; | 538 | return Node.none; |
| 540 | }, | 539 | }, |
| 541 | } | 540 | } |
lib/std/Random/benchmark.zig+6-5| ... | @@ -123,14 +123,15 @@ fn mode(comptime x: comptime_int) comptime_int { | ... | @@ -123,14 +123,15 @@ fn mode(comptime x: comptime_int) comptime_int { |
| 123 | return if (builtin.mode == .Debug) x / 64 else x; | 123 | return if (builtin.mode == .Debug) x / 64 else x; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | pub fn main() !void { | 126 | pub fn main(init: std.process.Init) !void { |
| 127 | const io = init.io; | ||
| 128 | const arena = init.arena.allocator(); | ||
| 129 | |||
| 127 | var stdout_buffer: [0x100]u8 = undefined; | 130 | var stdout_buffer: [0x100]u8 = undefined; |
| 128 | var stdout_writer = Io.File.stdout().writer(&stdout_buffer); | 131 | var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); |
| 129 | const stdout = &stdout_writer.interface; | 132 | const stdout = &stdout_writer.interface; |
| 130 | 133 | ||
| 131 | var buffer: [1024]u8 = undefined; | 134 | const args = try init.minimal.args.toSlice(arena); |
| 132 | var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); | ||
| 133 | const args = try std.process.argsAlloc(fixed.allocator()); | ||
| 134 | 135 | ||
| 135 | var filter: ?[]u8 = ""; | 136 | var filter: ?[]u8 = ""; |
| 136 | var count: usize = mode(128 * MiB); | 137 | var count: usize = mode(128 * MiB); |
lib/std/c.zig+22-22| ... | @@ -3764,8 +3764,8 @@ pub const W = switch (native_os) { | ... | @@ -3764,8 +3764,8 @@ pub const W = switch (native_os) { |
| 3764 | pub fn EXITSTATUS(x: u32) u8 { | 3764 | pub fn EXITSTATUS(x: u32) u8 { |
| 3765 | return @as(u8, @intCast(x >> 8)); | 3765 | return @as(u8, @intCast(x >> 8)); |
| 3766 | } | 3766 | } |
| 3767 | pub fn TERMSIG(x: u32) u32 { | 3767 | pub fn TERMSIG(x: u32) SIG { |
| 3768 | return status(x); | 3768 | return @enumFromInt(status(x)); |
| 3769 | } | 3769 | } |
| 3770 | pub fn STOPSIG(x: u32) u32 { | 3770 | pub fn STOPSIG(x: u32) u32 { |
| 3771 | return x >> 8; | 3771 | return x >> 8; |
| ... | @@ -3797,14 +3797,14 @@ pub const W = switch (native_os) { | ... | @@ -3797,14 +3797,14 @@ pub const W = switch (native_os) { |
| 3797 | pub fn EXITSTATUS(s: u32) u8 { | 3797 | pub fn EXITSTATUS(s: u32) u8 { |
| 3798 | return @as(u8, @intCast((s & 0xff00) >> 8)); | 3798 | return @as(u8, @intCast((s & 0xff00) >> 8)); |
| 3799 | } | 3799 | } |
| 3800 | pub fn TERMSIG(s: u32) u32 { | 3800 | pub fn TERMSIG(s: u32) SIG { |
| 3801 | return s & 0x7f; | 3801 | return @enumFromInt(s & 0x7f); |
| 3802 | } | 3802 | } |
| 3803 | pub fn STOPSIG(s: u32) u32 { | 3803 | pub fn STOPSIG(s: u32) u32 { |
| 3804 | return EXITSTATUS(s); | 3804 | return EXITSTATUS(s); |
| 3805 | } | 3805 | } |
| 3806 | pub fn IFEXITED(s: u32) bool { | 3806 | pub fn IFEXITED(s: u32) bool { |
| 3807 | return TERMSIG(s) == 0; | 3807 | return (s & 0x7f) == 0; |
| 3808 | } | 3808 | } |
| 3809 | pub fn IFSTOPPED(s: u32) bool { | 3809 | pub fn IFSTOPPED(s: u32) bool { |
| 3810 | return @as(u16, @truncate((((s & 0xffff) *% 0x10001) >> 8))) > 0x7f00; | 3810 | return @as(u16, @truncate((((s & 0xffff) *% 0x10001) >> 8))) > 0x7f00; |
| ... | @@ -3825,14 +3825,14 @@ pub const W = switch (native_os) { | ... | @@ -3825,14 +3825,14 @@ pub const W = switch (native_os) { |
| 3825 | pub fn EXITSTATUS(s: u32) u8 { | 3825 | pub fn EXITSTATUS(s: u32) u8 { |
| 3826 | return @as(u8, @intCast((s >> 8) & 0xff)); | 3826 | return @as(u8, @intCast((s >> 8) & 0xff)); |
| 3827 | } | 3827 | } |
| 3828 | pub fn TERMSIG(s: u32) u32 { | 3828 | pub fn TERMSIG(s: u32) SIG { |
| 3829 | return s & 0x7f; | 3829 | return @enumFromInt(s & 0x7f); |
| 3830 | } | 3830 | } |
| 3831 | pub fn STOPSIG(s: u32) u32 { | 3831 | pub fn STOPSIG(s: u32) u32 { |
| 3832 | return EXITSTATUS(s); | 3832 | return EXITSTATUS(s); |
| 3833 | } | 3833 | } |
| 3834 | pub fn IFEXITED(s: u32) bool { | 3834 | pub fn IFEXITED(s: u32) bool { |
| 3835 | return TERMSIG(s) == 0; | 3835 | return (s & 0x7f) == 0; |
| 3836 | } | 3836 | } |
| 3837 | 3837 | ||
| 3838 | pub fn IFCONTINUED(s: u32) bool { | 3838 | pub fn IFCONTINUED(s: u32) bool { |
| ... | @@ -3859,14 +3859,14 @@ pub const W = switch (native_os) { | ... | @@ -3859,14 +3859,14 @@ pub const W = switch (native_os) { |
| 3859 | pub fn EXITSTATUS(s: u32) u8 { | 3859 | pub fn EXITSTATUS(s: u32) u8 { |
| 3860 | return @as(u8, @intCast((s >> 8) & 0xff)); | 3860 | return @as(u8, @intCast((s >> 8) & 0xff)); |
| 3861 | } | 3861 | } |
| 3862 | pub fn TERMSIG(s: u32) u32 { | 3862 | pub fn TERMSIG(s: u32) SIG { |
| 3863 | return s & 0x7f; | 3863 | return @enumFromInt(s & 0x7f); |
| 3864 | } | 3864 | } |
| 3865 | pub fn STOPSIG(s: u32) u32 { | 3865 | pub fn STOPSIG(s: u32) u32 { |
| 3866 | return EXITSTATUS(s); | 3866 | return EXITSTATUS(s); |
| 3867 | } | 3867 | } |
| 3868 | pub fn IFEXITED(s: u32) bool { | 3868 | pub fn IFEXITED(s: u32) bool { |
| 3869 | return TERMSIG(s) == 0; | 3869 | return (s & 0x7f) == 0; |
| 3870 | } | 3870 | } |
| 3871 | 3871 | ||
| 3872 | pub fn IFCONTINUED(s: u32) bool { | 3872 | pub fn IFCONTINUED(s: u32) bool { |
| ... | @@ -3893,14 +3893,14 @@ pub const W = switch (native_os) { | ... | @@ -3893,14 +3893,14 @@ pub const W = switch (native_os) { |
| 3893 | pub fn EXITSTATUS(s: u32) u8 { | 3893 | pub fn EXITSTATUS(s: u32) u8 { |
| 3894 | return @as(u8, @intCast((s & 0xff00) >> 8)); | 3894 | return @as(u8, @intCast((s & 0xff00) >> 8)); |
| 3895 | } | 3895 | } |
| 3896 | pub fn TERMSIG(s: u32) u32 { | 3896 | pub fn TERMSIG(s: u32) SIG { |
| 3897 | return s & 0x7f; | 3897 | return @enumFromInt(s & 0x7f); |
| 3898 | } | 3898 | } |
| 3899 | pub fn STOPSIG(s: u32) u32 { | 3899 | pub fn STOPSIG(s: u32) u32 { |
| 3900 | return EXITSTATUS(s); | 3900 | return EXITSTATUS(s); |
| 3901 | } | 3901 | } |
| 3902 | pub fn IFEXITED(s: u32) bool { | 3902 | pub fn IFEXITED(s: u32) bool { |
| 3903 | return TERMSIG(s) == 0; | 3903 | return (s & 0x7f) == 0; |
| 3904 | } | 3904 | } |
| 3905 | pub fn IFSTOPPED(s: u32) bool { | 3905 | pub fn IFSTOPPED(s: u32) bool { |
| 3906 | return @as(u16, @truncate((((s & 0xffff) *% 0x10001) >> 8))) > 0x7f00; | 3906 | return @as(u16, @truncate((((s & 0xffff) *% 0x10001) >> 8))) > 0x7f00; |
| ... | @@ -3921,8 +3921,8 @@ pub const W = switch (native_os) { | ... | @@ -3921,8 +3921,8 @@ pub const W = switch (native_os) { |
| 3921 | return @as(u8, @intCast(s & 0xff)); | 3921 | return @as(u8, @intCast(s & 0xff)); |
| 3922 | } | 3922 | } |
| 3923 | 3923 | ||
| 3924 | pub fn TERMSIG(s: u32) u32 { | 3924 | pub fn TERMSIG(s: u32) SIG { |
| 3925 | return (s >> 8) & 0xff; | 3925 | return @enumFromInt((s >> 8) & 0xff); |
| 3926 | } | 3926 | } |
| 3927 | 3927 | ||
| 3928 | pub fn STOPSIG(s: u32) u32 { | 3928 | pub fn STOPSIG(s: u32) u32 { |
| ... | @@ -3949,14 +3949,14 @@ pub const W = switch (native_os) { | ... | @@ -3949,14 +3949,14 @@ pub const W = switch (native_os) { |
| 3949 | pub fn EXITSTATUS(s: u32) u8 { | 3949 | pub fn EXITSTATUS(s: u32) u8 { |
| 3950 | return @as(u8, @intCast((s >> 8) & 0xff)); | 3950 | return @as(u8, @intCast((s >> 8) & 0xff)); |
| 3951 | } | 3951 | } |
| 3952 | pub fn TERMSIG(s: u32) u32 { | 3952 | pub fn TERMSIG(s: u32) SIG { |
| 3953 | return (s & 0x7f); | 3953 | return @enumFromInt(s & 0x7f); |
| 3954 | } | 3954 | } |
| 3955 | pub fn STOPSIG(s: u32) u32 { | 3955 | pub fn STOPSIG(s: u32) u32 { |
| 3956 | return EXITSTATUS(s); | 3956 | return EXITSTATUS(s); |
| 3957 | } | 3957 | } |
| 3958 | pub fn IFEXITED(s: u32) bool { | 3958 | pub fn IFEXITED(s: u32) bool { |
| 3959 | return TERMSIG(s) == 0; | 3959 | return (s & 0x7f) == 0; |
| 3960 | } | 3960 | } |
| 3961 | 3961 | ||
| 3962 | pub fn IFCONTINUED(s: u32) bool { | 3962 | pub fn IFCONTINUED(s: u32) bool { |
| ... | @@ -3988,12 +3988,12 @@ pub const W = switch (native_os) { | ... | @@ -3988,12 +3988,12 @@ pub const W = switch (native_os) { |
| 3988 | return EXITSTATUS(s); | 3988 | return EXITSTATUS(s); |
| 3989 | } | 3989 | } |
| 3990 | 3990 | ||
| 3991 | pub fn TERMSIG(s: u32) u32 { | 3991 | pub fn TERMSIG(s: u32) SIG { |
| 3992 | return s & 0x7f; | 3992 | return @enumFromInt(s & 0x7f); |
| 3993 | } | 3993 | } |
| 3994 | 3994 | ||
| 3995 | pub fn IFEXITED(s: u32) bool { | 3995 | pub fn IFEXITED(s: u32) bool { |
| 3996 | return TERMSIG(s) == 0; | 3996 | return (s & 0x7f) == 0; |
| 3997 | } | 3997 | } |
| 3998 | 3998 | ||
| 3999 | pub fn IFSTOPPED(s: u32) bool { | 3999 | pub fn IFSTOPPED(s: u32) bool { |
lib/std/crypto/benchmark.zig+8-12| ... | @@ -503,16 +503,16 @@ fn mode(comptime x: comptime_int) comptime_int { | ... | @@ -503,16 +503,16 @@ fn mode(comptime x: comptime_int) comptime_int { |
| 503 | return if (builtin.mode == .Debug) x / 64 else x; | 503 | return if (builtin.mode == .Debug) x / 64 else x; |
| 504 | } | 504 | } |
| 505 | 505 | ||
| 506 | pub fn main() !void { | 506 | pub fn main(init: std.process.Init) !void { |
| 507 | const io = init.io; | ||
| 508 | const arena = init.arena.allocator(); | ||
| 509 | |||
| 507 | // Size of buffer is about size of printed message. | 510 | // Size of buffer is about size of printed message. |
| 508 | var stdout_buffer: [0x100]u8 = undefined; | 511 | var stdout_buffer: [0x100]u8 = undefined; |
| 509 | var stdout_writer = Io.File.stdout().writer(&stdout_buffer); | 512 | var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); |
| 510 | const stdout = &stdout_writer.interface; | 513 | const stdout = &stdout_writer.interface; |
| 511 | 514 | ||
| 512 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 515 | const args = try init.minimal.args.toSlice(arena); |
| 513 | defer arena.deinit(); | ||
| 514 | const arena_allocator = arena.allocator(); | ||
| 515 | const args = try std.process.argsAlloc(arena_allocator); | ||
| 516 | 516 | ||
| 517 | var filter: ?[]u8 = ""; | 517 | var filter: ?[]u8 = ""; |
| 518 | 518 | ||
| ... | @@ -556,13 +556,9 @@ pub fn main() !void { | ... | @@ -556,13 +556,9 @@ pub fn main() !void { |
| 556 | } | 556 | } |
| 557 | } | 557 | } |
| 558 | 558 | ||
| 559 | var io_threaded = std.Io.Threaded.init(arena_allocator, .{}); | ||
| 560 | defer io_threaded.deinit(); | ||
| 561 | const io = io_threaded.io(); | ||
| 562 | |||
| 563 | inline for (parallel_hashes) |H| { | 559 | inline for (parallel_hashes) |H| { |
| 564 | if (filter == null or std.mem.find(u8, H.name, filter.?) != null) { | 560 | if (filter == null or std.mem.find(u8, H.name, filter.?) != null) { |
| 565 | const throughput = try benchmarkHashParallel(H.ty, mode(128 * MiB), arena_allocator, io); | 561 | const throughput = try benchmarkHashParallel(H.ty, mode(128 * MiB), arena, io); |
| 566 | try stdout.print("{s:>17}: {:10} MiB/s\n", .{ H.name, throughput / (1 * MiB) }); | 562 | try stdout.print("{s:>17}: {:10} MiB/s\n", .{ H.name, throughput / (1 * MiB) }); |
| 567 | try stdout.flush(); | 563 | try stdout.flush(); |
| 568 | } | 564 | } |
| ... | @@ -634,7 +630,7 @@ pub fn main() !void { | ... | @@ -634,7 +630,7 @@ pub fn main() !void { |
| 634 | 630 | ||
| 635 | inline for (pwhashes) |H| { | 631 | inline for (pwhashes) |H| { |
| 636 | if (filter == null or std.mem.find(u8, H.name, filter.?) != null) { | 632 | if (filter == null or std.mem.find(u8, H.name, filter.?) != null) { |
| 637 | const throughput = try benchmarkPwhash(arena_allocator, H.ty, H.params, mode(64), io); | 633 | const throughput = try benchmarkPwhash(arena, H.ty, H.params, mode(64), io); |
| 638 | try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput }); | 634 | try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput }); |
| 639 | try stdout.flush(); | 635 | try stdout.flush(); |
| 640 | } | 636 | } |
lib/std/debug.zig+1-1| ... | @@ -40,7 +40,7 @@ pub const cpu_context = @import("debug/cpu_context.zig"); | ... | @@ -40,7 +40,7 @@ pub const cpu_context = @import("debug/cpu_context.zig"); |
| 40 | /// pub fn deinit(si: *SelfInfo, gpa: Allocator) void; | 40 | /// pub fn deinit(si: *SelfInfo, gpa: Allocator) void; |
| 41 | /// | 41 | /// |
| 42 | /// /// Returns the symbol and source location of the instruction at `address`. | 42 | /// /// Returns the symbol and source location of the instruction at `address`. |
| 43 | /// pub fn getSymbol(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError!Symbol; | 43 | /// pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) SelfInfoError!Symbol; |
| 44 | /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. | 44 | /// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`. |
| 45 | /// pub fn getModuleName(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError![]const u8; | 45 | /// pub fn getModuleName(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError![]const u8; |
| 46 | /// | 46 | /// |
lib/std/debug/ElfFile.zig+6-4| ... | @@ -67,15 +67,16 @@ pub const DebugInfoSearchPaths = struct { | ... | @@ -67,15 +67,16 @@ pub const DebugInfoSearchPaths = struct { |
| 67 | }; | 67 | }; |
| 68 | 68 | ||
| 69 | pub fn native(exe_path: []const u8) DebugInfoSearchPaths { | 69 | pub fn native(exe_path: []const u8) DebugInfoSearchPaths { |
| 70 | return .{ | 70 | if (std.Options.elf_debug_info_search_paths) |f| return f(exe_path); |
| 71 | if (std.Options.debug_threaded_io) |t| return .{ | ||
| 71 | .debuginfod_client = p: { | 72 | .debuginfod_client = p: { |
| 72 | if (std.posix.getenv("DEBUGINFOD_CACHE_PATH")) |p| { | 73 | if (t.environString("DEBUGINFOD_CACHE_PATH")) |p| { |
| 73 | break :p .{ p, "" }; | 74 | break :p .{ p, "" }; |
| 74 | } | 75 | } |
| 75 | if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| { | 76 | if (t.environString("XDG_CACHE_HOME")) |cache_path| { |
| 76 | break :p .{ cache_path, "/debuginfod_client" }; | 77 | break :p .{ cache_path, "/debuginfod_client" }; |
| 77 | } | 78 | } |
| 78 | if (std.posix.getenv("HOME")) |home_path| { | 79 | if (t.environString("HOME")) |home_path| { |
| 79 | break :p .{ home_path, "/.cache/debuginfod_client" }; | 80 | break :p .{ home_path, "/.cache/debuginfod_client" }; |
| 80 | } | 81 | } |
| 81 | break :p null; | 82 | break :p null; |
| ... | @@ -85,6 +86,7 @@ pub const DebugInfoSearchPaths = struct { | ... | @@ -85,6 +86,7 @@ pub const DebugInfoSearchPaths = struct { |
| 85 | }, | 86 | }, |
| 86 | .exe_dir = std.fs.path.dirname(exe_path) orelse ".", | 87 | .exe_dir = std.fs.path.dirname(exe_path) orelse ".", |
| 87 | }; | 88 | }; |
| 89 | @compileError("std.Options.elf_debug_info_search_paths must be provided"); | ||
| 88 | } | 90 | } |
| 89 | }; | 91 | }; |
| 90 | 92 |
lib/std/debug/SelfInfo/Elf.zig+1| ... | @@ -322,6 +322,7 @@ const Module = struct { | ... | @@ -322,6 +322,7 @@ const Module = struct { |
| 322 | if (mod.loaded_elf == null) mod.loaded_elf = loadElf(mod, gpa, io); | 322 | if (mod.loaded_elf == null) mod.loaded_elf = loadElf(mod, gpa, io); |
| 323 | return if (mod.loaded_elf.?) |*elf| elf else |err| err; | 323 | return if (mod.loaded_elf.?) |*elf| elf else |err| err; |
| 324 | } | 324 | } |
| 325 | |||
| 325 | fn loadElf(mod: *Module, gpa: Allocator, io: Io) Error!LoadedElf { | 326 | fn loadElf(mod: *Module, gpa: Allocator, io: Io) Error!LoadedElf { |
| 326 | const load_result = if (mod.name.len > 0) res: { | 327 | const load_result = if (mod.name.len > 0) res: { |
| 327 | var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo; | 328 | var file = Io.Dir.cwd().openFile(io, mod.name, .{}) catch return error.MissingDebugInfo; |
lib/std/dynamic_library.zig+18-10| ... | @@ -31,12 +31,20 @@ pub const DynLib = struct { | ... | @@ -31,12 +31,20 @@ pub const DynLib = struct { |
| 31 | 31 | ||
| 32 | /// Trusts the file. Malicious file will be able to execute arbitrary code. | 32 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 33 | pub fn open(path: []const u8) Error!DynLib { | 33 | pub fn open(path: []const u8) Error!DynLib { |
| 34 | return .{ .inner = try InnerType.open(path) }; | 34 | if (InnerType == ElfDynLib) { |
| 35 | return .{ .inner = try InnerType.open(path, null) }; | ||
| 36 | } else { | ||
| 37 | return .{ .inner = try InnerType.open(path) }; | ||
| 38 | } | ||
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | /// Trusts the file. Malicious file will be able to execute arbitrary code. | 41 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 38 | pub fn openZ(path_c: [*:0]const u8) Error!DynLib { | 42 | pub fn openZ(path_c: [*:0]const u8) Error!DynLib { |
| 39 | return .{ .inner = try InnerType.openZ(path_c) }; | 43 | if (InnerType == ElfDynLib) { |
| 44 | return .{ .inner = try InnerType.openZ(path_c, null) }; | ||
| 45 | } else { | ||
| 46 | return .{ .inner = try InnerType.openZ(path_c) }; | ||
| 47 | } | ||
| 40 | } | 48 | } |
| 41 | 49 | ||
| 42 | /// Trusts the file. | 50 | /// Trusts the file. |
| ... | @@ -197,7 +205,7 @@ pub const ElfDynLib = struct { | ... | @@ -197,7 +205,7 @@ pub const ElfDynLib = struct { |
| 197 | // - DT_RPATH of the calling binary is not used as a search path | 205 | // - DT_RPATH of the calling binary is not used as a search path |
| 198 | // - DT_RUNPATH of the calling binary is not used as a search path | 206 | // - DT_RUNPATH of the calling binary is not used as a search path |
| 199 | // - /etc/ld.so.cache is not read | 207 | // - /etc/ld.so.cache is not read |
| 200 | fn resolveFromName(io: Io, path_or_name: []const u8) !posix.fd_t { | 208 | fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !posix.fd_t { |
| 201 | // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname | 209 | // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname |
| 202 | if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| { | 210 | if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| { |
| 203 | return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | 211 | return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); |
| ... | @@ -207,7 +215,7 @@ pub const ElfDynLib = struct { | ... | @@ -207,7 +215,7 @@ pub const ElfDynLib = struct { |
| 207 | if (std.os.linux.geteuid() == std.os.linux.getuid() and | 215 | if (std.os.linux.geteuid() == std.os.linux.getuid() and |
| 208 | std.os.linux.getegid() == std.os.linux.getgid()) | 216 | std.os.linux.getegid() == std.os.linux.getgid()) |
| 209 | { | 217 | { |
| 210 | if (posix.getenvZ("LD_LIBRARY_PATH")) |ld_library_path| { | 218 | if (LD_LIBRARY_PATH) |ld_library_path| { |
| 211 | if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| { | 219 | if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| { |
| 212 | return fd; | 220 | return fd; |
| 213 | } | 221 | } |
| ... | @@ -221,10 +229,10 @@ pub const ElfDynLib = struct { | ... | @@ -221,10 +229,10 @@ pub const ElfDynLib = struct { |
| 221 | } | 229 | } |
| 222 | 230 | ||
| 223 | /// Trusts the file. Malicious file will be able to execute arbitrary code. | 231 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 224 | pub fn open(path: []const u8) Error!ElfDynLib { | 232 | pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib { |
| 225 | const io = std.Options.debug_io; | 233 | const io = std.Options.debug_io; |
| 226 | 234 | ||
| 227 | const fd = try resolveFromName(io, path); | 235 | const fd = try resolveFromName(io, path, LD_LIBRARY_PATH); |
| 228 | defer posix.close(fd); | 236 | defer posix.close(fd); |
| 229 | 237 | ||
| 230 | const file: Io.File = .{ .handle = fd }; | 238 | const file: Io.File = .{ .handle = fd }; |
| ... | @@ -371,8 +379,8 @@ pub const ElfDynLib = struct { | ... | @@ -371,8 +379,8 @@ pub const ElfDynLib = struct { |
| 371 | } | 379 | } |
| 372 | 380 | ||
| 373 | /// Trusts the file. Malicious file will be able to execute arbitrary code. | 381 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 374 | pub fn openZ(path_c: [*:0]const u8) Error!ElfDynLib { | 382 | pub fn openZ(path_c: [*:0]const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib { |
| 375 | return open(mem.sliceTo(path_c, 0)); | 383 | return open(mem.sliceTo(path_c, 0), LD_LIBRARY_PATH); |
| 376 | } | 384 | } |
| 377 | 385 | ||
| 378 | /// Trusts the file | 386 | /// Trusts the file |
| ... | @@ -554,8 +562,8 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str | ... | @@ -554,8 +562,8 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str |
| 554 | 562 | ||
| 555 | test "ElfDynLib" { | 563 | test "ElfDynLib" { |
| 556 | if (native_os != .linux) return error.SkipZigTest; | 564 | if (native_os != .linux) return error.SkipZigTest; |
| 557 | try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so")); | 565 | try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so", null)); |
| 558 | try testing.expectError(error.FileNotFound, ElfDynLib.openZ("invalid_so.so")); | 566 | try testing.expectError(error.FileNotFound, ElfDynLib.openZ("invalid_so.so", null)); |
| 559 | } | 567 | } |
| 560 | 568 | ||
| 561 | /// Separated to avoid referencing `WindowsDynLib`, because its field types may not | 569 | /// Separated to avoid referencing `WindowsDynLib`, because its field types may not |
lib/std/fs/path.zig+277-147| ... | @@ -13,16 +13,15 @@ | ... | @@ -13,16 +13,15 @@ |
| 13 | //! https://github.com/WebAssembly/wasi-filesystem/issues/17#issuecomment-1430639353 | 13 | //! https://github.com/WebAssembly/wasi-filesystem/issues/17#issuecomment-1430639353 |
| 14 | 14 | ||
| 15 | const builtin = @import("builtin"); | 15 | const builtin = @import("builtin"); |
| 16 | const native_os = builtin.target.os.tag; | ||
| 17 | |||
| 16 | const std = @import("../std.zig"); | 18 | const std = @import("../std.zig"); |
| 17 | const debug = std.debug; | 19 | const assert = std.debug.assert; |
| 18 | const assert = debug.assert; | ||
| 19 | const testing = std.testing; | 20 | const testing = std.testing; |
| 20 | const mem = std.mem; | 21 | const mem = std.mem; |
| 21 | const ascii = std.ascii; | 22 | const Allocator = std.mem.Allocator; |
| 22 | const Allocator = mem.Allocator; | 23 | const eqlIgnoreCaseWtf8 = std.os.windows.eqlIgnoreCaseWtf8; |
| 23 | const windows = std.os.windows; | 24 | const eqlIgnoreCaseWtf16 = std.os.windows.eqlIgnoreCaseWtf16; |
| 24 | const process = std.process; | ||
| 25 | const native_os = builtin.target.os.tag; | ||
| 26 | 25 | ||
| 27 | pub const sep_windows: u8 = '\\'; | 26 | pub const sep_windows: u8 = '\\'; |
| 28 | pub const sep_posix: u8 = '/'; | 27 | pub const sep_posix: u8 = '/'; |
| ... | @@ -281,7 +280,7 @@ pub fn isAbsolute(path: []const u8) bool { | ... | @@ -281,7 +280,7 @@ pub fn isAbsolute(path: []const u8) bool { |
| 281 | } | 280 | } |
| 282 | 281 | ||
| 283 | fn isAbsoluteWindowsImpl(comptime T: type, path: []const T) bool { | 282 | fn isAbsoluteWindowsImpl(comptime T: type, path: []const T) bool { |
| 284 | return switch (windows.getWin32PathType(T, path)) { | 283 | return switch (getWin32PathType(T, path)) { |
| 285 | // Unambiguously absolute | 284 | // Unambiguously absolute |
| 286 | .drive_absolute, .unc_absolute, .local_device, .root_local_device => true, | 285 | .drive_absolute, .unc_absolute, .local_device, .root_local_device => true, |
| 287 | // Unambiguously relative | 286 | // Unambiguously relative |
| ... | @@ -515,13 +514,13 @@ test parsePathPosix { | ... | @@ -515,13 +514,13 @@ test parsePathPosix { |
| 515 | 514 | ||
| 516 | pub fn WindowsPath2(comptime T: type) type { | 515 | pub fn WindowsPath2(comptime T: type) type { |
| 517 | return struct { | 516 | return struct { |
| 518 | kind: windows.Win32PathType, | 517 | kind: Win32PathType, |
| 519 | root: []const T, | 518 | root: []const T, |
| 520 | }; | 519 | }; |
| 521 | } | 520 | } |
| 522 | 521 | ||
| 523 | pub fn parsePathWindows(comptime T: type, path: []const T) WindowsPath2(T) { | 522 | pub fn parsePathWindows(comptime T: type, path: []const T) WindowsPath2(T) { |
| 524 | const kind = windows.getWin32PathType(T, path); | 523 | const kind = getWin32PathType(T, path); |
| 525 | const root = root: switch (kind) { | 524 | const root = root: switch (kind) { |
| 526 | .drive_absolute, .drive_relative => { | 525 | .drive_absolute, .drive_relative => { |
| 527 | const drive_letter_len = getDriveLetter(T, path).len; | 526 | const drive_letter_len = getDriveLetter(T, path).len; |
| ... | @@ -731,7 +730,7 @@ fn parseUNC(comptime T: type, path: []const T) WindowsUNC(T) { | ... | @@ -731,7 +730,7 @@ fn parseUNC(comptime T: type, path: []const T) WindowsUNC(T) { |
| 731 | // For the share, there can be any number of path separators between the server | 730 | // For the share, there can be any number of path separators between the server |
| 732 | // and the share, so we want to skip over all of them instead of just looking for | 731 | // and the share, so we want to skip over all of them instead of just looking for |
| 733 | // the first one. | 732 | // the first one. |
| 734 | var it = std.mem.tokenizeAny(T, path[server_end + 1 ..], any_sep); | 733 | var it = mem.tokenizeAny(T, path[server_end + 1 ..], any_sep); |
| 735 | const share = it.next() orelse return .{ | 734 | const share = it.next() orelse return .{ |
| 736 | .server = path[2..server_end], | 735 | .server = path[2..server_end], |
| 737 | .sep_after_server = true, | 736 | .sep_after_server = true, |
| ... | @@ -803,8 +802,8 @@ const DiskDesignatorKind = enum { drive, unc }; | ... | @@ -803,8 +802,8 @@ const DiskDesignatorKind = enum { drive, unc }; |
| 803 | /// `p1` and `p2` are both assumed to be the `kind` provided. | 802 | /// `p1` and `p2` are both assumed to be the `kind` provided. |
| 804 | fn compareDiskDesignators(comptime T: type, kind: DiskDesignatorKind, p1: []const T, p2: []const T) bool { | 803 | fn compareDiskDesignators(comptime T: type, kind: DiskDesignatorKind, p1: []const T, p2: []const T) bool { |
| 805 | const eql = switch (T) { | 804 | const eql = switch (T) { |
| 806 | u8 => windows.eqlIgnoreCaseWtf8, | 805 | u8 => eqlIgnoreCaseWtf8, |
| 807 | u16 => windows.eqlIgnoreCaseWtf16, | 806 | u16 => eqlIgnoreCaseWtf16, |
| 808 | else => @compileError("only u8 (WTF-8) and u16 (WTF-16LE) is supported"), | 807 | else => @compileError("only u8 (WTF-8) and u16 (WTF-16LE) is supported"), |
| 809 | }; | 808 | }; |
| 810 | switch (kind) { | 809 | switch (kind) { |
| ... | @@ -1094,10 +1093,14 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) Allocator | ... | @@ -1094,10 +1093,14 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) Allocator |
| 1094 | } | 1093 | } |
| 1095 | 1094 | ||
| 1096 | /// This function is like a series of `cd` statements executed one after another. | 1095 | /// This function is like a series of `cd` statements executed one after another. |
| 1096 | /// | ||
| 1097 | /// It resolves "." and ".." to the best of its ability, but will not convert relative paths to | 1097 | /// It resolves "." and ".." to the best of its ability, but will not convert relative paths to |
| 1098 | /// an absolute path, use Io.Dir.realpath instead. | 1098 | /// an absolute path, use Io.Dir.realpath instead. |
| 1099 | /// | ||
| 1099 | /// ".." components may persist in the resolved path if the resolved path is relative. | 1100 | /// ".." components may persist in the resolved path if the resolved path is relative. |
| 1101 | /// | ||
| 1100 | /// The result does not have a trailing path separator. | 1102 | /// The result does not have a trailing path separator. |
| 1103 | /// | ||
| 1101 | /// This function does not perform any syscalls. Executing this series of path | 1104 | /// This function does not perform any syscalls. Executing this series of path |
| 1102 | /// lookups on the actual filesystem may produce different results due to | 1105 | /// lookups on the actual filesystem may produce different results due to |
| 1103 | /// symlinks. | 1106 | /// symlinks. |
| ... | @@ -1494,25 +1497,54 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) !void { | ... | @@ -1494,25 +1497,54 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) !void { |
| 1494 | try testing.expectEqualSlices(u8, expected_output, basenameWindows(input)); | 1497 | try testing.expectEqualSlices(u8, expected_output, basenameWindows(input)); |
| 1495 | } | 1498 | } |
| 1496 | 1499 | ||
| 1497 | pub const RelativeError = std.process.GetCwdAllocError; | 1500 | /// Returns the non-absolute path from `from` to `to`. |
| 1498 | 1501 | /// | |
| 1499 | /// Returns the relative path from `from` to `to`. If `from` and `to` each | 1502 | /// Other than memory allocation, this is a pure function; the result solely |
| 1500 | /// resolve to the same path (after calling `resolve` on each), a zero-length | 1503 | /// depends on the input parameters. |
| 1501 | /// string is returned. | 1504 | /// |
| 1502 | /// On Windows, the result is not guaranteed to be relative, as the paths may be | 1505 | /// If `from` and `to` each resolve to the same path (after calling `resolve` |
| 1503 | /// on different volumes. In that case, the result will be the canonicalized absolute | 1506 | /// on each), a zero-length string is returned. |
| 1504 | /// path of `to`. | 1507 | /// |
| 1505 | pub fn relative(allocator: Allocator, from: []const u8, to: []const u8) RelativeError![]u8 { | 1508 | /// See `relativePosix` and `relativeWindows` for operating system specific |
| 1509 | /// details and for how `environ_map` is used. | ||
| 1510 | pub fn relative( | ||
| 1511 | gpa: Allocator, | ||
| 1512 | cwd: []const u8, | ||
| 1513 | environ_map: ?*const std.process.Environ.Map, | ||
| 1514 | from: []const u8, | ||
| 1515 | to: []const u8, | ||
| 1516 | ) Allocator.Error![]u8 { | ||
| 1506 | if (native_os == .windows) { | 1517 | if (native_os == .windows) { |
| 1507 | return relativeWindows(allocator, from, to); | 1518 | return relativeWindows(gpa, cwd, environ_map, from, to); |
| 1508 | } else { | 1519 | } else { |
| 1509 | return relativePosix(allocator, from, to); | 1520 | return relativePosix(gpa, cwd, from, to); |
| 1510 | } | 1521 | } |
| 1511 | } | 1522 | } |
| 1512 | 1523 | ||
| 1513 | pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ![]u8 { | 1524 | /// Returns the non-absolute path from `from` to `to` according to Windows rules. |
| 1514 | if (native_os != .windows) @compileError("this function relies on Windows-specific semantics"); | 1525 | /// |
| 1515 | 1526 | /// Other than memory allocation, this is a pure function; the result solely | |
| 1527 | /// depends on the input parameters. | ||
| 1528 | /// | ||
| 1529 | /// If `from` and `to` each resolve to the same path (after calling `resolve` | ||
| 1530 | /// on each), a zero-length string is returned. | ||
| 1531 | /// | ||
| 1532 | /// The result is not guaranteed to be relative, as the paths may be on | ||
| 1533 | /// different volumes. In that case, the result will be the canonicalized | ||
| 1534 | /// absolute path of `to`. | ||
| 1535 | /// | ||
| 1536 | /// Per-drive CWDs are stored in special semi-hidden environment variables of | ||
| 1537 | /// the format `=<drive-letter>:`, e.g. `=C:`. This type of CWD is purely a | ||
| 1538 | /// shell concept, so there's no guarantee that it'll be set or that it'll even | ||
| 1539 | /// be accurate. This is the only reason for the `environ_map` parameter. `null` is | ||
| 1540 | /// treated equivalent to the environment variable missing. | ||
| 1541 | pub fn relativeWindows( | ||
| 1542 | gpa: Allocator, | ||
| 1543 | cwd: []const u8, | ||
| 1544 | environ_map: ?*const std.process.Environ.Map, | ||
| 1545 | from: []const u8, | ||
| 1546 | to: []const u8, | ||
| 1547 | ) Allocator.Error![]u8 { | ||
| 1516 | const parsed_from = parsePathWindows(u8, from); | 1548 | const parsed_from = parsePathWindows(u8, from); |
| 1517 | const parsed_to = parsePathWindows(u8, to); | 1549 | const parsed_to = parsePathWindows(u8, to); |
| 1518 | 1550 | ||
| ... | @@ -1533,14 +1565,14 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! | ... | @@ -1533,14 +1565,14 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1533 | }; | 1565 | }; |
| 1534 | 1566 | ||
| 1535 | if (result_is_always_to) { | 1567 | if (result_is_always_to) { |
| 1536 | return windowsResolveAgainstCwd(allocator, to, parsed_to); | 1568 | return windowsResolveAgainstCwd(gpa, cwd, environ_map, to, parsed_to); |
| 1537 | } | 1569 | } |
| 1538 | 1570 | ||
| 1539 | const resolved_from = try windowsResolveAgainstCwd(allocator, from, parsed_from); | 1571 | const resolved_from = try windowsResolveAgainstCwd(gpa, cwd, environ_map, from, parsed_from); |
| 1540 | defer allocator.free(resolved_from); | 1572 | defer gpa.free(resolved_from); |
| 1541 | var clean_up_resolved_to = true; | 1573 | var clean_up_resolved_to = true; |
| 1542 | const resolved_to = try windowsResolveAgainstCwd(allocator, to, parsed_to); | 1574 | const resolved_to = try windowsResolveAgainstCwd(gpa, cwd, environ_map, to, parsed_to); |
| 1543 | defer if (clean_up_resolved_to) allocator.free(resolved_to); | 1575 | defer if (clean_up_resolved_to) gpa.free(resolved_to); |
| 1544 | 1576 | ||
| 1545 | const parsed_resolved_from = parsePathWindows(u8, resolved_from); | 1577 | const parsed_resolved_from = parsePathWindows(u8, resolved_from); |
| 1546 | const parsed_resolved_to = parsePathWindows(u8, resolved_to); | 1578 | const parsed_resolved_to = parsePathWindows(u8, resolved_to); |
| ... | @@ -1569,18 +1601,18 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! | ... | @@ -1569,18 +1601,18 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1569 | var from_it = mem.tokenizeAny(u8, resolved_from[parsed_resolved_from.root.len..], "/\\"); | 1601 | var from_it = mem.tokenizeAny(u8, resolved_from[parsed_resolved_from.root.len..], "/\\"); |
| 1570 | var to_it = mem.tokenizeAny(u8, resolved_to[parsed_resolved_to.root.len..], "/\\"); | 1602 | var to_it = mem.tokenizeAny(u8, resolved_to[parsed_resolved_to.root.len..], "/\\"); |
| 1571 | while (true) { | 1603 | while (true) { |
| 1572 | const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest()); | 1604 | const from_component = from_it.next() orelse return gpa.dupe(u8, to_it.rest()); |
| 1573 | const to_rest = to_it.rest(); | 1605 | const to_rest = to_it.rest(); |
| 1574 | if (to_it.next()) |to_component| { | 1606 | if (to_it.next()) |to_component| { |
| 1575 | if (windows.eqlIgnoreCaseWtf8(from_component, to_component)) | 1607 | if (eqlIgnoreCaseWtf8(from_component, to_component)) |
| 1576 | continue; | 1608 | continue; |
| 1577 | } | 1609 | } |
| 1578 | var up_index_end = "..".len; | 1610 | var up_index_end = "..".len; |
| 1579 | while (from_it.next()) |_| { | 1611 | while (from_it.next()) |_| { |
| 1580 | up_index_end += "\\..".len; | 1612 | up_index_end += "\\..".len; |
| 1581 | } | 1613 | } |
| 1582 | const result = try allocator.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len); | 1614 | const result = try gpa.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len); |
| 1583 | errdefer allocator.free(result); | 1615 | errdefer gpa.free(result); |
| 1584 | 1616 | ||
| 1585 | result[0..2].* = "..".*; | 1617 | result[0..2].* = "..".*; |
| 1586 | var result_index: usize = 2; | 1618 | var result_index: usize = 2; |
| ... | @@ -1597,85 +1629,60 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! | ... | @@ -1597,85 +1629,60 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1597 | result_index += to_component.len; | 1629 | result_index += to_component.len; |
| 1598 | } | 1630 | } |
| 1599 | 1631 | ||
| 1600 | return allocator.realloc(result, result_index); | 1632 | return gpa.realloc(result, result_index); |
| 1601 | } | 1633 | } |
| 1602 | return [_]u8{}; | 1634 | return [_]u8{}; |
| 1603 | } | 1635 | } |
| 1604 | 1636 | ||
| 1605 | fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: WindowsPath2(u8)) ![]u8 { | 1637 | fn windowsResolveAgainstCwd( |
| 1638 | gpa: Allocator, | ||
| 1639 | cwd: []const u8, | ||
| 1640 | environ_map: ?*const std.process.Environ.Map, | ||
| 1641 | path: []const u8, | ||
| 1642 | parsed: WindowsPath2(u8), | ||
| 1643 | ) ![]u8 { | ||
| 1606 | // Space for 256 WTF-16 code units; potentially 3 WTF-8 bytes per WTF-16 code unit | 1644 | // Space for 256 WTF-16 code units; potentially 3 WTF-8 bytes per WTF-16 code unit |
| 1607 | var temp_allocator_state = std.heap.stackFallback(256 * 3, allocator); | 1645 | var temp_allocator_state = std.heap.stackFallback(256 * 3, gpa); |
| 1608 | return switch (parsed.kind) { | 1646 | return switch (parsed.kind) { |
| 1609 | .drive_absolute, | 1647 | .drive_absolute, |
| 1610 | .unc_absolute, | 1648 | .unc_absolute, |
| 1611 | .root_local_device, | 1649 | .root_local_device, |
| 1612 | .local_device, | 1650 | .local_device, |
| 1613 | => try resolveWindows(allocator, &.{path}), | 1651 | => try resolveWindows(gpa, &.{path}), |
| 1614 | .relative => blk: { | ||
| 1615 | const temp_allocator = temp_allocator_state.get(); | ||
| 1616 | 1652 | ||
| 1617 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | 1653 | .relative => try resolveWindows(gpa, &.{ cwd, path }), |
| 1618 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | ||
| 1619 | 1654 | ||
| 1620 | const wtf8_len = std.unicode.calcWtf8Len(cwd_w); | ||
| 1621 | const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len); | ||
| 1622 | defer temp_allocator.free(wtf8_buf); | ||
| 1623 | assert(std.unicode.wtf16LeToWtf8(wtf8_buf, cwd_w) == wtf8_len); | ||
| 1624 | |||
| 1625 | break :blk try resolveWindows(allocator, &.{ wtf8_buf, path }); | ||
| 1626 | }, | ||
| 1627 | .rooted => blk: { | 1655 | .rooted => blk: { |
| 1628 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | 1656 | const parsed_cwd = parsePathWindows(u8, cwd); |
| 1629 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | ||
| 1630 | const parsed_cwd = parsePathWindows(u16, cwd_w); | ||
| 1631 | switch (parsed_cwd.kind) { | 1657 | switch (parsed_cwd.kind) { |
| 1632 | .drive_absolute => { | 1658 | .drive_absolute => { |
| 1633 | var drive_buf = "_:\\".*; | 1659 | var drive_buf = "_:\\".*; |
| 1634 | drive_buf[0] = @truncate(cwd_w[0]); | 1660 | drive_buf[0] = cwd[0]; |
| 1635 | break :blk try resolveWindows(allocator, &.{ &drive_buf, path }); | 1661 | break :blk try resolveWindows(gpa, &.{ &drive_buf, path }); |
| 1636 | }, | 1662 | }, |
| 1637 | .unc_absolute => { | 1663 | .unc_absolute => { |
| 1638 | const temp_allocator = temp_allocator_state.get(); | 1664 | break :blk try resolveWindows(gpa, &.{ parsed_cwd.root, path }); |
| 1639 | var root_buf = try temp_allocator.alloc(u8, parsed_cwd.root.len * 3); | ||
| 1640 | defer temp_allocator.free(root_buf); | ||
| 1641 | |||
| 1642 | const wtf8_len = std.unicode.wtf16LeToWtf8(root_buf, parsed_cwd.root); | ||
| 1643 | const root = root_buf[0..wtf8_len]; | ||
| 1644 | break :blk try resolveWindows(allocator, &.{ root, path }); | ||
| 1645 | }, | 1665 | }, |
| 1646 | // Effectively a malformed CWD, give up and just return a normalized path | 1666 | // Effectively a malformed CWD, give up and just return a normalized path |
| 1647 | else => break :blk try resolveWindows(allocator, &.{path}), | 1667 | else => break :blk try resolveWindows(gpa, &.{path}), |
| 1648 | } | 1668 | } |
| 1649 | }, | 1669 | }, |
| 1650 | .drive_relative => blk: { | 1670 | .drive_relative => blk: { |
| 1651 | const temp_allocator = temp_allocator_state.get(); | 1671 | const temp_allocator = temp_allocator_state.get(); |
| 1652 | const drive_cwd = drive_cwd: { | 1672 | const drive_cwd = drive_cwd: { |
| 1653 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | 1673 | const parsed_cwd = parsePathWindows(u8, cwd); |
| 1654 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | ||
| 1655 | const parsed_cwd = parsePathWindows(u16, cwd_w); | ||
| 1656 | 1674 | ||
| 1657 | if (parsed_cwd.kind == .drive_absolute) { | 1675 | if (parsed_cwd.kind == .drive_absolute) { |
| 1658 | const drive_letter_w = parsed_cwd.root[0]; | 1676 | const drive_letter_w = parsed_cwd.root[0]; |
| 1659 | const drive_letters_match = drive_letter_w <= 0x7F and | 1677 | const drive_letters_match = drive_letter_w <= 0x7F and |
| 1660 | ascii.toUpper(@intCast(drive_letter_w)) == ascii.toUpper(parsed.root[0]); | 1678 | std.ascii.toUpper(@intCast(drive_letter_w)) == std.ascii.toUpper(parsed.root[0]); |
| 1661 | if (drive_letters_match) { | 1679 | if (drive_letters_match) |
| 1662 | const wtf8_len = std.unicode.calcWtf8Len(cwd_w); | 1680 | break :drive_cwd cwd; |
| 1663 | const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len); | 1681 | |
| 1664 | assert(std.unicode.wtf16LeToWtf8(wtf8_buf, cwd_w) == wtf8_len); | 1682 | if (environ_map) |m| { |
| 1665 | break :drive_cwd wtf8_buf[0..]; | 1683 | if (m.get(&.{ '=', parsed.root[0], ':' })) |v| { |
| 1666 | } | 1684 | break :drive_cwd try temp_allocator.dupe(u8, v); |
| 1667 | 1685 | } | |
| 1668 | // Per-drive CWD's are stored in special semi-hidden environment variables | ||
| 1669 | // of the format `=<drive-letter>:`, e.g. `=C:`. This type of CWD is | ||
| 1670 | // purely a shell concept, so there's no guarantee that it'll be set | ||
| 1671 | // or that it'll even be accurate. | ||
| 1672 | var key_buf = std.unicode.wtf8ToWtf16LeStringLiteral("=_:").*; | ||
| 1673 | key_buf[1] = parsed.root[0]; | ||
| 1674 | if (std.process.getenvW(&key_buf)) |drive_cwd_w| { | ||
| 1675 | const wtf8_len = std.unicode.calcWtf8Len(drive_cwd_w); | ||
| 1676 | const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len); | ||
| 1677 | assert(std.unicode.wtf16LeToWtf8(wtf8_buf, drive_cwd_w) == wtf8_len); | ||
| 1678 | break :drive_cwd wtf8_buf[0..]; | ||
| 1679 | } | 1686 | } |
| 1680 | } | 1687 | } |
| 1681 | 1688 | ||
| ... | @@ -1686,16 +1693,20 @@ fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: Wind | ... | @@ -1686,16 +1693,20 @@ fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: Wind |
| 1686 | break :drive_cwd drive_buf; | 1693 | break :drive_cwd drive_buf; |
| 1687 | }; | 1694 | }; |
| 1688 | defer temp_allocator.free(drive_cwd); | 1695 | defer temp_allocator.free(drive_cwd); |
| 1689 | break :blk try resolveWindows(allocator, &.{ drive_cwd, path }); | 1696 | break :blk try resolveWindows(gpa, &.{ drive_cwd, path }); |
| 1690 | }, | 1697 | }, |
| 1691 | }; | 1698 | }; |
| 1692 | } | 1699 | } |
| 1693 | 1700 | ||
| 1694 | pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]u8 { | 1701 | /// Returns the non-absolute path from `from` to `to` according to Windows rules. |
| 1695 | if (native_os == .windows) @compileError("this function relies on semantics that do not apply to Windows"); | 1702 | /// |
| 1696 | 1703 | /// Other than memory allocation, this is a pure function; the result solely | |
| 1697 | const cwd = try process.getCwdAlloc(allocator); | 1704 | /// depends on the input parameters. |
| 1698 | defer allocator.free(cwd); | 1705 | /// |
| 1706 | /// If `from` and `to` each resolve to the same path (after calling `resolve` | ||
| 1707 | /// on each), a zero-length string is returned. | ||
| 1708 | /// | ||
| 1709 | pub fn relativePosix(allocator: Allocator, cwd: []const u8, from: []const u8, to: []const u8) Allocator.Error![]u8 { | ||
| 1699 | const resolved_from = try resolvePosix(allocator, &[_][]const u8{ cwd, from }); | 1710 | const resolved_from = try resolvePosix(allocator, &[_][]const u8{ cwd, from }); |
| 1700 | defer allocator.free(resolved_from); | 1711 | defer allocator.free(resolved_from); |
| 1701 | const resolved_to = try resolvePosix(allocator, &[_][]const u8{ cwd, to }); | 1712 | 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) ![] | ... | @@ -1736,69 +1747,67 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![] |
| 1736 | } | 1747 | } |
| 1737 | 1748 | ||
| 1738 | test relative { | 1749 | test relative { |
| 1739 | if (native_os == .windows) { | 1750 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); |
| 1740 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); | 1751 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 1741 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); | 1752 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| 1742 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); | 1753 | try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", ""); |
| 1743 | try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", ""); | 1754 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); |
| 1744 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); | 1755 | try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); |
| 1745 | try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); | 1756 | try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); |
| 1746 | try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); | 1757 | try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); |
| 1747 | try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); | 1758 | try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); |
| 1748 | try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); | 1759 | try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); |
| 1749 | try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); | 1760 | try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); |
| 1750 | try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); | 1761 | try testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); |
| 1751 | try testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); | 1762 | try testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); |
| 1752 | try testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); | 1763 | try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 1753 | try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); | 1764 | try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); |
| 1754 | try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); | 1765 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); |
| 1755 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); | 1766 | try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); |
| 1756 | try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); | 1767 | try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz"); |
| 1757 | try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz"); | 1768 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 1758 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); | 1769 | try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); |
| 1759 | try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); | 1770 | try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); |
| 1760 | try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); | 1771 | try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz"); |
| 1761 | try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz"); | 1772 | try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux"); |
| 1762 | try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux"); | 1773 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| 1763 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); | 1774 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); |
| 1764 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); | 1775 | |
| 1765 | 1776 | try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo"); | |
| 1766 | try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo"); | 1777 | try testRelativeWindows("c:foo", "c:foo\\bar", "bar"); |
| 1767 | try testRelativeWindows("c:foo", "c:foo\\bar", "bar"); | 1778 | try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo"); |
| 1768 | try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo"); | 1779 | try testRelativeWindows("\\foo", "\\foo\\bar", "bar"); |
| 1769 | try testRelativeWindows("\\foo", "\\foo\\bar", "bar"); | 1780 | |
| 1770 | 1781 | try testRelativeWindows("a/b/c", "a\\b", ".."); | |
| 1771 | try testRelativeWindows("a/b/c", "a\\b", ".."); | 1782 | try testRelativeWindows("a/b/c", "a", "..\\.."); |
| 1772 | try testRelativeWindows("a/b/c", "a", "..\\.."); | 1783 | try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); |
| 1773 | try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); | 1784 | |
| 1774 | 1785 | try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", ""); | |
| 1775 | try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", ""); | 1786 | // Unicode-aware case-insensitive path comparison |
| 1776 | // Unicode-aware case-insensitive path comparison | 1787 | try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", ""); |
| 1777 | try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", ""); | 1788 | |
| 1778 | } else { | 1789 | try testRelativePosix("/var/lib", "/var", ".."); |
| 1779 | try testRelativePosix("/var/lib", "/var", ".."); | 1790 | try testRelativePosix("/var/lib", "/bin", "../../bin"); |
| 1780 | try testRelativePosix("/var/lib", "/bin", "../../bin"); | 1791 | try testRelativePosix("/var/lib", "/var/lib", ""); |
| 1781 | try testRelativePosix("/var/lib", "/var/lib", ""); | 1792 | try testRelativePosix("/var/lib", "/var/apache", "../apache"); |
| 1782 | try testRelativePosix("/var/lib", "/var/apache", "../apache"); | 1793 | try testRelativePosix("/var/", "/var/lib", "lib"); |
| 1783 | try testRelativePosix("/var/", "/var/lib", "lib"); | 1794 | try testRelativePosix("/", "/var/lib", "var/lib"); |
| 1784 | try testRelativePosix("/", "/var/lib", "var/lib"); | 1795 | try testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); |
| 1785 | try testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); | 1796 | try testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); |
| 1786 | try testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); | 1797 | try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); |
| 1787 | try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); | 1798 | try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); |
| 1788 | try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); | 1799 | try testRelativePosix("/baz-quux", "/baz", "../baz"); |
| 1789 | try testRelativePosix("/baz-quux", "/baz", "../baz"); | 1800 | try testRelativePosix("/baz", "/baz-quux", "../baz-quux"); |
| 1790 | try testRelativePosix("/baz", "/baz-quux", "../baz-quux"); | ||
| 1791 | } | ||
| 1792 | } | 1801 | } |
| 1793 | 1802 | ||
| 1794 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { | 1803 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { |
| 1795 | const result = try relativePosix(testing.allocator, from, to); | 1804 | const result = try relativePosix(testing.allocator, ".", from, to); |
| 1796 | defer testing.allocator.free(result); | 1805 | defer testing.allocator.free(result); |
| 1797 | try testing.expectEqualStrings(expected_output, result); | 1806 | try testing.expectEqualStrings(expected_output, result); |
| 1798 | } | 1807 | } |
| 1799 | 1808 | ||
| 1800 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { | 1809 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { |
| 1801 | const result = try relativeWindows(testing.allocator, from, to); | 1810 | const result = try relativeWindows(testing.allocator, ".", null, from, to); |
| 1802 | defer testing.allocator.free(result); | 1811 | defer testing.allocator.free(result); |
| 1803 | try testing.expectEqualStrings(expected_output, result); | 1812 | try testing.expectEqualStrings(expected_output, result); |
| 1804 | } | 1813 | } |
| ... | @@ -2554,3 +2563,124 @@ pub const fmtAsUtf8Lossy = std.unicode.fmtUtf8; | ... | @@ -2554,3 +2563,124 @@ pub const fmtAsUtf8Lossy = std.unicode.fmtUtf8; |
| 2554 | /// a lossy conversion if the path contains any unpaired surrogates. | 2563 | /// a lossy conversion if the path contains any unpaired surrogates. |
| 2555 | /// Unpaired surrogates are replaced by the replacement character (U+FFFD). | 2564 | /// Unpaired surrogates are replaced by the replacement character (U+FFFD). |
| 2556 | pub const fmtWtf16LeAsUtf8Lossy = std.unicode.fmtUtf16Le; | 2565 | pub const fmtWtf16LeAsUtf8Lossy = std.unicode.fmtUtf16Le; |
| 2566 | |||
| 2567 | /// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type. | ||
| 2568 | pub const Win32PathType = enum { | ||
| 2569 | /// `\\server\share\foo` | ||
| 2570 | unc_absolute, | ||
| 2571 | /// `C:\foo` | ||
| 2572 | drive_absolute, | ||
| 2573 | /// `C:foo` | ||
| 2574 | drive_relative, | ||
| 2575 | /// `\foo` | ||
| 2576 | rooted, | ||
| 2577 | /// `foo` | ||
| 2578 | relative, | ||
| 2579 | /// `\\.\foo`, `\\?\foo` | ||
| 2580 | local_device, | ||
| 2581 | /// `\\.`, `\\?` | ||
| 2582 | root_local_device, | ||
| 2583 | }; | ||
| 2584 | |||
| 2585 | /// Get the path type of a Win32 namespace path. | ||
| 2586 | /// Similar to `RtlDetermineDosPathNameType_U`. | ||
| 2587 | /// If `T` is `u16`, then `path` should be encoded as WTF-16LE. | ||
| 2588 | pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType { | ||
| 2589 | if (path.len < 1) return .relative; | ||
| 2590 | |||
| 2591 | const windows_path = std.fs.path.PathType.windows; | ||
| 2592 | if (windows_path.isSep(T, path[0])) { | ||
| 2593 | // \x | ||
| 2594 | if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted; | ||
| 2595 | // \\. or \\? | ||
| 2596 | if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) { | ||
| 2597 | // exactly \\. or \\? with nothing trailing | ||
| 2598 | if (path.len == 3) return .root_local_device; | ||
| 2599 | // \\.\x or \\?\x | ||
| 2600 | if (windows_path.isSep(T, path[3])) return .local_device; | ||
| 2601 | } | ||
| 2602 | // \\x | ||
| 2603 | return .unc_absolute; | ||
| 2604 | } else { | ||
| 2605 | // Some choice has to be made about how non-ASCII code points as drive-letters are handled, since | ||
| 2606 | // path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification | ||
| 2607 | // for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code | ||
| 2608 | // units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so | ||
| 2609 | // checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16. | ||
| 2610 | // | ||
| 2611 | // `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers | ||
| 2612 | // `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get | ||
| 2613 | // classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded | ||
| 2614 | // in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path). | ||
| 2615 | // | ||
| 2616 | // The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both | ||
| 2617 | // WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI, | ||
| 2618 | // drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you | ||
| 2619 | // to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will | ||
| 2620 | // allow you to set any WTF-16 code unit as a drive letter. | ||
| 2621 | // | ||
| 2622 | // Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g. | ||
| 2623 | // `cd /D €:\` will work, filesystem functions still work, etc. | ||
| 2624 | // | ||
| 2625 | // The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't | ||
| 2626 | // just check path[0], path[1], path[2]. | ||
| 2627 | const colon_i: usize = switch (T) { | ||
| 2628 | u8 => i: { | ||
| 2629 | const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative; | ||
| 2630 | // Conveniently, 4-byte sequences in WTF-8 have the same starting code point | ||
| 2631 | // as 2-code-unit sequences in WTF-16. | ||
| 2632 | if (code_point_len > 3) return .relative; | ||
| 2633 | break :i code_point_len; | ||
| 2634 | }, | ||
| 2635 | u16 => 1, | ||
| 2636 | else => @compileError("unsupported type: " ++ @typeName(T)), | ||
| 2637 | }; | ||
| 2638 | // x | ||
| 2639 | if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative; | ||
| 2640 | // x:\ | ||
| 2641 | if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute; | ||
| 2642 | // x: | ||
| 2643 | return .drive_relative; | ||
| 2644 | } | ||
| 2645 | } | ||
| 2646 | |||
| 2647 | test getWin32PathType { | ||
| 2648 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "")); | ||
| 2649 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x")); | ||
| 2650 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\")); | ||
| 2651 | |||
| 2652 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//.")); | ||
| 2653 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?")); | ||
| 2654 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?")); | ||
| 2655 | |||
| 2656 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x")); | ||
| 2657 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x")); | ||
| 2658 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x")); | ||
| 2659 | // local device paths require a path separator after the root, otherwise it is considered a UNC path | ||
| 2660 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x")); | ||
| 2661 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x")); | ||
| 2662 | |||
| 2663 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//")); | ||
| 2664 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x")); | ||
| 2665 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x")); | ||
| 2666 | |||
| 2667 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x")); | ||
| 2668 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/")); | ||
| 2669 | |||
| 2670 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:")); | ||
| 2671 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc")); | ||
| 2672 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c")); | ||
| 2673 | |||
| 2674 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\")); | ||
| 2675 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc")); | ||
| 2676 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c")); | ||
| 2677 | |||
| 2678 | // Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter | ||
| 2679 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\")); | ||
| 2680 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\"))); | ||
| 2681 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:")); | ||
| 2682 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:"))); | ||
| 2683 | // But code points that are encoded as two WTF-16 code units are not | ||
| 2684 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\")); | ||
| 2685 | try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\"))); | ||
| 2686 | } |
lib/std/fs/test.zig+1-1| ... | @@ -79,7 +79,7 @@ const PathType = enum { | ... | @@ -79,7 +79,7 @@ const PathType = enum { |
| 79 | // using '127.0.0.1' as the server name and '<drive letter>$' as the share name. | 79 | // using '127.0.0.1' as the server name and '<drive letter>$' as the share name. |
| 80 | var fd_path_buf: [Dir.max_path_bytes]u8 = undefined; | 80 | var fd_path_buf: [Dir.max_path_bytes]u8 = undefined; |
| 81 | const dir_path = fd_path_buf[0..try dir.realPath(io, &fd_path_buf)]; | 81 | const dir_path = fd_path_buf[0..try dir.realPath(io, &fd_path_buf)]; |
| 82 | const windows_path_type = windows.getWin32PathType(u8, dir_path); | 82 | const windows_path_type = Dir.path.getWin32PathType(u8, dir_path); |
| 83 | switch (windows_path_type) { | 83 | switch (windows_path_type) { |
| 84 | .unc_absolute => return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }), | 84 | .unc_absolute => return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }), |
| 85 | .drive_absolute => { | 85 | .drive_absolute => { |
lib/std/hash/benchmark.zig+6-5| ... | @@ -353,14 +353,15 @@ fn mode(comptime x: comptime_int) comptime_int { | ... | @@ -353,14 +353,15 @@ fn mode(comptime x: comptime_int) comptime_int { |
| 353 | return if (builtin.mode == .Debug) x / 64 else x; | 353 | return if (builtin.mode == .Debug) x / 64 else x; |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | pub fn main() !void { | 356 | pub fn main(init: std.process.Init) !void { |
| 357 | const io = init.io; | ||
| 358 | const arena = init.arena.allocator(); | ||
| 359 | |||
| 357 | var stdout_buffer: [0x100]u8 = undefined; | 360 | var stdout_buffer: [0x100]u8 = undefined; |
| 358 | var stdout_writer = Io.File.stdout().writer(&stdout_buffer); | 361 | var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); |
| 359 | const stdout = &stdout_writer.interface; | 362 | const stdout = &stdout_writer.interface; |
| 360 | 363 | ||
| 361 | var buffer: [1024]u8 = undefined; | 364 | const args = try init.minimal.args.toSlice(arena); |
| 362 | var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); | ||
| 363 | const args = try std.process.argsAlloc(fixed.allocator()); | ||
| 364 | 365 | ||
| 365 | var filter: ?[]u8 = ""; | 366 | var filter: ?[]u8 = ""; |
| 366 | var count: usize = mode(128 * MiB); | 367 | var count: usize = mode(128 * MiB); |
lib/std/heap/debug_allocator.zig+5-2| ... | @@ -1272,9 +1272,12 @@ test "shrink large object to large object" { | ... | @@ -1272,9 +1272,12 @@ test "shrink large object to large object" { |
| 1272 | } | 1272 | } |
| 1273 | 1273 | ||
| 1274 | test "shrink large object to large object with larger alignment" { | 1274 | test "shrink large object to large object with larger alignment" { |
| 1275 | if (!builtin.link_libc and builtin.os.tag == .wasi) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/22731 | 1275 | if (builtin.os.tag == .wasi) { |
| 1276 | // https://github.com/ziglang/zig/issues/22731 | ||
| 1277 | return error.SkipZigTest; | ||
| 1278 | } | ||
| 1276 | 1279 | ||
| 1277 | var gpa = DebugAllocator(test_config){}; | 1280 | var gpa: DebugAllocator(test_config) = .{}; |
| 1278 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | 1281 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1279 | const allocator = gpa.allocator(); | 1282 | const allocator = gpa.allocator(); |
| 1280 | 1283 |
lib/std/http/Client.zig+9-10| ... | @@ -1307,7 +1307,7 @@ pub fn deinit(client: *Client) void { | ... | @@ -1307,7 +1307,7 @@ pub fn deinit(client: *Client) void { |
| 1307 | /// Asserts the client has no active connections. | 1307 | /// Asserts the client has no active connections. |
| 1308 | /// Uses `arena` for a few small allocations that must outlive the client, or | 1308 | /// Uses `arena` for a few small allocations that must outlive the client, or |
| 1309 | /// at least until those fields are set to different values. | 1309 | /// at least until those fields are set to different values. |
| 1310 | pub fn initDefaultProxies(client: *Client, arena: Allocator) !void { | 1310 | pub fn initDefaultProxies(client: *Client, arena: Allocator, environ_map: *std.process.Environ.Map) !void { |
| 1311 | // Prevent any new connections from being created. | 1311 | // Prevent any new connections from being created. |
| 1312 | client.connection_pool.mutex.lock(); | 1312 | client.connection_pool.mutex.lock(); |
| 1313 | defer client.connection_pool.mutex.unlock(); | 1313 | defer client.connection_pool.mutex.unlock(); |
| ... | @@ -1315,27 +1315,26 @@ pub fn initDefaultProxies(client: *Client, arena: Allocator) !void { | ... | @@ -1315,27 +1315,26 @@ pub fn initDefaultProxies(client: *Client, arena: Allocator) !void { |
| 1315 | assert(client.connection_pool.used.first == null); // There are active requests. | 1315 | assert(client.connection_pool.used.first == null); // There are active requests. |
| 1316 | 1316 | ||
| 1317 | if (client.http_proxy == null) { | 1317 | if (client.http_proxy == null) { |
| 1318 | client.http_proxy = try createProxyFromEnvVar(arena, &.{ | 1318 | client.http_proxy = try createProxyFromEnvVar(arena, environ_map, &.{ |
| 1319 | "http_proxy", "HTTP_PROXY", "all_proxy", "ALL_PROXY", | 1319 | "http_proxy", "HTTP_PROXY", "all_proxy", "ALL_PROXY", |
| 1320 | }); | 1320 | }); |
| 1321 | } | 1321 | } |
| 1322 | 1322 | ||
| 1323 | if (client.https_proxy == null) { | 1323 | if (client.https_proxy == null) { |
| 1324 | client.https_proxy = try createProxyFromEnvVar(arena, &.{ | 1324 | client.https_proxy = try createProxyFromEnvVar(arena, environ_map, &.{ |
| 1325 | "https_proxy", "HTTPS_PROXY", "all_proxy", "ALL_PROXY", | 1325 | "https_proxy", "HTTPS_PROXY", "all_proxy", "ALL_PROXY", |
| 1326 | }); | 1326 | }); |
| 1327 | } | 1327 | } |
| 1328 | } | 1328 | } |
| 1329 | 1329 | ||
| 1330 | fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !?*Proxy { | 1330 | fn createProxyFromEnvVar( |
| 1331 | arena: Allocator, | ||
| 1332 | environ_map: *std.process.Environ.Map, | ||
| 1333 | env_var_names: []const []const u8, | ||
| 1334 | ) !?*Proxy { | ||
| 1331 | const content = for (env_var_names) |name| { | 1335 | const content = for (env_var_names) |name| { |
| 1332 | const content = std.process.getEnvVarOwned(arena, name) catch |err| switch (err) { | 1336 | const content = environ_map.get(name) orelse continue; |
| 1333 | error.EnvironmentVariableNotFound => continue, | ||
| 1334 | else => |e| return e, | ||
| 1335 | }; | ||
| 1336 | |||
| 1337 | if (content.len == 0) continue; | 1337 | if (content.len == 0) continue; |
| 1338 | |||
| 1339 | break content; | 1338 | break content; |
| 1340 | } else return null; | 1339 | } else return null; |
| 1341 | 1340 |
lib/std/os.zig+1-64| ... | @@ -1,27 +1,4 @@ | ... | @@ -1,27 +1,4 @@ |
| 1 | //! This file contains thin wrappers around OS-specific APIs, with these | ||
| 2 | //! specific goals in mind: | ||
| 3 | //! * Convert "errno"-style error codes into Zig errors. | ||
| 4 | //! * When null-terminated byte buffers are required, provide APIs which accept | ||
| 5 | //! slices as well as APIs which accept null-terminated byte buffers. Same goes | ||
| 6 | //! for WTF-16LE encoding. | ||
| 7 | //! * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide | ||
| 8 | //! cross platform abstracting. | ||
| 9 | //! * When there exists a corresponding libc function and linking libc, the libc | ||
| 10 | //! implementation is used. Exceptions are made for known buggy areas of libc. | ||
| 11 | //! On Linux libc can be side-stepped by using `std.os.linux` directly. | ||
| 12 | //! * For Windows, this file represents the API that libc would provide for | ||
| 13 | //! Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`. | ||
| 14 | |||
| 15 | const root = @import("root"); | ||
| 16 | const std = @import("std.zig"); | ||
| 17 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 18 | const assert = std.debug.assert; | ||
| 19 | const math = std.math; | ||
| 20 | const mem = std.mem; | ||
| 21 | const elf = std.elf; | ||
| 22 | const fs = std.fs; | ||
| 23 | const dl = @import("dynamic_library.zig"); | ||
| 24 | const posix = std.posix; | ||
| 25 | const native_os = builtin.os.tag; | 2 | const native_os = builtin.os.tag; |
| 26 | 3 | ||
| 27 | pub const linux = @import("os/linux.zig"); | 4 | pub const linux = @import("os/linux.zig"); |
| ... | @@ -33,47 +10,7 @@ pub const windows = @import("os/windows.zig"); | ... | @@ -33,47 +10,7 @@ pub const windows = @import("os/windows.zig"); |
| 33 | 10 | ||
| 34 | test { | 11 | test { |
| 35 | _ = linux; | 12 | _ = linux; |
| 36 | if (native_os == .uefi) { | 13 | if (native_os == .uefi) _ = uefi; |
| 37 | _ = uefi; | ||
| 38 | } | ||
| 39 | _ = wasi; | 14 | _ = wasi; |
| 40 | _ = windows; | 15 | _ = windows; |
| 41 | } | 16 | } |
| 42 | |||
| 43 | /// See also `getenv`. Populated by startup code before main(). | ||
| 44 | /// TODO this is a footgun because the value will be undefined when using `zig build-lib`. | ||
| 45 | /// https://github.com/ziglang/zig/issues/4524 | ||
| 46 | pub var environ: [][*:0]u8 = undefined; | ||
| 47 | |||
| 48 | /// Populated by startup code before main(). | ||
| 49 | /// Not available on WASI or Windows without libc. See `std.process.argsAlloc` | ||
| 50 | /// or `std.process.argsWithAllocator` for a cross-platform alternative. | ||
| 51 | pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (native_os) { | ||
| 52 | .windows => @compileError("argv isn't supported on Windows: use std.process.argsAlloc instead"), | ||
| 53 | .wasi => @compileError("argv isn't supported on WASI: use std.process.argsAlloc instead"), | ||
| 54 | else => undefined, | ||
| 55 | }; | ||
| 56 | |||
| 57 | pub const FstatError = error{ | ||
| 58 | SystemResources, | ||
| 59 | AccessDenied, | ||
| 60 | Unexpected, | ||
| 61 | }; | ||
| 62 | |||
| 63 | pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t { | ||
| 64 | var stat: wasi.filestat_t = undefined; | ||
| 65 | switch (wasi.fd_filestat_get(fd, &stat)) { | ||
| 66 | .SUCCESS => return stat, | ||
| 67 | .INVAL => unreachable, | ||
| 68 | .BADF => unreachable, // Always a race condition. | ||
| 69 | .NOMEM => return error.SystemResources, | ||
| 70 | .ACCES => return error.AccessDenied, | ||
| 71 | .NOTCAPABLE => return error.AccessDenied, | ||
| 72 | else => |err| return posix.unexpectedErrno(err), | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | pub fn defaultWasiCwd() std.os.wasi.fd_t { | ||
| 77 | // Expect the first preopen to be current working directory. | ||
| 78 | return 3; | ||
| 79 | } |
lib/std/os/emscripten.zig+3-3| ... | @@ -224,14 +224,14 @@ pub const W = struct { | ... | @@ -224,14 +224,14 @@ pub const W = struct { |
| 224 | pub fn EXITSTATUS(s: u32) u8 { | 224 | pub fn EXITSTATUS(s: u32) u8 { |
| 225 | return @as(u8, @intCast((s & 0xff00) >> 8)); | 225 | return @as(u8, @intCast((s & 0xff00) >> 8)); |
| 226 | } | 226 | } |
| 227 | pub fn TERMSIG(s: u32) u32 { | 227 | pub fn TERMSIG(s: u32) SIG { |
| 228 | return s & 0x7f; | 228 | return @enumFromInt(s & 0x7f); |
| 229 | } | 229 | } |
| 230 | pub fn STOPSIG(s: u32) u32 { | 230 | pub fn STOPSIG(s: u32) u32 { |
| 231 | return EXITSTATUS(s); | 231 | return EXITSTATUS(s); |
| 232 | } | 232 | } |
| 233 | pub fn IFEXITED(s: u32) bool { | 233 | pub fn IFEXITED(s: u32) bool { |
| 234 | return TERMSIG(s) == 0; | 234 | return (s & 0x7f) == 0; |
| 235 | } | 235 | } |
| 236 | pub fn IFSTOPPED(s: u32) bool { | 236 | pub fn IFSTOPPED(s: u32) bool { |
| 237 | return @as(u16, @truncate(((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; | 237 | return @as(u16, @truncate(((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; |
lib/std/os/linux.zig+22-5| ... | @@ -1598,8 +1598,15 @@ pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize { | ... | @@ -1598,8 +1598,15 @@ pub fn wait4(pid: pid_t, status: *u32, flags: u32, usage: ?*rusage) usize { |
| 1598 | ); | 1598 | ); |
| 1599 | } | 1599 | } |
| 1600 | 1600 | ||
| 1601 | pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize { | 1601 | pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32, usage: ?*rusage) usize { |
| 1602 | return syscall5(.waitid, @intFromEnum(id_type), @as(usize, @bitCast(@as(isize, id))), @intFromPtr(infop), flags, 0); | 1602 | return syscall5( |
| 1603 | .waitid, | ||
| 1604 | @intFromEnum(id_type), | ||
| 1605 | @as(usize, @bitCast(@as(isize, id))), | ||
| 1606 | @intFromPtr(infop), | ||
| 1607 | flags, | ||
| 1608 | @intFromPtr(usage), | ||
| 1609 | ); | ||
| 1603 | } | 1610 | } |
| 1604 | 1611 | ||
| 1605 | pub const F = struct { | 1612 | pub const F = struct { |
| ... | @@ -3616,14 +3623,14 @@ pub const W = struct { | ... | @@ -3616,14 +3623,14 @@ pub const W = struct { |
| 3616 | pub fn EXITSTATUS(s: u32) u8 { | 3623 | pub fn EXITSTATUS(s: u32) u8 { |
| 3617 | return @as(u8, @intCast((s & 0xff00) >> 8)); | 3624 | return @as(u8, @intCast((s & 0xff00) >> 8)); |
| 3618 | } | 3625 | } |
| 3619 | pub fn TERMSIG(s: u32) u32 { | 3626 | pub fn TERMSIG(s: u32) SIG { |
| 3620 | return s & 0x7f; | 3627 | return @enumFromInt(s & 0x7f); |
| 3621 | } | 3628 | } |
| 3622 | pub fn STOPSIG(s: u32) u32 { | 3629 | pub fn STOPSIG(s: u32) u32 { |
| 3623 | return EXITSTATUS(s); | 3630 | return EXITSTATUS(s); |
| 3624 | } | 3631 | } |
| 3625 | pub fn IFEXITED(s: u32) bool { | 3632 | pub fn IFEXITED(s: u32) bool { |
| 3626 | return TERMSIG(s) == 0; | 3633 | return (s & 0x7f) == 0; |
| 3627 | } | 3634 | } |
| 3628 | pub fn IFSTOPPED(s: u32) bool { | 3635 | pub fn IFSTOPPED(s: u32) bool { |
| 3629 | return @as(u16, @truncate(((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; | 3636 | return @as(u16, @truncate(((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; |
| ... | @@ -6205,6 +6212,16 @@ const siginfo_fields_union = extern union { | ... | @@ -6205,6 +6212,16 @@ const siginfo_fields_union = extern union { |
| 6205 | }, | 6212 | }, |
| 6206 | }; | 6213 | }; |
| 6207 | 6214 | ||
| 6215 | pub const CLD = enum(i32) { | ||
| 6216 | EXITED = 1, | ||
| 6217 | KILLED = 2, | ||
| 6218 | DUMPED = 3, | ||
| 6219 | TRAPPED = 4, | ||
| 6220 | STOPPED = 5, | ||
| 6221 | CONTINUED = 6, | ||
| 6222 | _, | ||
| 6223 | }; | ||
| 6224 | |||
| 6208 | pub const siginfo_t = if (is_mips) | 6225 | pub const siginfo_t = if (is_mips) |
| 6209 | extern struct { | 6226 | extern struct { |
| 6210 | signo: SIG, | 6227 | signo: SIG, |
lib/std/os/linux/IoUring/test.zig+1-1| ... | @@ -280,7 +280,7 @@ test "splice/read" { | ... | @@ -280,7 +280,7 @@ test "splice/read" { |
| 280 | var buffer_read = [_]u8{98} ** 20; | 280 | var buffer_read = [_]u8{98} ** 20; |
| 281 | try file_src.writeStreamingAll(io, &buffer_write); | 281 | try file_src.writeStreamingAll(io, &buffer_write); |
| 282 | 282 | ||
| 283 | const fds = try posix.pipe(); | 283 | const fds = try std.Io.Threaded.pipe2(.{}); |
| 284 | const pipe_offset: u64 = std.math.maxInt(u64); | 284 | const pipe_offset: u64 = std.math.maxInt(u64); |
| 285 | 285 | ||
| 286 | const sqe_splice_to_pipe = try ring.splice(0x11111111, fd_src, 0, fds[1], pipe_offset, buffer_write.len); | 286 | const sqe_splice_to_pipe = try ring.splice(0x11111111, fd_src, 0, fds[1], pipe_offset, buffer_write.len); |
lib/std/os/linux/syscalls.zig+1-2| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | // This file is automatically generated, DO NOT edit it manually. | 1 | // This file is automatically generated by tools/generate_linux_syscalls.zig |
| 2 | // See tools/generate_linux_syscalls.zig for more info. | ||
| 3 | // This list current as of kernel: 6.18.2 | 2 | // This list current as of kernel: 6.18.2 |
| 4 | 3 | ||
| 5 | pub const X86 = enum(usize) { | 4 | pub const X86 = enum(usize) { |
lib/std/os/windows.zig+9-141| ... | @@ -2927,7 +2927,7 @@ pub fn CreateSymbolicLink( | ... | @@ -2927,7 +2927,7 @@ pub fn CreateSymbolicLink( |
| 2927 | // Already an NT path, no need to do anything to it | 2927 | // Already an NT path, no need to do anything to it |
| 2928 | break :target_path target_path; | 2928 | break :target_path target_path; |
| 2929 | } else { | 2929 | } else { |
| 2930 | switch (getWin32PathType(u16, target_path)) { | 2930 | switch (std.fs.path.getWin32PathType(u16, target_path)) { |
| 2931 | // Rooted paths need to avoid getting put through wToPrefixedFileW | 2931 | // Rooted paths need to avoid getting put through wToPrefixedFileW |
| 2932 | // (and they are treated as relative in this context) | 2932 | // (and they are treated as relative in this context) |
| 2933 | // Note: It seems that rooted paths in symbolic links are relative to | 2933 | // 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 | ... | @@ -3756,17 +3756,6 @@ pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) Ge |
| 3756 | return buf_ptr[0..rc :0]; | 3756 | return buf_ptr[0..rc :0]; |
| 3757 | } | 3757 | } |
| 3758 | 3758 | ||
| 3759 | pub const TerminateProcessError = error{ AccessDenied, Unexpected }; | ||
| 3760 | |||
| 3761 | pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void { | ||
| 3762 | if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) { | ||
| 3763 | switch (GetLastError()) { | ||
| 3764 | Win32Error.ACCESS_DENIED => return error.AccessDenied, | ||
| 3765 | else => |err| return unexpectedError(err), | ||
| 3766 | } | ||
| 3767 | } | ||
| 3768 | } | ||
| 3769 | |||
| 3770 | pub const NtAllocateVirtualMemoryError = error{ | 3759 | pub const NtAllocateVirtualMemoryError = error{ |
| 3771 | AccessDenied, | 3760 | AccessDenied, |
| 3772 | InvalidParameter, | 3761 | InvalidParameter, |
| ... | @@ -3919,7 +3908,7 @@ pub fn CreateProcessW( | ... | @@ -3919,7 +3908,7 @@ pub fn CreateProcessW( |
| 3919 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, | 3908 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, |
| 3920 | bInheritHandles: BOOL, | 3909 | bInheritHandles: BOOL, |
| 3921 | dwCreationFlags: CreateProcessFlags, | 3910 | dwCreationFlags: CreateProcessFlags, |
| 3922 | lpEnvironment: ?*anyopaque, | 3911 | lpEnvironment: ?[*:0]u16, |
| 3923 | lpCurrentDirectory: ?LPCWSTR, | 3912 | lpCurrentDirectory: ?LPCWSTR, |
| 3924 | lpStartupInfo: *STARTUPINFOW, | 3913 | lpStartupInfo: *STARTUPINFOW, |
| 3925 | lpProcessInformation: *PROCESS_INFORMATION, | 3914 | lpProcessInformation: *PROCESS_INFORMATION, |
| ... | @@ -4235,7 +4224,7 @@ pub const RemoveDotDirsError = error{TooManyParentDirs}; | ... | @@ -4235,7 +4224,7 @@ pub const RemoveDotDirsError = error{TooManyParentDirs}; |
| 4235 | /// 2) all repeating back slashes have been collapsed | 4224 | /// 2) all repeating back slashes have been collapsed |
| 4236 | /// 3) the path is a relative one (does not start with a back slash) | 4225 | /// 3) the path is a relative one (does not start with a back slash) |
| 4237 | pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize { | 4226 | pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize { |
| 4238 | std.debug.assert(path.len == 0 or path[0] != '\\'); | 4227 | assert(path.len == 0 or path[0] != '\\'); |
| 4239 | 4228 | ||
| 4240 | var write_idx: usize = 0; | 4229 | var write_idx: usize = 0; |
| 4241 | var read_idx: usize = 0; | 4230 | var read_idx: usize = 0; |
| ... | @@ -4251,7 +4240,7 @@ pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!us | ... | @@ -4251,7 +4240,7 @@ pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!us |
| 4251 | } | 4240 | } |
| 4252 | if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) { | 4241 | if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) { |
| 4253 | if (write_idx == 0) return error.TooManyParentDirs; | 4242 | if (write_idx == 0) return error.TooManyParentDirs; |
| 4254 | std.debug.assert(write_idx >= 2); | 4243 | assert(write_idx >= 2); |
| 4255 | write_idx -= 1; | 4244 | write_idx -= 1; |
| 4256 | while (true) { | 4245 | while (true) { |
| 4257 | write_idx -= 1; | 4246 | write_idx -= 1; |
| ... | @@ -4353,7 +4342,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE | ... | @@ -4353,7 +4342,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4353 | path_space.data[path_space.len] = 0; | 4342 | path_space.data[path_space.len] = 0; |
| 4354 | return path_space; | 4343 | return path_space; |
| 4355 | } else { | 4344 | } else { |
| 4356 | const path_type = getWin32PathType(u16, path); | 4345 | const path_type = std.fs.path.getWin32PathType(u16, path); |
| 4357 | var path_space: PathSpace = undefined; | 4346 | var path_space: PathSpace = undefined; |
| 4358 | if (path_type == .local_device) { | 4347 | if (path_type == .local_device) { |
| 4359 | switch (getLocalDevicePathType(u16, path)) { | 4348 | switch (getLocalDevicePathType(u16, path)) { |
| ... | @@ -4491,8 +4480,8 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE | ... | @@ -4491,8 +4480,8 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4491 | if (path_type == .unc_absolute) { | 4480 | if (path_type == .unc_absolute) { |
| 4492 | // Now add in the UNC, the `C` should overwrite the first `\` of the | 4481 | // Now add in the UNC, the `C` should overwrite the first `\` of the |
| 4493 | // FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>` | 4482 | // FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>` |
| 4494 | std.debug.assert(path_space.data[path_buf_offset] == '\\'); | 4483 | assert(path_space.data[path_buf_offset] == '\\'); |
| 4495 | std.debug.assert(path_space.data[path_buf_offset + 1] == '\\'); | 4484 | assert(path_space.data[path_buf_offset + 1] == '\\'); |
| 4496 | const unc = [_]u16{ 'U', 'N', 'C' }; | 4485 | const unc = [_]u16{ 'U', 'N', 'C' }; |
| 4497 | path_space.data[nt_prefix.len..][0..unc.len].* = unc; | 4486 | path_space.data[nt_prefix.len..][0..unc.len].* = unc; |
| 4498 | } | 4487 | } |
| ... | @@ -4500,127 +4489,6 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE | ... | @@ -4500,127 +4489,6 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4500 | } | 4489 | } |
| 4501 | } | 4490 | } |
| 4502 | 4491 | ||
| 4503 | /// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type. | ||
| 4504 | pub const Win32PathType = enum { | ||
| 4505 | /// `\\server\share\foo` | ||
| 4506 | unc_absolute, | ||
| 4507 | /// `C:\foo` | ||
| 4508 | drive_absolute, | ||
| 4509 | /// `C:foo` | ||
| 4510 | drive_relative, | ||
| 4511 | /// `\foo` | ||
| 4512 | rooted, | ||
| 4513 | /// `foo` | ||
| 4514 | relative, | ||
| 4515 | /// `\\.\foo`, `\\?\foo` | ||
| 4516 | local_device, | ||
| 4517 | /// `\\.`, `\\?` | ||
| 4518 | root_local_device, | ||
| 4519 | }; | ||
| 4520 | |||
| 4521 | /// Get the path type of a Win32 namespace path. | ||
| 4522 | /// Similar to `RtlDetermineDosPathNameType_U`. | ||
| 4523 | /// If `T` is `u16`, then `path` should be encoded as WTF-16LE. | ||
| 4524 | pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType { | ||
| 4525 | if (path.len < 1) return .relative; | ||
| 4526 | |||
| 4527 | const windows_path = std.fs.path.PathType.windows; | ||
| 4528 | if (windows_path.isSep(T, path[0])) { | ||
| 4529 | // \x | ||
| 4530 | if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted; | ||
| 4531 | // \\. or \\? | ||
| 4532 | if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) { | ||
| 4533 | // exactly \\. or \\? with nothing trailing | ||
| 4534 | if (path.len == 3) return .root_local_device; | ||
| 4535 | // \\.\x or \\?\x | ||
| 4536 | if (windows_path.isSep(T, path[3])) return .local_device; | ||
| 4537 | } | ||
| 4538 | // \\x | ||
| 4539 | return .unc_absolute; | ||
| 4540 | } else { | ||
| 4541 | // Some choice has to be made about how non-ASCII code points as drive-letters are handled, since | ||
| 4542 | // path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification | ||
| 4543 | // for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code | ||
| 4544 | // units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so | ||
| 4545 | // checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16. | ||
| 4546 | // | ||
| 4547 | // `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers | ||
| 4548 | // `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get | ||
| 4549 | // classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded | ||
| 4550 | // in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path). | ||
| 4551 | // | ||
| 4552 | // The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both | ||
| 4553 | // WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI, | ||
| 4554 | // drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you | ||
| 4555 | // to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will | ||
| 4556 | // allow you to set any WTF-16 code unit as a drive letter. | ||
| 4557 | // | ||
| 4558 | // Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g. | ||
| 4559 | // `cd /D €:\` will work, filesystem functions still work, etc. | ||
| 4560 | // | ||
| 4561 | // The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't | ||
| 4562 | // just check path[0], path[1], path[2]. | ||
| 4563 | const colon_i: usize = switch (T) { | ||
| 4564 | u8 => i: { | ||
| 4565 | const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative; | ||
| 4566 | // Conveniently, 4-byte sequences in WTF-8 have the same starting code point | ||
| 4567 | // as 2-code-unit sequences in WTF-16. | ||
| 4568 | if (code_point_len > 3) return .relative; | ||
| 4569 | break :i code_point_len; | ||
| 4570 | }, | ||
| 4571 | u16 => 1, | ||
| 4572 | else => @compileError("unsupported type: " ++ @typeName(T)), | ||
| 4573 | }; | ||
| 4574 | // x | ||
| 4575 | if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative; | ||
| 4576 | // x:\ | ||
| 4577 | if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute; | ||
| 4578 | // x: | ||
| 4579 | return .drive_relative; | ||
| 4580 | } | ||
| 4581 | } | ||
| 4582 | |||
| 4583 | test getWin32PathType { | ||
| 4584 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "")); | ||
| 4585 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x")); | ||
| 4586 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\")); | ||
| 4587 | |||
| 4588 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//.")); | ||
| 4589 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?")); | ||
| 4590 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?")); | ||
| 4591 | |||
| 4592 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x")); | ||
| 4593 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x")); | ||
| 4594 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x")); | ||
| 4595 | // local device paths require a path separator after the root, otherwise it is considered a UNC path | ||
| 4596 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x")); | ||
| 4597 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x")); | ||
| 4598 | |||
| 4599 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//")); | ||
| 4600 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x")); | ||
| 4601 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x")); | ||
| 4602 | |||
| 4603 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x")); | ||
| 4604 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/")); | ||
| 4605 | |||
| 4606 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:")); | ||
| 4607 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc")); | ||
| 4608 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c")); | ||
| 4609 | |||
| 4610 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\")); | ||
| 4611 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc")); | ||
| 4612 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c")); | ||
| 4613 | |||
| 4614 | // Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter | ||
| 4615 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\")); | ||
| 4616 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\"))); | ||
| 4617 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:")); | ||
| 4618 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:"))); | ||
| 4619 | // But code points that are encoded as two WTF-16 code units are not | ||
| 4620 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\")); | ||
| 4621 | try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\"))); | ||
| 4622 | } | ||
| 4623 | |||
| 4624 | /// Returns true if the path starts with `\??\`, which is indicative of an NT path | 4492 | /// Returns true if the path starts with `\??\`, which is indicative of an NT path |
| 4625 | /// but is not enough to fully distinguish between NT paths and Win32 paths, as | 4493 | /// but is not enough to fully distinguish between NT paths and Win32 paths, as |
| 4626 | /// `\??\` is not actually a distinct prefix but rather the path to a special virtual | 4494 | /// `\??\` is not actually a distinct prefix but rather the path to a special virtual |
| ... | @@ -4660,10 +4528,10 @@ const LocalDevicePathType = enum { | ... | @@ -4660,10 +4528,10 @@ const LocalDevicePathType = enum { |
| 4660 | }; | 4528 | }; |
| 4661 | 4529 | ||
| 4662 | /// Only relevant for Win32 -> NT path conversion. | 4530 | /// Only relevant for Win32 -> NT path conversion. |
| 4663 | /// Asserts `path` is of type `Win32PathType.local_device`. | 4531 | /// Asserts `path` is of type `std.fs.path.Win32PathType.local_device`. |
| 4664 | fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType { | 4532 | fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType { |
| 4665 | if (std.debug.runtime_safety) { | 4533 | if (std.debug.runtime_safety) { |
| 4666 | std.debug.assert(getWin32PathType(T, path) == .local_device); | 4534 | assert(std.fs.path.getWin32PathType(T, path) == .local_device); |
| 4667 | } | 4535 | } |
| 4668 | 4536 | ||
| 4669 | const backslash = mem.nativeToLittle(T, '\\'); | 4537 | const backslash = mem.nativeToLittle(T, '\\'); |
lib/std/os/windows/kernel32.zig+1-1| ... | @@ -265,7 +265,7 @@ pub extern "kernel32" fn CreateProcessW( | ... | @@ -265,7 +265,7 @@ pub extern "kernel32" fn CreateProcessW( |
| 265 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, | 265 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, |
| 266 | bInheritHandles: BOOL, | 266 | bInheritHandles: BOOL, |
| 267 | dwCreationFlags: windows.CreateProcessFlags, | 267 | dwCreationFlags: windows.CreateProcessFlags, |
| 268 | lpEnvironment: ?LPVOID, | 268 | lpEnvironment: ?[*:0]const u16, |
| 269 | lpCurrentDirectory: ?LPCWSTR, | 269 | lpCurrentDirectory: ?LPCWSTR, |
| 270 | lpStartupInfo: *STARTUPINFOW, | 270 | lpStartupInfo: *STARTUPINFOW, |
| 271 | lpProcessInformation: *PROCESS_INFORMATION, | 271 | lpProcessInformation: *PROCESS_INFORMATION, |
lib/std/os/windows/test.zig+3-3| ... | @@ -274,8 +274,8 @@ test "getWin32PathType vs RtlDetermineDosPathNameType_U" { | ... | @@ -274,8 +274,8 @@ test "getWin32PathType vs RtlDetermineDosPathNameType_U" { |
| 274 | std.debug.assert(std.unicode.wtf16LeToWtf8(wtf8_buf.items, path) == wtf8_len); | 274 | std.debug.assert(std.unicode.wtf16LeToWtf8(wtf8_buf.items, path) == wtf8_len); |
| 275 | 275 | ||
| 276 | const windows_type = RtlDetermineDosPathNameType_U(path); | 276 | const windows_type = RtlDetermineDosPathNameType_U(path); |
| 277 | const wtf16_type = windows.getWin32PathType(u16, path); | 277 | const wtf16_type = std.fs.path.getWin32PathType(u16, path); |
| 278 | const wtf8_type = windows.getWin32PathType(u8, wtf8_buf.items); | 278 | const wtf8_type = std.fs.path.getWin32PathType(u8, wtf8_buf.items); |
| 279 | 279 | ||
| 280 | checkPathType(windows_type, wtf16_type) catch |err| { | 280 | checkPathType(windows_type, wtf16_type) catch |err| { |
| 281 | std.debug.print("expected type {}, got {} for path: {f}\n", .{ windows_type, wtf16_type, std.unicode.fmtUtf16Le(path) }); | 281 | 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" { | ... | @@ -295,7 +295,7 @@ test "getWin32PathType vs RtlDetermineDosPathNameType_U" { |
| 295 | } | 295 | } |
| 296 | } | 296 | } |
| 297 | 297 | ||
| 298 | fn checkPathType(windows_type: RTL_PATH_TYPE, zig_type: windows.Win32PathType) !void { | 298 | fn checkPathType(windows_type: RTL_PATH_TYPE, zig_type: std.fs.path.Win32PathType) !void { |
| 299 | const expected_windows_type: RTL_PATH_TYPE = switch (zig_type) { | 299 | const expected_windows_type: RTL_PATH_TYPE = switch (zig_type) { |
| 300 | .unc_absolute => .UncAbsolute, | 300 | .unc_absolute => .UncAbsolute, |
| 301 | .drive_absolute => .DriveAbsolute, | 301 | .drive_absolute => .DriveAbsolute, |
lib/std/posix.zig+1-550| ... | @@ -772,214 +772,10 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O | ... | @@ -772,214 +772,10 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O |
| 772 | } | 772 | } |
| 773 | } | 773 | } |
| 774 | 774 | ||
| 775 | pub fn dup(old_fd: fd_t) !fd_t { | ||
| 776 | const rc = system.dup(old_fd); | ||
| 777 | return switch (errno(rc)) { | ||
| 778 | .SUCCESS => return @intCast(rc), | ||
| 779 | .MFILE => error.ProcessFdQuotaExceeded, | ||
| 780 | .BADF => unreachable, // invalid file descriptor | ||
| 781 | else => |err| return unexpectedErrno(err), | ||
| 782 | }; | ||
| 783 | } | ||
| 784 | |||
| 785 | pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { | ||
| 786 | while (true) { | ||
| 787 | switch (errno(system.dup2(old_fd, new_fd))) { | ||
| 788 | .SUCCESS => return, | ||
| 789 | .BUSY, .INTR => continue, | ||
| 790 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 791 | .INVAL => unreachable, // invalid parameters passed to dup2 | ||
| 792 | .BADF => unreachable, // invalid file descriptor | ||
| 793 | else => |err| return unexpectedErrno(err), | ||
| 794 | } | ||
| 795 | } | ||
| 796 | } | ||
| 797 | |||
| 798 | pub fn getpid() pid_t { | ||
| 799 | return system.getpid(); | ||
| 800 | } | ||
| 801 | |||
| 802 | pub fn getppid() pid_t { | 775 | pub fn getppid() pid_t { |
| 803 | return system.getppid(); | 776 | return system.getppid(); |
| 804 | } | 777 | } |
| 805 | 778 | ||
| 806 | pub const ExecveError = error{ | ||
| 807 | SystemResources, | ||
| 808 | AccessDenied, | ||
| 809 | PermissionDenied, | ||
| 810 | InvalidExe, | ||
| 811 | FileSystem, | ||
| 812 | IsDir, | ||
| 813 | FileNotFound, | ||
| 814 | NotDir, | ||
| 815 | FileBusy, | ||
| 816 | ProcessFdQuotaExceeded, | ||
| 817 | SystemFdQuotaExceeded, | ||
| 818 | NameTooLong, | ||
| 819 | } || UnexpectedError; | ||
| 820 | |||
| 821 | /// This function ignores PATH environment variable. See `execvpeZ` for that. | ||
| 822 | pub fn execveZ( | ||
| 823 | path: [*:0]const u8, | ||
| 824 | child_argv: [*:null]const ?[*:0]const u8, | ||
| 825 | envp: [*:null]const ?[*:0]const u8, | ||
| 826 | ) ExecveError { | ||
| 827 | switch (errno(system.execve(path, child_argv, envp))) { | ||
| 828 | .SUCCESS => unreachable, | ||
| 829 | .FAULT => unreachable, | ||
| 830 | .@"2BIG" => return error.SystemResources, | ||
| 831 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 832 | .NAMETOOLONG => return error.NameTooLong, | ||
| 833 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 834 | .NOMEM => return error.SystemResources, | ||
| 835 | .ACCES => return error.AccessDenied, | ||
| 836 | .PERM => return error.PermissionDenied, | ||
| 837 | .INVAL => return error.InvalidExe, | ||
| 838 | .NOEXEC => return error.InvalidExe, | ||
| 839 | .IO => return error.FileSystem, | ||
| 840 | .LOOP => return error.FileSystem, | ||
| 841 | .ISDIR => return error.IsDir, | ||
| 842 | .NOENT => return error.FileNotFound, | ||
| 843 | .NOTDIR => return error.NotDir, | ||
| 844 | .TXTBSY => return error.FileBusy, | ||
| 845 | else => |err| switch (native_os) { | ||
| 846 | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (err) { | ||
| 847 | .BADEXEC => return error.InvalidExe, | ||
| 848 | .BADARCH => return error.InvalidExe, | ||
| 849 | else => return unexpectedErrno(err), | ||
| 850 | }, | ||
| 851 | .linux => switch (err) { | ||
| 852 | .LIBBAD => return error.InvalidExe, | ||
| 853 | else => return unexpectedErrno(err), | ||
| 854 | }, | ||
| 855 | else => return unexpectedErrno(err), | ||
| 856 | }, | ||
| 857 | } | ||
| 858 | } | ||
| 859 | |||
| 860 | pub const Arg0Expand = enum { | ||
| 861 | expand, | ||
| 862 | no_expand, | ||
| 863 | }; | ||
| 864 | |||
| 865 | /// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable, | ||
| 866 | /// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall. | ||
| 867 | /// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in. | ||
| 868 | pub fn execvpeZ_expandArg0( | ||
| 869 | comptime arg0_expand: Arg0Expand, | ||
| 870 | file: [*:0]const u8, | ||
| 871 | child_argv: switch (arg0_expand) { | ||
| 872 | .expand => [*:null]?[*:0]const u8, | ||
| 873 | .no_expand => [*:null]const ?[*:0]const u8, | ||
| 874 | }, | ||
| 875 | envp: [*:null]const ?[*:0]const u8, | ||
| 876 | ) ExecveError { | ||
| 877 | const file_slice = mem.sliceTo(file, 0); | ||
| 878 | if (mem.findScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp); | ||
| 879 | |||
| 880 | const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; | ||
| 881 | // Use of PATH_MAX here is valid as the path_buf will be passed | ||
| 882 | // directly to the operating system in execveZ. | ||
| 883 | var path_buf: [PATH_MAX]u8 = undefined; | ||
| 884 | var it = mem.tokenizeScalar(u8, PATH, ':'); | ||
| 885 | var seen_eacces = false; | ||
| 886 | var err: ExecveError = error.FileNotFound; | ||
| 887 | |||
| 888 | // In case of expanding arg0 we must put it back if we return with an error. | ||
| 889 | const prev_arg0 = child_argv[0]; | ||
| 890 | defer switch (arg0_expand) { | ||
| 891 | .expand => child_argv[0] = prev_arg0, | ||
| 892 | .no_expand => {}, | ||
| 893 | }; | ||
| 894 | |||
| 895 | while (it.next()) |search_path| { | ||
| 896 | const path_len = search_path.len + file_slice.len + 1; | ||
| 897 | if (path_buf.len < path_len + 1) return error.NameTooLong; | ||
| 898 | @memcpy(path_buf[0..search_path.len], search_path); | ||
| 899 | path_buf[search_path.len] = '/'; | ||
| 900 | @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice); | ||
| 901 | path_buf[path_len] = 0; | ||
| 902 | const full_path = path_buf[0..path_len :0].ptr; | ||
| 903 | switch (arg0_expand) { | ||
| 904 | .expand => child_argv[0] = full_path, | ||
| 905 | .no_expand => {}, | ||
| 906 | } | ||
| 907 | err = execveZ(full_path, child_argv, envp); | ||
| 908 | switch (err) { | ||
| 909 | error.AccessDenied => seen_eacces = true, | ||
| 910 | error.FileNotFound, error.NotDir => {}, | ||
| 911 | else => |e| return e, | ||
| 912 | } | ||
| 913 | } | ||
| 914 | if (seen_eacces) return error.AccessDenied; | ||
| 915 | return err; | ||
| 916 | } | ||
| 917 | |||
| 918 | /// This function also uses the PATH environment variable to get the full path to the executable. | ||
| 919 | /// If `file` is an absolute path, this is the same as `execveZ`. | ||
| 920 | pub fn execvpeZ( | ||
| 921 | file: [*:0]const u8, | ||
| 922 | argv_ptr: [*:null]const ?[*:0]const u8, | ||
| 923 | envp: [*:null]const ?[*:0]const u8, | ||
| 924 | ) ExecveError { | ||
| 925 | return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp); | ||
| 926 | } | ||
| 927 | |||
| 928 | /// Get an environment variable. | ||
| 929 | /// See also `getenvZ`. | ||
| 930 | pub fn getenv(key: []const u8) ?[:0]const u8 { | ||
| 931 | if (native_os == .windows) { | ||
| 932 | @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."); | ||
| 933 | } | ||
| 934 | if (mem.findScalar(u8, key, '=') != null) { | ||
| 935 | return null; | ||
| 936 | } | ||
| 937 | if (builtin.link_libc) { | ||
| 938 | var ptr = std.c.environ; | ||
| 939 | while (ptr[0]) |line| : (ptr += 1) { | ||
| 940 | var line_i: usize = 0; | ||
| 941 | while (line[line_i] != 0) : (line_i += 1) { | ||
| 942 | if (line_i == key.len) break; | ||
| 943 | if (line[line_i] != key[line_i]) break; | ||
| 944 | } | ||
| 945 | if ((line_i != key.len) or (line[line_i] != '=')) continue; | ||
| 946 | |||
| 947 | return mem.sliceTo(line + line_i + 1, 0); | ||
| 948 | } | ||
| 949 | return null; | ||
| 950 | } | ||
| 951 | if (native_os == .wasi) { | ||
| 952 | @compileError("std.posix.getenv is unavailable for WASI. See std.process.getEnvMap or std.process.getEnvVarOwned for a cross-platform API."); | ||
| 953 | } | ||
| 954 | // The simplified start logic doesn't populate environ. | ||
| 955 | if (std.start.simplified_logic) return null; | ||
| 956 | // TODO see https://github.com/ziglang/zig/issues/4524 | ||
| 957 | for (std.os.environ) |ptr| { | ||
| 958 | var line_i: usize = 0; | ||
| 959 | while (ptr[line_i] != 0) : (line_i += 1) { | ||
| 960 | if (line_i == key.len) break; | ||
| 961 | if (ptr[line_i] != key[line_i]) break; | ||
| 962 | } | ||
| 963 | if ((line_i != key.len) or (ptr[line_i] != '=')) continue; | ||
| 964 | |||
| 965 | return mem.sliceTo(ptr + line_i + 1, 0); | ||
| 966 | } | ||
| 967 | return null; | ||
| 968 | } | ||
| 969 | |||
| 970 | /// Get an environment variable with a null-terminated name. | ||
| 971 | /// See also `getenv`. | ||
| 972 | pub fn getenvZ(key: [*:0]const u8) ?[:0]const u8 { | ||
| 973 | if (builtin.link_libc) { | ||
| 974 | const value = system.getenv(key) orelse return null; | ||
| 975 | return mem.sliceTo(value, 0); | ||
| 976 | } | ||
| 977 | if (native_os == .windows) { | ||
| 978 | @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."); | ||
| 979 | } | ||
| 980 | return getenv(mem.sliceTo(key, 0)); | ||
| 981 | } | ||
| 982 | |||
| 983 | pub const GetCwdError = error{ | 779 | pub const GetCwdError = error{ |
| 984 | NameTooLong, | 780 | NameTooLong, |
| 985 | CurrentWorkingDirectoryUnlinked, | 781 | CurrentWorkingDirectoryUnlinked, |
| ... | @@ -1013,198 +809,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { | ... | @@ -1013,198 +809,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { |
| 1013 | } | 809 | } |
| 1014 | } | 810 | } |
| 1015 | 811 | ||
| 1016 | /// On Windows, `sub_dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1017 | /// On WASI, `sub_dir_path` should be encoded as valid UTF-8. | ||
| 1018 | /// On other platforms, `sub_dir_path` is an opaque sequence of bytes with no particular encoding. | ||
| 1019 | pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirError!void { | ||
| 1020 | if (native_os == .windows) { | ||
| 1021 | @compileError("use std.Io instead"); | ||
| 1022 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 1023 | @compileError("use std.Io instead"); | ||
| 1024 | } else { | ||
| 1025 | const sub_dir_path_c = try toPosixPath(sub_dir_path); | ||
| 1026 | return mkdiratZ(dir_fd, &sub_dir_path_c, mode); | ||
| 1027 | } | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | /// Same as `mkdirat` except the parameters are null-terminated. | ||
| 1031 | pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void { | ||
| 1032 | if (native_os == .windows) { | ||
| 1033 | @compileError("use std.Io instead"); | ||
| 1034 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 1035 | @compileError("use std.Io instead"); | ||
| 1036 | } | ||
| 1037 | switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { | ||
| 1038 | .SUCCESS => return, | ||
| 1039 | .ACCES => return error.AccessDenied, | ||
| 1040 | .BADF => unreachable, | ||
| 1041 | .PERM => return error.PermissionDenied, | ||
| 1042 | .DQUOT => return error.DiskQuota, | ||
| 1043 | .EXIST => return error.PathAlreadyExists, | ||
| 1044 | .FAULT => unreachable, | ||
| 1045 | .LOOP => return error.SymLinkLoop, | ||
| 1046 | .MLINK => return error.LinkQuotaExceeded, | ||
| 1047 | .NAMETOOLONG => return error.NameTooLong, | ||
| 1048 | .NOENT => return error.FileNotFound, | ||
| 1049 | .NOMEM => return error.SystemResources, | ||
| 1050 | .NOSPC => return error.NoSpaceLeft, | ||
| 1051 | .NOTDIR => return error.NotDir, | ||
| 1052 | .ROFS => return error.ReadOnlyFileSystem, | ||
| 1053 | // dragonfly: when dir_fd is unlinked from filesystem | ||
| 1054 | .NOTCONN => return error.FileNotFound, | ||
| 1055 | .ILSEQ => return error.BadPathName, | ||
| 1056 | else => |err| return unexpectedErrno(err), | ||
| 1057 | } | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | pub const MakeDirError = std.Io.Dir.CreateDirError; | ||
| 1061 | |||
| 1062 | /// Create a directory. | ||
| 1063 | /// `mode` is ignored on Windows and WASI. | ||
| 1064 | /// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1065 | /// On WASI, `dir_path` should be encoded as valid UTF-8. | ||
| 1066 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | ||
| 1067 | pub fn mkdir(dir_path: []const u8, mode: mode_t) MakeDirError!void { | ||
| 1068 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 1069 | return mkdirat(AT.FDCWD, dir_path, mode); | ||
| 1070 | } else if (native_os == .windows) { | ||
| 1071 | const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path); | ||
| 1072 | return mkdirW(dir_path_w.span(), mode); | ||
| 1073 | } else { | ||
| 1074 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1075 | return mkdirZ(&dir_path_c, mode); | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | /// Same as `mkdir` but the parameter is null-terminated. | ||
| 1080 | /// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1081 | /// On WASI, `dir_path` should be encoded as valid UTF-8. | ||
| 1082 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | ||
| 1083 | pub fn mkdirZ(dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void { | ||
| 1084 | if (native_os == .windows) { | ||
| 1085 | const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path); | ||
| 1086 | return mkdirW(dir_path_w.span(), mode); | ||
| 1087 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 1088 | return mkdir(mem.sliceTo(dir_path, 0), mode); | ||
| 1089 | } | ||
| 1090 | switch (errno(system.mkdir(dir_path, mode))) { | ||
| 1091 | .SUCCESS => return, | ||
| 1092 | .ACCES => return error.AccessDenied, | ||
| 1093 | .PERM => return error.PermissionDenied, | ||
| 1094 | .DQUOT => return error.DiskQuota, | ||
| 1095 | .EXIST => return error.PathAlreadyExists, | ||
| 1096 | .FAULT => unreachable, | ||
| 1097 | .LOOP => return error.SymLinkLoop, | ||
| 1098 | .MLINK => return error.LinkQuotaExceeded, | ||
| 1099 | .NAMETOOLONG => return error.NameTooLong, | ||
| 1100 | .NOENT => return error.FileNotFound, | ||
| 1101 | .NOMEM => return error.SystemResources, | ||
| 1102 | .NOSPC => return error.NoSpaceLeft, | ||
| 1103 | .NOTDIR => return error.NotDir, | ||
| 1104 | .ROFS => return error.ReadOnlyFileSystem, | ||
| 1105 | .ILSEQ => return error.BadPathName, | ||
| 1106 | else => |err| return unexpectedErrno(err), | ||
| 1107 | } | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | /// Windows-only. Same as `mkdir` but the parameters is WTF16LE encoded. | ||
| 1111 | pub fn mkdirW(dir_path_w: []const u16, mode: mode_t) MakeDirError!void { | ||
| 1112 | _ = mode; | ||
| 1113 | const sub_dir_handle = windows.OpenFile(dir_path_w, .{ | ||
| 1114 | .dir = Io.Dir.cwd().handle, | ||
| 1115 | .access_mask = .{ | ||
| 1116 | .STANDARD = .{ .SYNCHRONIZE = true }, | ||
| 1117 | .GENERIC = .{ .READ = true }, | ||
| 1118 | }, | ||
| 1119 | .creation = .CREATE, | ||
| 1120 | .filter = .dir_only, | ||
| 1121 | }) catch |err| switch (err) { | ||
| 1122 | error.IsDir => return error.Unexpected, | ||
| 1123 | error.PipeBusy => return error.Unexpected, | ||
| 1124 | error.NoDevice => return error.Unexpected, | ||
| 1125 | error.WouldBlock => return error.Unexpected, | ||
| 1126 | error.AntivirusInterference => return error.Unexpected, | ||
| 1127 | error.OperationCanceled => return error.Unexpected, | ||
| 1128 | else => |e| return e, | ||
| 1129 | }; | ||
| 1130 | windows.CloseHandle(sub_dir_handle); | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | pub const ChangeCurDirError = error{ | ||
| 1134 | AccessDenied, | ||
| 1135 | FileSystem, | ||
| 1136 | SymLinkLoop, | ||
| 1137 | NameTooLong, | ||
| 1138 | FileNotFound, | ||
| 1139 | SystemResources, | ||
| 1140 | NotDir, | ||
| 1141 | /// WASI: file paths must be valid UTF-8. | ||
| 1142 | /// Windows: file paths provided by the user must be valid WTF-8. | ||
| 1143 | /// https://wtf-8.codeberg.page/ | ||
| 1144 | BadPathName, | ||
| 1145 | } || UnexpectedError; | ||
| 1146 | |||
| 1147 | /// Changes the current working directory of the calling process. | ||
| 1148 | /// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1149 | /// On WASI, `dir_path` should be encoded as valid UTF-8. | ||
| 1150 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | ||
| 1151 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { | ||
| 1152 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 1153 | @compileError("unsupported OS"); | ||
| 1154 | } else if (native_os == .windows) { | ||
| 1155 | @compileError("unsupported OS"); | ||
| 1156 | } else { | ||
| 1157 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1158 | return chdirZ(&dir_path_c); | ||
| 1159 | } | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | /// Same as `chdir` except the parameter is null-terminated. | ||
| 1163 | /// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1164 | /// On WASI, `dir_path` should be encoded as valid UTF-8. | ||
| 1165 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | ||
| 1166 | pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { | ||
| 1167 | if (native_os == .windows) { | ||
| 1168 | @compileError("unsupported OS"); | ||
| 1169 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 1170 | @compileError("unsupported OS"); | ||
| 1171 | } | ||
| 1172 | switch (errno(system.chdir(dir_path))) { | ||
| 1173 | .SUCCESS => return, | ||
| 1174 | .ACCES => return error.AccessDenied, | ||
| 1175 | .FAULT => unreachable, | ||
| 1176 | .IO => return error.FileSystem, | ||
| 1177 | .LOOP => return error.SymLinkLoop, | ||
| 1178 | .NAMETOOLONG => return error.NameTooLong, | ||
| 1179 | .NOENT => return error.FileNotFound, | ||
| 1180 | .NOMEM => return error.SystemResources, | ||
| 1181 | .NOTDIR => return error.NotDir, | ||
| 1182 | .ILSEQ => return error.BadPathName, | ||
| 1183 | else => |err| return unexpectedErrno(err), | ||
| 1184 | } | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | pub const FchdirError = error{ | ||
| 1188 | AccessDenied, | ||
| 1189 | NotDir, | ||
| 1190 | FileSystem, | ||
| 1191 | } || UnexpectedError; | ||
| 1192 | |||
| 1193 | pub fn fchdir(dirfd: fd_t) FchdirError!void { | ||
| 1194 | if (dirfd == AT.FDCWD) return; | ||
| 1195 | while (true) { | ||
| 1196 | switch (errno(system.fchdir(dirfd))) { | ||
| 1197 | .SUCCESS => return, | ||
| 1198 | .ACCES => return error.AccessDenied, | ||
| 1199 | .BADF => unreachable, | ||
| 1200 | .NOTDIR => return error.NotDir, | ||
| 1201 | .INTR => continue, | ||
| 1202 | .IO => return error.FileSystem, | ||
| 1203 | else => |err| return unexpectedErrno(err), | ||
| 1204 | } | ||
| 1205 | } | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | pub const SetEidError = error{ | 812 | pub const SetEidError = error{ |
| 1209 | InvalidUserId, | 813 | InvalidUserId, |
| 1210 | PermissionDenied, | 814 | PermissionDenied, |
| ... | @@ -1231,16 +835,6 @@ pub fn seteuid(uid: uid_t) SetEidError!void { | ... | @@ -1231,16 +835,6 @@ pub fn seteuid(uid: uid_t) SetEidError!void { |
| 1231 | } | 835 | } |
| 1232 | } | 836 | } |
| 1233 | 837 | ||
| 1234 | pub fn setreuid(ruid: uid_t, euid: uid_t) SetIdError!void { | ||
| 1235 | switch (errno(system.setreuid(ruid, euid))) { | ||
| 1236 | .SUCCESS => return, | ||
| 1237 | .AGAIN => return error.ResourceLimitReached, | ||
| 1238 | .INVAL => return error.InvalidUserId, | ||
| 1239 | .PERM => return error.PermissionDenied, | ||
| 1240 | else => |err| return unexpectedErrno(err), | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | pub fn setgid(gid: gid_t) SetIdError!void { | 838 | pub fn setgid(gid: gid_t) SetIdError!void { |
| 1245 | switch (errno(system.setgid(gid))) { | 839 | switch (errno(system.setgid(gid))) { |
| 1246 | .SUCCESS => return, | 840 | .SUCCESS => return, |
| ... | @@ -1260,34 +854,6 @@ pub fn setegid(uid: uid_t) SetEidError!void { | ... | @@ -1260,34 +854,6 @@ pub fn setegid(uid: uid_t) SetEidError!void { |
| 1260 | } | 854 | } |
| 1261 | } | 855 | } |
| 1262 | 856 | ||
| 1263 | pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void { | ||
| 1264 | switch (errno(system.setregid(rgid, egid))) { | ||
| 1265 | .SUCCESS => return, | ||
| 1266 | .AGAIN => return error.ResourceLimitReached, | ||
| 1267 | .INVAL => return error.InvalidUserId, | ||
| 1268 | .PERM => return error.PermissionDenied, | ||
| 1269 | else => |err| return unexpectedErrno(err), | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | pub const SetPgidError = error{ | ||
| 1274 | ProcessAlreadyExec, | ||
| 1275 | InvalidProcessGroupId, | ||
| 1276 | PermissionDenied, | ||
| 1277 | ProcessNotFound, | ||
| 1278 | } || UnexpectedError; | ||
| 1279 | |||
| 1280 | pub fn setpgid(pid: pid_t, pgid: pid_t) SetPgidError!void { | ||
| 1281 | switch (errno(system.setpgid(pid, pgid))) { | ||
| 1282 | .SUCCESS => return, | ||
| 1283 | .ACCES => return error.ProcessAlreadyExec, | ||
| 1284 | .INVAL => return error.InvalidProcessGroupId, | ||
| 1285 | .PERM => return error.PermissionDenied, | ||
| 1286 | .SRCH => return error.ProcessNotFound, | ||
| 1287 | else => |err| return unexpectedErrno(err), | ||
| 1288 | } | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | pub fn getuid() uid_t { | 857 | pub fn getuid() uid_t { |
| 1292 | return system.getuid(); | 858 | return system.getuid(); |
| 1293 | } | 859 | } |
| ... | @@ -1899,53 +1465,12 @@ pub fn getsockoptError(sockfd: fd_t) ConnectError!void { | ... | @@ -1899,53 +1465,12 @@ pub fn getsockoptError(sockfd: fd_t) ConnectError!void { |
| 1899 | } | 1465 | } |
| 1900 | } | 1466 | } |
| 1901 | 1467 | ||
| 1902 | pub const WaitPidResult = struct { | ||
| 1903 | pid: pid_t, | ||
| 1904 | status: u32, | ||
| 1905 | }; | ||
| 1906 | |||
| 1907 | /// Use this version of the `waitpid` wrapper if you spawned your child process using explicit | ||
| 1908 | /// `fork` and `execve` method. | ||
| 1909 | pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult { | ||
| 1910 | var status: if (builtin.link_libc) c_int else u32 = undefined; | ||
| 1911 | while (true) { | ||
| 1912 | const rc = system.waitpid(pid, &status, @intCast(flags)); | ||
| 1913 | switch (errno(rc)) { | ||
| 1914 | .SUCCESS => return .{ | ||
| 1915 | .pid = @intCast(rc), | ||
| 1916 | .status = @bitCast(status), | ||
| 1917 | }, | ||
| 1918 | .INTR => continue, | ||
| 1919 | .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 1920 | .INVAL => unreachable, // Invalid flags. | ||
| 1921 | else => unreachable, | ||
| 1922 | } | ||
| 1923 | } | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult { | ||
| 1927 | var status: if (builtin.link_libc) c_int else u32 = undefined; | ||
| 1928 | while (true) { | ||
| 1929 | const rc = system.wait4(pid, &status, @intCast(flags), ru); | ||
| 1930 | switch (errno(rc)) { | ||
| 1931 | .SUCCESS => return .{ | ||
| 1932 | .pid = @intCast(rc), | ||
| 1933 | .status = @bitCast(status), | ||
| 1934 | }, | ||
| 1935 | .INTR => continue, | ||
| 1936 | .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 1937 | .INVAL => unreachable, // Invalid flags. | ||
| 1938 | else => unreachable, | ||
| 1939 | } | ||
| 1940 | } | ||
| 1941 | } | ||
| 1942 | |||
| 1943 | pub const FStatError = std.Io.File.StatError; | 1468 | pub const FStatError = std.Io.File.StatError; |
| 1944 | 1469 | ||
| 1945 | /// Return information about a file descriptor. | 1470 | /// Return information about a file descriptor. |
| 1946 | pub fn fstat(fd: fd_t) FStatError!Stat { | 1471 | pub fn fstat(fd: fd_t) FStatError!Stat { |
| 1947 | if (native_os == .wasi and !builtin.link_libc) { | 1472 | if (native_os == .wasi and !builtin.link_libc) { |
| 1948 | return Stat.fromFilestat(try std.os.fstat_wasi(fd)); | 1473 | @compileError("unsupported OS"); |
| 1949 | } | 1474 | } |
| 1950 | 1475 | ||
| 1951 | var stat = mem.zeroes(Stat); | 1476 | var stat = mem.zeroes(Stat); |
| ... | @@ -2468,80 +1993,6 @@ pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void { | ... | @@ -2468,80 +1993,6 @@ pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void { |
| 2468 | } | 1993 | } |
| 2469 | } | 1994 | } |
| 2470 | 1995 | ||
| 2471 | pub const PipeError = error{ | ||
| 2472 | SystemFdQuotaExceeded, | ||
| 2473 | ProcessFdQuotaExceeded, | ||
| 2474 | } || UnexpectedError; | ||
| 2475 | |||
| 2476 | /// Creates a unidirectional data channel that can be used for interprocess communication. | ||
| 2477 | pub fn pipe() PipeError![2]fd_t { | ||
| 2478 | var fds: [2]fd_t = undefined; | ||
| 2479 | switch (errno(system.pipe(&fds))) { | ||
| 2480 | .SUCCESS => return fds, | ||
| 2481 | .INVAL => unreachable, // Invalid parameters to pipe() | ||
| 2482 | .FAULT => unreachable, // Invalid fds pointer | ||
| 2483 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 2484 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 2485 | else => |err| return unexpectedErrno(err), | ||
| 2486 | } | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | pub fn pipe2(flags: O) PipeError![2]fd_t { | ||
| 2490 | if (@TypeOf(system.pipe2) != void) { | ||
| 2491 | var fds: [2]fd_t = undefined; | ||
| 2492 | switch (errno(system.pipe2(&fds, flags))) { | ||
| 2493 | .SUCCESS => return fds, | ||
| 2494 | .INVAL => unreachable, // Invalid flags | ||
| 2495 | .FAULT => unreachable, // Invalid fds pointer | ||
| 2496 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 2497 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 2498 | else => |err| return unexpectedErrno(err), | ||
| 2499 | } | ||
| 2500 | } | ||
| 2501 | |||
| 2502 | const fds: [2]fd_t = try pipe(); | ||
| 2503 | errdefer { | ||
| 2504 | close(fds[0]); | ||
| 2505 | close(fds[1]); | ||
| 2506 | } | ||
| 2507 | |||
| 2508 | // https://github.com/ziglang/zig/issues/18882 | ||
| 2509 | if (@as(u32, @bitCast(flags)) == 0) | ||
| 2510 | return fds; | ||
| 2511 | |||
| 2512 | // CLOEXEC is special, it's a file descriptor flag and must be set using | ||
| 2513 | // F.SETFD. | ||
| 2514 | if (flags.CLOEXEC) { | ||
| 2515 | for (fds) |fd| { | ||
| 2516 | switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) { | ||
| 2517 | .SUCCESS => {}, | ||
| 2518 | .INVAL => unreachable, // Invalid flags | ||
| 2519 | .BADF => unreachable, // Always a race condition | ||
| 2520 | else => |err| return unexpectedErrno(err), | ||
| 2521 | } | ||
| 2522 | } | ||
| 2523 | } | ||
| 2524 | |||
| 2525 | const new_flags: u32 = f: { | ||
| 2526 | var new_flags = flags; | ||
| 2527 | new_flags.CLOEXEC = false; | ||
| 2528 | break :f @bitCast(new_flags); | ||
| 2529 | }; | ||
| 2530 | // Set every other flag affecting the file status using F.SETFL. | ||
| 2531 | if (new_flags != 0) { | ||
| 2532 | for (fds) |fd| { | ||
| 2533 | switch (errno(system.fcntl(fd, F.SETFL, new_flags))) { | ||
| 2534 | .SUCCESS => {}, | ||
| 2535 | .INVAL => unreachable, // Invalid flags | ||
| 2536 | .BADF => unreachable, // Always a race condition | ||
| 2537 | else => |err| return unexpectedErrno(err), | ||
| 2538 | } | ||
| 2539 | } | ||
| 2540 | } | ||
| 2541 | |||
| 2542 | return fds; | ||
| 2543 | } | ||
| 2544 | |||
| 2545 | pub const SysCtlError = error{ | 1996 | pub const SysCtlError = error{ |
| 2546 | PermissionDenied, | 1997 | PermissionDenied, |
| 2547 | SystemResources, | 1998 | SystemResources, |
lib/std/posix/test.zig+5-41| ... | @@ -22,10 +22,10 @@ const tmpDir = std.testing.tmpDir; | ... | @@ -22,10 +22,10 @@ const tmpDir = std.testing.tmpDir; |
| 22 | 22 | ||
| 23 | test "check WASI CWD" { | 23 | test "check WASI CWD" { |
| 24 | if (native_os == .wasi) { | 24 | if (native_os == .wasi) { |
| 25 | if (std.options.wasiCwd() != 3) { | 25 | const cwd: Dir = .cwd(); |
| 26 | if (cwd.handle != 3) { | ||
| 26 | @panic("WASI code that uses cwd (like this test) needs a preopen for cwd (add '--dir=.' to wasmtime)"); | 27 | @panic("WASI code that uses cwd (like this test) needs a preopen for cwd (add '--dir=.' to wasmtime)"); |
| 27 | } | 28 | } |
| 28 | |||
| 29 | if (!builtin.link_libc) { | 29 | if (!builtin.link_libc) { |
| 30 | // WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls | 30 | // WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls |
| 31 | try expectEqual(3, posix.AT.FDCWD); | 31 | try expectEqual(3, posix.AT.FDCWD); |
| ... | @@ -131,7 +131,7 @@ test "pipe" { | ... | @@ -131,7 +131,7 @@ test "pipe" { |
| 131 | if (native_os == .windows or native_os == .wasi) | 131 | if (native_os == .windows or native_os == .wasi) |
| 132 | return error.SkipZigTest; | 132 | return error.SkipZigTest; |
| 133 | 133 | ||
| 134 | const fds = try posix.pipe(); | 134 | const fds = try std.Io.Threaded.pipe2(.{}); |
| 135 | try expect((try posix.write(fds[1], "hello")) == 5); | 135 | try expect((try posix.write(fds[1], "hello")) == 5); |
| 136 | var buf: [16]u8 = undefined; | 136 | var buf: [16]u8 = undefined; |
| 137 | try expect((try posix.read(fds[0], buf[0..])) == 5); | 137 | try expect((try posix.read(fds[0], buf[0..])) == 5); |
| ... | @@ -140,11 +140,6 @@ test "pipe" { | ... | @@ -140,11 +140,6 @@ test "pipe" { |
| 140 | posix.close(fds[0]); | 140 | posix.close(fds[0]); |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | test "argsAlloc" { | ||
| 144 | const args = try std.process.argsAlloc(std.testing.allocator); | ||
| 145 | std.process.argsFree(std.testing.allocator, args); | ||
| 146 | } | ||
| 147 | |||
| 148 | test "memfd_create" { | 143 | test "memfd_create" { |
| 149 | const io = testing.io; | 144 | const io = testing.io; |
| 150 | 145 | ||
| ... | @@ -438,42 +433,11 @@ test "sigset add/del" { | ... | @@ -438,42 +433,11 @@ test "sigset add/del" { |
| 438 | } | 433 | } |
| 439 | } | 434 | } |
| 440 | 435 | ||
| 441 | test "dup & dup2" { | ||
| 442 | switch (native_os) { | ||
| 443 | .linux, .illumos => {}, | ||
| 444 | else => return error.SkipZigTest, | ||
| 445 | } | ||
| 446 | |||
| 447 | const io = testing.io; | ||
| 448 | |||
| 449 | var tmp = tmpDir(.{}); | ||
| 450 | defer tmp.cleanup(); | ||
| 451 | |||
| 452 | { | ||
| 453 | var file = try tmp.dir.createFile(io, "os_dup_test", .{}); | ||
| 454 | defer file.close(io); | ||
| 455 | |||
| 456 | var duped = Io.File{ .handle = try posix.dup(file.handle) }; | ||
| 457 | defer duped.close(io); | ||
| 458 | try duped.writeStreamingAll(io, "dup"); | ||
| 459 | |||
| 460 | // Tests aren't run in parallel so using the next fd shouldn't be an issue. | ||
| 461 | const new_fd = duped.handle + 1; | ||
| 462 | try posix.dup2(file.handle, new_fd); | ||
| 463 | var dup2ed = Io.File{ .handle = new_fd }; | ||
| 464 | defer dup2ed.close(io); | ||
| 465 | try dup2ed.writeStreamingAll(io, "dup2"); | ||
| 466 | } | ||
| 467 | |||
| 468 | var buffer: [8]u8 = undefined; | ||
| 469 | try expectEqualStrings("dupdup2", try tmp.dir.readFile(io, "os_dup_test", &buffer)); | ||
| 470 | } | ||
| 471 | |||
| 472 | test "getpid" { | 436 | test "getpid" { |
| 473 | if (native_os == .wasi) return error.SkipZigTest; | 437 | if (native_os == .wasi) return error.SkipZigTest; |
| 474 | if (native_os == .windows) return error.SkipZigTest; | 438 | if (native_os == .windows) return error.SkipZigTest; |
| 475 | 439 | ||
| 476 | try expect(posix.getpid() != 0); | 440 | try expect(posix.system.getpid() != 0); |
| 477 | } | 441 | } |
| 478 | 442 | ||
| 479 | test "getppid" { | 443 | test "getppid" { |
| ... | @@ -532,7 +496,7 @@ test "rename smoke test" { | ... | @@ -532,7 +496,7 @@ test "rename smoke test" { |
| 532 | // Create some directory | 496 | // Create some directory |
| 533 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" }); | 497 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" }); |
| 534 | defer gpa.free(file_path); | 498 | defer gpa.free(file_path); |
| 535 | try posix.mkdir(file_path, mode); | 499 | try Io.Dir.createDirAbsolute(io, file_path, .fromMode(mode)); |
| 536 | 500 | ||
| 537 | // Rename the directory | 501 | // Rename the directory |
| 538 | const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" }); | 502 | const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" }); |
lib/std/process.zig+294-1742| ... | @@ -16,10 +16,8 @@ const unicode = std.unicode; | ... | @@ -16,10 +16,8 @@ const unicode = std.unicode; |
| 16 | const max_path_bytes = std.fs.max_path_bytes; | 16 | const max_path_bytes = std.fs.max_path_bytes; |
| 17 | 17 | ||
| 18 | pub const Child = @import("process/Child.zig"); | 18 | pub const Child = @import("process/Child.zig"); |
| 19 | pub const changeCurDir = posix.chdir; | 19 | pub const Args = @import("process/Args.zig"); |
| 20 | pub const changeCurDirZ = posix.chdirZ; | 20 | pub const Environ = @import("process/Environ.zig"); |
| 21 | |||
| 22 | pub const GetCwdError = posix.GetCwdError; | ||
| 23 | 21 | ||
| 24 | /// This is the global, process-wide protection to coordinate stderr writes. | 22 | /// This is the global, process-wide protection to coordinate stderr writes. |
| 25 | /// | 23 | /// |
| ... | @@ -28,6 +26,40 @@ pub const GetCwdError = posix.GetCwdError; | ... | @@ -28,6 +26,40 @@ pub const GetCwdError = posix.GetCwdError; |
| 28 | /// information. | 26 | /// information. |
| 29 | pub var stderr_thread_mutex: std.Thread.Mutex.Recursive = .init; | 27 | pub var stderr_thread_mutex: std.Thread.Mutex.Recursive = .init; |
| 30 | 28 | ||
| 29 | /// A standard set of pre-initialized useful APIs for programs to take | ||
| 30 | /// advantage of. This is the type of the first parameter of the main function. | ||
| 31 | /// Applications wanting more flexibility can accept `Init.Minimal` instead. | ||
| 32 | /// | ||
| 33 | /// Completion of https://github.com/ziglang/zig/issues/24510 will also allow | ||
| 34 | /// the second parameter of the main function to be a custom struct that | ||
| 35 | /// contain auto-parsed CLI arguments. | ||
| 36 | pub const Init = struct { | ||
| 37 | /// `Init` is a superset of `Minimal`; the latter is included here. | ||
| 38 | minimal: Minimal, | ||
| 39 | /// Permanent storage for the entire process, cleaned automatically on | ||
| 40 | /// exit. Not threadsafe. | ||
| 41 | arena: *std.heap.ArenaAllocator, | ||
| 42 | /// A default-selected general purpose allocator for temporary heap | ||
| 43 | /// allocations. Debug mode will set up leak checking if possible. | ||
| 44 | /// Threadsafe. | ||
| 45 | gpa: Allocator, | ||
| 46 | /// An appropriate default Io implementation based on the target | ||
| 47 | /// configuration. Debug mode will set up leak checking if possible. | ||
| 48 | io: Io, | ||
| 49 | /// Environment variables, initialized with `gpa`. Not threadsafe. | ||
| 50 | environ_map: *Environ.Map, | ||
| 51 | |||
| 52 | /// Alternative to `Init` as the first parameter of the main function. | ||
| 53 | pub const Minimal = struct { | ||
| 54 | /// Environment variables. | ||
| 55 | environ: Environ, | ||
| 56 | /// Command line arguments. | ||
| 57 | args: Args, | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | |||
| 61 | pub const GetCwdError = posix.GetCwdError; | ||
| 62 | |||
| 31 | /// The result is a slice of `out_buffer`, from index `0`. | 63 | /// The result is a slice of `out_buffer`, from index `0`. |
| 32 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). | 64 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| 33 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. | 65 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. |
| ... | @@ -73,1484 +105,6 @@ test getCwdAlloc { | ... | @@ -73,1484 +105,6 @@ test getCwdAlloc { |
| 73 | testing.allocator.free(cwd); | 105 | testing.allocator.free(cwd); |
| 74 | } | 106 | } |
| 75 | 107 | ||
| 76 | pub const EnvMap = struct { | ||
| 77 | hash_map: HashMap, | ||
| 78 | |||
| 79 | const HashMap = std.HashMap( | ||
| 80 | []const u8, | ||
| 81 | []const u8, | ||
| 82 | EnvNameHashContext, | ||
| 83 | std.hash_map.default_max_load_percentage, | ||
| 84 | ); | ||
| 85 | |||
| 86 | pub const Size = HashMap.Size; | ||
| 87 | |||
| 88 | pub const EnvNameHashContext = struct { | ||
| 89 | fn upcase(c: u21) u21 { | ||
| 90 | if (c <= std.math.maxInt(u16)) | ||
| 91 | return windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c))); | ||
| 92 | return c; | ||
| 93 | } | ||
| 94 | |||
| 95 | pub fn hash(self: @This(), s: []const u8) u64 { | ||
| 96 | _ = self; | ||
| 97 | if (native_os == .windows) { | ||
| 98 | var h = std.hash.Wyhash.init(0); | ||
| 99 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); | ||
| 100 | while (it.nextCodepoint()) |cp| { | ||
| 101 | const cp_upper = upcase(cp); | ||
| 102 | h.update(&[_]u8{ | ||
| 103 | @as(u8, @intCast((cp_upper >> 16) & 0xff)), | ||
| 104 | @as(u8, @intCast((cp_upper >> 8) & 0xff)), | ||
| 105 | @as(u8, @intCast((cp_upper >> 0) & 0xff)), | ||
| 106 | }); | ||
| 107 | } | ||
| 108 | return h.final(); | ||
| 109 | } | ||
| 110 | return std.hash_map.hashString(s); | ||
| 111 | } | ||
| 112 | |||
| 113 | pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { | ||
| 114 | _ = self; | ||
| 115 | if (native_os == .windows) { | ||
| 116 | var it_a = unicode.Wtf8View.initUnchecked(a).iterator(); | ||
| 117 | var it_b = unicode.Wtf8View.initUnchecked(b).iterator(); | ||
| 118 | while (true) { | ||
| 119 | const c_a = it_a.nextCodepoint() orelse break; | ||
| 120 | const c_b = it_b.nextCodepoint() orelse return false; | ||
| 121 | if (upcase(c_a) != upcase(c_b)) | ||
| 122 | return false; | ||
| 123 | } | ||
| 124 | return if (it_b.nextCodepoint()) |_| false else true; | ||
| 125 | } | ||
| 126 | return std.hash_map.eqlString(a, b); | ||
| 127 | } | ||
| 128 | }; | ||
| 129 | |||
| 130 | /// Create a EnvMap backed by a specific allocator. | ||
| 131 | /// That allocator will be used for both backing allocations | ||
| 132 | /// and string deduplication. | ||
| 133 | pub fn init(allocator: Allocator) EnvMap { | ||
| 134 | return EnvMap{ .hash_map = HashMap.init(allocator) }; | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Free the backing storage of the map, as well as all | ||
| 138 | /// of the stored keys and values. | ||
| 139 | pub fn deinit(self: *EnvMap) void { | ||
| 140 | var it = self.hash_map.iterator(); | ||
| 141 | while (it.next()) |entry| { | ||
| 142 | self.free(entry.key_ptr.*); | ||
| 143 | self.free(entry.value_ptr.*); | ||
| 144 | } | ||
| 145 | |||
| 146 | self.hash_map.deinit(); | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Same as `put` but the key and value become owned by the EnvMap rather | ||
| 150 | /// than being copied. | ||
| 151 | /// If `putMove` fails, the ownership of key and value does not transfer. | ||
| 152 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 153 | pub fn putMove(self: *EnvMap, key: []u8, value: []u8) !void { | ||
| 154 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 155 | const get_or_put = try self.hash_map.getOrPut(key); | ||
| 156 | if (get_or_put.found_existing) { | ||
| 157 | self.free(get_or_put.key_ptr.*); | ||
| 158 | self.free(get_or_put.value_ptr.*); | ||
| 159 | get_or_put.key_ptr.* = key; | ||
| 160 | } | ||
| 161 | get_or_put.value_ptr.* = value; | ||
| 162 | } | ||
| 163 | |||
| 164 | /// `key` and `value` are copied into the EnvMap. | ||
| 165 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 166 | pub fn put(self: *EnvMap, key: []const u8, value: []const u8) !void { | ||
| 167 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 168 | const value_copy = try self.copy(value); | ||
| 169 | errdefer self.free(value_copy); | ||
| 170 | const get_or_put = try self.hash_map.getOrPut(key); | ||
| 171 | if (get_or_put.found_existing) { | ||
| 172 | self.free(get_or_put.value_ptr.*); | ||
| 173 | } else { | ||
| 174 | get_or_put.key_ptr.* = self.copy(key) catch |err| { | ||
| 175 | _ = self.hash_map.remove(key); | ||
| 176 | return err; | ||
| 177 | }; | ||
| 178 | } | ||
| 179 | get_or_put.value_ptr.* = value_copy; | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Find the address of the value associated with a key. | ||
| 183 | /// The returned pointer is invalidated if the map resizes. | ||
| 184 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 185 | pub fn getPtr(self: EnvMap, key: []const u8) ?*[]const u8 { | ||
| 186 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 187 | return self.hash_map.getPtr(key); | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Return the map's copy of the value associated with | ||
| 191 | /// a key. The returned string is invalidated if this | ||
| 192 | /// key is removed from the map. | ||
| 193 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 194 | pub fn get(self: EnvMap, key: []const u8) ?[]const u8 { | ||
| 195 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 196 | return self.hash_map.get(key); | ||
| 197 | } | ||
| 198 | |||
| 199 | /// Removes the item from the map and frees its value. | ||
| 200 | /// This invalidates the value returned by get() for this key. | ||
| 201 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 202 | pub fn remove(self: *EnvMap, key: []const u8) void { | ||
| 203 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 204 | const kv = self.hash_map.fetchRemove(key) orelse return; | ||
| 205 | self.free(kv.key); | ||
| 206 | self.free(kv.value); | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Returns the number of KV pairs stored in the map. | ||
| 210 | pub fn count(self: EnvMap) HashMap.Size { | ||
| 211 | return self.hash_map.count(); | ||
| 212 | } | ||
| 213 | |||
| 214 | /// Returns an iterator over entries in the map. | ||
| 215 | pub fn iterator(self: *const EnvMap) HashMap.Iterator { | ||
| 216 | return self.hash_map.iterator(); | ||
| 217 | } | ||
| 218 | |||
| 219 | /// Returns a full copy of `em` allocated with `gpa`, which is not necessarily | ||
| 220 | /// the same allocator used to allocate `em`. | ||
| 221 | pub fn clone(em: *const EnvMap, gpa: Allocator) Allocator.Error!EnvMap { | ||
| 222 | var new: EnvMap = .init(gpa); | ||
| 223 | errdefer new.deinit(); | ||
| 224 | // Since we need to dupe the keys and values, the only way for error handling to not be a | ||
| 225 | // nightmare is to add keys to an empty map one-by-one. This could be avoided if this | ||
| 226 | // abstraction were a bit less... OOP-esque. | ||
| 227 | try new.hash_map.ensureUnusedCapacity(em.hash_map.count()); | ||
| 228 | var it = em.hash_map.iterator(); | ||
| 229 | while (it.next()) |entry| { | ||
| 230 | try new.put(entry.key_ptr.*, entry.value_ptr.*); | ||
| 231 | } | ||
| 232 | return new; | ||
| 233 | } | ||
| 234 | |||
| 235 | fn free(self: EnvMap, value: []const u8) void { | ||
| 236 | self.hash_map.allocator.free(value); | ||
| 237 | } | ||
| 238 | |||
| 239 | fn copy(self: EnvMap, value: []const u8) ![]u8 { | ||
| 240 | return self.hash_map.allocator.dupe(u8, value); | ||
| 241 | } | ||
| 242 | }; | ||
| 243 | |||
| 244 | test EnvMap { | ||
| 245 | var env = EnvMap.init(testing.allocator); | ||
| 246 | defer env.deinit(); | ||
| 247 | |||
| 248 | try env.put("SOMETHING_NEW", "hello"); | ||
| 249 | try testing.expectEqualStrings("hello", env.get("SOMETHING_NEW").?); | ||
| 250 | try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); | ||
| 251 | |||
| 252 | // overwrite | ||
| 253 | try env.put("SOMETHING_NEW", "something"); | ||
| 254 | try testing.expectEqualStrings("something", env.get("SOMETHING_NEW").?); | ||
| 255 | try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); | ||
| 256 | |||
| 257 | // a new longer name to test the Windows-specific conversion buffer | ||
| 258 | try env.put("SOMETHING_NEW_AND_LONGER", "1"); | ||
| 259 | try testing.expectEqualStrings("1", env.get("SOMETHING_NEW_AND_LONGER").?); | ||
| 260 | try testing.expectEqual(@as(EnvMap.Size, 2), env.count()); | ||
| 261 | |||
| 262 | // case insensitivity on Windows only | ||
| 263 | if (native_os == .windows) { | ||
| 264 | try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?); | ||
| 265 | } else { | ||
| 266 | try testing.expect(null == env.get("something_New_aNd_LONGER")); | ||
| 267 | } | ||
| 268 | |||
| 269 | var it = env.iterator(); | ||
| 270 | var count: EnvMap.Size = 0; | ||
| 271 | while (it.next()) |entry| { | ||
| 272 | 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.*); | ||
| 273 | try testing.expect(is_an_expected_name); | ||
| 274 | count += 1; | ||
| 275 | } | ||
| 276 | try testing.expectEqual(@as(EnvMap.Size, 2), count); | ||
| 277 | |||
| 278 | env.remove("SOMETHING_NEW"); | ||
| 279 | try testing.expect(env.get("SOMETHING_NEW") == null); | ||
| 280 | |||
| 281 | try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); | ||
| 282 | |||
| 283 | if (native_os == .windows) { | ||
| 284 | // test Unicode case-insensitivity on Windows | ||
| 285 | try env.put("КИРиллИЦА", "something else"); | ||
| 286 | try testing.expectEqualStrings("something else", env.get("кириллица").?); | ||
| 287 | |||
| 288 | // and WTF-8 that's not valid UTF-8 | ||
| 289 | const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{ | ||
| 290 | std.mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate | ||
| 291 | }); | ||
| 292 | defer testing.allocator.free(wtf8_with_surrogate_pair); | ||
| 293 | |||
| 294 | try env.put(wtf8_with_surrogate_pair, wtf8_with_surrogate_pair); | ||
| 295 | try testing.expectEqualSlices(u8, wtf8_with_surrogate_pair, env.get(wtf8_with_surrogate_pair).?); | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | pub const GetEnvMapError = error{ | ||
| 300 | OutOfMemory, | ||
| 301 | /// WASI-only. `environ_sizes_get` or `environ_get` | ||
| 302 | /// failed for an unexpected reason. | ||
| 303 | Unexpected, | ||
| 304 | }; | ||
| 305 | |||
| 306 | /// Returns a snapshot of the environment variables of the current process. | ||
| 307 | /// Any modifications to the resulting EnvMap will not be reflected in the environment, and | ||
| 308 | /// likewise, any future modifications to the environment will not be reflected in the EnvMap. | ||
| 309 | /// Caller owns resulting `EnvMap` and should call its `deinit` fn when done. | ||
| 310 | pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { | ||
| 311 | var result = EnvMap.init(allocator); | ||
| 312 | errdefer result.deinit(); | ||
| 313 | |||
| 314 | if (native_os == .windows) { | ||
| 315 | const ptr = windows.peb().ProcessParameters.Environment; | ||
| 316 | |||
| 317 | var i: usize = 0; | ||
| 318 | while (ptr[i] != 0) { | ||
| 319 | const key_start = i; | ||
| 320 | |||
| 321 | // There are some special environment variables that start with =, | ||
| 322 | // so we need a special case to not treat = as a key/value separator | ||
| 323 | // if it's the first character. | ||
| 324 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 325 | if (ptr[key_start] == '=') i += 1; | ||
| 326 | |||
| 327 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | ||
| 328 | const key_w = ptr[key_start..i]; | ||
| 329 | const key = try unicode.wtf16LeToWtf8Alloc(allocator, key_w); | ||
| 330 | errdefer allocator.free(key); | ||
| 331 | |||
| 332 | if (ptr[i] == '=') i += 1; | ||
| 333 | |||
| 334 | const value_start = i; | ||
| 335 | while (ptr[i] != 0) : (i += 1) {} | ||
| 336 | const value_w = ptr[value_start..i]; | ||
| 337 | const value = try unicode.wtf16LeToWtf8Alloc(allocator, value_w); | ||
| 338 | errdefer allocator.free(value); | ||
| 339 | |||
| 340 | i += 1; // skip over null byte | ||
| 341 | |||
| 342 | try result.putMove(key, value); | ||
| 343 | } | ||
| 344 | return result; | ||
| 345 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 346 | var environ_count: usize = undefined; | ||
| 347 | var environ_buf_size: usize = undefined; | ||
| 348 | |||
| 349 | const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size); | ||
| 350 | if (environ_sizes_get_ret != .SUCCESS) { | ||
| 351 | return posix.unexpectedErrno(environ_sizes_get_ret); | ||
| 352 | } | ||
| 353 | |||
| 354 | if (environ_count == 0) { | ||
| 355 | return result; | ||
| 356 | } | ||
| 357 | |||
| 358 | const environ = try allocator.alloc([*:0]u8, environ_count); | ||
| 359 | defer allocator.free(environ); | ||
| 360 | const environ_buf = try allocator.alloc(u8, environ_buf_size); | ||
| 361 | defer allocator.free(environ_buf); | ||
| 362 | |||
| 363 | const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr); | ||
| 364 | if (environ_get_ret != .SUCCESS) { | ||
| 365 | return posix.unexpectedErrno(environ_get_ret); | ||
| 366 | } | ||
| 367 | |||
| 368 | for (environ) |env| { | ||
| 369 | const pair = mem.sliceTo(env, 0); | ||
| 370 | var parts = mem.splitScalar(u8, pair, '='); | ||
| 371 | const key = parts.first(); | ||
| 372 | const value = parts.rest(); | ||
| 373 | try result.put(key, value); | ||
| 374 | } | ||
| 375 | return result; | ||
| 376 | } else if (builtin.link_libc) { | ||
| 377 | var ptr = std.c.environ; | ||
| 378 | while (ptr[0]) |line| : (ptr += 1) { | ||
| 379 | var line_i: usize = 0; | ||
| 380 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | ||
| 381 | const key = line[0..line_i]; | ||
| 382 | |||
| 383 | var end_i: usize = line_i; | ||
| 384 | while (line[end_i] != 0) : (end_i += 1) {} | ||
| 385 | const value = line[line_i + 1 .. end_i]; | ||
| 386 | |||
| 387 | try result.put(key, value); | ||
| 388 | } | ||
| 389 | return result; | ||
| 390 | } else { | ||
| 391 | for (std.os.environ) |line| { | ||
| 392 | var line_i: usize = 0; | ||
| 393 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | ||
| 394 | const key = line[0..line_i]; | ||
| 395 | |||
| 396 | var end_i: usize = line_i; | ||
| 397 | while (line[end_i] != 0) : (end_i += 1) {} | ||
| 398 | const value = line[line_i + 1 .. end_i]; | ||
| 399 | |||
| 400 | try result.put(key, value); | ||
| 401 | } | ||
| 402 | return result; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | test getEnvMap { | ||
| 407 | var env = try getEnvMap(testing.allocator); | ||
| 408 | defer env.deinit(); | ||
| 409 | } | ||
| 410 | |||
| 411 | pub const GetEnvVarOwnedError = error{ | ||
| 412 | OutOfMemory, | ||
| 413 | EnvironmentVariableNotFound, | ||
| 414 | |||
| 415 | /// On Windows, environment variable keys provided by the user must be valid WTF-8. | ||
| 416 | /// https://wtf-8.codeberg.page/ | ||
| 417 | InvalidWtf8, | ||
| 418 | }; | ||
| 419 | |||
| 420 | /// Caller must free returned memory. | ||
| 421 | /// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/), | ||
| 422 | /// then `error.InvalidWtf8` is returned. | ||
| 423 | /// On Windows, the value is encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 424 | /// On other platforms, the value is an opaque sequence of bytes with no particular encoding. | ||
| 425 | pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { | ||
| 426 | if (native_os == .windows) { | ||
| 427 | const result_w = blk: { | ||
| 428 | var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); | ||
| 429 | const stack_allocator = stack_alloc.get(); | ||
| 430 | const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); | ||
| 431 | defer stack_allocator.free(key_w); | ||
| 432 | |||
| 433 | break :blk getenvW(key_w) orelse return error.EnvironmentVariableNotFound; | ||
| 434 | }; | ||
| 435 | // wtf16LeToWtf8Alloc can only fail with OutOfMemory | ||
| 436 | return unicode.wtf16LeToWtf8Alloc(allocator, result_w); | ||
| 437 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 438 | var envmap = getEnvMap(allocator) catch return error.OutOfMemory; | ||
| 439 | defer envmap.deinit(); | ||
| 440 | const val = envmap.get(key) orelse return error.EnvironmentVariableNotFound; | ||
| 441 | return allocator.dupe(u8, val); | ||
| 442 | } else { | ||
| 443 | const result = posix.getenv(key) orelse return error.EnvironmentVariableNotFound; | ||
| 444 | return allocator.dupe(u8, result); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | /// On Windows, `key` must be valid WTF-8. | ||
| 449 | pub inline fn hasEnvVarConstant(comptime key: []const u8) bool { | ||
| 450 | if (native_os == .windows) { | ||
| 451 | const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key); | ||
| 452 | return getenvW(key_w) != null; | ||
| 453 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 454 | return false; | ||
| 455 | } else { | ||
| 456 | return posix.getenv(key) != null; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | /// On Windows, `key` must be valid WTF-8. | ||
| 461 | pub inline fn hasNonEmptyEnvVarConstant(comptime key: []const u8) bool { | ||
| 462 | if (native_os == .windows) { | ||
| 463 | const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key); | ||
| 464 | const value = getenvW(key_w) orelse return false; | ||
| 465 | return value.len != 0; | ||
| 466 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 467 | return false; | ||
| 468 | } else { | ||
| 469 | const value = posix.getenv(key) orelse return false; | ||
| 470 | return value.len != 0; | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | pub const ParseEnvVarIntError = std.fmt.ParseIntError || error{EnvironmentVariableNotFound}; | ||
| 475 | |||
| 476 | /// Parses an environment variable as an integer. | ||
| 477 | /// | ||
| 478 | /// Since the key is comptime-known, no allocation is needed. | ||
| 479 | /// | ||
| 480 | /// On Windows, `key` must be valid WTF-8. | ||
| 481 | pub fn parseEnvVarInt(comptime key: []const u8, comptime I: type, base: u8) ParseEnvVarIntError!I { | ||
| 482 | if (native_os == .windows) { | ||
| 483 | const key_w = comptime std.unicode.wtf8ToWtf16LeStringLiteral(key); | ||
| 484 | const text = getenvW(key_w) orelse return error.EnvironmentVariableNotFound; | ||
| 485 | return std.fmt.parseIntWithGenericCharacter(I, u16, text, base); | ||
| 486 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 487 | @compileError("parseEnvVarInt is not supported for WASI without libc"); | ||
| 488 | } else { | ||
| 489 | const text = posix.getenv(key) orelse return error.EnvironmentVariableNotFound; | ||
| 490 | return std.fmt.parseInt(I, text, base); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | |||
| 494 | pub const HasEnvVarError = error{ | ||
| 495 | OutOfMemory, | ||
| 496 | |||
| 497 | /// On Windows, environment variable keys provided by the user must be valid WTF-8. | ||
| 498 | /// https://wtf-8.codeberg.page/ | ||
| 499 | InvalidWtf8, | ||
| 500 | }; | ||
| 501 | |||
| 502 | /// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/), | ||
| 503 | /// then `error.InvalidWtf8` is returned. | ||
| 504 | pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool { | ||
| 505 | if (native_os == .windows) { | ||
| 506 | var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); | ||
| 507 | const stack_allocator = stack_alloc.get(); | ||
| 508 | const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); | ||
| 509 | defer stack_allocator.free(key_w); | ||
| 510 | return getenvW(key_w) != null; | ||
| 511 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 512 | var envmap = getEnvMap(allocator) catch return error.OutOfMemory; | ||
| 513 | defer envmap.deinit(); | ||
| 514 | return envmap.getPtr(key) != null; | ||
| 515 | } else { | ||
| 516 | return posix.getenv(key) != null; | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | /// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/), | ||
| 521 | /// then `error.InvalidWtf8` is returned. | ||
| 522 | pub fn hasNonEmptyEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool { | ||
| 523 | if (native_os == .windows) { | ||
| 524 | var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); | ||
| 525 | const stack_allocator = stack_alloc.get(); | ||
| 526 | const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); | ||
| 527 | defer stack_allocator.free(key_w); | ||
| 528 | const value = getenvW(key_w) orelse return false; | ||
| 529 | return value.len != 0; | ||
| 530 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 531 | var envmap = getEnvMap(allocator) catch return error.OutOfMemory; | ||
| 532 | defer envmap.deinit(); | ||
| 533 | const value = envmap.getPtr(key) orelse return false; | ||
| 534 | return value.len != 0; | ||
| 535 | } else { | ||
| 536 | const value = posix.getenv(key) orelse return false; | ||
| 537 | return value.len != 0; | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. | ||
| 542 | /// The returned slice points to memory in the PEB. | ||
| 543 | /// | ||
| 544 | /// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString. | ||
| 545 | /// | ||
| 546 | /// See also: | ||
| 547 | /// * `std.posix.getenv` | ||
| 548 | /// * `getEnvMap` | ||
| 549 | /// * `getEnvVarOwned` | ||
| 550 | /// * `hasEnvVarConstant` | ||
| 551 | /// * `hasEnvVar` | ||
| 552 | pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 { | ||
| 553 | if (native_os != .windows) { | ||
| 554 | @compileError("Windows-only"); | ||
| 555 | } | ||
| 556 | const key_slice = mem.sliceTo(key, 0); | ||
| 557 | // '=' anywhere but the start makes this an invalid environment variable name | ||
| 558 | if (key_slice.len > 0 and std.mem.findScalar(u16, key_slice[1..], '=') != null) { | ||
| 559 | return null; | ||
| 560 | } | ||
| 561 | const ptr = windows.peb().ProcessParameters.Environment; | ||
| 562 | var i: usize = 0; | ||
| 563 | while (ptr[i] != 0) { | ||
| 564 | const key_value = mem.sliceTo(ptr[i..], 0); | ||
| 565 | |||
| 566 | // There are some special environment variables that start with =, | ||
| 567 | // so we need a special case to not treat = as a key/value separator | ||
| 568 | // if it's the first character. | ||
| 569 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 570 | const equal_search_start: usize = if (key_value[0] == '=') 1 else 0; | ||
| 571 | const equal_index = std.mem.findScalarPos(u16, key_value, equal_search_start, '=') orelse { | ||
| 572 | // This is enforced by CreateProcess. | ||
| 573 | // If violated, CreateProcess will fail with INVALID_PARAMETER. | ||
| 574 | unreachable; // must contain a = | ||
| 575 | }; | ||
| 576 | |||
| 577 | const this_key = key_value[0..equal_index]; | ||
| 578 | if (windows.eqlIgnoreCaseWtf16(key_slice, this_key)) { | ||
| 579 | return key_value[equal_index + 1 ..]; | ||
| 580 | } | ||
| 581 | |||
| 582 | // skip past the NUL terminator | ||
| 583 | i += key_value.len + 1; | ||
| 584 | } | ||
| 585 | return null; | ||
| 586 | } | ||
| 587 | |||
| 588 | test getEnvVarOwned { | ||
| 589 | try testing.expectError( | ||
| 590 | error.EnvironmentVariableNotFound, | ||
| 591 | getEnvVarOwned(std.testing.allocator, "BADENV"), | ||
| 592 | ); | ||
| 593 | } | ||
| 594 | |||
| 595 | test hasEnvVarConstant { | ||
| 596 | if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest; | ||
| 597 | |||
| 598 | try testing.expect(!hasEnvVarConstant("BADENV")); | ||
| 599 | } | ||
| 600 | |||
| 601 | test hasEnvVar { | ||
| 602 | const has_env = try hasEnvVar(std.testing.allocator, "BADENV"); | ||
| 603 | try testing.expect(!has_env); | ||
| 604 | } | ||
| 605 | |||
| 606 | pub const ArgIteratorPosix = struct { | ||
| 607 | index: usize, | ||
| 608 | count: usize, | ||
| 609 | |||
| 610 | pub const InitError = error{}; | ||
| 611 | |||
| 612 | pub fn init() ArgIteratorPosix { | ||
| 613 | return ArgIteratorPosix{ | ||
| 614 | .index = 0, | ||
| 615 | .count = std.os.argv.len, | ||
| 616 | }; | ||
| 617 | } | ||
| 618 | |||
| 619 | pub fn next(self: *ArgIteratorPosix) ?[:0]const u8 { | ||
| 620 | if (self.index == self.count) return null; | ||
| 621 | |||
| 622 | const s = std.os.argv[self.index]; | ||
| 623 | self.index += 1; | ||
| 624 | return mem.sliceTo(s, 0); | ||
| 625 | } | ||
| 626 | |||
| 627 | pub fn skip(self: *ArgIteratorPosix) bool { | ||
| 628 | if (self.index == self.count) return false; | ||
| 629 | |||
| 630 | self.index += 1; | ||
| 631 | return true; | ||
| 632 | } | ||
| 633 | }; | ||
| 634 | |||
| 635 | pub const ArgIteratorWasi = struct { | ||
| 636 | allocator: Allocator, | ||
| 637 | index: usize, | ||
| 638 | args: [][:0]u8, | ||
| 639 | |||
| 640 | pub const InitError = error{OutOfMemory} || posix.UnexpectedError; | ||
| 641 | |||
| 642 | /// You must call deinit to free the internal buffer of the | ||
| 643 | /// iterator after you are done. | ||
| 644 | pub fn init(allocator: Allocator) InitError!ArgIteratorWasi { | ||
| 645 | const fetched_args = try ArgIteratorWasi.internalInit(allocator); | ||
| 646 | return ArgIteratorWasi{ | ||
| 647 | .allocator = allocator, | ||
| 648 | .index = 0, | ||
| 649 | .args = fetched_args, | ||
| 650 | }; | ||
| 651 | } | ||
| 652 | |||
| 653 | fn internalInit(allocator: Allocator) InitError![][:0]u8 { | ||
| 654 | var count: usize = undefined; | ||
| 655 | var buf_size: usize = undefined; | ||
| 656 | |||
| 657 | switch (std.os.wasi.args_sizes_get(&count, &buf_size)) { | ||
| 658 | .SUCCESS => {}, | ||
| 659 | else => |err| return posix.unexpectedErrno(err), | ||
| 660 | } | ||
| 661 | |||
| 662 | if (count == 0) { | ||
| 663 | return &[_][:0]u8{}; | ||
| 664 | } | ||
| 665 | |||
| 666 | const argv = try allocator.alloc([*:0]u8, count); | ||
| 667 | defer allocator.free(argv); | ||
| 668 | |||
| 669 | const argv_buf = try allocator.alloc(u8, buf_size); | ||
| 670 | |||
| 671 | switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) { | ||
| 672 | .SUCCESS => {}, | ||
| 673 | else => |err| return posix.unexpectedErrno(err), | ||
| 674 | } | ||
| 675 | |||
| 676 | var result_args = try allocator.alloc([:0]u8, count); | ||
| 677 | var i: usize = 0; | ||
| 678 | while (i < count) : (i += 1) { | ||
| 679 | result_args[i] = mem.sliceTo(argv[i], 0); | ||
| 680 | } | ||
| 681 | |||
| 682 | return result_args; | ||
| 683 | } | ||
| 684 | |||
| 685 | pub fn next(self: *ArgIteratorWasi) ?[:0]const u8 { | ||
| 686 | if (self.index == self.args.len) return null; | ||
| 687 | |||
| 688 | const arg = self.args[self.index]; | ||
| 689 | self.index += 1; | ||
| 690 | return arg; | ||
| 691 | } | ||
| 692 | |||
| 693 | pub fn skip(self: *ArgIteratorWasi) bool { | ||
| 694 | if (self.index == self.args.len) return false; | ||
| 695 | |||
| 696 | self.index += 1; | ||
| 697 | return true; | ||
| 698 | } | ||
| 699 | |||
| 700 | /// Call to free the internal buffer of the iterator. | ||
| 701 | pub fn deinit(self: *ArgIteratorWasi) void { | ||
| 702 | // Nothing is allocated when there are no args | ||
| 703 | if (self.args.len == 0) return; | ||
| 704 | |||
| 705 | const last_item = self.args[self.args.len - 1]; | ||
| 706 | const last_byte_addr = @intFromPtr(last_item.ptr) + last_item.len + 1; // null terminated | ||
| 707 | const first_item_ptr = self.args[0].ptr; | ||
| 708 | const len = last_byte_addr - @intFromPtr(first_item_ptr); | ||
| 709 | self.allocator.free(first_item_ptr[0..len]); | ||
| 710 | self.allocator.free(self.args); | ||
| 711 | } | ||
| 712 | }; | ||
| 713 | |||
| 714 | /// Iterator that implements the Windows command-line parsing algorithm. | ||
| 715 | /// The implementation is intended to be compatible with the post-2008 C runtime, | ||
| 716 | /// but is *not* intended to be compatible with `CommandLineToArgvW` since | ||
| 717 | /// `CommandLineToArgvW` uses the pre-2008 parsing rules. | ||
| 718 | /// | ||
| 719 | /// This iterator faithfully implements the parsing behavior observed from the C runtime with | ||
| 720 | /// one exception: if the command-line string is empty, the iterator will immediately complete | ||
| 721 | /// without returning any arguments (whereas the C runtime will return a single argument | ||
| 722 | /// representing the name of the current executable). | ||
| 723 | /// | ||
| 724 | /// The essential parts of the algorithm are described in Microsoft's documentation: | ||
| 725 | /// | ||
| 726 | /// - https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170#parsing-c-command-line-arguments | ||
| 727 | /// | ||
| 728 | /// David Deley explains some additional undocumented quirks in great detail: | ||
| 729 | /// | ||
| 730 | /// - https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES | ||
| 731 | pub const ArgIteratorWindows = struct { | ||
| 732 | allocator: Allocator, | ||
| 733 | /// Encoded as WTF-16 LE. | ||
| 734 | cmd_line: []const u16, | ||
| 735 | index: usize = 0, | ||
| 736 | /// Owned by the iterator. Long enough to hold contiguous NUL-terminated slices | ||
| 737 | /// of each argument encoded as WTF-8. | ||
| 738 | buffer: []u8, | ||
| 739 | start: usize = 0, | ||
| 740 | end: usize = 0, | ||
| 741 | |||
| 742 | pub const InitError = error{OutOfMemory}; | ||
| 743 | |||
| 744 | /// `cmd_line_w` *must* be a WTF16-LE-encoded string. | ||
| 745 | /// | ||
| 746 | /// The iterator stores and uses `cmd_line_w`, so its memory must be valid for | ||
| 747 | /// at least as long as the returned ArgIteratorWindows. | ||
| 748 | pub fn init(allocator: Allocator, cmd_line_w: []const u16) InitError!ArgIteratorWindows { | ||
| 749 | const wtf8_len = unicode.calcWtf8Len(cmd_line_w); | ||
| 750 | |||
| 751 | // This buffer must be large enough to contain contiguous NUL-terminated slices | ||
| 752 | // of each argument. | ||
| 753 | // - During parsing, the length of a parsed argument will always be equal to | ||
| 754 | // to less than its unparsed length | ||
| 755 | // - The first argument needs one extra byte of space allocated for its NUL | ||
| 756 | // terminator, but for each subsequent argument the necessary whitespace | ||
| 757 | // between arguments guarantees room for their NUL terminator(s). | ||
| 758 | const buffer = try allocator.alloc(u8, wtf8_len + 1); | ||
| 759 | errdefer allocator.free(buffer); | ||
| 760 | |||
| 761 | return .{ | ||
| 762 | .allocator = allocator, | ||
| 763 | .cmd_line = cmd_line_w, | ||
| 764 | .buffer = buffer, | ||
| 765 | }; | ||
| 766 | } | ||
| 767 | |||
| 768 | /// Returns the next argument and advances the iterator. Returns `null` if at the end of the | ||
| 769 | /// command-line string. The iterator owns the returned slice. | ||
| 770 | /// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 771 | pub fn next(self: *ArgIteratorWindows) ?[:0]const u8 { | ||
| 772 | return self.nextWithStrategy(next_strategy); | ||
| 773 | } | ||
| 774 | |||
| 775 | /// Skips the next argument and advances the iterator. Returns `true` if an argument was | ||
| 776 | /// skipped, `false` if at the end of the command-line string. | ||
| 777 | pub fn skip(self: *ArgIteratorWindows) bool { | ||
| 778 | return self.nextWithStrategy(skip_strategy); | ||
| 779 | } | ||
| 780 | |||
| 781 | const next_strategy = struct { | ||
| 782 | const T = ?[:0]const u8; | ||
| 783 | |||
| 784 | const eof = null; | ||
| 785 | |||
| 786 | /// Returns '\' if any backslashes are emitted, otherwise returns `last_emitted_code_unit`. | ||
| 787 | fn emitBackslashes(self: *ArgIteratorWindows, count: usize, last_emitted_code_unit: ?u16) ?u16 { | ||
| 788 | for (0..count) |_| { | ||
| 789 | self.buffer[self.end] = '\\'; | ||
| 790 | self.end += 1; | ||
| 791 | } | ||
| 792 | return if (count != 0) '\\' else last_emitted_code_unit; | ||
| 793 | } | ||
| 794 | |||
| 795 | /// If `last_emitted_code_unit` and `code_unit` form a surrogate pair, then | ||
| 796 | /// the previously emitted high surrogate is overwritten by the codepoint encoded | ||
| 797 | /// by the surrogate pair, and `null` is returned. | ||
| 798 | /// Otherwise, `code_unit` is emitted and returned. | ||
| 799 | fn emitCharacter(self: *ArgIteratorWindows, code_unit: u16, last_emitted_code_unit: ?u16) ?u16 { | ||
| 800 | // Because we are emitting WTF-8, we need to | ||
| 801 | // check to see if we've emitted two consecutive surrogate | ||
| 802 | // codepoints that form a valid surrogate pair in order | ||
| 803 | // to ensure that we're always emitting well-formed WTF-8 | ||
| 804 | // (https://wtf-8.codeberg.page/#concatenating). | ||
| 805 | // | ||
| 806 | // If we do have a valid surrogate pair, we need to emit | ||
| 807 | // the UTF-8 sequence for the codepoint that they encode | ||
| 808 | // instead of the WTF-8 encoding for the two surrogate pairs | ||
| 809 | // separately. | ||
| 810 | // | ||
| 811 | // This is relevant when dealing with a WTF-16 encoded | ||
| 812 | // command line like this: | ||
| 813 | // "<0xD801>"<0xDC37> | ||
| 814 | // which would get parsed and converted to WTF-8 as: | ||
| 815 | // <0xED><0xA0><0x81><0xED><0xB0><0xB7> | ||
| 816 | // but instead, we need to recognize the surrogate pair | ||
| 817 | // and emit the codepoint it encodes, which in this | ||
| 818 | // example is U+10437 (𐐷), which is encoded in UTF-8 as: | ||
| 819 | // <0xF0><0x90><0x90><0xB7> | ||
| 820 | if (last_emitted_code_unit != null and | ||
| 821 | std.unicode.utf16IsLowSurrogate(code_unit) and | ||
| 822 | std.unicode.utf16IsHighSurrogate(last_emitted_code_unit.?)) | ||
| 823 | { | ||
| 824 | const codepoint = std.unicode.utf16DecodeSurrogatePair(&.{ last_emitted_code_unit.?, code_unit }) catch unreachable; | ||
| 825 | |||
| 826 | // Unpaired surrogate is 3 bytes long | ||
| 827 | const dest = self.buffer[self.end - 3 ..]; | ||
| 828 | const len = unicode.utf8Encode(codepoint, dest) catch unreachable; | ||
| 829 | // All codepoints that require a surrogate pair (> U+FFFF) are encoded as 4 bytes | ||
| 830 | assert(len == 4); | ||
| 831 | self.end += 1; | ||
| 832 | return null; | ||
| 833 | } | ||
| 834 | |||
| 835 | const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable; | ||
| 836 | self.end += wtf8_len; | ||
| 837 | return code_unit; | ||
| 838 | } | ||
| 839 | |||
| 840 | fn yieldArg(self: *ArgIteratorWindows) [:0]const u8 { | ||
| 841 | self.buffer[self.end] = 0; | ||
| 842 | const arg = self.buffer[self.start..self.end :0]; | ||
| 843 | self.end += 1; | ||
| 844 | self.start = self.end; | ||
| 845 | return arg; | ||
| 846 | } | ||
| 847 | }; | ||
| 848 | |||
| 849 | const skip_strategy = struct { | ||
| 850 | const T = bool; | ||
| 851 | |||
| 852 | const eof = false; | ||
| 853 | |||
| 854 | fn emitBackslashes(_: *ArgIteratorWindows, _: usize, last_emitted_code_unit: ?u16) ?u16 { | ||
| 855 | return last_emitted_code_unit; | ||
| 856 | } | ||
| 857 | |||
| 858 | fn emitCharacter(_: *ArgIteratorWindows, _: u16, last_emitted_code_unit: ?u16) ?u16 { | ||
| 859 | return last_emitted_code_unit; | ||
| 860 | } | ||
| 861 | |||
| 862 | fn yieldArg(_: *ArgIteratorWindows) bool { | ||
| 863 | return true; | ||
| 864 | } | ||
| 865 | }; | ||
| 866 | |||
| 867 | fn nextWithStrategy(self: *ArgIteratorWindows, comptime strategy: type) strategy.T { | ||
| 868 | var last_emitted_code_unit: ?u16 = null; | ||
| 869 | // The first argument (the executable name) uses different parsing rules. | ||
| 870 | if (self.index == 0) { | ||
| 871 | if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) { | ||
| 872 | // Immediately complete the iterator. | ||
| 873 | // The C runtime would return the name of the current executable here. | ||
| 874 | return strategy.eof; | ||
| 875 | } | ||
| 876 | |||
| 877 | var inside_quotes = false; | ||
| 878 | while (true) : (self.index += 1) { | ||
| 879 | const char = if (self.index != self.cmd_line.len) | ||
| 880 | mem.littleToNative(u16, self.cmd_line[self.index]) | ||
| 881 | else | ||
| 882 | 0; | ||
| 883 | switch (char) { | ||
| 884 | 0 => { | ||
| 885 | return strategy.yieldArg(self); | ||
| 886 | }, | ||
| 887 | '"' => { | ||
| 888 | inside_quotes = !inside_quotes; | ||
| 889 | }, | ||
| 890 | ' ', '\t' => { | ||
| 891 | if (inside_quotes) { | ||
| 892 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 893 | } else { | ||
| 894 | self.index += 1; | ||
| 895 | return strategy.yieldArg(self); | ||
| 896 | } | ||
| 897 | }, | ||
| 898 | else => { | ||
| 899 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 900 | }, | ||
| 901 | } | ||
| 902 | } | ||
| 903 | } | ||
| 904 | |||
| 905 | // Skip spaces and tabs. The iterator completes if we reach the end of the string here. | ||
| 906 | while (true) : (self.index += 1) { | ||
| 907 | const char = if (self.index != self.cmd_line.len) | ||
| 908 | mem.littleToNative(u16, self.cmd_line[self.index]) | ||
| 909 | else | ||
| 910 | 0; | ||
| 911 | switch (char) { | ||
| 912 | 0 => return strategy.eof, | ||
| 913 | ' ', '\t' => continue, | ||
| 914 | else => break, | ||
| 915 | } | ||
| 916 | } | ||
| 917 | |||
| 918 | // Parsing rules for subsequent arguments: | ||
| 919 | // | ||
| 920 | // - The end of the string always terminates the current argument. | ||
| 921 | // - When not in 'inside_quotes' mode, a space or tab terminates the current argument. | ||
| 922 | // - 2n backslashes followed by a quote emit n backslashes (note: n can be zero). | ||
| 923 | // If in 'inside_quotes' and the quote is immediately followed by a second quote, | ||
| 924 | // one quote is emitted and the other is skipped, otherwise, the quote is skipped | ||
| 925 | // and 'inside_quotes' is toggled. | ||
| 926 | // - 2n + 1 backslashes followed by a quote emit n backslashes followed by a quote. | ||
| 927 | // - n backslashes not followed by a quote emit n backslashes. | ||
| 928 | var backslash_count: usize = 0; | ||
| 929 | var inside_quotes = false; | ||
| 930 | while (true) : (self.index += 1) { | ||
| 931 | const char = if (self.index != self.cmd_line.len) | ||
| 932 | mem.littleToNative(u16, self.cmd_line[self.index]) | ||
| 933 | else | ||
| 934 | 0; | ||
| 935 | switch (char) { | ||
| 936 | 0 => { | ||
| 937 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); | ||
| 938 | return strategy.yieldArg(self); | ||
| 939 | }, | ||
| 940 | ' ', '\t' => { | ||
| 941 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); | ||
| 942 | backslash_count = 0; | ||
| 943 | if (inside_quotes) { | ||
| 944 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 945 | } else return strategy.yieldArg(self); | ||
| 946 | }, | ||
| 947 | '"' => { | ||
| 948 | const char_is_escaped_quote = backslash_count % 2 != 0; | ||
| 949 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count / 2, last_emitted_code_unit); | ||
| 950 | backslash_count = 0; | ||
| 951 | if (char_is_escaped_quote) { | ||
| 952 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); | ||
| 953 | } else { | ||
| 954 | if (inside_quotes and | ||
| 955 | self.index + 1 != self.cmd_line.len and | ||
| 956 | mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"') | ||
| 957 | { | ||
| 958 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); | ||
| 959 | self.index += 1; | ||
| 960 | } else { | ||
| 961 | inside_quotes = !inside_quotes; | ||
| 962 | } | ||
| 963 | } | ||
| 964 | }, | ||
| 965 | '\\' => { | ||
| 966 | backslash_count += 1; | ||
| 967 | }, | ||
| 968 | else => { | ||
| 969 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); | ||
| 970 | backslash_count = 0; | ||
| 971 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 972 | }, | ||
| 973 | } | ||
| 974 | } | ||
| 975 | } | ||
| 976 | |||
| 977 | /// Frees the iterator's copy of the command-line string and all previously returned | ||
| 978 | /// argument slices. | ||
| 979 | pub fn deinit(self: *ArgIteratorWindows) void { | ||
| 980 | self.allocator.free(self.buffer); | ||
| 981 | } | ||
| 982 | }; | ||
| 983 | |||
| 984 | /// Optional parameters for `ArgIteratorGeneral` | ||
| 985 | pub const ArgIteratorGeneralOptions = struct { | ||
| 986 | comments: bool = false, | ||
| 987 | single_quotes: bool = false, | ||
| 988 | }; | ||
| 989 | |||
| 990 | /// A general Iterator to parse a string into a set of arguments | ||
| 991 | pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { | ||
| 992 | return struct { | ||
| 993 | allocator: Allocator, | ||
| 994 | index: usize = 0, | ||
| 995 | cmd_line: []const u8, | ||
| 996 | |||
| 997 | /// Should the cmd_line field be free'd (using the allocator) on deinit()? | ||
| 998 | free_cmd_line_on_deinit: bool, | ||
| 999 | |||
| 1000 | /// buffer MUST be long enough to hold the cmd_line plus a null terminator. | ||
| 1001 | /// buffer will we free'd (using the allocator) on deinit() | ||
| 1002 | buffer: []u8, | ||
| 1003 | start: usize = 0, | ||
| 1004 | end: usize = 0, | ||
| 1005 | |||
| 1006 | pub const Self = @This(); | ||
| 1007 | |||
| 1008 | pub const InitError = error{OutOfMemory}; | ||
| 1009 | |||
| 1010 | /// cmd_line_utf8 MUST remain valid and constant while using this instance | ||
| 1011 | pub fn init(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self { | ||
| 1012 | const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1); | ||
| 1013 | errdefer allocator.free(buffer); | ||
| 1014 | |||
| 1015 | return Self{ | ||
| 1016 | .allocator = allocator, | ||
| 1017 | .cmd_line = cmd_line_utf8, | ||
| 1018 | .free_cmd_line_on_deinit = false, | ||
| 1019 | .buffer = buffer, | ||
| 1020 | }; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | /// cmd_line_utf8 will be free'd (with the allocator) on deinit() | ||
| 1024 | pub fn initTakeOwnership(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self { | ||
| 1025 | const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1); | ||
| 1026 | errdefer allocator.free(buffer); | ||
| 1027 | |||
| 1028 | return Self{ | ||
| 1029 | .allocator = allocator, | ||
| 1030 | .cmd_line = cmd_line_utf8, | ||
| 1031 | .free_cmd_line_on_deinit = true, | ||
| 1032 | .buffer = buffer, | ||
| 1033 | }; | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | // Skips over whitespace in the cmd_line. | ||
| 1037 | // Returns false if the terminating sentinel is reached, true otherwise. | ||
| 1038 | // Also skips over comments (if supported). | ||
| 1039 | fn skipWhitespace(self: *Self) bool { | ||
| 1040 | while (true) : (self.index += 1) { | ||
| 1041 | const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; | ||
| 1042 | switch (character) { | ||
| 1043 | 0 => return false, | ||
| 1044 | ' ', '\t', '\r', '\n' => continue, | ||
| 1045 | '#' => { | ||
| 1046 | if (options.comments) { | ||
| 1047 | while (true) : (self.index += 1) { | ||
| 1048 | switch (self.cmd_line[self.index]) { | ||
| 1049 | '\n' => break, | ||
| 1050 | 0 => return false, | ||
| 1051 | else => continue, | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | continue; | ||
| 1055 | } else { | ||
| 1056 | break; | ||
| 1057 | } | ||
| 1058 | }, | ||
| 1059 | else => break, | ||
| 1060 | } | ||
| 1061 | } | ||
| 1062 | return true; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | pub fn skip(self: *Self) bool { | ||
| 1066 | if (!self.skipWhitespace()) { | ||
| 1067 | return false; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | var backslash_count: usize = 0; | ||
| 1071 | var in_quote = false; | ||
| 1072 | while (true) : (self.index += 1) { | ||
| 1073 | const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; | ||
| 1074 | switch (character) { | ||
| 1075 | 0 => return true, | ||
| 1076 | '"', '\'' => { | ||
| 1077 | if (!options.single_quotes and character == '\'') { | ||
| 1078 | backslash_count = 0; | ||
| 1079 | continue; | ||
| 1080 | } | ||
| 1081 | const quote_is_real = backslash_count % 2 == 0; | ||
| 1082 | if (quote_is_real) { | ||
| 1083 | in_quote = !in_quote; | ||
| 1084 | } | ||
| 1085 | }, | ||
| 1086 | '\\' => { | ||
| 1087 | backslash_count += 1; | ||
| 1088 | }, | ||
| 1089 | ' ', '\t', '\r', '\n' => { | ||
| 1090 | if (!in_quote) { | ||
| 1091 | return true; | ||
| 1092 | } | ||
| 1093 | backslash_count = 0; | ||
| 1094 | }, | ||
| 1095 | else => { | ||
| 1096 | backslash_count = 0; | ||
| 1097 | continue; | ||
| 1098 | }, | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | /// Returns a slice of the internal buffer that contains the next argument. | ||
| 1104 | /// Returns null when it reaches the end. | ||
| 1105 | pub fn next(self: *Self) ?[:0]const u8 { | ||
| 1106 | if (!self.skipWhitespace()) { | ||
| 1107 | return null; | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | var backslash_count: usize = 0; | ||
| 1111 | var in_quote = false; | ||
| 1112 | while (true) : (self.index += 1) { | ||
| 1113 | const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; | ||
| 1114 | switch (character) { | ||
| 1115 | 0 => { | ||
| 1116 | self.emitBackslashes(backslash_count); | ||
| 1117 | self.buffer[self.end] = 0; | ||
| 1118 | const token = self.buffer[self.start..self.end :0]; | ||
| 1119 | self.end += 1; | ||
| 1120 | self.start = self.end; | ||
| 1121 | return token; | ||
| 1122 | }, | ||
| 1123 | '"', '\'' => { | ||
| 1124 | if (!options.single_quotes and character == '\'') { | ||
| 1125 | self.emitBackslashes(backslash_count); | ||
| 1126 | backslash_count = 0; | ||
| 1127 | self.emitCharacter(character); | ||
| 1128 | continue; | ||
| 1129 | } | ||
| 1130 | const quote_is_real = backslash_count % 2 == 0; | ||
| 1131 | self.emitBackslashes(backslash_count / 2); | ||
| 1132 | backslash_count = 0; | ||
| 1133 | |||
| 1134 | if (quote_is_real) { | ||
| 1135 | in_quote = !in_quote; | ||
| 1136 | } else { | ||
| 1137 | self.emitCharacter('"'); | ||
| 1138 | } | ||
| 1139 | }, | ||
| 1140 | '\\' => { | ||
| 1141 | backslash_count += 1; | ||
| 1142 | }, | ||
| 1143 | ' ', '\t', '\r', '\n' => { | ||
| 1144 | self.emitBackslashes(backslash_count); | ||
| 1145 | backslash_count = 0; | ||
| 1146 | if (in_quote) { | ||
| 1147 | self.emitCharacter(character); | ||
| 1148 | } else { | ||
| 1149 | self.buffer[self.end] = 0; | ||
| 1150 | const token = self.buffer[self.start..self.end :0]; | ||
| 1151 | self.end += 1; | ||
| 1152 | self.start = self.end; | ||
| 1153 | return token; | ||
| 1154 | } | ||
| 1155 | }, | ||
| 1156 | else => { | ||
| 1157 | self.emitBackslashes(backslash_count); | ||
| 1158 | backslash_count = 0; | ||
| 1159 | self.emitCharacter(character); | ||
| 1160 | }, | ||
| 1161 | } | ||
| 1162 | } | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | fn emitBackslashes(self: *Self, emit_count: usize) void { | ||
| 1166 | var i: usize = 0; | ||
| 1167 | while (i < emit_count) : (i += 1) { | ||
| 1168 | self.emitCharacter('\\'); | ||
| 1169 | } | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | fn emitCharacter(self: *Self, char: u8) void { | ||
| 1173 | self.buffer[self.end] = char; | ||
| 1174 | self.end += 1; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | /// Call to free the internal buffer of the iterator. | ||
| 1178 | pub fn deinit(self: *Self) void { | ||
| 1179 | self.allocator.free(self.buffer); | ||
| 1180 | |||
| 1181 | if (self.free_cmd_line_on_deinit) { | ||
| 1182 | self.allocator.free(self.cmd_line); | ||
| 1183 | } | ||
| 1184 | } | ||
| 1185 | }; | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | /// Cross-platform command line argument iterator. | ||
| 1189 | pub const ArgIterator = struct { | ||
| 1190 | const InnerType = switch (native_os) { | ||
| 1191 | .windows => ArgIteratorWindows, | ||
| 1192 | .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi, | ||
| 1193 | else => ArgIteratorPosix, | ||
| 1194 | }; | ||
| 1195 | |||
| 1196 | inner: InnerType, | ||
| 1197 | |||
| 1198 | /// Initialize the args iterator. Consider using initWithAllocator() instead | ||
| 1199 | /// for cross-platform compatibility. | ||
| 1200 | pub fn init() ArgIterator { | ||
| 1201 | if (native_os == .wasi) { | ||
| 1202 | @compileError("In WASI, use initWithAllocator instead."); | ||
| 1203 | } | ||
| 1204 | if (native_os == .windows) { | ||
| 1205 | @compileError("In Windows, use initWithAllocator instead."); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | return ArgIterator{ .inner = InnerType.init() }; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | pub const InitError = InnerType.InitError; | ||
| 1212 | |||
| 1213 | /// You must deinitialize iterator's internal buffers by calling `deinit` when done. | ||
| 1214 | pub fn initWithAllocator(allocator: Allocator) InitError!ArgIterator { | ||
| 1215 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 1216 | return ArgIterator{ .inner = try InnerType.init(allocator) }; | ||
| 1217 | } | ||
| 1218 | if (native_os == .windows) { | ||
| 1219 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; | ||
| 1220 | const cmd_line_w = cmd_line.Buffer.?[0 .. cmd_line.Length / 2]; | ||
| 1221 | return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) }; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | return ArgIterator{ .inner = InnerType.init() }; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | /// Get the next argument. Returns 'null' if we are at the end. | ||
| 1228 | /// Returned slice is pointing to the iterator's internal buffer. | ||
| 1229 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1230 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. | ||
| 1231 | pub fn next(self: *ArgIterator) ?([:0]const u8) { | ||
| 1232 | return self.inner.next(); | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | /// Parse past 1 argument without capturing it. | ||
| 1236 | /// Returns `true` if skipped an arg, `false` if we are at the end. | ||
| 1237 | pub fn skip(self: *ArgIterator) bool { | ||
| 1238 | return self.inner.skip(); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | /// Call this to free the iterator's internal buffer if the iterator | ||
| 1242 | /// was created with `initWithAllocator` function. | ||
| 1243 | pub fn deinit(self: *ArgIterator) void { | ||
| 1244 | // Unless we're targeting WASI or Windows, this is a no-op. | ||
| 1245 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 1246 | self.inner.deinit(); | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | if (native_os == .windows) { | ||
| 1250 | self.inner.deinit(); | ||
| 1251 | } | ||
| 1252 | } | ||
| 1253 | }; | ||
| 1254 | |||
| 1255 | /// Holds the command-line arguments, with the program name as the first entry. | ||
| 1256 | /// Use argsWithAllocator() for cross-platform code. | ||
| 1257 | pub fn args() ArgIterator { | ||
| 1258 | return ArgIterator.init(); | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | /// You must deinitialize iterator's internal buffers by calling `deinit` when done. | ||
| 1262 | pub fn argsWithAllocator(allocator: Allocator) ArgIterator.InitError!ArgIterator { | ||
| 1263 | return ArgIterator.initWithAllocator(allocator); | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | /// Caller must call argsFree on result. | ||
| 1267 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1268 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. | ||
| 1269 | pub fn argsAlloc(allocator: Allocator) ![][:0]u8 { | ||
| 1270 | // TODO refactor to only make 1 allocation. | ||
| 1271 | var it = try argsWithAllocator(allocator); | ||
| 1272 | defer it.deinit(); | ||
| 1273 | |||
| 1274 | var contents = std.array_list.Managed(u8).init(allocator); | ||
| 1275 | defer contents.deinit(); | ||
| 1276 | |||
| 1277 | var slice_list = std.array_list.Managed(usize).init(allocator); | ||
| 1278 | defer slice_list.deinit(); | ||
| 1279 | |||
| 1280 | while (it.next()) |arg| { | ||
| 1281 | try contents.appendSlice(arg[0 .. arg.len + 1]); | ||
| 1282 | try slice_list.append(arg.len); | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | const contents_slice = contents.items; | ||
| 1286 | const slice_sizes = slice_list.items; | ||
| 1287 | const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len); | ||
| 1288 | const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len); | ||
| 1289 | const buf = try allocator.alignedAlloc(u8, .of([]u8), total_bytes); | ||
| 1290 | errdefer allocator.free(buf); | ||
| 1291 | |||
| 1292 | const result_slice_list = mem.bytesAsSlice([:0]u8, buf[0..slice_list_bytes]); | ||
| 1293 | const result_contents = buf[slice_list_bytes..]; | ||
| 1294 | @memcpy(result_contents[0..contents_slice.len], contents_slice); | ||
| 1295 | |||
| 1296 | var contents_index: usize = 0; | ||
| 1297 | for (slice_sizes, 0..) |len, i| { | ||
| 1298 | const new_index = contents_index + len; | ||
| 1299 | result_slice_list[i] = result_contents[contents_index..new_index :0]; | ||
| 1300 | contents_index = new_index + 1; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | return result_slice_list; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | pub fn argsFree(allocator: Allocator, args_alloc: []const [:0]u8) void { | ||
| 1307 | var total_bytes: usize = 0; | ||
| 1308 | for (args_alloc) |arg| { | ||
| 1309 | total_bytes += @sizeOf([]u8) + arg.len + 1; | ||
| 1310 | } | ||
| 1311 | const unaligned_allocated_buf = @as([*]const u8, @ptrCast(args_alloc.ptr))[0..total_bytes]; | ||
| 1312 | const aligned_allocated_buf: []align(@alignOf([]u8)) const u8 = @alignCast(unaligned_allocated_buf); | ||
| 1313 | return allocator.free(aligned_allocated_buf); | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | test ArgIteratorWindows { | ||
| 1317 | const t = testArgIteratorWindows; | ||
| 1318 | |||
| 1319 | try t( | ||
| 1320 | \\"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\")" | ||
| 1321 | , &.{ | ||
| 1322 | \\C:\Program Files\zig\zig.exe | ||
| 1323 | , | ||
| 1324 | \\run | ||
| 1325 | , | ||
| 1326 | \\.\src\main.zig | ||
| 1327 | , | ||
| 1328 | \\-target | ||
| 1329 | , | ||
| 1330 | \\x86_64-windows-gnu | ||
| 1331 | , | ||
| 1332 | \\-O | ||
| 1333 | , | ||
| 1334 | \\ReleaseSafe | ||
| 1335 | , | ||
| 1336 | \\-- | ||
| 1337 | , | ||
| 1338 | \\--emoji=🗿 | ||
| 1339 | , | ||
| 1340 | \\--eval=new Regex("Dwayne \"The Rock\" Johnson") | ||
| 1341 | , | ||
| 1342 | }); | ||
| 1343 | |||
| 1344 | // Empty | ||
| 1345 | try t("", &.{}); | ||
| 1346 | |||
| 1347 | // Separators | ||
| 1348 | try t("aa bb cc", &.{ "aa", "bb", "cc" }); | ||
| 1349 | try t("aa\tbb\tcc", &.{ "aa", "bb", "cc" }); | ||
| 1350 | try t("aa\nbb\ncc", &.{"aa\nbb\ncc"}); | ||
| 1351 | try t("aa\r\nbb\r\ncc", &.{"aa\r\nbb\r\ncc"}); | ||
| 1352 | try t("aa\rbb\rcc", &.{"aa\rbb\rcc"}); | ||
| 1353 | try t("aa\x07bb\x07cc", &.{"aa\x07bb\x07cc"}); | ||
| 1354 | try t("aa\x7Fbb\x7Fcc", &.{"aa\x7Fbb\x7Fcc"}); | ||
| 1355 | try t("aa🦎bb🦎cc", &.{"aa🦎bb🦎cc"}); | ||
| 1356 | |||
| 1357 | // Leading/trailing whitespace | ||
| 1358 | try t(" ", &.{""}); | ||
| 1359 | try t(" aa bb ", &.{ "", "aa", "bb" }); | ||
| 1360 | try t("\t\t", &.{""}); | ||
| 1361 | try t("\t\taa\t\tbb\t\t", &.{ "", "aa", "bb" }); | ||
| 1362 | try t("\n\n", &.{"\n\n"}); | ||
| 1363 | try t("\n\naa\n\nbb\n\n", &.{"\n\naa\n\nbb\n\n"}); | ||
| 1364 | |||
| 1365 | // Executable name with quotes/backslashes | ||
| 1366 | try t("\"aa bb\tcc\ndd\"", &.{"aa bb\tcc\ndd"}); | ||
| 1367 | try t("\"", &.{""}); | ||
| 1368 | try t("\"\"", &.{""}); | ||
| 1369 | try t("\"\"\"", &.{""}); | ||
| 1370 | try t("\"\"\"\"", &.{""}); | ||
| 1371 | try t("\"\"\"\"\"", &.{""}); | ||
| 1372 | try t("aa\"bb\"cc\"dd", &.{"aabbccdd"}); | ||
| 1373 | try t("aa\"bb cc\"dd", &.{"aabb ccdd"}); | ||
| 1374 | try t("\"aa\\\"bb\"", &.{"aa\\bb"}); | ||
| 1375 | try t("\"aa\\\\\"", &.{"aa\\\\"}); | ||
| 1376 | try t("aa\\\"bb", &.{"aa\\bb"}); | ||
| 1377 | try t("aa\\\\\"bb", &.{"aa\\\\bb"}); | ||
| 1378 | |||
| 1379 | // Arguments with quotes/backslashes | ||
| 1380 | try t(". \"aa bb\tcc\ndd\"", &.{ ".", "aa bb\tcc\ndd" }); | ||
| 1381 | try t(". aa\" \"bb\"\t\"cc\"\n\"dd\"", &.{ ".", "aa bb\tcc\ndd" }); | ||
| 1382 | try t(". ", &.{"."}); | ||
| 1383 | try t(". \"", &.{ ".", "" }); | ||
| 1384 | try t(". \"\"", &.{ ".", "" }); | ||
| 1385 | try t(". \"\"\"", &.{ ".", "\"" }); | ||
| 1386 | try t(". \"\"\"\"", &.{ ".", "\"" }); | ||
| 1387 | try t(". \"\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 1388 | try t(". \"\"\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 1389 | try t(". \" \"", &.{ ".", " " }); | ||
| 1390 | try t(". \" \"\"", &.{ ".", " \"" }); | ||
| 1391 | try t(". \" \"\"\"", &.{ ".", " \"" }); | ||
| 1392 | try t(". \" \"\"\"\"", &.{ ".", " \"\"" }); | ||
| 1393 | try t(". \" \"\"\"\"\"", &.{ ".", " \"\"" }); | ||
| 1394 | try t(". \" \"\"\"\"\"\"", &.{ ".", " \"\"\"" }); | ||
| 1395 | try t(". \\\"", &.{ ".", "\"" }); | ||
| 1396 | try t(". \\\"\"", &.{ ".", "\"" }); | ||
| 1397 | try t(". \\\"\"\"", &.{ ".", "\"" }); | ||
| 1398 | try t(". \\\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 1399 | try t(". \\\"\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 1400 | try t(". \\\"\"\"\"\"\"", &.{ ".", "\"\"\"" }); | ||
| 1401 | try t(". \" \\\"", &.{ ".", " \"" }); | ||
| 1402 | try t(". \" \\\"\"", &.{ ".", " \"" }); | ||
| 1403 | try t(". \" \\\"\"\"", &.{ ".", " \"\"" }); | ||
| 1404 | try t(". \" \\\"\"\"\"", &.{ ".", " \"\"" }); | ||
| 1405 | try t(". \" \\\"\"\"\"\"", &.{ ".", " \"\"\"" }); | ||
| 1406 | try t(". \" \\\"\"\"\"\"\"", &.{ ".", " \"\"\"" }); | ||
| 1407 | try t(". aa\\bb\\\\cc\\\\\\dd", &.{ ".", "aa\\bb\\\\cc\\\\\\dd" }); | ||
| 1408 | try t(". \\\\\\\"aa bb\"", &.{ ".", "\\\"aa", "bb" }); | ||
| 1409 | try t(". \\\\\\\\\"aa bb\"", &.{ ".", "\\\\aa bb" }); | ||
| 1410 | |||
| 1411 | // From https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#results-of-parsing-command-lines | ||
| 1412 | try t( | ||
| 1413 | \\foo.exe "abc" d e | ||
| 1414 | , &.{ "foo.exe", "abc", "d", "e" }); | ||
| 1415 | try t( | ||
| 1416 | \\foo.exe a\\b d"e f"g h | ||
| 1417 | , &.{ "foo.exe", "a\\\\b", "de fg", "h" }); | ||
| 1418 | try t( | ||
| 1419 | \\foo.exe a\\\"b c d | ||
| 1420 | , &.{ "foo.exe", "a\\\"b", "c", "d" }); | ||
| 1421 | try t( | ||
| 1422 | \\foo.exe a\\\\"b c" d e | ||
| 1423 | , &.{ "foo.exe", "a\\\\b c", "d", "e" }); | ||
| 1424 | try t( | ||
| 1425 | \\foo.exe a"b"" c d | ||
| 1426 | , &.{ "foo.exe", "ab\" c d" }); | ||
| 1427 | |||
| 1428 | // From https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX | ||
| 1429 | try t("foo.exe CallMeIshmael", &.{ "foo.exe", "CallMeIshmael" }); | ||
| 1430 | try t("foo.exe \"Call Me Ishmael\"", &.{ "foo.exe", "Call Me Ishmael" }); | ||
| 1431 | try t("foo.exe Cal\"l Me I\"shmael", &.{ "foo.exe", "Call Me Ishmael" }); | ||
| 1432 | try t("foo.exe CallMe\\\"Ishmael", &.{ "foo.exe", "CallMe\"Ishmael" }); | ||
| 1433 | try t("foo.exe \"CallMe\\\"Ishmael\"", &.{ "foo.exe", "CallMe\"Ishmael" }); | ||
| 1434 | try t("foo.exe \"Call Me Ishmael\\\\\"", &.{ "foo.exe", "Call Me Ishmael\\" }); | ||
| 1435 | try t("foo.exe \"CallMe\\\\\\\"Ishmael\"", &.{ "foo.exe", "CallMe\\\"Ishmael" }); | ||
| 1436 | try t("foo.exe a\\\\\\b", &.{ "foo.exe", "a\\\\\\b" }); | ||
| 1437 | try t("foo.exe \"a\\\\\\b\"", &.{ "foo.exe", "a\\\\\\b" }); | ||
| 1438 | |||
| 1439 | // Surrogate pair encoding of 𐐷 separated by quotes. | ||
| 1440 | // Encoded as WTF-16: | ||
| 1441 | // "<0xD801>"<0xDC37> | ||
| 1442 | // Encoded as WTF-8: | ||
| 1443 | // "<0xED><0xA0><0x81>"<0xED><0xB0><0xB7> | ||
| 1444 | // During parsing, the quotes drop out and the surrogate pair | ||
| 1445 | // should end up encoded as its normal UTF-8 representation. | ||
| 1446 | try t("foo.exe \"\xed\xa0\x81\"\xed\xb0\xb7", &.{ "foo.exe", "𐐷" }); | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | fn testArgIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void { | ||
| 1450 | const cmd_line_w = try unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line); | ||
| 1451 | defer testing.allocator.free(cmd_line_w); | ||
| 1452 | |||
| 1453 | // next | ||
| 1454 | { | ||
| 1455 | var it = try ArgIteratorWindows.init(testing.allocator, cmd_line_w); | ||
| 1456 | defer it.deinit(); | ||
| 1457 | |||
| 1458 | for (expected_args) |expected| { | ||
| 1459 | if (it.next()) |actual| { | ||
| 1460 | try testing.expectEqualStrings(expected, actual); | ||
| 1461 | } else { | ||
| 1462 | return error.TestUnexpectedResult; | ||
| 1463 | } | ||
| 1464 | } | ||
| 1465 | try testing.expect(it.next() == null); | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | // skip | ||
| 1469 | { | ||
| 1470 | var it = try ArgIteratorWindows.init(testing.allocator, cmd_line_w); | ||
| 1471 | defer it.deinit(); | ||
| 1472 | |||
| 1473 | for (0..expected_args.len) |_| { | ||
| 1474 | try testing.expect(it.skip()); | ||
| 1475 | } | ||
| 1476 | try testing.expect(!it.skip()); | ||
| 1477 | } | ||
| 1478 | } | ||
| 1479 | |||
| 1480 | test "general arg parsing" { | ||
| 1481 | try testGeneralCmdLine("a b\tc d", &.{ "a", "b", "c", "d" }); | ||
| 1482 | try testGeneralCmdLine("\"abc\" d e", &.{ "abc", "d", "e" }); | ||
| 1483 | try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &.{ "a\\\\\\b", "de fg", "h" }); | ||
| 1484 | try testGeneralCmdLine("a\\\\\\\"b c d", &.{ "a\\\"b", "c", "d" }); | ||
| 1485 | try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &.{ "a\\\\b c", "d", "e" }); | ||
| 1486 | try testGeneralCmdLine("a b\tc \"d f", &.{ "a", "b", "c", "d f" }); | ||
| 1487 | try testGeneralCmdLine("j k l\\", &.{ "j", "k", "l\\" }); | ||
| 1488 | try testGeneralCmdLine("\"\" x y z\\\\", &.{ "", "x", "y", "z\\\\" }); | ||
| 1489 | |||
| 1490 | try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &.{ | ||
| 1491 | ".\\..\\zig-cache\\build", | ||
| 1492 | "bin\\zig.exe", | ||
| 1493 | ".\\..", | ||
| 1494 | ".\\..\\zig-cache", | ||
| 1495 | "--help", | ||
| 1496 | }); | ||
| 1497 | |||
| 1498 | try testGeneralCmdLine( | ||
| 1499 | \\ 'foo' "bar" | ||
| 1500 | , &.{ "'foo'", "bar" }); | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { | ||
| 1504 | var it = try ArgIteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line); | ||
| 1505 | defer it.deinit(); | ||
| 1506 | for (expected_args) |expected_arg| { | ||
| 1507 | const arg = it.next().?; | ||
| 1508 | try testing.expectEqualStrings(expected_arg, arg); | ||
| 1509 | } | ||
| 1510 | try testing.expect(it.next() == null); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | test "response file arg parsing" { | ||
| 1514 | try testResponseFileCmdLine( | ||
| 1515 | \\a b | ||
| 1516 | \\c d\ | ||
| 1517 | , &.{ "a", "b", "c", "d\\" }); | ||
| 1518 | try testResponseFileCmdLine("a b c d\\", &.{ "a", "b", "c", "d\\" }); | ||
| 1519 | |||
| 1520 | try testResponseFileCmdLine( | ||
| 1521 | \\j | ||
| 1522 | \\ k l # this is a comment \\ \\\ \\\\ "none" "\\" "\\\" | ||
| 1523 | \\ "m" #another comment | ||
| 1524 | \\ | ||
| 1525 | , &.{ "j", "k", "l", "m" }); | ||
| 1526 | |||
| 1527 | try testResponseFileCmdLine( | ||
| 1528 | \\ "" q "" | ||
| 1529 | \\ "r s # t" "u\" v" #another comment | ||
| 1530 | \\ | ||
| 1531 | , &.{ "", "q", "", "r s # t", "u\" v" }); | ||
| 1532 | |||
| 1533 | try testResponseFileCmdLine( | ||
| 1534 | \\ -l"advapi32" a# b#c d# | ||
| 1535 | \\e\\\ | ||
| 1536 | , &.{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" }); | ||
| 1537 | |||
| 1538 | try testResponseFileCmdLine( | ||
| 1539 | \\ 'foo' "bar" | ||
| 1540 | , &.{ "foo", "bar" }); | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { | ||
| 1544 | var it = try ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }) | ||
| 1545 | .init(std.testing.allocator, input_cmd_line); | ||
| 1546 | defer it.deinit(); | ||
| 1547 | for (expected_args) |expected_arg| { | ||
| 1548 | const arg = it.next().?; | ||
| 1549 | try testing.expectEqualStrings(expected_arg, arg); | ||
| 1550 | } | ||
| 1551 | try testing.expect(it.next() == null); | ||
| 1552 | } | ||
| 1553 | |||
| 1554 | pub const UserInfo = struct { | 108 | pub const UserInfo = struct { |
| 1555 | uid: posix.uid_t, | 109 | uid: posix.uid_t, |
| 1556 | gid: posix.gid_t, | 110 | gid: posix.gid_t, |
| ... | @@ -1706,71 +260,278 @@ pub fn getBaseAddress() usize { | ... | @@ -1706,71 +260,278 @@ pub fn getBaseAddress() usize { |
| 1706 | } | 260 | } |
| 1707 | } | 261 | } |
| 1708 | 262 | ||
| 1709 | /// Tells whether calling the `execv` or `execve` functions will be a compile error. | 263 | /// Tells whether the target operating system supports replacing the current |
| 1710 | pub const can_execv = switch (native_os) { | 264 | /// process image. If this is `false` then calling `replace` or `replaceFile` |
| 265 | /// functions will return `error.OperationUnsupported`. | ||
| 266 | pub const can_replace = switch (native_os) { | ||
| 1711 | .windows, .haiku, .wasi => false, | 267 | .windows, .haiku, .wasi => false, |
| 1712 | else => true, | 268 | else => true, |
| 1713 | }; | 269 | }; |
| 1714 | 270 | ||
| 1715 | /// Tells whether spawning child processes is supported (e.g. via Child) | 271 | /// Tells whether spawning child processes is supported. |
| 1716 | pub const can_spawn = switch (native_os) { | 272 | pub const can_spawn = switch (native_os) { |
| 1717 | .wasi, .ios, .tvos, .visionos, .watchos => false, | 273 | .wasi, .ios, .tvos, .visionos, .watchos => false, |
| 1718 | else => true, | 274 | else => true, |
| 1719 | }; | 275 | }; |
| 1720 | 276 | ||
| 1721 | pub const ExecvError = std.posix.ExecveError || error{OutOfMemory}; | 277 | pub const ReplaceError = error{ |
| 1722 | 278 | /// The target operating system cannot replace the process image with a new | |
| 1723 | /// Replaces the current process image with the executed process. | 279 | /// one. |
| 1724 | /// This function must allocate memory to add a null terminating bytes on path and each arg. | 280 | OperationUnsupported, |
| 1725 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null | 281 | SystemResources, |
| 1726 | /// pointers after the args and after the environment variables. | 282 | AccessDenied, |
| 1727 | /// `argv[0]` is the executable path. | 283 | PermissionDenied, |
| 1728 | /// This function also uses the PATH environment variable to get the full path to the executable. | 284 | InvalidExe, |
| 1729 | /// Due to the heap-allocation, it is illegal to call this function in a fork() child. | 285 | FileSystem, |
| 1730 | /// For that use case, use the `std.posix` functions directly. | 286 | IsDir, |
| 1731 | pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError { | 287 | FileNotFound, |
| 1732 | return execve(allocator, argv, null); | 288 | NotDir, |
| 289 | FileBusy, | ||
| 290 | ProcessFdQuotaExceeded, | ||
| 291 | SystemFdQuotaExceeded, | ||
| 292 | } || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; | ||
| 293 | |||
| 294 | pub const ReplaceOptions = struct { | ||
| 295 | argv: []const []const u8, | ||
| 296 | expand_arg0: ArgExpansion = .no_expand, | ||
| 297 | /// Replaces the environment when provided. The PATH value from here is | ||
| 298 | /// never used to resolve `argv[0]`. | ||
| 299 | environ_map: ?*const Environ.Map = null, | ||
| 300 | }; | ||
| 301 | |||
| 302 | /// Replaces the current process image with the executed process. If this | ||
| 303 | /// function succeeds, it does not return. | ||
| 304 | /// | ||
| 305 | /// `argv[0]` is the name of the process to replace the current one with. If it | ||
| 306 | /// is not already a file path (i.e. it contains '/'), it is resolved into a | ||
| 307 | /// file path based on PATH from the parent environment. | ||
| 308 | /// | ||
| 309 | /// It is illegal to call this function in a fork() child. | ||
| 310 | pub fn replace(io: Io, options: ReplaceOptions) ReplaceError { | ||
| 311 | return io.vtable.processReplace(io.userdata, options); | ||
| 1733 | } | 312 | } |
| 1734 | 313 | ||
| 1735 | /// Replaces the current process image with the executed process. | 314 | /// Replaces the current process image with the executed process. If this |
| 1736 | /// This function must allocate memory to add a null terminating bytes on path and each arg. | 315 | /// function succeeds, it does not return. |
| 1737 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null | 316 | /// |
| 1738 | /// pointers after the args and after the environment variables. | 317 | /// `argv[0]` is the file path of the process to replace the current one with, |
| 1739 | /// `argv[0]` is the executable path. | 318 | /// relative to `dir`. It is *always* treated as a file path, even if it does |
| 1740 | /// This function also uses the PATH environment variable to get the full path to the executable. | 319 | /// not contain '/'. |
| 1741 | /// Due to the heap-allocation, it is illegal to call this function in a fork() child. | 320 | /// |
| 1742 | /// For that use case, use the `std.posix` functions directly. | 321 | /// It is illegal to call this function in a fork() child. |
| 1743 | pub fn execve( | 322 | pub fn replacePath(io: Io, dir: Io.Dir, options: ReplaceOptions) ReplaceError { |
| 1744 | allocator: Allocator, | 323 | return io.vtable.processReplacePath(io.userdata, dir, options); |
| 324 | } | ||
| 325 | |||
| 326 | pub const ArgExpansion = enum { expand, no_expand }; | ||
| 327 | |||
| 328 | /// File name extensions supported natively by `CreateProcess()` on Windows. | ||
| 329 | pub const WindowsExtension = enum { bat, cmd, com, exe }; | ||
| 330 | |||
| 331 | pub const SpawnError = error{ | ||
| 332 | /// The operating system does not support creating child processes. | ||
| 333 | OperationUnsupported, | ||
| 334 | OutOfMemory, | ||
| 335 | /// POSIX-only. `StdIo.ignore` was selected and opening `/dev/null` returned ENODEV. | ||
| 336 | NoDevice, | ||
| 337 | /// Windows-only. `cwd` or `argv` was provided and it was invalid WTF-8. | ||
| 338 | /// https://wtf-8.codeberg.page/ | ||
| 339 | InvalidWtf8, | ||
| 340 | /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process. | ||
| 341 | CurrentWorkingDirectoryUnlinked, | ||
| 342 | /// Windows-only. NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed | ||
| 343 | /// within arguments when executing a `.bat`/`.cmd` script. | ||
| 344 | /// - NUL/LF signifiies end of arguments, so anything afterwards | ||
| 345 | /// would be lost after execution. | ||
| 346 | /// - CR is stripped by `cmd.exe`, so any CR codepoints | ||
| 347 | /// would be lost after execution. | ||
| 348 | InvalidBatchScriptArg, | ||
| 349 | SystemResources, | ||
| 350 | AccessDenied, | ||
| 351 | PermissionDenied, | ||
| 352 | InvalidExe, | ||
| 353 | FileSystem, | ||
| 354 | IsDir, | ||
| 355 | FileNotFound, | ||
| 356 | NotDir, | ||
| 357 | FileBusy, | ||
| 358 | ProcessFdQuotaExceeded, | ||
| 359 | SystemFdQuotaExceeded, | ||
| 360 | ResourceLimitReached, | ||
| 361 | InvalidUserId, | ||
| 362 | InvalidProcessGroupId, | ||
| 363 | SymLinkLoop, | ||
| 364 | InvalidName, | ||
| 365 | /// An attempt was made to change the process group ID of one of the | ||
| 366 | /// children of the calling process and the child had already performed an | ||
| 367 | /// image replacement. | ||
| 368 | ProcessAlreadyExec, | ||
| 369 | } || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; | ||
| 370 | |||
| 371 | pub const SpawnOptions = struct { | ||
| 1745 | argv: []const []const u8, | 372 | argv: []const []const u8, |
| 1746 | env_map: ?*const EnvMap, | 373 | |
| 1747 | ) ExecvError { | 374 | /// Set to change the current working directory when spawning the child process. |
| 1748 | if (!can_execv) @compileError("The target OS does not support execv"); | 375 | cwd: ?[]const u8 = null, |
| 1749 | 376 | /// Set to change the current working directory when spawning the child process. | |
| 1750 | var arena_allocator = std.heap.ArenaAllocator.init(allocator); | 377 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 |
| 1751 | defer arena_allocator.deinit(); | 378 | /// Once that is done, `cwd` will be deprecated in favor of this field. |
| 1752 | const arena = arena_allocator.allocator(); | 379 | cwd_dir: ?Io.Dir = null, |
| 1753 | 380 | /// Replaces the child environment when provided. The PATH value from here | |
| 1754 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, argv.len, null); | 381 | /// is not used to resolve `argv[0]`; that resolution always uses parent |
| 1755 | for (argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | 382 | /// environment. |
| 1756 | 383 | environ_map: ?*const Environ.Map = null, | |
| 1757 | const envp = m: { | 384 | expand_arg0: ArgExpansion = .no_expand, |
| 1758 | if (env_map) |m| { | 385 | /// When populated, a pipe will be created for the child process to |
| 1759 | const envp_buf = try createNullDelimitedEnvMap(arena, m); | 386 | /// communicate progress back to the parent. The file descriptor of the |
| 1760 | break :m envp_buf.ptr; | 387 | /// write end of the pipe will be specified in the `ZIG_PROGRESS` |
| 1761 | } else if (builtin.link_libc) { | 388 | /// environment variable inside the child process. The progress reported by |
| 1762 | break :m std.c.environ; | 389 | /// the child will be attached to this progress node in the parent process. |
| 1763 | } else if (builtin.output_mode == .Exe) { | 390 | /// |
| 1764 | // Then we have Zig start code and this works. | 391 | /// The child's progress tree will be grafted into the parent's progress tree, |
| 1765 | // TODO type-safety for null-termination of `os.environ`. | 392 | /// by substituting this node with the child's root node. |
| 1766 | break :m @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr)); | 393 | progress_node: std.Progress.Node = std.Progress.Node.none, |
| 1767 | } else { | 394 | |
| 1768 | // TODO come up with a solution for this. | 395 | stdin: StdIo = .inherit, |
| 1769 | @compileError("missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process"); | 396 | stdout: StdIo = .inherit, |
| 1770 | } | 397 | stderr: StdIo = .inherit, |
| 398 | |||
| 399 | /// Set to true to obtain rusage information for the child process. | ||
| 400 | /// Depending on the target platform and implementation status, the | ||
| 401 | /// requested statistics may or may not be available. If they are | ||
| 402 | /// available, then the `resource_usage_statistics` field will be populated | ||
| 403 | /// after calling `wait`. | ||
| 404 | /// On Linux and Darwin, this obtains rusage statistics from wait4(). | ||
| 405 | request_resource_usage_statistics: bool = false, | ||
| 406 | |||
| 407 | /// Set to change the user id when spawning the child process. | ||
| 408 | uid: ?posix.uid_t = null, | ||
| 409 | /// Set to change the group id when spawning the child process. | ||
| 410 | gid: ?posix.gid_t = null, | ||
| 411 | /// Set to change the process group id when spawning the child process. | ||
| 412 | pgid: ?posix.pid_t = null, | ||
| 413 | |||
| 414 | /// Start child process in suspended state. | ||
| 415 | /// For Posix systems it's started as if SIGSTOP was sent. | ||
| 416 | start_suspended: bool = false, | ||
| 417 | /// Windows-only. Sets the CREATE_NO_WINDOW flag in CreateProcess. | ||
| 418 | create_no_window: bool = false, | ||
| 419 | /// Darwin-only. Disable ASLR for the child process. | ||
| 420 | disable_aslr: bool = false, | ||
| 421 | |||
| 422 | /// Behavior of the child process's standard input, output, and error streams. | ||
| 423 | pub const StdIo = union(enum) { | ||
| 424 | /// Inherit the corresponding stream from the parent process. | ||
| 425 | inherit, | ||
| 426 | /// Pass an already open file from the parent to the child. | ||
| 427 | file: File, | ||
| 428 | /// Pass a null stream to the child process by opening "/dev/null" on POSIX | ||
| 429 | /// and "NUL" on Windows. | ||
| 430 | ignore, | ||
| 431 | /// Create a new pipe for the stream. | ||
| 432 | /// | ||
| 433 | /// The corresponding field (`stdout`, `stderr`, or `stdin`) will be | ||
| 434 | /// assigned a `File` object that can be used to read from or write to the | ||
| 435 | /// pipe. | ||
| 436 | pipe, | ||
| 437 | /// Spawn the child process with the corresponding stream missing. This | ||
| 438 | /// will likely result in the child encountering EBADF if it tries to use | ||
| 439 | /// stdin, stdout, or stderr, or if only one stream is closed, it will | ||
| 440 | /// result in them getting mixed up. Generally, this option is for advanced | ||
| 441 | /// use cases only. | ||
| 442 | close, | ||
| 1771 | }; | 443 | }; |
| 444 | }; | ||
| 445 | |||
| 446 | /// Creates a child process. | ||
| 447 | /// | ||
| 448 | /// `argv[0]` is the name of the program to execute. If it is not already a | ||
| 449 | /// file path (i.e. it contains '/'), it is resolved into a file path based on | ||
| 450 | /// PATH from the parent environment. | ||
| 451 | pub fn spawn(io: Io, options: SpawnOptions) SpawnError!Child { | ||
| 452 | return io.vtable.processSpawn(io.userdata, options); | ||
| 453 | } | ||
| 1772 | 454 | ||
| 1773 | return posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp); | 455 | /// Creates a child process. |
| 456 | /// | ||
| 457 | /// `argv[0]` is the file path of the program to execute, relative to `dir`. It | ||
| 458 | /// is *always* treated as a file path, even if it does not contain '/'. | ||
| 459 | pub fn spawnPath(io: Io, dir: Io.Dir, options: SpawnOptions) SpawnError!Child { | ||
| 460 | return io.vtable.processSpawnPath(io.userdata, dir, options); | ||
| 461 | } | ||
| 462 | |||
| 463 | pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{ | ||
| 464 | StdoutStreamTooLong, | ||
| 465 | StderrStreamTooLong, | ||
| 466 | }; | ||
| 467 | |||
| 468 | pub const RunOptions = struct { | ||
| 469 | argv: []const []const u8, | ||
| 470 | max_output_bytes: usize = 50 * 1024, | ||
| 471 | |||
| 472 | /// Set to change the current working directory when spawning the child process. | ||
| 473 | cwd: ?[]const u8 = null, | ||
| 474 | /// Set to change the current working directory when spawning the child process. | ||
| 475 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 | ||
| 476 | /// Once that is done, `cwd` will be deprecated in favor of this field. | ||
| 477 | cwd_dir: ?Io.Dir = null, | ||
| 478 | /// Replaces the child environment when provided. The PATH value from here | ||
| 479 | /// is not used to resolve `argv[0]`; that resolution always uses parent | ||
| 480 | /// environment. | ||
| 481 | environ_map: ?*const Environ.Map = null, | ||
| 482 | expand_arg0: ArgExpansion = .no_expand, | ||
| 483 | /// When populated, a pipe will be created for the child process to | ||
| 484 | /// communicate progress back to the parent. The file descriptor of the | ||
| 485 | /// write end of the pipe will be specified in the `ZIG_PROGRESS` | ||
| 486 | /// environment variable inside the child process. The progress reported by | ||
| 487 | /// the child will be attached to this progress node in the parent process. | ||
| 488 | /// | ||
| 489 | /// The child's progress tree will be grafted into the parent's progress tree, | ||
| 490 | /// by substituting this node with the child's root node. | ||
| 491 | progress_node: std.Progress.Node = std.Progress.Node.none, | ||
| 492 | /// Windows-only. Sets the CREATE_NO_WINDOW flag in CreateProcess. | ||
| 493 | create_no_window: bool = true, | ||
| 494 | /// Darwin-only. Disable ASLR for the child process. | ||
| 495 | disable_aslr: bool = false, | ||
| 496 | }; | ||
| 497 | |||
| 498 | pub const RunResult = struct { | ||
| 499 | term: Child.Term, | ||
| 500 | stdout: []u8, | ||
| 501 | stderr: []u8, | ||
| 502 | }; | ||
| 503 | |||
| 504 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. | ||
| 505 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. | ||
| 506 | pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult { | ||
| 507 | var child = try spawn(io, .{ | ||
| 508 | .argv = options.argv, | ||
| 509 | .cwd = options.cwd, | ||
| 510 | .cwd_dir = options.cwd_dir, | ||
| 511 | .environ_map = options.environ_map, | ||
| 512 | .expand_arg0 = options.expand_arg0, | ||
| 513 | .progress_node = options.progress_node, | ||
| 514 | .create_no_window = options.create_no_window, | ||
| 515 | .disable_aslr = options.disable_aslr, | ||
| 516 | |||
| 517 | .stdin = .ignore, | ||
| 518 | .stdout = .pipe, | ||
| 519 | .stderr = .pipe, | ||
| 520 | }); | ||
| 521 | defer child.kill(io); | ||
| 522 | |||
| 523 | var stdout: std.ArrayList(u8) = .empty; | ||
| 524 | defer stdout.deinit(gpa); | ||
| 525 | var stderr: std.ArrayList(u8) = .empty; | ||
| 526 | defer stderr.deinit(gpa); | ||
| 527 | |||
| 528 | try child.collectOutput(gpa, &stdout, &stderr, options.max_output_bytes); | ||
| 529 | |||
| 530 | return .{ | ||
| 531 | .stdout = try stdout.toOwnedSlice(gpa), | ||
| 532 | .stderr = try stderr.toOwnedSlice(gpa), | ||
| 533 | .term = try child.wait(io), | ||
| 534 | }; | ||
| 1774 | } | 535 | } |
| 1775 | 536 | ||
| 1776 | pub const TotalSystemMemoryError = error{ | 537 | pub const TotalSystemMemoryError = error{ |
| ... | @@ -1903,215 +664,6 @@ test raiseFileDescriptorLimit { | ... | @@ -1903,215 +664,6 @@ test raiseFileDescriptorLimit { |
| 1903 | raiseFileDescriptorLimit(); | 664 | raiseFileDescriptorLimit(); |
| 1904 | } | 665 | } |
| 1905 | 666 | ||
| 1906 | pub const CreateEnvironOptions = struct { | ||
| 1907 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. | ||
| 1908 | /// If non-null, negative means to remove the environment variable, and >= 0 | ||
| 1909 | /// means to provide it with the given integer. | ||
| 1910 | zig_progress_fd: ?i32 = null, | ||
| 1911 | }; | ||
| 1912 | |||
| 1913 | /// Creates a null-delimited environment variable block in the format | ||
| 1914 | /// expected by POSIX, from a hash map plus options. | ||
| 1915 | pub fn createEnvironFromMap( | ||
| 1916 | arena: Allocator, | ||
| 1917 | map: *const EnvMap, | ||
| 1918 | options: CreateEnvironOptions, | ||
| 1919 | ) Allocator.Error![:null]?[*:0]u8 { | ||
| 1920 | const ZigProgressAction = enum { nothing, edit, delete, add }; | ||
| 1921 | const zig_progress_action: ZigProgressAction = a: { | ||
| 1922 | const fd = options.zig_progress_fd orelse break :a .nothing; | ||
| 1923 | const contains = map.get("ZIG_PROGRESS") != null; | ||
| 1924 | if (fd >= 0) { | ||
| 1925 | break :a if (contains) .edit else .add; | ||
| 1926 | } else { | ||
| 1927 | if (contains) break :a .delete; | ||
| 1928 | } | ||
| 1929 | break :a .nothing; | ||
| 1930 | }; | ||
| 1931 | |||
| 1932 | const envp_count: usize = c: { | ||
| 1933 | var count: usize = map.count(); | ||
| 1934 | switch (zig_progress_action) { | ||
| 1935 | .add => count += 1, | ||
| 1936 | .delete => count -= 1, | ||
| 1937 | .nothing, .edit => {}, | ||
| 1938 | } | ||
| 1939 | break :c count; | ||
| 1940 | }; | ||
| 1941 | |||
| 1942 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | ||
| 1943 | var i: usize = 0; | ||
| 1944 | |||
| 1945 | if (zig_progress_action == .add) { | ||
| 1946 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | ||
| 1947 | i += 1; | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | { | ||
| 1951 | var it = map.iterator(); | ||
| 1952 | while (it.next()) |pair| { | ||
| 1953 | if (mem.eql(u8, pair.key_ptr.*, "ZIG_PROGRESS")) switch (zig_progress_action) { | ||
| 1954 | .add => unreachable, | ||
| 1955 | .delete => continue, | ||
| 1956 | .edit => { | ||
| 1957 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{ | ||
| 1958 | pair.key_ptr.*, options.zig_progress_fd.?, | ||
| 1959 | }, 0); | ||
| 1960 | i += 1; | ||
| 1961 | continue; | ||
| 1962 | }, | ||
| 1963 | .nothing => {}, | ||
| 1964 | }; | ||
| 1965 | |||
| 1966 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); | ||
| 1967 | i += 1; | ||
| 1968 | } | ||
| 1969 | } | ||
| 1970 | |||
| 1971 | assert(i == envp_count); | ||
| 1972 | return envp_buf; | ||
| 1973 | } | ||
| 1974 | |||
| 1975 | /// Creates a null-delimited environment variable block in the format | ||
| 1976 | /// expected by POSIX, from a hash map plus options. | ||
| 1977 | pub fn createEnvironFromExisting( | ||
| 1978 | arena: Allocator, | ||
| 1979 | existing: [*:null]const ?[*:0]const u8, | ||
| 1980 | options: CreateEnvironOptions, | ||
| 1981 | ) Allocator.Error![:null]?[*:0]u8 { | ||
| 1982 | const existing_count, const contains_zig_progress = c: { | ||
| 1983 | var count: usize = 0; | ||
| 1984 | var contains = false; | ||
| 1985 | while (existing[count]) |line| : (count += 1) { | ||
| 1986 | contains = contains or mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS"); | ||
| 1987 | } | ||
| 1988 | break :c .{ count, contains }; | ||
| 1989 | }; | ||
| 1990 | const ZigProgressAction = enum { nothing, edit, delete, add }; | ||
| 1991 | const zig_progress_action: ZigProgressAction = a: { | ||
| 1992 | const fd = options.zig_progress_fd orelse break :a .nothing; | ||
| 1993 | if (fd >= 0) { | ||
| 1994 | break :a if (contains_zig_progress) .edit else .add; | ||
| 1995 | } else { | ||
| 1996 | if (contains_zig_progress) break :a .delete; | ||
| 1997 | } | ||
| 1998 | break :a .nothing; | ||
| 1999 | }; | ||
| 2000 | |||
| 2001 | const envp_count: usize = c: { | ||
| 2002 | var count: usize = existing_count; | ||
| 2003 | switch (zig_progress_action) { | ||
| 2004 | .add => count += 1, | ||
| 2005 | .delete => count -= 1, | ||
| 2006 | .nothing, .edit => {}, | ||
| 2007 | } | ||
| 2008 | break :c count; | ||
| 2009 | }; | ||
| 2010 | |||
| 2011 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | ||
| 2012 | var i: usize = 0; | ||
| 2013 | var existing_index: usize = 0; | ||
| 2014 | |||
| 2015 | if (zig_progress_action == .add) { | ||
| 2016 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | ||
| 2017 | i += 1; | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | while (existing[existing_index]) |line| : (existing_index += 1) { | ||
| 2021 | if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | ||
| 2022 | .add => unreachable, | ||
| 2023 | .delete => continue, | ||
| 2024 | .edit => { | ||
| 2025 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | ||
| 2026 | i += 1; | ||
| 2027 | continue; | ||
| 2028 | }, | ||
| 2029 | .nothing => {}, | ||
| 2030 | }; | ||
| 2031 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); | ||
| 2032 | i += 1; | ||
| 2033 | } | ||
| 2034 | |||
| 2035 | assert(i == envp_count); | ||
| 2036 | return envp_buf; | ||
| 2037 | } | ||
| 2038 | |||
| 2039 | pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) Allocator.Error![:null]?[*:0]u8 { | ||
| 2040 | return createEnvironFromMap(arena, env_map, .{}); | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | test createNullDelimitedEnvMap { | ||
| 2044 | const allocator = testing.allocator; | ||
| 2045 | var envmap = EnvMap.init(allocator); | ||
| 2046 | defer envmap.deinit(); | ||
| 2047 | |||
| 2048 | try envmap.put("HOME", "/home/ifreund"); | ||
| 2049 | try envmap.put("WAYLAND_DISPLAY", "wayland-1"); | ||
| 2050 | try envmap.put("DISPLAY", ":1"); | ||
| 2051 | try envmap.put("DEBUGINFOD_URLS", " "); | ||
| 2052 | try envmap.put("XCURSOR_SIZE", "24"); | ||
| 2053 | |||
| 2054 | var arena = std.heap.ArenaAllocator.init(allocator); | ||
| 2055 | defer arena.deinit(); | ||
| 2056 | const environ = try createNullDelimitedEnvMap(arena.allocator(), &envmap); | ||
| 2057 | |||
| 2058 | try testing.expectEqual(@as(usize, 5), environ.len); | ||
| 2059 | |||
| 2060 | inline for (.{ | ||
| 2061 | "HOME=/home/ifreund", | ||
| 2062 | "WAYLAND_DISPLAY=wayland-1", | ||
| 2063 | "DISPLAY=:1", | ||
| 2064 | "DEBUGINFOD_URLS= ", | ||
| 2065 | "XCURSOR_SIZE=24", | ||
| 2066 | }) |target| { | ||
| 2067 | for (environ) |variable| { | ||
| 2068 | if (mem.eql(u8, mem.span(variable orelse continue), target)) break; | ||
| 2069 | } else { | ||
| 2070 | try testing.expect(false); // Environment variable not found | ||
| 2071 | } | ||
| 2072 | } | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | /// Caller must free result. | ||
| 2076 | pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ![]u16 { | ||
| 2077 | // count bytes needed | ||
| 2078 | const max_chars_needed = x: { | ||
| 2079 | // Only need 2 trailing NUL code units for an empty environment | ||
| 2080 | var max_chars_needed: usize = if (env_map.count() == 0) 2 else 1; | ||
| 2081 | var it = env_map.iterator(); | ||
| 2082 | while (it.next()) |pair| { | ||
| 2083 | // +1 for '=' | ||
| 2084 | // +1 for null byte | ||
| 2085 | max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; | ||
| 2086 | } | ||
| 2087 | break :x max_chars_needed; | ||
| 2088 | }; | ||
| 2089 | const result = try allocator.alloc(u16, max_chars_needed); | ||
| 2090 | errdefer allocator.free(result); | ||
| 2091 | |||
| 2092 | var it = env_map.iterator(); | ||
| 2093 | var i: usize = 0; | ||
| 2094 | while (it.next()) |pair| { | ||
| 2095 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*); | ||
| 2096 | result[i] = '='; | ||
| 2097 | i += 1; | ||
| 2098 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*); | ||
| 2099 | result[i] = 0; | ||
| 2100 | i += 1; | ||
| 2101 | } | ||
| 2102 | result[i] = 0; | ||
| 2103 | i += 1; | ||
| 2104 | // An empty environment is a special case that requires a redundant | ||
| 2105 | // NUL terminator. CreateProcess will read the second code unit even | ||
| 2106 | // though theoretically the first should be enough to recognize that the | ||
| 2107 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) | ||
| 2108 | if (env_map.count() == 0) { | ||
| 2109 | result[i] = 0; | ||
| 2110 | i += 1; | ||
| 2111 | } | ||
| 2112 | return try allocator.realloc(result, i); | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | /// Logs an error and then terminates the process with exit code 1. | 667 | /// Logs an error and then terminates the process with exit code 1. |
| 2116 | pub fn fatal(comptime format: []const u8, format_arguments: anytype) noreturn { | 668 | pub fn fatal(comptime format: []const u8, format_arguments: anytype) noreturn { |
| 2117 | std.log.err(format, format_arguments); | 669 | std.log.err(format, format_arguments); |
lib/std/process/Args.zig created+976| ... | @@ -0,0 +1,976 @@ | ||
| 1 | const Args = @This(); | ||
| 2 | |||
| 3 | const builtin = @import("builtin"); | ||
| 4 | const native_os = builtin.os.tag; | ||
| 5 | |||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | const testing = std.testing; | ||
| 10 | |||
| 11 | vector: Vector, | ||
| 12 | |||
| 13 | /// On WASI without libc, this is `void` because the environment has to be | ||
| 14 | /// queried and heap-allocated at runtime. | ||
| 15 | pub const Vector = switch (native_os) { | ||
| 16 | .windows => []const u16, // WTF-16 encoded | ||
| 17 | .wasi => switch (builtin.link_libc) { | ||
| 18 | false => void, | ||
| 19 | true => []const [*:0]const u8, | ||
| 20 | }, | ||
| 21 | .freestanding, .other => void, | ||
| 22 | else => []const [*:0]const u8, | ||
| 23 | }; | ||
| 24 | |||
| 25 | /// Cross-platform access to command line one argument at a time. | ||
| 26 | pub const Iterator = struct { | ||
| 27 | const Inner = switch (native_os) { | ||
| 28 | .windows => Windows, | ||
| 29 | .wasi => if (builtin.link_libc) Posix else Wasi, | ||
| 30 | else => Posix, | ||
| 31 | }; | ||
| 32 | |||
| 33 | inner: Inner, | ||
| 34 | |||
| 35 | /// Initialize the args iterator. Consider using `initAllocator` instead | ||
| 36 | /// for cross-platform compatibility. | ||
| 37 | pub fn init(a: Args) Iterator { | ||
| 38 | if (native_os == .wasi) @compileError("In WASI, use initAllocator instead."); | ||
| 39 | if (native_os == .windows) @compileError("In Windows, use initAllocator instead."); | ||
| 40 | return .{ .inner = .init(a) }; | ||
| 41 | } | ||
| 42 | |||
| 43 | pub const InitError = Inner.InitError; | ||
| 44 | |||
| 45 | /// You must deinitialize iterator's internal buffers by calling `deinit` when done. | ||
| 46 | pub fn initAllocator(a: Args, gpa: Allocator) InitError!Iterator { | ||
| 47 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 48 | return .{ .inner = try .init(gpa) }; | ||
| 49 | } | ||
| 50 | if (native_os == .windows) { | ||
| 51 | return .{ .inner = try .init(gpa, a.vector) }; | ||
| 52 | } | ||
| 53 | |||
| 54 | return .{ .inner = .init(a) }; | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Return subsequent argument, or `null` if no more remaining. | ||
| 58 | /// | ||
| 59 | /// Returned slice is pointing to the iterator's internal buffer. | ||
| 60 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 61 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. | ||
| 62 | pub fn next(it: *Iterator) ?[:0]const u8 { | ||
| 63 | return it.inner.next(); | ||
| 64 | } | ||
| 65 | |||
| 66 | /// Parse past 1 argument without capturing it. | ||
| 67 | /// Returns `true` if skipped an arg, `false` if we are at the end. | ||
| 68 | pub fn skip(it: *Iterator) bool { | ||
| 69 | return it.inner.skip(); | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Required to release resources if the iterator was initialized with | ||
| 73 | /// `initAllocator` function. | ||
| 74 | pub fn deinit(it: *Iterator) void { | ||
| 75 | // Unless we're targeting WASI or Windows, this is a no-op. | ||
| 76 | if (native_os == .wasi and !builtin.link_libc) it.inner.deinit(); | ||
| 77 | if (native_os == .windows) it.inner.deinit(); | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Iterator that implements the Windows command-line parsing algorithm. | ||
| 81 | /// | ||
| 82 | /// The implementation is intended to be compatible with the post-2008 C runtime, | ||
| 83 | /// but is *not* intended to be compatible with `CommandLineToArgvW` since | ||
| 84 | /// `CommandLineToArgvW` uses the pre-2008 parsing rules. | ||
| 85 | /// | ||
| 86 | /// This iterator faithfully implements the parsing behavior observed from the C runtime with | ||
| 87 | /// one exception: if the command-line string is empty, the iterator will immediately complete | ||
| 88 | /// without returning any arguments (whereas the C runtime will return a single argument | ||
| 89 | /// representing the name of the current executable). | ||
| 90 | /// | ||
| 91 | /// The essential parts of the algorithm are described in Microsoft's documentation: | ||
| 92 | /// | ||
| 93 | /// - https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170#parsing-c-command-line-arguments | ||
| 94 | /// | ||
| 95 | /// David Deley explains some additional undocumented quirks in great detail: | ||
| 96 | /// | ||
| 97 | /// - https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES | ||
| 98 | pub const Windows = struct { | ||
| 99 | allocator: Allocator, | ||
| 100 | /// Encoded as WTF-16 LE. | ||
| 101 | cmd_line: []const u16, | ||
| 102 | index: usize = 0, | ||
| 103 | /// Owned by the iterator. Long enough to hold contiguous NUL-terminated slices | ||
| 104 | /// of each argument encoded as WTF-8. | ||
| 105 | buffer: []u8, | ||
| 106 | start: usize = 0, | ||
| 107 | end: usize = 0, | ||
| 108 | |||
| 109 | pub const InitError = error{OutOfMemory}; | ||
| 110 | |||
| 111 | /// `cmd_line_w` *must* be a WTF16-LE-encoded string. | ||
| 112 | /// | ||
| 113 | /// The iterator stores and uses `cmd_line_w`, so its memory must be valid for | ||
| 114 | /// at least as long as the returned Windows. | ||
| 115 | pub fn init(gpa: Allocator, cmd_line_w: []const u16) Windows.InitError!Windows { | ||
| 116 | const wtf8_len = std.unicode.calcWtf8Len(cmd_line_w); | ||
| 117 | |||
| 118 | // This buffer must be large enough to contain contiguous NUL-terminated slices | ||
| 119 | // of each argument. | ||
| 120 | // - During parsing, the length of a parsed argument will always be equal to | ||
| 121 | // to less than its unparsed length | ||
| 122 | // - The first argument needs one extra byte of space allocated for its NUL | ||
| 123 | // terminator, but for each subsequent argument the necessary whitespace | ||
| 124 | // between arguments guarantees room for their NUL terminator(s). | ||
| 125 | const buffer = try gpa.alloc(u8, wtf8_len + 1); | ||
| 126 | errdefer gpa.free(buffer); | ||
| 127 | |||
| 128 | return .{ | ||
| 129 | .allocator = gpa, | ||
| 130 | .cmd_line = cmd_line_w, | ||
| 131 | .buffer = buffer, | ||
| 132 | }; | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Returns the next argument and advances the iterator. Returns `null` if at the end of the | ||
| 136 | /// command-line string. The iterator owns the returned slice. | ||
| 137 | /// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 138 | pub fn next(self: *Windows) ?[:0]const u8 { | ||
| 139 | return self.nextWithStrategy(next_strategy); | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Skips the next argument and advances the iterator. Returns `true` if an argument was | ||
| 143 | /// skipped, `false` if at the end of the command-line string. | ||
| 144 | pub fn skip(self: *Windows) bool { | ||
| 145 | return self.nextWithStrategy(skip_strategy); | ||
| 146 | } | ||
| 147 | |||
| 148 | const next_strategy = struct { | ||
| 149 | const T = ?[:0]const u8; | ||
| 150 | |||
| 151 | const eof = null; | ||
| 152 | |||
| 153 | /// Returns '\' if any backslashes are emitted, otherwise returns `last_emitted_code_unit`. | ||
| 154 | fn emitBackslashes(self: *Windows, count: usize, last_emitted_code_unit: ?u16) ?u16 { | ||
| 155 | for (0..count) |_| { | ||
| 156 | self.buffer[self.end] = '\\'; | ||
| 157 | self.end += 1; | ||
| 158 | } | ||
| 159 | return if (count != 0) '\\' else last_emitted_code_unit; | ||
| 160 | } | ||
| 161 | |||
| 162 | /// If `last_emitted_code_unit` and `code_unit` form a surrogate pair, then | ||
| 163 | /// the previously emitted high surrogate is overwritten by the codepoint encoded | ||
| 164 | /// by the surrogate pair, and `null` is returned. | ||
| 165 | /// Otherwise, `code_unit` is emitted and returned. | ||
| 166 | fn emitCharacter(self: *Windows, code_unit: u16, last_emitted_code_unit: ?u16) ?u16 { | ||
| 167 | // Because we are emitting WTF-8, we need to | ||
| 168 | // check to see if we've emitted two consecutive surrogate | ||
| 169 | // codepoints that form a valid surrogate pair in order | ||
| 170 | // to ensure that we're always emitting well-formed WTF-8 | ||
| 171 | // (https://wtf-8.codeberg.page/#concatenating). | ||
| 172 | // | ||
| 173 | // If we do have a valid surrogate pair, we need to emit | ||
| 174 | // the UTF-8 sequence for the codepoint that they encode | ||
| 175 | // instead of the WTF-8 encoding for the two surrogate pairs | ||
| 176 | // separately. | ||
| 177 | // | ||
| 178 | // This is relevant when dealing with a WTF-16 encoded | ||
| 179 | // command line like this: | ||
| 180 | // "<0xD801>"<0xDC37> | ||
| 181 | // which would get parsed and converted to WTF-8 as: | ||
| 182 | // <0xED><0xA0><0x81><0xED><0xB0><0xB7> | ||
| 183 | // but instead, we need to recognize the surrogate pair | ||
| 184 | // and emit the codepoint it encodes, which in this | ||
| 185 | // example is U+10437 (𐐷), which is encoded in UTF-8 as: | ||
| 186 | // <0xF0><0x90><0x90><0xB7> | ||
| 187 | if (last_emitted_code_unit != null and | ||
| 188 | std.unicode.utf16IsLowSurrogate(code_unit) and | ||
| 189 | std.unicode.utf16IsHighSurrogate(last_emitted_code_unit.?)) | ||
| 190 | { | ||
| 191 | const codepoint = std.unicode.utf16DecodeSurrogatePair(&.{ last_emitted_code_unit.?, code_unit }) catch unreachable; | ||
| 192 | |||
| 193 | // Unpaired surrogate is 3 bytes long | ||
| 194 | const dest = self.buffer[self.end - 3 ..]; | ||
| 195 | const len = std.unicode.utf8Encode(codepoint, dest) catch unreachable; | ||
| 196 | // All codepoints that require a surrogate pair (> U+FFFF) are encoded as 4 bytes | ||
| 197 | assert(len == 4); | ||
| 198 | self.end += 1; | ||
| 199 | return null; | ||
| 200 | } | ||
| 201 | |||
| 202 | const wtf8_len = std.unicode.wtf8Encode(code_unit, self.buffer[self.end..]) catch unreachable; | ||
| 203 | self.end += wtf8_len; | ||
| 204 | return code_unit; | ||
| 205 | } | ||
| 206 | |||
| 207 | fn yieldArg(self: *Windows) [:0]const u8 { | ||
| 208 | self.buffer[self.end] = 0; | ||
| 209 | const arg = self.buffer[self.start..self.end :0]; | ||
| 210 | self.end += 1; | ||
| 211 | self.start = self.end; | ||
| 212 | return arg; | ||
| 213 | } | ||
| 214 | }; | ||
| 215 | |||
| 216 | const skip_strategy = struct { | ||
| 217 | const T = bool; | ||
| 218 | |||
| 219 | const eof = false; | ||
| 220 | |||
| 221 | fn emitBackslashes(_: *Windows, _: usize, last_emitted_code_unit: ?u16) ?u16 { | ||
| 222 | return last_emitted_code_unit; | ||
| 223 | } | ||
| 224 | |||
| 225 | fn emitCharacter(_: *Windows, _: u16, last_emitted_code_unit: ?u16) ?u16 { | ||
| 226 | return last_emitted_code_unit; | ||
| 227 | } | ||
| 228 | |||
| 229 | fn yieldArg(_: *Windows) bool { | ||
| 230 | return true; | ||
| 231 | } | ||
| 232 | }; | ||
| 233 | |||
| 234 | fn nextWithStrategy(self: *Windows, comptime strategy: type) strategy.T { | ||
| 235 | var last_emitted_code_unit: ?u16 = null; | ||
| 236 | // The first argument (the executable name) uses different parsing rules. | ||
| 237 | if (self.index == 0) { | ||
| 238 | if (self.cmd_line.len == 0 or self.cmd_line[0] == 0) { | ||
| 239 | // Immediately complete the iterator. | ||
| 240 | // The C runtime would return the name of the current executable here. | ||
| 241 | return strategy.eof; | ||
| 242 | } | ||
| 243 | |||
| 244 | var inside_quotes = false; | ||
| 245 | while (true) : (self.index += 1) { | ||
| 246 | const char = if (self.index != self.cmd_line.len) | ||
| 247 | std.mem.littleToNative(u16, self.cmd_line[self.index]) | ||
| 248 | else | ||
| 249 | 0; | ||
| 250 | switch (char) { | ||
| 251 | 0 => { | ||
| 252 | return strategy.yieldArg(self); | ||
| 253 | }, | ||
| 254 | '"' => { | ||
| 255 | inside_quotes = !inside_quotes; | ||
| 256 | }, | ||
| 257 | ' ', '\t' => { | ||
| 258 | if (inside_quotes) { | ||
| 259 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 260 | } else { | ||
| 261 | self.index += 1; | ||
| 262 | return strategy.yieldArg(self); | ||
| 263 | } | ||
| 264 | }, | ||
| 265 | else => { | ||
| 266 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 267 | }, | ||
| 268 | } | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | // Skip spaces and tabs. The iterator completes if we reach the end of the string here. | ||
| 273 | while (true) : (self.index += 1) { | ||
| 274 | const char = if (self.index != self.cmd_line.len) | ||
| 275 | std.mem.littleToNative(u16, self.cmd_line[self.index]) | ||
| 276 | else | ||
| 277 | 0; | ||
| 278 | switch (char) { | ||
| 279 | 0 => return strategy.eof, | ||
| 280 | ' ', '\t' => continue, | ||
| 281 | else => break, | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | // Parsing rules for subsequent arguments: | ||
| 286 | // | ||
| 287 | // - The end of the string always terminates the current argument. | ||
| 288 | // - When not in 'inside_quotes' mode, a space or tab terminates the current argument. | ||
| 289 | // - 2n backslashes followed by a quote emit n backslashes (note: n can be zero). | ||
| 290 | // If in 'inside_quotes' and the quote is immediately followed by a second quote, | ||
| 291 | // one quote is emitted and the other is skipped, otherwise, the quote is skipped | ||
| 292 | // and 'inside_quotes' is toggled. | ||
| 293 | // - 2n + 1 backslashes followed by a quote emit n backslashes followed by a quote. | ||
| 294 | // - n backslashes not followed by a quote emit n backslashes. | ||
| 295 | var backslash_count: usize = 0; | ||
| 296 | var inside_quotes = false; | ||
| 297 | while (true) : (self.index += 1) { | ||
| 298 | const char = if (self.index != self.cmd_line.len) | ||
| 299 | std.mem.littleToNative(u16, self.cmd_line[self.index]) | ||
| 300 | else | ||
| 301 | 0; | ||
| 302 | switch (char) { | ||
| 303 | 0 => { | ||
| 304 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); | ||
| 305 | return strategy.yieldArg(self); | ||
| 306 | }, | ||
| 307 | ' ', '\t' => { | ||
| 308 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); | ||
| 309 | backslash_count = 0; | ||
| 310 | if (inside_quotes) { | ||
| 311 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 312 | } else return strategy.yieldArg(self); | ||
| 313 | }, | ||
| 314 | '"' => { | ||
| 315 | const char_is_escaped_quote = backslash_count % 2 != 0; | ||
| 316 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count / 2, last_emitted_code_unit); | ||
| 317 | backslash_count = 0; | ||
| 318 | if (char_is_escaped_quote) { | ||
| 319 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); | ||
| 320 | } else { | ||
| 321 | if (inside_quotes and | ||
| 322 | self.index + 1 != self.cmd_line.len and | ||
| 323 | std.mem.littleToNative(u16, self.cmd_line[self.index + 1]) == '"') | ||
| 324 | { | ||
| 325 | last_emitted_code_unit = strategy.emitCharacter(self, '"', last_emitted_code_unit); | ||
| 326 | self.index += 1; | ||
| 327 | } else { | ||
| 328 | inside_quotes = !inside_quotes; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | }, | ||
| 332 | '\\' => { | ||
| 333 | backslash_count += 1; | ||
| 334 | }, | ||
| 335 | else => { | ||
| 336 | last_emitted_code_unit = strategy.emitBackslashes(self, backslash_count, last_emitted_code_unit); | ||
| 337 | backslash_count = 0; | ||
| 338 | last_emitted_code_unit = strategy.emitCharacter(self, char, last_emitted_code_unit); | ||
| 339 | }, | ||
| 340 | } | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | /// Frees the iterator's copy of the command-line string and all previously returned | ||
| 345 | /// argument slices. | ||
| 346 | pub fn deinit(self: *Windows) void { | ||
| 347 | self.allocator.free(self.buffer); | ||
| 348 | } | ||
| 349 | }; | ||
| 350 | |||
| 351 | pub const Posix = struct { | ||
| 352 | remaining: Vector, | ||
| 353 | |||
| 354 | pub const InitError = error{}; | ||
| 355 | |||
| 356 | pub fn init(a: Args) Posix { | ||
| 357 | return .{ .remaining = a.vector }; | ||
| 358 | } | ||
| 359 | |||
| 360 | pub fn next(it: *Posix) ?[:0]const u8 { | ||
| 361 | if (it.remaining.len == 0) return null; | ||
| 362 | const arg = it.remaining[0]; | ||
| 363 | it.remaining = it.remaining[1..]; | ||
| 364 | return std.mem.sliceTo(arg, 0); | ||
| 365 | } | ||
| 366 | |||
| 367 | pub fn skip(it: *Posix) bool { | ||
| 368 | if (it.remaining.len == 0) return false; | ||
| 369 | it.remaining = it.remaining[1..]; | ||
| 370 | return true; | ||
| 371 | } | ||
| 372 | }; | ||
| 373 | |||
| 374 | pub const Wasi = struct { | ||
| 375 | allocator: Allocator, | ||
| 376 | index: usize, | ||
| 377 | args: [][:0]u8, | ||
| 378 | |||
| 379 | pub const InitError = error{OutOfMemory} || std.posix.UnexpectedError; | ||
| 380 | |||
| 381 | /// You must call deinit to free the internal buffer of the | ||
| 382 | /// iterator after you are done. | ||
| 383 | pub fn init(allocator: Allocator) Wasi.InitError!Wasi { | ||
| 384 | const fetched_args = try Wasi.internalInit(allocator); | ||
| 385 | return Wasi{ | ||
| 386 | .allocator = allocator, | ||
| 387 | .index = 0, | ||
| 388 | .args = fetched_args, | ||
| 389 | }; | ||
| 390 | } | ||
| 391 | |||
| 392 | fn internalInit(allocator: Allocator) Wasi.InitError![][:0]u8 { | ||
| 393 | var count: usize = undefined; | ||
| 394 | var buf_size: usize = undefined; | ||
| 395 | |||
| 396 | switch (std.os.wasi.args_sizes_get(&count, &buf_size)) { | ||
| 397 | .SUCCESS => {}, | ||
| 398 | else => |err| return std.posix.unexpectedErrno(err), | ||
| 399 | } | ||
| 400 | |||
| 401 | if (count == 0) { | ||
| 402 | return &[_][:0]u8{}; | ||
| 403 | } | ||
| 404 | |||
| 405 | const argv = try allocator.alloc([*:0]u8, count); | ||
| 406 | defer allocator.free(argv); | ||
| 407 | |||
| 408 | const argv_buf = try allocator.alloc(u8, buf_size); | ||
| 409 | |||
| 410 | switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) { | ||
| 411 | .SUCCESS => {}, | ||
| 412 | else => |err| return std.posix.unexpectedErrno(err), | ||
| 413 | } | ||
| 414 | |||
| 415 | var result_args = try allocator.alloc([:0]u8, count); | ||
| 416 | var i: usize = 0; | ||
| 417 | while (i < count) : (i += 1) { | ||
| 418 | result_args[i] = std.mem.sliceTo(argv[i], 0); | ||
| 419 | } | ||
| 420 | |||
| 421 | return result_args; | ||
| 422 | } | ||
| 423 | |||
| 424 | pub fn next(self: *Wasi) ?[:0]const u8 { | ||
| 425 | if (self.index == self.args.len) return null; | ||
| 426 | |||
| 427 | const arg = self.args[self.index]; | ||
| 428 | self.index += 1; | ||
| 429 | return arg; | ||
| 430 | } | ||
| 431 | |||
| 432 | pub fn skip(self: *Wasi) bool { | ||
| 433 | if (self.index == self.args.len) return false; | ||
| 434 | |||
| 435 | self.index += 1; | ||
| 436 | return true; | ||
| 437 | } | ||
| 438 | |||
| 439 | /// Call to free the internal buffer of the iterator. | ||
| 440 | pub fn deinit(self: *Wasi) void { | ||
| 441 | // Nothing is allocated when there are no args | ||
| 442 | if (self.args.len == 0) return; | ||
| 443 | |||
| 444 | const last_item = self.args[self.args.len - 1]; | ||
| 445 | const last_byte_addr = @intFromPtr(last_item.ptr) + last_item.len + 1; // null terminated | ||
| 446 | const first_item_ptr = self.args[0].ptr; | ||
| 447 | const len = last_byte_addr - @intFromPtr(first_item_ptr); | ||
| 448 | self.allocator.free(first_item_ptr[0..len]); | ||
| 449 | self.allocator.free(self.args); | ||
| 450 | } | ||
| 451 | }; | ||
| 452 | }; | ||
| 453 | |||
| 454 | /// Holds the command-line arguments, with the program name as the first entry. | ||
| 455 | /// Use `iterateAllocator` for cross-platform code. | ||
| 456 | pub fn iterate(a: Args) Iterator { | ||
| 457 | return .init(a); | ||
| 458 | } | ||
| 459 | |||
| 460 | /// You must deinitialize iterator's internal buffers by calling `deinit` when | ||
| 461 | /// done. | ||
| 462 | pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator { | ||
| 463 | return .initAllocator(a, gpa); | ||
| 464 | } | ||
| 465 | |||
| 466 | pub const ToSliceError = Iterator.Windows.InitError || Iterator.Wasi.InitError; | ||
| 467 | |||
| 468 | /// Returned value may reference several allocations and may point into `a`. | ||
| 469 | /// Thefore, an arena-style allocator must be used. | ||
| 470 | /// | ||
| 471 | /// * On Windows, the result is encoded as | ||
| 472 | /// [WTF-8](https://wtf-8.codeberg.page/). | ||
| 473 | /// * On other platforms, the result is an opaque sequence of bytes with no | ||
| 474 | /// particular encoding. | ||
| 475 | /// | ||
| 476 | /// See also: | ||
| 477 | /// * `iterate` | ||
| 478 | /// * `iterateAllocator` | ||
| 479 | pub fn toSlice(a: Args, arena: Allocator) ToSliceError![]const [:0]const u8 { | ||
| 480 | if (native_os == .windows) { | ||
| 481 | var it = try a.iterateAllocator(arena); | ||
| 482 | var contents: std.ArrayList(u8) = .empty; | ||
| 483 | var slice_list: std.ArrayList(usize) = .empty; | ||
| 484 | while (it.next()) |arg| { | ||
| 485 | try contents.appendSlice(arena, arg[0 .. arg.len + 1]); | ||
| 486 | try slice_list.append(arena, arg.len); | ||
| 487 | } | ||
| 488 | const contents_slice = contents.items; | ||
| 489 | const slice_sizes = slice_list.items; | ||
| 490 | const slice_list_bytes = std.math.mul(usize, @sizeOf([]u8), slice_sizes.len) catch return error.OutOfMemory; | ||
| 491 | const total_bytes = std.math.add(usize, slice_list_bytes, contents_slice.len) catch return error.OutOfMemory; | ||
| 492 | const buf = try arena.alignedAlloc(u8, .of([]u8), total_bytes); | ||
| 493 | errdefer arena.free(buf); | ||
| 494 | |||
| 495 | const result_slice_list = std.mem.bytesAsSlice([:0]u8, buf[0..slice_list_bytes]); | ||
| 496 | const result_contents = buf[slice_list_bytes..]; | ||
| 497 | @memcpy(result_contents[0..contents_slice.len], contents_slice); | ||
| 498 | |||
| 499 | var contents_index: usize = 0; | ||
| 500 | for (slice_sizes, 0..) |len, i| { | ||
| 501 | const new_index = contents_index + len; | ||
| 502 | result_slice_list[i] = result_contents[contents_index..new_index :0]; | ||
| 503 | contents_index = new_index + 1; | ||
| 504 | } | ||
| 505 | |||
| 506 | return result_slice_list; | ||
| 507 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 508 | var count: usize = undefined; | ||
| 509 | var buf_size: usize = undefined; | ||
| 510 | |||
| 511 | switch (std.os.wasi.args_sizes_get(&count, &buf_size)) { | ||
| 512 | .SUCCESS => {}, | ||
| 513 | else => |err| return std.posix.unexpectedErrno(err), | ||
| 514 | } | ||
| 515 | |||
| 516 | if (count == 0) return &.{}; | ||
| 517 | |||
| 518 | const argv = try arena.alloc([*:0]u8, count); | ||
| 519 | const argv_buf = try arena.alloc(u8, buf_size); | ||
| 520 | |||
| 521 | switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) { | ||
| 522 | .SUCCESS => {}, | ||
| 523 | else => |err| return std.posix.unexpectedErrno(err), | ||
| 524 | } | ||
| 525 | |||
| 526 | const args = try arena.alloc([:0]const u8, count); | ||
| 527 | for (args, argv) |*dst, src| dst.* = std.mem.sliceTo(src, 0); | ||
| 528 | return args; | ||
| 529 | } else { | ||
| 530 | const args = try arena.alloc([:0]const u8, a.vector.len); | ||
| 531 | for (args, a.vector) |*dst, src| dst.* = std.mem.sliceTo(src, 0); | ||
| 532 | return args; | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | test "Iterator.Windows" { | ||
| 537 | const t = testIteratorWindows; | ||
| 538 | |||
| 539 | try t( | ||
| 540 | \\"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\")" | ||
| 541 | , &.{ | ||
| 542 | \\C:\Program Files\zig\zig.exe | ||
| 543 | , | ||
| 544 | \\run | ||
| 545 | , | ||
| 546 | \\.\src\main.zig | ||
| 547 | , | ||
| 548 | \\-target | ||
| 549 | , | ||
| 550 | \\x86_64-windows-gnu | ||
| 551 | , | ||
| 552 | \\-O | ||
| 553 | , | ||
| 554 | \\ReleaseSafe | ||
| 555 | , | ||
| 556 | \\-- | ||
| 557 | , | ||
| 558 | \\--emoji=🗿 | ||
| 559 | , | ||
| 560 | \\--eval=new Regex("Dwayne \"The Rock\" Johnson") | ||
| 561 | , | ||
| 562 | }); | ||
| 563 | |||
| 564 | // Empty | ||
| 565 | try t("", &.{}); | ||
| 566 | |||
| 567 | // Separators | ||
| 568 | try t("aa bb cc", &.{ "aa", "bb", "cc" }); | ||
| 569 | try t("aa\tbb\tcc", &.{ "aa", "bb", "cc" }); | ||
| 570 | try t("aa\nbb\ncc", &.{"aa\nbb\ncc"}); | ||
| 571 | try t("aa\r\nbb\r\ncc", &.{"aa\r\nbb\r\ncc"}); | ||
| 572 | try t("aa\rbb\rcc", &.{"aa\rbb\rcc"}); | ||
| 573 | try t("aa\x07bb\x07cc", &.{"aa\x07bb\x07cc"}); | ||
| 574 | try t("aa\x7Fbb\x7Fcc", &.{"aa\x7Fbb\x7Fcc"}); | ||
| 575 | try t("aa🦎bb🦎cc", &.{"aa🦎bb🦎cc"}); | ||
| 576 | |||
| 577 | // Leading/trailing whitespace | ||
| 578 | try t(" ", &.{""}); | ||
| 579 | try t(" aa bb ", &.{ "", "aa", "bb" }); | ||
| 580 | try t("\t\t", &.{""}); | ||
| 581 | try t("\t\taa\t\tbb\t\t", &.{ "", "aa", "bb" }); | ||
| 582 | try t("\n\n", &.{"\n\n"}); | ||
| 583 | try t("\n\naa\n\nbb\n\n", &.{"\n\naa\n\nbb\n\n"}); | ||
| 584 | |||
| 585 | // Executable name with quotes/backslashes | ||
| 586 | try t("\"aa bb\tcc\ndd\"", &.{"aa bb\tcc\ndd"}); | ||
| 587 | try t("\"", &.{""}); | ||
| 588 | try t("\"\"", &.{""}); | ||
| 589 | try t("\"\"\"", &.{""}); | ||
| 590 | try t("\"\"\"\"", &.{""}); | ||
| 591 | try t("\"\"\"\"\"", &.{""}); | ||
| 592 | try t("aa\"bb\"cc\"dd", &.{"aabbccdd"}); | ||
| 593 | try t("aa\"bb cc\"dd", &.{"aabb ccdd"}); | ||
| 594 | try t("\"aa\\\"bb\"", &.{"aa\\bb"}); | ||
| 595 | try t("\"aa\\\\\"", &.{"aa\\\\"}); | ||
| 596 | try t("aa\\\"bb", &.{"aa\\bb"}); | ||
| 597 | try t("aa\\\\\"bb", &.{"aa\\\\bb"}); | ||
| 598 | |||
| 599 | // Arguments with quotes/backslashes | ||
| 600 | try t(". \"aa bb\tcc\ndd\"", &.{ ".", "aa bb\tcc\ndd" }); | ||
| 601 | try t(". aa\" \"bb\"\t\"cc\"\n\"dd\"", &.{ ".", "aa bb\tcc\ndd" }); | ||
| 602 | try t(". ", &.{"."}); | ||
| 603 | try t(". \"", &.{ ".", "" }); | ||
| 604 | try t(". \"\"", &.{ ".", "" }); | ||
| 605 | try t(". \"\"\"", &.{ ".", "\"" }); | ||
| 606 | try t(". \"\"\"\"", &.{ ".", "\"" }); | ||
| 607 | try t(". \"\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 608 | try t(". \"\"\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 609 | try t(". \" \"", &.{ ".", " " }); | ||
| 610 | try t(". \" \"\"", &.{ ".", " \"" }); | ||
| 611 | try t(". \" \"\"\"", &.{ ".", " \"" }); | ||
| 612 | try t(". \" \"\"\"\"", &.{ ".", " \"\"" }); | ||
| 613 | try t(". \" \"\"\"\"\"", &.{ ".", " \"\"" }); | ||
| 614 | try t(". \" \"\"\"\"\"\"", &.{ ".", " \"\"\"" }); | ||
| 615 | try t(". \\\"", &.{ ".", "\"" }); | ||
| 616 | try t(". \\\"\"", &.{ ".", "\"" }); | ||
| 617 | try t(". \\\"\"\"", &.{ ".", "\"" }); | ||
| 618 | try t(". \\\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 619 | try t(". \\\"\"\"\"\"", &.{ ".", "\"\"" }); | ||
| 620 | try t(". \\\"\"\"\"\"\"", &.{ ".", "\"\"\"" }); | ||
| 621 | try t(". \" \\\"", &.{ ".", " \"" }); | ||
| 622 | try t(". \" \\\"\"", &.{ ".", " \"" }); | ||
| 623 | try t(". \" \\\"\"\"", &.{ ".", " \"\"" }); | ||
| 624 | try t(". \" \\\"\"\"\"", &.{ ".", " \"\"" }); | ||
| 625 | try t(". \" \\\"\"\"\"\"", &.{ ".", " \"\"\"" }); | ||
| 626 | try t(". \" \\\"\"\"\"\"\"", &.{ ".", " \"\"\"" }); | ||
| 627 | try t(". aa\\bb\\\\cc\\\\\\dd", &.{ ".", "aa\\bb\\\\cc\\\\\\dd" }); | ||
| 628 | try t(". \\\\\\\"aa bb\"", &.{ ".", "\\\"aa", "bb" }); | ||
| 629 | try t(". \\\\\\\\\"aa bb\"", &.{ ".", "\\\\aa bb" }); | ||
| 630 | |||
| 631 | // From https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args#results-of-parsing-command-lines | ||
| 632 | try t( | ||
| 633 | \\foo.exe "abc" d e | ||
| 634 | , &.{ "foo.exe", "abc", "d", "e" }); | ||
| 635 | try t( | ||
| 636 | \\foo.exe a\\b d"e f"g h | ||
| 637 | , &.{ "foo.exe", "a\\\\b", "de fg", "h" }); | ||
| 638 | try t( | ||
| 639 | \\foo.exe a\\\"b c d | ||
| 640 | , &.{ "foo.exe", "a\\\"b", "c", "d" }); | ||
| 641 | try t( | ||
| 642 | \\foo.exe a\\\\"b c" d e | ||
| 643 | , &.{ "foo.exe", "a\\\\b c", "d", "e" }); | ||
| 644 | try t( | ||
| 645 | \\foo.exe a"b"" c d | ||
| 646 | , &.{ "foo.exe", "ab\" c d" }); | ||
| 647 | |||
| 648 | // From https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESEX | ||
| 649 | try t("foo.exe CallMeIshmael", &.{ "foo.exe", "CallMeIshmael" }); | ||
| 650 | try t("foo.exe \"Call Me Ishmael\"", &.{ "foo.exe", "Call Me Ishmael" }); | ||
| 651 | try t("foo.exe Cal\"l Me I\"shmael", &.{ "foo.exe", "Call Me Ishmael" }); | ||
| 652 | try t("foo.exe CallMe\\\"Ishmael", &.{ "foo.exe", "CallMe\"Ishmael" }); | ||
| 653 | try t("foo.exe \"CallMe\\\"Ishmael\"", &.{ "foo.exe", "CallMe\"Ishmael" }); | ||
| 654 | try t("foo.exe \"Call Me Ishmael\\\\\"", &.{ "foo.exe", "Call Me Ishmael\\" }); | ||
| 655 | try t("foo.exe \"CallMe\\\\\\\"Ishmael\"", &.{ "foo.exe", "CallMe\\\"Ishmael" }); | ||
| 656 | try t("foo.exe a\\\\\\b", &.{ "foo.exe", "a\\\\\\b" }); | ||
| 657 | try t("foo.exe \"a\\\\\\b\"", &.{ "foo.exe", "a\\\\\\b" }); | ||
| 658 | |||
| 659 | // Surrogate pair encoding of 𐐷 separated by quotes. | ||
| 660 | // Encoded as WTF-16: | ||
| 661 | // "<0xD801>"<0xDC37> | ||
| 662 | // Encoded as WTF-8: | ||
| 663 | // "<0xED><0xA0><0x81>"<0xED><0xB0><0xB7> | ||
| 664 | // During parsing, the quotes drop out and the surrogate pair | ||
| 665 | // should end up encoded as its normal UTF-8 representation. | ||
| 666 | try t("foo.exe \"\xed\xa0\x81\"\xed\xb0\xb7", &.{ "foo.exe", "𐐷" }); | ||
| 667 | } | ||
| 668 | |||
| 669 | fn testIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void { | ||
| 670 | const cmd_line_w = try std.unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line); | ||
| 671 | defer testing.allocator.free(cmd_line_w); | ||
| 672 | |||
| 673 | // next | ||
| 674 | { | ||
| 675 | var it = try Iterator.Windows.init(testing.allocator, cmd_line_w); | ||
| 676 | defer it.deinit(); | ||
| 677 | |||
| 678 | for (expected_args) |expected| { | ||
| 679 | if (it.next()) |actual| { | ||
| 680 | try testing.expectEqualStrings(expected, actual); | ||
| 681 | } else { | ||
| 682 | return error.TestUnexpectedResult; | ||
| 683 | } | ||
| 684 | } | ||
| 685 | try testing.expect(it.next() == null); | ||
| 686 | } | ||
| 687 | |||
| 688 | // skip | ||
| 689 | { | ||
| 690 | var it = try Iterator.Windows.init(testing.allocator, cmd_line_w); | ||
| 691 | defer it.deinit(); | ||
| 692 | |||
| 693 | for (0..expected_args.len) |_| { | ||
| 694 | try testing.expect(it.skip()); | ||
| 695 | } | ||
| 696 | try testing.expect(!it.skip()); | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | test "general parsing" { | ||
| 701 | try testGeneralCmdLine("a b\tc d", &.{ "a", "b", "c", "d" }); | ||
| 702 | try testGeneralCmdLine("\"abc\" d e", &.{ "abc", "d", "e" }); | ||
| 703 | try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &.{ "a\\\\\\b", "de fg", "h" }); | ||
| 704 | try testGeneralCmdLine("a\\\\\\\"b c d", &.{ "a\\\"b", "c", "d" }); | ||
| 705 | try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &.{ "a\\\\b c", "d", "e" }); | ||
| 706 | try testGeneralCmdLine("a b\tc \"d f", &.{ "a", "b", "c", "d f" }); | ||
| 707 | try testGeneralCmdLine("j k l\\", &.{ "j", "k", "l\\" }); | ||
| 708 | try testGeneralCmdLine("\"\" x y z\\\\", &.{ "", "x", "y", "z\\\\" }); | ||
| 709 | |||
| 710 | try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &.{ | ||
| 711 | ".\\..\\zig-cache\\build", | ||
| 712 | "bin\\zig.exe", | ||
| 713 | ".\\..", | ||
| 714 | ".\\..\\zig-cache", | ||
| 715 | "--help", | ||
| 716 | }); | ||
| 717 | |||
| 718 | try testGeneralCmdLine( | ||
| 719 | \\ 'foo' "bar" | ||
| 720 | , &.{ "'foo'", "bar" }); | ||
| 721 | } | ||
| 722 | |||
| 723 | fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { | ||
| 724 | var it = try IteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line); | ||
| 725 | defer it.deinit(); | ||
| 726 | for (expected_args) |expected_arg| { | ||
| 727 | const arg = it.next().?; | ||
| 728 | try testing.expectEqualStrings(expected_arg, arg); | ||
| 729 | } | ||
| 730 | try testing.expect(it.next() == null); | ||
| 731 | } | ||
| 732 | |||
| 733 | /// Optional parameters for `IteratorGeneral` | ||
| 734 | pub const IteratorGeneralOptions = struct { | ||
| 735 | comments: bool = false, | ||
| 736 | single_quotes: bool = false, | ||
| 737 | }; | ||
| 738 | |||
| 739 | /// A general Iterator to parse a string into a set of arguments | ||
| 740 | pub fn IteratorGeneral(comptime options: IteratorGeneralOptions) type { | ||
| 741 | return struct { | ||
| 742 | allocator: Allocator, | ||
| 743 | index: usize = 0, | ||
| 744 | cmd_line: []const u8, | ||
| 745 | |||
| 746 | /// Should the cmd_line field be free'd (using the allocator) on deinit()? | ||
| 747 | free_cmd_line_on_deinit: bool, | ||
| 748 | |||
| 749 | /// buffer MUST be long enough to hold the cmd_line plus a null terminator. | ||
| 750 | /// buffer will we free'd (using the allocator) on deinit() | ||
| 751 | buffer: []u8, | ||
| 752 | start: usize = 0, | ||
| 753 | end: usize = 0, | ||
| 754 | |||
| 755 | pub const Self = @This(); | ||
| 756 | |||
| 757 | pub const InitError = error{OutOfMemory}; | ||
| 758 | |||
| 759 | /// cmd_line_utf8 MUST remain valid and constant while using this instance | ||
| 760 | pub fn init(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self { | ||
| 761 | const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1); | ||
| 762 | errdefer allocator.free(buffer); | ||
| 763 | |||
| 764 | return Self{ | ||
| 765 | .allocator = allocator, | ||
| 766 | .cmd_line = cmd_line_utf8, | ||
| 767 | .free_cmd_line_on_deinit = false, | ||
| 768 | .buffer = buffer, | ||
| 769 | }; | ||
| 770 | } | ||
| 771 | |||
| 772 | /// cmd_line_utf8 will be free'd (with the allocator) on deinit() | ||
| 773 | pub fn initTakeOwnership(allocator: Allocator, cmd_line_utf8: []const u8) InitError!Self { | ||
| 774 | const buffer = try allocator.alloc(u8, cmd_line_utf8.len + 1); | ||
| 775 | errdefer allocator.free(buffer); | ||
| 776 | |||
| 777 | return Self{ | ||
| 778 | .allocator = allocator, | ||
| 779 | .cmd_line = cmd_line_utf8, | ||
| 780 | .free_cmd_line_on_deinit = true, | ||
| 781 | .buffer = buffer, | ||
| 782 | }; | ||
| 783 | } | ||
| 784 | |||
| 785 | // Skips over whitespace in the cmd_line. | ||
| 786 | // Returns false if the terminating sentinel is reached, true otherwise. | ||
| 787 | // Also skips over comments (if supported). | ||
| 788 | fn skipWhitespace(self: *Self) bool { | ||
| 789 | while (true) : (self.index += 1) { | ||
| 790 | const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; | ||
| 791 | switch (character) { | ||
| 792 | 0 => return false, | ||
| 793 | ' ', '\t', '\r', '\n' => continue, | ||
| 794 | '#' => { | ||
| 795 | if (options.comments) { | ||
| 796 | while (true) : (self.index += 1) { | ||
| 797 | switch (self.cmd_line[self.index]) { | ||
| 798 | '\n' => break, | ||
| 799 | 0 => return false, | ||
| 800 | else => continue, | ||
| 801 | } | ||
| 802 | } | ||
| 803 | continue; | ||
| 804 | } else { | ||
| 805 | break; | ||
| 806 | } | ||
| 807 | }, | ||
| 808 | else => break, | ||
| 809 | } | ||
| 810 | } | ||
| 811 | return true; | ||
| 812 | } | ||
| 813 | |||
| 814 | pub fn skip(self: *Self) bool { | ||
| 815 | if (!self.skipWhitespace()) { | ||
| 816 | return false; | ||
| 817 | } | ||
| 818 | |||
| 819 | var backslash_count: usize = 0; | ||
| 820 | var in_quote = false; | ||
| 821 | while (true) : (self.index += 1) { | ||
| 822 | const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; | ||
| 823 | switch (character) { | ||
| 824 | 0 => return true, | ||
| 825 | '"', '\'' => { | ||
| 826 | if (!options.single_quotes and character == '\'') { | ||
| 827 | backslash_count = 0; | ||
| 828 | continue; | ||
| 829 | } | ||
| 830 | const quote_is_real = backslash_count % 2 == 0; | ||
| 831 | if (quote_is_real) { | ||
| 832 | in_quote = !in_quote; | ||
| 833 | } | ||
| 834 | }, | ||
| 835 | '\\' => { | ||
| 836 | backslash_count += 1; | ||
| 837 | }, | ||
| 838 | ' ', '\t', '\r', '\n' => { | ||
| 839 | if (!in_quote) { | ||
| 840 | return true; | ||
| 841 | } | ||
| 842 | backslash_count = 0; | ||
| 843 | }, | ||
| 844 | else => { | ||
| 845 | backslash_count = 0; | ||
| 846 | continue; | ||
| 847 | }, | ||
| 848 | } | ||
| 849 | } | ||
| 850 | } | ||
| 851 | |||
| 852 | /// Returns a slice of the internal buffer that contains the next argument. | ||
| 853 | /// Returns null when it reaches the end. | ||
| 854 | pub fn next(self: *Self) ?[:0]const u8 { | ||
| 855 | if (!self.skipWhitespace()) { | ||
| 856 | return null; | ||
| 857 | } | ||
| 858 | |||
| 859 | var backslash_count: usize = 0; | ||
| 860 | var in_quote = false; | ||
| 861 | while (true) : (self.index += 1) { | ||
| 862 | const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; | ||
| 863 | switch (character) { | ||
| 864 | 0 => { | ||
| 865 | self.emitBackslashes(backslash_count); | ||
| 866 | self.buffer[self.end] = 0; | ||
| 867 | const token = self.buffer[self.start..self.end :0]; | ||
| 868 | self.end += 1; | ||
| 869 | self.start = self.end; | ||
| 870 | return token; | ||
| 871 | }, | ||
| 872 | '"', '\'' => { | ||
| 873 | if (!options.single_quotes and character == '\'') { | ||
| 874 | self.emitBackslashes(backslash_count); | ||
| 875 | backslash_count = 0; | ||
| 876 | self.emitCharacter(character); | ||
| 877 | continue; | ||
| 878 | } | ||
| 879 | const quote_is_real = backslash_count % 2 == 0; | ||
| 880 | self.emitBackslashes(backslash_count / 2); | ||
| 881 | backslash_count = 0; | ||
| 882 | |||
| 883 | if (quote_is_real) { | ||
| 884 | in_quote = !in_quote; | ||
| 885 | } else { | ||
| 886 | self.emitCharacter('"'); | ||
| 887 | } | ||
| 888 | }, | ||
| 889 | '\\' => { | ||
| 890 | backslash_count += 1; | ||
| 891 | }, | ||
| 892 | ' ', '\t', '\r', '\n' => { | ||
| 893 | self.emitBackslashes(backslash_count); | ||
| 894 | backslash_count = 0; | ||
| 895 | if (in_quote) { | ||
| 896 | self.emitCharacter(character); | ||
| 897 | } else { | ||
| 898 | self.buffer[self.end] = 0; | ||
| 899 | const token = self.buffer[self.start..self.end :0]; | ||
| 900 | self.end += 1; | ||
| 901 | self.start = self.end; | ||
| 902 | return token; | ||
| 903 | } | ||
| 904 | }, | ||
| 905 | else => { | ||
| 906 | self.emitBackslashes(backslash_count); | ||
| 907 | backslash_count = 0; | ||
| 908 | self.emitCharacter(character); | ||
| 909 | }, | ||
| 910 | } | ||
| 911 | } | ||
| 912 | } | ||
| 913 | |||
| 914 | fn emitBackslashes(self: *Self, emit_count: usize) void { | ||
| 915 | var i: usize = 0; | ||
| 916 | while (i < emit_count) : (i += 1) { | ||
| 917 | self.emitCharacter('\\'); | ||
| 918 | } | ||
| 919 | } | ||
| 920 | |||
| 921 | fn emitCharacter(self: *Self, char: u8) void { | ||
| 922 | self.buffer[self.end] = char; | ||
| 923 | self.end += 1; | ||
| 924 | } | ||
| 925 | |||
| 926 | /// Call to free the internal buffer of the iterator. | ||
| 927 | pub fn deinit(self: *Self) void { | ||
| 928 | self.allocator.free(self.buffer); | ||
| 929 | |||
| 930 | if (self.free_cmd_line_on_deinit) { | ||
| 931 | self.allocator.free(self.cmd_line); | ||
| 932 | } | ||
| 933 | } | ||
| 934 | }; | ||
| 935 | } | ||
| 936 | |||
| 937 | test "response file arg parsing" { | ||
| 938 | try testResponseFileCmdLine( | ||
| 939 | \\a b | ||
| 940 | \\c d\ | ||
| 941 | , &.{ "a", "b", "c", "d\\" }); | ||
| 942 | try testResponseFileCmdLine("a b c d\\", &.{ "a", "b", "c", "d\\" }); | ||
| 943 | |||
| 944 | try testResponseFileCmdLine( | ||
| 945 | \\j | ||
| 946 | \\ k l # this is a comment \\ \\\ \\\\ "none" "\\" "\\\" | ||
| 947 | \\ "m" #another comment | ||
| 948 | \\ | ||
| 949 | , &.{ "j", "k", "l", "m" }); | ||
| 950 | |||
| 951 | try testResponseFileCmdLine( | ||
| 952 | \\ "" q "" | ||
| 953 | \\ "r s # t" "u\" v" #another comment | ||
| 954 | \\ | ||
| 955 | , &.{ "", "q", "", "r s # t", "u\" v" }); | ||
| 956 | |||
| 957 | try testResponseFileCmdLine( | ||
| 958 | \\ -l"advapi32" a# b#c d# | ||
| 959 | \\e\\\ | ||
| 960 | , &.{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" }); | ||
| 961 | |||
| 962 | try testResponseFileCmdLine( | ||
| 963 | \\ 'foo' "bar" | ||
| 964 | , &.{ "foo", "bar" }); | ||
| 965 | } | ||
| 966 | |||
| 967 | fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { | ||
| 968 | var it = try IteratorGeneral(.{ .comments = true, .single_quotes = true }) | ||
| 969 | .init(std.testing.allocator, input_cmd_line); | ||
| 970 | defer it.deinit(); | ||
| 971 | for (expected_args) |expected_arg| { | ||
| 972 | const arg = it.next().?; | ||
| 973 | try testing.expectEqualStrings(expected_arg, arg); | ||
| 974 | } | ||
| 975 | try testing.expect(it.next() == null); | ||
| 976 | } | ||
lib/std/process/Child.zig+39-1760| ... | @@ -5,116 +5,38 @@ const native_os = builtin.os.tag; | ... | @@ -5,116 +5,38 @@ const native_os = builtin.os.tag; |
| 5 | 5 | ||
| 6 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const Io = std.Io; | 7 | const Io = std.Io; |
| 8 | const unicode = std.unicode; | ||
| 9 | const fs = std.fs; | ||
| 10 | const process = std.process; | 8 | const process = std.process; |
| 11 | const File = std.Io.File; | 9 | const File = std.Io.File; |
| 12 | const windows = std.os.windows; | ||
| 13 | const linux = std.os.linux; | ||
| 14 | const posix = std.posix; | ||
| 15 | const mem = std.mem; | ||
| 16 | const EnvMap = std.process.EnvMap; | ||
| 17 | const maxInt = std.math.maxInt; | ||
| 18 | const assert = std.debug.assert; | 10 | const assert = std.debug.assert; |
| 19 | const Allocator = std.mem.Allocator; | 11 | const Allocator = std.mem.Allocator; |
| 20 | const ArrayList = std.ArrayList; | 12 | const ArrayList = std.ArrayList; |
| 21 | 13 | ||
| 22 | pub const Id = switch (native_os) { | 14 | pub const Id = switch (native_os) { |
| 23 | .windows => windows.HANDLE, | 15 | .windows => std.os.windows.HANDLE, |
| 24 | .wasi => void, | 16 | .wasi => void, |
| 25 | else => posix.pid_t, | 17 | else => std.posix.pid_t, |
| 26 | }; | 18 | }; |
| 27 | 19 | ||
| 28 | /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`. | 20 | /// After `wait` or `kill` is called, this becomes `null`. |
| 29 | /// On Windows this is the hProcess. | 21 | /// On Windows this is the hProcess. |
| 30 | /// On POSIX this is the pid. | 22 | /// On POSIX this is the pid. |
| 31 | id: Id, | 23 | id: ?Id, |
| 32 | thread_handle: if (native_os == .windows) windows.HANDLE else void, | 24 | thread_handle: if (native_os == .windows) std.os.windows.HANDLE else void, |
| 33 | |||
| 34 | allocator: Allocator, | ||
| 35 | |||
| 36 | /// The writing end of the child process's standard input pipe. | 25 | /// The writing end of the child process's standard input pipe. |
| 37 | /// Usage requires `stdin_behavior == StdIo.Pipe`. | 26 | /// Usage requires `process.SpawnOptions.StdIo.pipe`. |
| 38 | /// Available after calling `spawn()`. | ||
| 39 | stdin: ?File, | 27 | stdin: ?File, |
| 40 | |||
| 41 | /// The reading end of the child process's standard output pipe. | 28 | /// The reading end of the child process's standard output pipe. |
| 42 | /// Usage requires `stdout_behavior == StdIo.Pipe`. | 29 | /// Usage requires `process.SpawnOptions.StdIo.pipe`. |
| 43 | /// Available after calling `spawn()`. | ||
| 44 | stdout: ?File, | 30 | stdout: ?File, |
| 45 | |||
| 46 | /// The reading end of the child process's standard error pipe. | 31 | /// The reading end of the child process's standard error pipe. |
| 47 | /// Usage requires `stderr_behavior == StdIo.Pipe`. | 32 | /// Usage requires `process.SpawnOptions.StdIo.pipe`. |
| 48 | /// Available after calling `spawn()`. | ||
| 49 | stderr: ?File, | 33 | stderr: ?File, |
| 50 | |||
| 51 | /// Terminated state of the child process. | ||
| 52 | /// Available after calling `wait()`. | ||
| 53 | term: ?(SpawnError!Term), | ||
| 54 | |||
| 55 | argv: []const []const u8, | ||
| 56 | |||
| 57 | /// Leave as null to use the current env map using the supplied allocator. | ||
| 58 | /// Required if unable to access the current env map (e.g. building a library on | ||
| 59 | /// some platforms). | ||
| 60 | env_map: ?*const EnvMap, | ||
| 61 | |||
| 62 | stdin_behavior: StdIo, | ||
| 63 | stdout_behavior: StdIo, | ||
| 64 | stderr_behavior: StdIo, | ||
| 65 | |||
| 66 | /// Set to change the user id when spawning the child process. | ||
| 67 | uid: if (native_os == .windows or native_os == .wasi) void else ?posix.uid_t, | ||
| 68 | |||
| 69 | /// Set to change the group id when spawning the child process. | ||
| 70 | gid: if (native_os == .windows or native_os == .wasi) void else ?posix.gid_t, | ||
| 71 | |||
| 72 | /// Set to change the process group id when spawning the child process. | ||
| 73 | pgid: if (native_os == .windows or native_os == .wasi) void else ?posix.pid_t, | ||
| 74 | |||
| 75 | /// Set to change the current working directory when spawning the child process. | ||
| 76 | cwd: ?[]const u8, | ||
| 77 | /// Set to change the current working directory when spawning the child process. | ||
| 78 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 | ||
| 79 | /// Once that is done, `cwd` will be deprecated in favor of this field. | ||
| 80 | cwd_dir: ?Io.Dir = null, | ||
| 81 | |||
| 82 | err_pipe: if (native_os == .windows) void else ?posix.fd_t, | ||
| 83 | |||
| 84 | expand_arg0: Arg0Expand, | ||
| 85 | |||
| 86 | /// Darwin-only. Disable ASLR for the child process. | ||
| 87 | disable_aslr: bool = false, | ||
| 88 | |||
| 89 | /// Start child process in suspended state. | ||
| 90 | /// For Posix systems it's started as if SIGSTOP was sent. | ||
| 91 | start_suspended: bool = false, | ||
| 92 | |||
| 93 | /// Windows-only. Sets the CREATE_NO_WINDOW flag in CreateProcess. | ||
| 94 | create_no_window: bool = false, | ||
| 95 | |||
| 96 | /// Set to true to obtain rusage information for the child process. | ||
| 97 | /// Depending on the target platform and implementation status, the | ||
| 98 | /// requested statistics may or may not be available. If they are | ||
| 99 | /// available, then the `resource_usage_statistics` field will be populated | ||
| 100 | /// after calling `wait`. | ||
| 101 | /// On Linux and Darwin, this obtains rusage statistics from wait4(). | ||
| 102 | request_resource_usage_statistics: bool = false, | ||
| 103 | |||
| 104 | /// This is available after calling wait if | 34 | /// This is available after calling wait if |
| 105 | /// `request_resource_usage_statistics` was set to `true` before calling | 35 | /// `request_resource_usage_statistics` was set to `true` before calling |
| 106 | /// `spawn`. | 36 | /// `spawn`. |
| 37 | /// TODO move this data into `Term` | ||
| 107 | resource_usage_statistics: ResourceUsageStatistics = .{}, | 38 | resource_usage_statistics: ResourceUsageStatistics = .{}, |
| 108 | 39 | request_resource_usage_statistics: bool, | |
| 109 | /// When populated, a pipe will be created for the child process to | ||
| 110 | /// communicate progress back to the parent. The file descriptor of the | ||
| 111 | /// write end of the pipe will be specified in the `ZIG_PROGRESS` | ||
| 112 | /// environment variable inside the child process. The progress reported by | ||
| 113 | /// the child will be attached to this progress node in the parent process. | ||
| 114 | /// | ||
| 115 | /// The child's progress tree will be grafted into the parent's progress tree, | ||
| 116 | /// by substituting this node with the child's root node. | ||
| 117 | progress_node: std.Progress.Node = std.Progress.Node.none, | ||
| 118 | 40 | ||
| 119 | pub const ResourceUsageStatistics = struct { | 41 | pub const ResourceUsageStatistics = struct { |
| 120 | rusage: @TypeOf(rusage_init) = rusage_init, | 42 | rusage: @TypeOf(rusage_init) = rusage_init, |
| ... | @@ -164,233 +86,60 @@ pub const ResourceUsageStatistics = struct { | ... | @@ -164,233 +86,60 @@ pub const ResourceUsageStatistics = struct { |
| 164 | .tvos, | 86 | .tvos, |
| 165 | .visionos, | 87 | .visionos, |
| 166 | .watchos, | 88 | .watchos, |
| 167 | => @as(?posix.rusage, null), | 89 | => @as(?std.posix.rusage, null), |
| 168 | .windows => @as(?windows.VM_COUNTERS, null), | 90 | .windows => @as(?std.os.windows.VM_COUNTERS, null), |
| 169 | else => {}, | 91 | else => {}, |
| 170 | }; | 92 | }; |
| 171 | }; | 93 | }; |
| 172 | 94 | ||
| 173 | pub const Arg0Expand = posix.Arg0Expand; | ||
| 174 | |||
| 175 | pub const SpawnError = error{ | ||
| 176 | OutOfMemory, | ||
| 177 | |||
| 178 | /// POSIX-only. `StdIo.Ignore` was selected and opening `/dev/null` returned ENODEV. | ||
| 179 | NoDevice, | ||
| 180 | |||
| 181 | /// Windows-only. `cwd` or `argv` was provided and it was invalid WTF-8. | ||
| 182 | /// https://wtf-8.codeberg.page/ | ||
| 183 | InvalidWtf8, | ||
| 184 | |||
| 185 | /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process. | ||
| 186 | CurrentWorkingDirectoryUnlinked, | ||
| 187 | |||
| 188 | /// Windows-only. NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed | ||
| 189 | /// within arguments when executing a `.bat`/`.cmd` script. | ||
| 190 | /// - NUL/LF signifiies end of arguments, so anything afterwards | ||
| 191 | /// would be lost after execution. | ||
| 192 | /// - CR is stripped by `cmd.exe`, so any CR codepoints | ||
| 193 | /// would be lost after execution. | ||
| 194 | InvalidBatchScriptArg, | ||
| 195 | } || | ||
| 196 | posix.ExecveError || | ||
| 197 | posix.SetIdError || | ||
| 198 | posix.SetPgidError || | ||
| 199 | posix.ChangeCurDirError || | ||
| 200 | windows.CreateProcessError || | ||
| 201 | windows.GetProcessMemoryInfoError || | ||
| 202 | windows.WaitForSingleObjectError; | ||
| 203 | |||
| 204 | pub const Term = union(enum) { | 95 | pub const Term = union(enum) { |
| 205 | Exited: u8, | 96 | exited: u8, |
| 206 | Signal: u32, | 97 | signal: std.posix.SIG, |
| 207 | Stopped: u32, | 98 | stopped: u32, |
| 208 | Unknown: u32, | 99 | unknown: u32, |
| 209 | }; | ||
| 210 | |||
| 211 | /// Behavior of the child process's standard input, output, and error | ||
| 212 | /// streams. | ||
| 213 | pub const StdIo = enum { | ||
| 214 | /// Inherit the stream from the parent process. | ||
| 215 | Inherit, | ||
| 216 | |||
| 217 | /// Pass a null stream to the child process. | ||
| 218 | /// This is /dev/null on POSIX and NUL on Windows. | ||
| 219 | Ignore, | ||
| 220 | |||
| 221 | /// Create a pipe for the stream. | ||
| 222 | /// The corresponding field (`stdout`, `stderr`, or `stdin`) | ||
| 223 | /// will be assigned a `File` object that can be used | ||
| 224 | /// to read from or write to the pipe. | ||
| 225 | Pipe, | ||
| 226 | |||
| 227 | /// Close the stream after the child process spawns. | ||
| 228 | Close, | ||
| 229 | }; | 100 | }; |
| 230 | 101 | ||
| 231 | /// First argument in argv is the executable. | 102 | /// Requests for the operating system to forcibly terminate the child process, |
| 232 | pub fn init(argv: []const []const u8, allocator: Allocator) Child { | 103 | /// then blocks until it terminates, then cleans up all resources. |
| 233 | return .{ | 104 | /// |
| 234 | .allocator = allocator, | 105 | /// Idempotent and does nothing after `wait` returns. |
| 235 | .argv = argv, | 106 | /// |
| 236 | .id = undefined, | 107 | /// Uncancelable. Ignores unexpected errors from the operating system. |
| 237 | .thread_handle = undefined, | 108 | pub fn kill(child: *Child, io: Io) void { |
| 238 | .err_pipe = if (native_os == .windows) {} else null, | 109 | if (child.id == null) { |
| 239 | .term = null, | 110 | assert(child.stdin == null); |
| 240 | .env_map = null, | 111 | assert(child.stdout == null); |
| 241 | .cwd = null, | 112 | assert(child.stderr == null); |
| 242 | .uid = if (native_os == .windows or native_os == .wasi) {} else null, | ||
| 243 | .gid = if (native_os == .windows or native_os == .wasi) {} else null, | ||
| 244 | .pgid = if (native_os == .windows or native_os == .wasi) {} else null, | ||
| 245 | .stdin = null, | ||
| 246 | .stdout = null, | ||
| 247 | .stderr = null, | ||
| 248 | .stdin_behavior = .Inherit, | ||
| 249 | .stdout_behavior = .Inherit, | ||
| 250 | .stderr_behavior = .Inherit, | ||
| 251 | .expand_arg0 = .no_expand, | ||
| 252 | }; | ||
| 253 | } | ||
| 254 | |||
| 255 | pub fn setUserName(self: *Child, name: []const u8) !void { | ||
| 256 | const user_info = try process.getUserInfo(name); | ||
| 257 | self.uid = user_info.uid; | ||
| 258 | self.gid = user_info.gid; | ||
| 259 | } | ||
| 260 | |||
| 261 | /// On success must call `kill` or `wait`. | ||
| 262 | /// After spawning the `id` is available. | ||
| 263 | pub fn spawn(self: *Child, io: Io) SpawnError!void { | ||
| 264 | if (!process.can_spawn) { | ||
| 265 | @compileError("the target operating system cannot spawn processes"); | ||
| 266 | } | ||
| 267 | |||
| 268 | if (native_os == .windows) { | ||
| 269 | return self.spawnWindows(io); | ||
| 270 | } else { | ||
| 271 | return self.spawnPosix(io); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | pub fn spawnAndWait(child: *Child, io: Io) SpawnError!Term { | ||
| 276 | try child.spawn(io); | ||
| 277 | return child.wait(io); | ||
| 278 | } | ||
| 279 | |||
| 280 | /// Forcibly terminates child process and then cleans up all resources. | ||
| 281 | pub fn kill(self: *Child, io: Io) !Term { | ||
| 282 | if (native_os == .windows) { | ||
| 283 | return self.killWindows(io, 1); | ||
| 284 | } else { | ||
| 285 | return self.killPosix(io); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | pub fn killWindows(self: *Child, io: Io, exit_code: windows.UINT) !Term { | ||
| 290 | if (self.term) |term| { | ||
| 291 | self.cleanupStreams(io); | ||
| 292 | return term; | ||
| 293 | } | ||
| 294 | |||
| 295 | windows.TerminateProcess(self.id, exit_code) catch |err| switch (err) { | ||
| 296 | error.AccessDenied => { | ||
| 297 | // Usually when TerminateProcess triggers a ACCESS_DENIED error, it | ||
| 298 | // indicates that the process has already exited, but there may be | ||
| 299 | // some rare edge cases where our process handle no longer has the | ||
| 300 | // PROCESS_TERMINATE access right, so let's do another check to make | ||
| 301 | // sure the process is really no longer running: | ||
| 302 | windows.WaitForSingleObjectEx(self.id, 0, false) catch return err; | ||
| 303 | return error.AlreadyTerminated; | ||
| 304 | }, | ||
| 305 | else => return err, | ||
| 306 | }; | ||
| 307 | try self.waitUnwrappedWindows(io); | ||
| 308 | return self.term.?; | ||
| 309 | } | ||
| 310 | |||
| 311 | pub fn killPosix(self: *Child, io: Io) !Term { | ||
| 312 | if (self.term) |term| { | ||
| 313 | self.cleanupStreams(io); | ||
| 314 | return term; | ||
| 315 | } | ||
| 316 | posix.kill(self.id, posix.SIG.TERM) catch |err| switch (err) { | ||
| 317 | error.ProcessNotFound => return error.AlreadyTerminated, | ||
| 318 | else => return err, | ||
| 319 | }; | ||
| 320 | self.waitUnwrappedPosix(io); | ||
| 321 | return self.term.?; | ||
| 322 | } | ||
| 323 | |||
| 324 | pub const WaitError = SpawnError || std.os.windows.GetProcessMemoryInfoError; | ||
| 325 | |||
| 326 | /// On some targets, `spawn` may not report all spawn errors, such as `error.InvalidExe`. | ||
| 327 | /// This function will block until any spawn errors can be reported, and return them. | ||
| 328 | pub fn waitForSpawn(self: *Child) SpawnError!void { | ||
| 329 | if (native_os == .windows) return; // `spawn` reports everything | ||
| 330 | if (self.term) |term| { | ||
| 331 | _ = term catch |spawn_err| return spawn_err; | ||
| 332 | return; | 113 | return; |
| 333 | } | 114 | } |
| 334 | 115 | io.vtable.childKill(io.userdata, child); | |
| 335 | const err_pipe = self.err_pipe orelse return; | 116 | assert(child.id == null); |
| 336 | self.err_pipe = null; | ||
| 337 | // Wait for the child to report any errors in or before `execvpe`. | ||
| 338 | const report = readIntFd(err_pipe); | ||
| 339 | posix.close(err_pipe); | ||
| 340 | if (report) |child_err_int| { | ||
| 341 | const child_err: SpawnError = @errorCast(@errorFromInt(child_err_int)); | ||
| 342 | self.term = child_err; | ||
| 343 | return child_err; | ||
| 344 | } else |read_err| switch (read_err) { | ||
| 345 | error.EndOfStream => { | ||
| 346 | // Write end closed by CLOEXEC at the time of the `execvpe` call, | ||
| 347 | // indicating success. | ||
| 348 | }, | ||
| 349 | else => { | ||
| 350 | // Problem reading the error from the error reporting pipe. We | ||
| 351 | // don't know if the child is alive or dead. Better to assume it is | ||
| 352 | // alive so the resource does not risk being leaked. | ||
| 353 | }, | ||
| 354 | } | ||
| 355 | } | 117 | } |
| 356 | 118 | ||
| 119 | pub const WaitError = error{ | ||
| 120 | AccessDenied, | ||
| 121 | } || Io.Cancelable || Io.UnexpectedError; | ||
| 122 | |||
| 357 | /// Blocks until child process terminates and then cleans up all resources. | 123 | /// Blocks until child process terminates and then cleans up all resources. |
| 358 | pub fn wait(self: *Child, io: Io) WaitError!Term { | 124 | pub fn wait(child: *Child, io: Io) WaitError!Term { |
| 359 | try self.waitForSpawn(); // report spawn errors | 125 | assert(child.id != null); |
| 360 | if (self.term) |term| { | 126 | return io.vtable.childWait(io.userdata, child); |
| 361 | self.cleanupStreams(io); | ||
| 362 | return term; | ||
| 363 | } | ||
| 364 | switch (native_os) { | ||
| 365 | .windows => try self.waitUnwrappedWindows(io), | ||
| 366 | else => self.waitUnwrappedPosix(io), | ||
| 367 | } | ||
| 368 | self.id = undefined; | ||
| 369 | return self.term.?; | ||
| 370 | } | 127 | } |
| 371 | 128 | ||
| 372 | pub const RunResult = struct { | ||
| 373 | term: Term, | ||
| 374 | stdout: []u8, | ||
| 375 | stderr: []u8, | ||
| 376 | }; | ||
| 377 | |||
| 378 | /// Collect the output from the process's stdout and stderr. Will return once all output | 129 | /// Collect the output from the process's stdout and stderr. Will return once all output |
| 379 | /// has been collected. This does not mean that the process has ended. `wait` should still | 130 | /// has been collected. This does not mean that the process has ended. `wait` should still |
| 380 | /// be called to wait for and clean up the process. | 131 | /// be called to wait for and clean up the process. |
| 381 | /// | 132 | /// |
| 382 | /// The process must be started with stdout_behavior and stderr_behavior == .Pipe | 133 | /// The process must have been started with stdout and stderr set to |
| 134 | /// `process.SpawnOptions.StdIo.pipe`. | ||
| 383 | pub fn collectOutput( | 135 | pub fn collectOutput( |
| 384 | child: Child, | 136 | child: *const Child, |
| 385 | /// Used for `stdout` and `stderr`. | 137 | /// Used for `stdout` and `stderr`. |
| 386 | allocator: Allocator, | 138 | allocator: Allocator, |
| 387 | stdout: *ArrayList(u8), | 139 | stdout: *ArrayList(u8), |
| 388 | stderr: *ArrayList(u8), | 140 | stderr: *ArrayList(u8), |
| 389 | max_output_bytes: usize, | 141 | max_output_bytes: usize, |
| 390 | ) !void { | 142 | ) !void { |
| 391 | assert(child.stdout_behavior == .Pipe); | ||
| 392 | assert(child.stderr_behavior == .Pipe); | ||
| 393 | |||
| 394 | var poller = std.Io.poll(allocator, enum { stdout, stderr }, .{ | 143 | var poller = std.Io.poll(allocator, enum { stdout, stderr }, .{ |
| 395 | .stdout = child.stdout.?, | 144 | .stdout = child.stdout.?, |
| 396 | .stderr = child.stderr.?, | 145 | .stderr = child.stderr.?, |
| ... | @@ -427,1473 +176,3 @@ pub fn collectOutput( | ... | @@ -427,1473 +176,3 @@ pub fn collectOutput( |
| 427 | return error.StderrStreamTooLong; | 176 | return error.StderrStreamTooLong; |
| 428 | } | 177 | } |
| 429 | } | 178 | } |
| 430 | |||
| 431 | pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{ | ||
| 432 | StdoutStreamTooLong, | ||
| 433 | StderrStreamTooLong, | ||
| 434 | }; | ||
| 435 | |||
| 436 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. | ||
| 437 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. | ||
| 438 | pub fn run(allocator: Allocator, io: Io, args: struct { | ||
| 439 | argv: []const []const u8, | ||
| 440 | cwd: ?[]const u8 = null, | ||
| 441 | cwd_dir: ?Io.Dir = null, | ||
| 442 | /// Required if unable to access the current env map (e.g. building a | ||
| 443 | /// library on some platforms). | ||
| 444 | env_map: ?*const EnvMap = null, | ||
| 445 | max_output_bytes: usize = 50 * 1024, | ||
| 446 | expand_arg0: Arg0Expand = .no_expand, | ||
| 447 | progress_node: std.Progress.Node = std.Progress.Node.none, | ||
| 448 | }) RunError!RunResult { | ||
| 449 | var child = Child.init(args.argv, allocator); | ||
| 450 | child.stdin_behavior = .Ignore; | ||
| 451 | child.stdout_behavior = .Pipe; | ||
| 452 | child.stderr_behavior = .Pipe; | ||
| 453 | child.cwd = args.cwd; | ||
| 454 | child.cwd_dir = args.cwd_dir; | ||
| 455 | child.env_map = args.env_map; | ||
| 456 | child.expand_arg0 = args.expand_arg0; | ||
| 457 | child.progress_node = args.progress_node; | ||
| 458 | |||
| 459 | var stdout: ArrayList(u8) = .empty; | ||
| 460 | defer stdout.deinit(allocator); | ||
| 461 | var stderr: ArrayList(u8) = .empty; | ||
| 462 | defer stderr.deinit(allocator); | ||
| 463 | |||
| 464 | try child.spawn(io); | ||
| 465 | errdefer { | ||
| 466 | _ = child.kill(io) catch {}; | ||
| 467 | } | ||
| 468 | try child.collectOutput(allocator, &stdout, &stderr, args.max_output_bytes); | ||
| 469 | |||
| 470 | return .{ | ||
| 471 | .stdout = try stdout.toOwnedSlice(allocator), | ||
| 472 | .stderr = try stderr.toOwnedSlice(allocator), | ||
| 473 | .term = try child.wait(io), | ||
| 474 | }; | ||
| 475 | } | ||
| 476 | |||
| 477 | fn waitUnwrappedWindows(self: *Child, io: Io) WaitError!void { | ||
| 478 | const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false); | ||
| 479 | |||
| 480 | self.term = @as(SpawnError!Term, x: { | ||
| 481 | var exit_code: windows.DWORD = undefined; | ||
| 482 | if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) { | ||
| 483 | break :x Term{ .Unknown = 0 }; | ||
| 484 | } else { | ||
| 485 | break :x Term{ .Exited = @as(u8, @truncate(exit_code)) }; | ||
| 486 | } | ||
| 487 | }); | ||
| 488 | |||
| 489 | if (self.request_resource_usage_statistics) { | ||
| 490 | self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id); | ||
| 491 | } | ||
| 492 | |||
| 493 | posix.close(self.id); | ||
| 494 | posix.close(self.thread_handle); | ||
| 495 | self.cleanupStreams(io); | ||
| 496 | return result; | ||
| 497 | } | ||
| 498 | |||
| 499 | fn waitUnwrappedPosix(self: *Child, io: Io) void { | ||
| 500 | const res: posix.WaitPidResult = res: { | ||
| 501 | if (self.request_resource_usage_statistics) { | ||
| 502 | switch (native_os) { | ||
| 503 | .dragonfly, | ||
| 504 | .freebsd, | ||
| 505 | .netbsd, | ||
| 506 | .openbsd, | ||
| 507 | .illumos, | ||
| 508 | .linux, | ||
| 509 | .serenity, | ||
| 510 | .driverkit, | ||
| 511 | .ios, | ||
| 512 | .maccatalyst, | ||
| 513 | .macos, | ||
| 514 | .tvos, | ||
| 515 | .visionos, | ||
| 516 | .watchos, | ||
| 517 | => { | ||
| 518 | var ru: posix.rusage = undefined; | ||
| 519 | const res = posix.wait4(self.id, 0, &ru); | ||
| 520 | self.resource_usage_statistics.rusage = ru; | ||
| 521 | break :res res; | ||
| 522 | }, | ||
| 523 | else => {}, | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | break :res posix.waitpid(self.id, 0); | ||
| 528 | }; | ||
| 529 | const status = res.status; | ||
| 530 | self.cleanupStreams(io); | ||
| 531 | self.handleWaitResult(status); | ||
| 532 | } | ||
| 533 | |||
| 534 | fn handleWaitResult(self: *Child, status: u32) void { | ||
| 535 | self.term = statusToTerm(status); | ||
| 536 | } | ||
| 537 | |||
| 538 | fn cleanupStreams(self: *Child, io: Io) void { | ||
| 539 | if (self.stdin) |*stdin| { | ||
| 540 | stdin.close(io); | ||
| 541 | self.stdin = null; | ||
| 542 | } | ||
| 543 | if (self.stdout) |*stdout| { | ||
| 544 | stdout.close(io); | ||
| 545 | self.stdout = null; | ||
| 546 | } | ||
| 547 | if (self.stderr) |*stderr| { | ||
| 548 | stderr.close(io); | ||
| 549 | self.stderr = null; | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | fn statusToTerm(status: u32) Term { | ||
| 554 | return if (posix.W.IFEXITED(status)) | ||
| 555 | Term{ .Exited = posix.W.EXITSTATUS(status) } | ||
| 556 | else if (posix.W.IFSIGNALED(status)) | ||
| 557 | Term{ .Signal = posix.W.TERMSIG(status) } | ||
| 558 | else if (posix.W.IFSTOPPED(status)) | ||
| 559 | Term{ .Stopped = posix.W.STOPSIG(status) } | ||
| 560 | else | ||
| 561 | Term{ .Unknown = status }; | ||
| 562 | } | ||
| 563 | |||
| 564 | fn spawnPosix(self: *Child, io: Io) SpawnError!void { | ||
| 565 | // The child process does need to access (one end of) these pipes. However, | ||
| 566 | // we must initially set CLOEXEC to avoid a race condition. If another thread | ||
| 567 | // is racing to spawn a different child process, we don't want it to inherit | ||
| 568 | // these FDs in any scenario; that would mean that, for instance, calls to | ||
| 569 | // `poll` from the parent would not report the child's stdout as closing when | ||
| 570 | // expected, since the other child may retain a reference to the write end of | ||
| 571 | // the pipe. So, we create the pipes with CLOEXEC initially. After fork, we | ||
| 572 | // need to do something in the new child to make sure we preserve the reference | ||
| 573 | // we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it | ||
| 574 | // turns out, we `dup2` everything anyway, so there's no need! | ||
| 575 | const pipe_flags: posix.O = .{ .CLOEXEC = true }; | ||
| 576 | |||
| 577 | const stdin_pipe = if (self.stdin_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined; | ||
| 578 | errdefer if (self.stdin_behavior == .Pipe) { | ||
| 579 | destroyPipe(stdin_pipe); | ||
| 580 | }; | ||
| 581 | |||
| 582 | const stdout_pipe = if (self.stdout_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined; | ||
| 583 | errdefer if (self.stdout_behavior == .Pipe) { | ||
| 584 | destroyPipe(stdout_pipe); | ||
| 585 | }; | ||
| 586 | |||
| 587 | const stderr_pipe = if (self.stderr_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined; | ||
| 588 | errdefer if (self.stderr_behavior == .Pipe) { | ||
| 589 | destroyPipe(stderr_pipe); | ||
| 590 | }; | ||
| 591 | |||
| 592 | const any_ignore = (self.stdin_behavior == .Ignore or self.stdout_behavior == .Ignore or self.stderr_behavior == .Ignore); | ||
| 593 | const dev_null_fd = if (any_ignore) | ||
| 594 | posix.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) { | ||
| 595 | error.PathAlreadyExists => unreachable, | ||
| 596 | error.NoSpaceLeft => unreachable, | ||
| 597 | error.FileTooBig => unreachable, | ||
| 598 | error.DeviceBusy => unreachable, | ||
| 599 | error.FileLocksUnsupported => unreachable, | ||
| 600 | error.BadPathName => unreachable, // Windows-only | ||
| 601 | error.WouldBlock => unreachable, | ||
| 602 | error.NetworkNotFound => unreachable, // Windows-only | ||
| 603 | error.Canceled => unreachable, // temporarily in the posix error set | ||
| 604 | error.SharingViolation => unreachable, // Windows-only | ||
| 605 | error.PipeBusy => unreachable, // not a pipe | ||
| 606 | error.AntivirusInterference => unreachable, // Windows-only | ||
| 607 | else => |e| return e, | ||
| 608 | } | ||
| 609 | else | ||
| 610 | undefined; | ||
| 611 | defer { | ||
| 612 | if (any_ignore) posix.close(dev_null_fd); | ||
| 613 | } | ||
| 614 | |||
| 615 | const prog_pipe: [2]posix.fd_t = p: { | ||
| 616 | if (self.progress_node.index == .none) { | ||
| 617 | break :p .{ -1, -1 }; | ||
| 618 | } else { | ||
| 619 | // We use CLOEXEC for the same reason as in `pipe_flags`. | ||
| 620 | break :p try posix.pipe2(.{ .NONBLOCK = true, .CLOEXEC = true }); | ||
| 621 | } | ||
| 622 | }; | ||
| 623 | errdefer destroyPipe(prog_pipe); | ||
| 624 | |||
| 625 | var arena_allocator = std.heap.ArenaAllocator.init(self.allocator); | ||
| 626 | defer arena_allocator.deinit(); | ||
| 627 | const arena = arena_allocator.allocator(); | ||
| 628 | |||
| 629 | // The POSIX standard does not allow malloc() between fork() and execve(), | ||
| 630 | // and `self.allocator` may be a libc allocator. | ||
| 631 | // I have personally observed the child process deadlocking when it tries | ||
| 632 | // to call malloc() due to a heap allocation between fork() and execve(), | ||
| 633 | // in musl v1.1.24. | ||
| 634 | // Additionally, we want to reduce the number of possible ways things | ||
| 635 | // can fail between fork() and execve(). | ||
| 636 | // Therefore, we do all the allocation for the execve() before the fork(). | ||
| 637 | // This means we must do the null-termination of argv and env vars here. | ||
| 638 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null); | ||
| 639 | for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | ||
| 640 | |||
| 641 | const prog_fileno = 3; | ||
| 642 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); | ||
| 643 | |||
| 644 | const envp: [*:null]const ?[*:0]const u8 = m: { | ||
| 645 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; | ||
| 646 | if (self.env_map) |env_map| { | ||
| 647 | break :m (try process.createEnvironFromMap(arena, env_map, .{ | ||
| 648 | .zig_progress_fd = prog_fd, | ||
| 649 | })).ptr; | ||
| 650 | } else if (builtin.link_libc) { | ||
| 651 | break :m (try process.createEnvironFromExisting(arena, std.c.environ, .{ | ||
| 652 | .zig_progress_fd = prog_fd, | ||
| 653 | })).ptr; | ||
| 654 | } else if (builtin.output_mode == .Exe) { | ||
| 655 | // Then we have Zig start code and this works. | ||
| 656 | // TODO type-safety for null-termination of `os.environ`. | ||
| 657 | break :m (try process.createEnvironFromExisting(arena, @ptrCast(std.os.environ.ptr), .{ | ||
| 658 | .zig_progress_fd = prog_fd, | ||
| 659 | })).ptr; | ||
| 660 | } else { | ||
| 661 | // TODO come up with a solution for this. | ||
| 662 | @panic("missing std lib enhancement: std.process.Child implementation has no way to collect the environment variables to forward to the child process"); | ||
| 663 | } | ||
| 664 | }; | ||
| 665 | |||
| 666 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. | ||
| 667 | // It is closed by the child (via CLOEXEC) without writing if `execvpe` succeeds. | ||
| 668 | const err_pipe: [2]posix.fd_t = try posix.pipe2(.{ .CLOEXEC = true }); | ||
| 669 | errdefer destroyPipe(err_pipe); | ||
| 670 | |||
| 671 | const pid_result = try posix.fork(); | ||
| 672 | if (pid_result == 0) { | ||
| 673 | // we are the child | ||
| 674 | setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 675 | setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 676 | setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 677 | |||
| 678 | if (self.cwd_dir) |cwd| { | ||
| 679 | posix.fchdir(cwd.handle) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 680 | } else if (self.cwd) |cwd| { | ||
| 681 | posix.chdir(cwd) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 682 | } | ||
| 683 | |||
| 684 | // Must happen after fchdir above, the cwd file descriptor might be | ||
| 685 | // equal to prog_fileno and be clobbered by this dup2 call. | ||
| 686 | if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 687 | |||
| 688 | if (self.gid) |gid| { | ||
| 689 | posix.setregid(gid, gid) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 690 | } | ||
| 691 | |||
| 692 | if (self.uid) |uid| { | ||
| 693 | posix.setreuid(uid, uid) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 694 | } | ||
| 695 | |||
| 696 | if (self.pgid) |pid| { | ||
| 697 | posix.setpgid(0, pid) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 698 | } | ||
| 699 | |||
| 700 | if (self.start_suspended) { | ||
| 701 | posix.kill(posix.getpid(), .STOP) catch |err| forkChildErrReport(io, err_pipe[1], err); | ||
| 702 | } | ||
| 703 | |||
| 704 | const err = switch (self.expand_arg0) { | ||
| 705 | .expand => posix.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp), | ||
| 706 | .no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp), | ||
| 707 | }; | ||
| 708 | forkChildErrReport(io, err_pipe[1], err); | ||
| 709 | } | ||
| 710 | |||
| 711 | // we are the parent | ||
| 712 | errdefer comptime unreachable; // The child is forked; we must not error from now on | ||
| 713 | |||
| 714 | posix.close(err_pipe[1]); // make sure only the child holds the write end open | ||
| 715 | self.err_pipe = err_pipe[0]; | ||
| 716 | |||
| 717 | const pid: i32 = @intCast(pid_result); | ||
| 718 | if (self.stdin_behavior == .Pipe) { | ||
| 719 | self.stdin = .{ .handle = stdin_pipe[1] }; | ||
| 720 | } else { | ||
| 721 | self.stdin = null; | ||
| 722 | } | ||
| 723 | if (self.stdout_behavior == .Pipe) { | ||
| 724 | self.stdout = .{ .handle = stdout_pipe[0] }; | ||
| 725 | } else { | ||
| 726 | self.stdout = null; | ||
| 727 | } | ||
| 728 | if (self.stderr_behavior == .Pipe) { | ||
| 729 | self.stderr = .{ .handle = stderr_pipe[0] }; | ||
| 730 | } else { | ||
| 731 | self.stderr = null; | ||
| 732 | } | ||
| 733 | |||
| 734 | self.id = pid; | ||
| 735 | self.term = null; | ||
| 736 | |||
| 737 | if (self.stdin_behavior == .Pipe) { | ||
| 738 | posix.close(stdin_pipe[0]); | ||
| 739 | } | ||
| 740 | if (self.stdout_behavior == .Pipe) { | ||
| 741 | posix.close(stdout_pipe[1]); | ||
| 742 | } | ||
| 743 | if (self.stderr_behavior == .Pipe) { | ||
| 744 | posix.close(stderr_pipe[1]); | ||
| 745 | } | ||
| 746 | |||
| 747 | if (prog_pipe[1] != -1) { | ||
| 748 | posix.close(prog_pipe[1]); | ||
| 749 | } | ||
| 750 | self.progress_node.setIpcFd(prog_pipe[0]); | ||
| 751 | } | ||
| 752 | |||
| 753 | fn spawnWindows(self: *Child, io: Io) SpawnError!void { | ||
| 754 | var saAttr = windows.SECURITY_ATTRIBUTES{ | ||
| 755 | .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), | ||
| 756 | .bInheritHandle = windows.TRUE, | ||
| 757 | .lpSecurityDescriptor = null, | ||
| 758 | }; | ||
| 759 | |||
| 760 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); | ||
| 761 | |||
| 762 | const nul_handle = if (any_ignore) | ||
| 763 | // "\Device\Null" or "\??\NUL" | ||
| 764 | windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{ | ||
| 765 | .access_mask = .{ | ||
| 766 | .STANDARD = .{ .SYNCHRONIZE = true }, | ||
| 767 | .GENERIC = .{ .WRITE = true, .READ = true }, | ||
| 768 | }, | ||
| 769 | .sa = &saAttr, | ||
| 770 | .creation = .OPEN, | ||
| 771 | }) catch |err| switch (err) { | ||
| 772 | error.PathAlreadyExists => return error.Unexpected, // not possible for "NUL" | ||
| 773 | error.PipeBusy => return error.Unexpected, // not possible for "NUL" | ||
| 774 | error.NoDevice => return error.Unexpected, // not possible for "NUL" | ||
| 775 | error.FileNotFound => return error.Unexpected, // not possible for "NUL" | ||
| 776 | error.AccessDenied => return error.Unexpected, // not possible for "NUL" | ||
| 777 | error.NameTooLong => return error.Unexpected, // not possible for "NUL" | ||
| 778 | error.WouldBlock => return error.Unexpected, // not possible for "NUL" | ||
| 779 | error.NetworkNotFound => return error.Unexpected, // not possible for "NUL" | ||
| 780 | error.AntivirusInterference => return error.Unexpected, // not possible for "NUL" | ||
| 781 | error.OperationCanceled => return error.Unexpected, // we're not canceling the operation | ||
| 782 | else => |e| return e, | ||
| 783 | } | ||
| 784 | else | ||
| 785 | undefined; | ||
| 786 | defer { | ||
| 787 | if (any_ignore) posix.close(nul_handle); | ||
| 788 | } | ||
| 789 | |||
| 790 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; | ||
| 791 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; | ||
| 792 | switch (self.stdin_behavior) { | ||
| 793 | StdIo.Pipe => { | ||
| 794 | try windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); | ||
| 795 | }, | ||
| 796 | StdIo.Ignore => { | ||
| 797 | g_hChildStd_IN_Rd = nul_handle; | ||
| 798 | }, | ||
| 799 | StdIo.Inherit => { | ||
| 800 | g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null; | ||
| 801 | }, | ||
| 802 | StdIo.Close => { | ||
| 803 | g_hChildStd_IN_Rd = null; | ||
| 804 | }, | ||
| 805 | } | ||
| 806 | errdefer if (self.stdin_behavior == StdIo.Pipe) { | ||
| 807 | windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); | ||
| 808 | }; | ||
| 809 | |||
| 810 | var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; | ||
| 811 | var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; | ||
| 812 | switch (self.stdout_behavior) { | ||
| 813 | StdIo.Pipe => { | ||
| 814 | try windowsMakeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); | ||
| 815 | }, | ||
| 816 | StdIo.Ignore => { | ||
| 817 | g_hChildStd_OUT_Wr = nul_handle; | ||
| 818 | }, | ||
| 819 | StdIo.Inherit => { | ||
| 820 | g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null; | ||
| 821 | }, | ||
| 822 | StdIo.Close => { | ||
| 823 | g_hChildStd_OUT_Wr = null; | ||
| 824 | }, | ||
| 825 | } | ||
| 826 | errdefer if (self.stdout_behavior == StdIo.Pipe) { | ||
| 827 | windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); | ||
| 828 | }; | ||
| 829 | |||
| 830 | var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; | ||
| 831 | var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; | ||
| 832 | switch (self.stderr_behavior) { | ||
| 833 | StdIo.Pipe => { | ||
| 834 | try windowsMakeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); | ||
| 835 | }, | ||
| 836 | StdIo.Ignore => { | ||
| 837 | g_hChildStd_ERR_Wr = nul_handle; | ||
| 838 | }, | ||
| 839 | StdIo.Inherit => { | ||
| 840 | g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null; | ||
| 841 | }, | ||
| 842 | StdIo.Close => { | ||
| 843 | g_hChildStd_ERR_Wr = null; | ||
| 844 | }, | ||
| 845 | } | ||
| 846 | errdefer if (self.stderr_behavior == StdIo.Pipe) { | ||
| 847 | windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); | ||
| 848 | }; | ||
| 849 | |||
| 850 | var siStartInfo = windows.STARTUPINFOW{ | ||
| 851 | .cb = @sizeOf(windows.STARTUPINFOW), | ||
| 852 | .hStdError = g_hChildStd_ERR_Wr, | ||
| 853 | .hStdOutput = g_hChildStd_OUT_Wr, | ||
| 854 | .hStdInput = g_hChildStd_IN_Rd, | ||
| 855 | .dwFlags = windows.STARTF_USESTDHANDLES, | ||
| 856 | |||
| 857 | .lpReserved = null, | ||
| 858 | .lpDesktop = null, | ||
| 859 | .lpTitle = null, | ||
| 860 | .dwX = 0, | ||
| 861 | .dwY = 0, | ||
| 862 | .dwXSize = 0, | ||
| 863 | .dwYSize = 0, | ||
| 864 | .dwXCountChars = 0, | ||
| 865 | .dwYCountChars = 0, | ||
| 866 | .dwFillAttribute = 0, | ||
| 867 | .wShowWindow = 0, | ||
| 868 | .cbReserved2 = 0, | ||
| 869 | .lpReserved2 = null, | ||
| 870 | }; | ||
| 871 | var piProcInfo: windows.PROCESS_INFORMATION = undefined; | ||
| 872 | |||
| 873 | const cwd_w = if (self.cwd) |cwd| try unicode.wtf8ToWtf16LeAllocZ(self.allocator, cwd) else null; | ||
| 874 | defer if (cwd_w) |cwd| self.allocator.free(cwd); | ||
| 875 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; | ||
| 876 | |||
| 877 | const maybe_envp_buf = if (self.env_map) |env_map| try process.createWindowsEnvBlock(self.allocator, env_map) else null; | ||
| 878 | defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); | ||
| 879 | const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; | ||
| 880 | |||
| 881 | const app_name_wtf8 = self.argv[0]; | ||
| 882 | const app_name_is_absolute = fs.path.isAbsolute(app_name_wtf8); | ||
| 883 | |||
| 884 | // the cwd set in Child is in effect when choosing the executable path | ||
| 885 | // to match posix semantics | ||
| 886 | var cwd_path_w_needs_free = false; | ||
| 887 | const cwd_path_w = x: { | ||
| 888 | // If the app name is absolute, then we need to use its dirname as the cwd | ||
| 889 | if (app_name_is_absolute) { | ||
| 890 | cwd_path_w_needs_free = true; | ||
| 891 | const dir = fs.path.dirname(app_name_wtf8).?; | ||
| 892 | break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, dir); | ||
| 893 | } else if (self.cwd) |cwd| { | ||
| 894 | cwd_path_w_needs_free = true; | ||
| 895 | break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, cwd); | ||
| 896 | } else { | ||
| 897 | break :x &[_:0]u16{}; // empty for cwd | ||
| 898 | } | ||
| 899 | }; | ||
| 900 | defer if (cwd_path_w_needs_free) self.allocator.free(cwd_path_w); | ||
| 901 | |||
| 902 | // If the app name has more than just a filename, then we need to separate that | ||
| 903 | // into the basename and dirname and use the dirname as an addition to the cwd | ||
| 904 | // path. This is because NtQueryDirectoryFile cannot accept FileName params with | ||
| 905 | // path separators. | ||
| 906 | const app_basename_wtf8 = fs.path.basename(app_name_wtf8); | ||
| 907 | // If the app name is absolute, then the cwd will already have the app's dirname in it, | ||
| 908 | // so only populate app_dirname if app name is a relative path with > 0 path separators. | ||
| 909 | const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) fs.path.dirname(app_name_wtf8) else null; | ||
| 910 | const app_dirname_w: ?[:0]u16 = x: { | ||
| 911 | if (maybe_app_dirname_wtf8) |app_dirname_wtf8| { | ||
| 912 | break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_dirname_wtf8); | ||
| 913 | } | ||
| 914 | break :x null; | ||
| 915 | }; | ||
| 916 | defer if (app_dirname_w != null) self.allocator.free(app_dirname_w.?); | ||
| 917 | |||
| 918 | const app_name_w = try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_basename_wtf8); | ||
| 919 | defer self.allocator.free(app_name_w); | ||
| 920 | |||
| 921 | const flags: windows.CreateProcessFlags = .{ | ||
| 922 | .create_suspended = self.start_suspended, | ||
| 923 | .create_unicode_environment = true, | ||
| 924 | .create_no_window = self.create_no_window, | ||
| 925 | }; | ||
| 926 | |||
| 927 | run: { | ||
| 928 | const PATH: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{}; | ||
| 929 | const PATHEXT: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{}; | ||
| 930 | |||
| 931 | // In case the command ends up being a .bat/.cmd script, we need to escape things using the cmd.exe rules | ||
| 932 | // and invoke cmd.exe ourselves in order to mitigate arbitrary command execution from maliciously | ||
| 933 | // constructed arguments. | ||
| 934 | // | ||
| 935 | // We'll need to wait until we're actually trying to run the command to know for sure | ||
| 936 | // if the resolved command has the `.bat` or `.cmd` extension, so we defer actually | ||
| 937 | // serializing the command line until we determine how it should be serialized. | ||
| 938 | var cmd_line_cache = WindowsCommandLineCache.init(self.allocator, self.argv); | ||
| 939 | defer cmd_line_cache.deinit(); | ||
| 940 | |||
| 941 | var app_buf: ArrayList(u16) = .empty; | ||
| 942 | defer app_buf.deinit(self.allocator); | ||
| 943 | |||
| 944 | try app_buf.appendSlice(self.allocator, app_name_w); | ||
| 945 | |||
| 946 | var dir_buf: ArrayList(u16) = .empty; | ||
| 947 | defer dir_buf.deinit(self.allocator); | ||
| 948 | |||
| 949 | if (cwd_path_w.len > 0) { | ||
| 950 | try dir_buf.appendSlice(self.allocator, cwd_path_w); | ||
| 951 | } | ||
| 952 | if (app_dirname_w) |app_dir| { | ||
| 953 | if (dir_buf.items.len > 0) try dir_buf.append(self.allocator, fs.path.sep); | ||
| 954 | try dir_buf.appendSlice(self.allocator, app_dir); | ||
| 955 | } | ||
| 956 | |||
| 957 | windowsCreateProcessPathExt(self.allocator, io, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| { | ||
| 958 | const original_err = switch (no_path_err) { | ||
| 959 | // argv[0] contains unsupported characters that will never resolve to a valid exe. | ||
| 960 | error.InvalidArg0 => return error.FileNotFound, | ||
| 961 | error.FileNotFound, error.InvalidExe, error.AccessDenied => |e| e, | ||
| 962 | error.UnrecoverableInvalidExe => return error.InvalidExe, | ||
| 963 | else => |e| return e, | ||
| 964 | }; | ||
| 965 | |||
| 966 | // If the app name had path separators, that disallows PATH searching, | ||
| 967 | // and there's no need to search the PATH if the app name is absolute. | ||
| 968 | // We still search the path if the cwd is absolute because of the | ||
| 969 | // "cwd set in Child is in effect when choosing the executable path | ||
| 970 | // to match posix semantics" behavior--we don't want to skip searching | ||
| 971 | // the PATH just because we were trying to set the cwd of the child process. | ||
| 972 | if (app_dirname_w != null or app_name_is_absolute) { | ||
| 973 | return original_err; | ||
| 974 | } | ||
| 975 | |||
| 976 | var it = mem.tokenizeScalar(u16, PATH, ';'); | ||
| 977 | while (it.next()) |search_path| { | ||
| 978 | dir_buf.clearRetainingCapacity(); | ||
| 979 | try dir_buf.appendSlice(self.allocator, search_path); | ||
| 980 | |||
| 981 | if (windowsCreateProcessPathExt(self.allocator, io, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) { | ||
| 982 | break :run; | ||
| 983 | } else |err| switch (err) { | ||
| 984 | // argv[0] contains unsupported characters that will never resolve to a valid exe. | ||
| 985 | error.InvalidArg0 => return error.FileNotFound, | ||
| 986 | error.FileNotFound, error.AccessDenied, error.InvalidExe => continue, | ||
| 987 | error.UnrecoverableInvalidExe => return error.InvalidExe, | ||
| 988 | else => |e| return e, | ||
| 989 | } | ||
| 990 | } else { | ||
| 991 | return original_err; | ||
| 992 | } | ||
| 993 | }; | ||
| 994 | } | ||
| 995 | |||
| 996 | if (g_hChildStd_IN_Wr) |h| { | ||
| 997 | self.stdin = File{ .handle = h }; | ||
| 998 | } else { | ||
| 999 | self.stdin = null; | ||
| 1000 | } | ||
| 1001 | if (g_hChildStd_OUT_Rd) |h| { | ||
| 1002 | self.stdout = File{ .handle = h }; | ||
| 1003 | } else { | ||
| 1004 | self.stdout = null; | ||
| 1005 | } | ||
| 1006 | if (g_hChildStd_ERR_Rd) |h| { | ||
| 1007 | self.stderr = File{ .handle = h }; | ||
| 1008 | } else { | ||
| 1009 | self.stderr = null; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | self.id = piProcInfo.hProcess; | ||
| 1013 | self.thread_handle = piProcInfo.hThread; | ||
| 1014 | self.term = null; | ||
| 1015 | |||
| 1016 | if (self.stdin_behavior == StdIo.Pipe) { | ||
| 1017 | posix.close(g_hChildStd_IN_Rd.?); | ||
| 1018 | } | ||
| 1019 | if (self.stderr_behavior == StdIo.Pipe) { | ||
| 1020 | posix.close(g_hChildStd_ERR_Wr.?); | ||
| 1021 | } | ||
| 1022 | if (self.stdout_behavior == StdIo.Pipe) { | ||
| 1023 | posix.close(g_hChildStd_OUT_Wr.?); | ||
| 1024 | } | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { | ||
| 1028 | switch (stdio) { | ||
| 1029 | .Pipe => try posix.dup2(pipe_fd, std_fileno), | ||
| 1030 | .Close => posix.close(std_fileno), | ||
| 1031 | .Inherit => {}, | ||
| 1032 | .Ignore => try posix.dup2(dev_null_fd, std_fileno), | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | fn destroyPipe(pipe: [2]posix.fd_t) void { | ||
| 1037 | if (pipe[0] != -1) posix.close(pipe[0]); | ||
| 1038 | if (pipe[0] != pipe[1]) posix.close(pipe[1]); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | // Child of fork calls this to report an error to the fork parent. | ||
| 1042 | // Then the child exits. | ||
| 1043 | fn forkChildErrReport(io: Io, fd: i32, err: Child.SpawnError) noreturn { | ||
| 1044 | writeIntFd(io, fd, @as(ErrInt, @intFromError(err))) catch {}; | ||
| 1045 | // If we're linking libc, some naughty applications may have registered atexit handlers | ||
| 1046 | // which we really do not want to run in the fork child. I caught LLVM doing this and | ||
| 1047 | // it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne, | ||
| 1048 | // "Why'd you have to go and make things so complicated?" | ||
| 1049 | if (builtin.link_libc) { | ||
| 1050 | // The _exit(2) function does nothing but make the exit syscall, unlike exit(3) | ||
| 1051 | std.c._exit(1); | ||
| 1052 | } | ||
| 1053 | posix.system.exit(1); | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | fn writeIntFd(io: Io, fd: i32, value: ErrInt) !void { | ||
| 1057 | var buffer: [8]u8 = undefined; | ||
| 1058 | var fw: File.Writer = .initStreaming(.{ .handle = fd }, io, &buffer); | ||
| 1059 | fw.interface.writeInt(u64, value, .little) catch unreachable; | ||
| 1060 | fw.interface.flush() catch return error.SystemResources; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | fn readIntFd(fd: i32) !ErrInt { | ||
| 1064 | var buffer: [8]u8 = undefined; | ||
| 1065 | var i: usize = 0; | ||
| 1066 | while (i < buffer.len) { | ||
| 1067 | const n = try std.posix.read(fd, buffer[i..]); | ||
| 1068 | if (n == 0) return error.EndOfStream; | ||
| 1069 | i += n; | ||
| 1070 | } | ||
| 1071 | const int = mem.readInt(u64, &buffer, .little); | ||
| 1072 | return @intCast(int); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); | ||
| 1076 | |||
| 1077 | /// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path. | ||
| 1078 | /// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path. | ||
| 1079 | /// Note: `app_buf` should not contain any leading path separators. | ||
| 1080 | /// Note: If the dir is the cwd, dir_buf should be empty (len = 0). | ||
| 1081 | fn windowsCreateProcessPathExt( | ||
| 1082 | allocator: Allocator, | ||
| 1083 | io: Io, | ||
| 1084 | dir_buf: *ArrayList(u16), | ||
| 1085 | app_buf: *ArrayList(u16), | ||
| 1086 | pathext: [:0]const u16, | ||
| 1087 | cmd_line_cache: *WindowsCommandLineCache, | ||
| 1088 | envp_ptr: ?[*]u16, | ||
| 1089 | cwd_ptr: ?[*:0]u16, | ||
| 1090 | flags: windows.CreateProcessFlags, | ||
| 1091 | lpStartupInfo: *windows.STARTUPINFOW, | ||
| 1092 | lpProcessInformation: *windows.PROCESS_INFORMATION, | ||
| 1093 | ) !void { | ||
| 1094 | const app_name_len = app_buf.items.len; | ||
| 1095 | const dir_path_len = dir_buf.items.len; | ||
| 1096 | |||
| 1097 | if (app_name_len == 0) return error.FileNotFound; | ||
| 1098 | |||
| 1099 | defer app_buf.shrinkRetainingCapacity(app_name_len); | ||
| 1100 | defer dir_buf.shrinkRetainingCapacity(dir_path_len); | ||
| 1101 | |||
| 1102 | // The name of the game here is to avoid CreateProcessW calls at all costs, | ||
| 1103 | // and only ever try calling it when we have a real candidate for execution. | ||
| 1104 | // Secondarily, we want to minimize the number of syscalls used when checking | ||
| 1105 | // for each PATHEXT-appended version of the app name. | ||
| 1106 | // | ||
| 1107 | // An overview of the technique used: | ||
| 1108 | // - Open the search directory for iteration (either cwd or a path from PATH) | ||
| 1109 | // - Use NtQueryDirectoryFile with a wildcard filename of `<app name>*` to | ||
| 1110 | // check if anything that could possibly match either the unappended version | ||
| 1111 | // of the app name or any of the versions with a PATHEXT value appended exists. | ||
| 1112 | // - If the wildcard NtQueryDirectoryFile call found nothing, we can exit early | ||
| 1113 | // without needing to use PATHEXT at all. | ||
| 1114 | // | ||
| 1115 | // This allows us to use a <open dir, NtQueryDirectoryFile, close dir> sequence | ||
| 1116 | // for any directory that doesn't contain any possible matches, instead of having | ||
| 1117 | // to use a separate look up for each individual filename combination (unappended + | ||
| 1118 | // each PATHEXT appended). For directories where the wildcard *does* match something, | ||
| 1119 | // we iterate the matches and take note of any that are either the unappended version, | ||
| 1120 | // or a version with a supported PATHEXT appended. We then try calling CreateProcessW | ||
| 1121 | // with the found versions in the appropriate order. | ||
| 1122 | |||
| 1123 | // In the future, child process execution needs to move to Io implementation. | ||
| 1124 | // Under those conditions, here we will have access to lower level directory | ||
| 1125 | // opening function knowing which implementation we are in. Here, we imitate | ||
| 1126 | // that scenario. | ||
| 1127 | var dir = dir: { | ||
| 1128 | // needs to be null-terminated | ||
| 1129 | try dir_buf.append(allocator, 0); | ||
| 1130 | defer dir_buf.shrinkRetainingCapacity(dir_path_len); | ||
| 1131 | const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; | ||
| 1132 | const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z); | ||
| 1133 | break :dir Io.Threaded.dirOpenDirWindows(.cwd(), prefixed_path.span(), .{ | ||
| 1134 | .iterate = true, | ||
| 1135 | }) catch return error.FileNotFound; | ||
| 1136 | }; | ||
| 1137 | defer dir.close(io); | ||
| 1138 | |||
| 1139 | // Add wildcard and null-terminator | ||
| 1140 | try app_buf.append(allocator, '*'); | ||
| 1141 | try app_buf.append(allocator, 0); | ||
| 1142 | const app_name_wildcard = app_buf.items[0 .. app_buf.items.len - 1 :0]; | ||
| 1143 | |||
| 1144 | // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries | ||
| 1145 | // returned per NtQueryDirectoryFile call. | ||
| 1146 | var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined; | ||
| 1147 | const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2); | ||
| 1148 | if (file_information_buf.len < file_info_maximum_single_entry_size) { | ||
| 1149 | @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry"); | ||
| 1150 | } | ||
| 1151 | var io_status: windows.IO_STATUS_BLOCK = undefined; | ||
| 1152 | |||
| 1153 | const num_supported_pathext = @typeInfo(WindowsExtension).@"enum".fields.len; | ||
| 1154 | var pathext_seen = [_]bool{false} ** num_supported_pathext; | ||
| 1155 | var any_pathext_seen = false; | ||
| 1156 | var unappended_exists = false; | ||
| 1157 | |||
| 1158 | // Fully iterate the wildcard matches via NtQueryDirectoryFile and take note of all versions | ||
| 1159 | // of the app_name we should try to spawn. | ||
| 1160 | // Note: This is necessary because the order of the files returned is filesystem-dependent: | ||
| 1161 | // On NTFS, `blah.exe*` will always return `blah.exe` first if it exists. | ||
| 1162 | // On FAT32, it's possible for something like `blah.exe.obj` to be returned first. | ||
| 1163 | while (true) { | ||
| 1164 | const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong; | ||
| 1165 | var app_name_unicode_string = windows.UNICODE_STRING{ | ||
| 1166 | .Length = app_name_len_bytes, | ||
| 1167 | .MaximumLength = app_name_len_bytes, | ||
| 1168 | .Buffer = @constCast(app_name_wildcard.ptr), | ||
| 1169 | }; | ||
| 1170 | const rc = windows.ntdll.NtQueryDirectoryFile( | ||
| 1171 | dir.handle, | ||
| 1172 | null, | ||
| 1173 | null, | ||
| 1174 | null, | ||
| 1175 | &io_status, | ||
| 1176 | &file_information_buf, | ||
| 1177 | file_information_buf.len, | ||
| 1178 | .Directory, | ||
| 1179 | windows.FALSE, // single result | ||
| 1180 | &app_name_unicode_string, | ||
| 1181 | windows.FALSE, // restart iteration | ||
| 1182 | ); | ||
| 1183 | |||
| 1184 | // If we get nothing with the wildcard, then we can just bail out | ||
| 1185 | // as we know appending PATHEXT will not yield anything. | ||
| 1186 | switch (rc) { | ||
| 1187 | .SUCCESS => {}, | ||
| 1188 | .NO_SUCH_FILE => return error.FileNotFound, | ||
| 1189 | .NO_MORE_FILES => break, | ||
| 1190 | .ACCESS_DENIED => return error.AccessDenied, | ||
| 1191 | else => return windows.unexpectedStatus(rc), | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | // According to the docs, this can only happen if there is not enough room in the | ||
| 1195 | // buffer to write at least one complete FILE_DIRECTORY_INFORMATION entry. | ||
| 1196 | // Therefore, this condition should not be possible to hit with the buffer size we use. | ||
| 1197 | std.debug.assert(io_status.Information != 0); | ||
| 1198 | |||
| 1199 | var it = windows.FileInformationIterator(windows.FILE_DIRECTORY_INFORMATION){ .buf = &file_information_buf }; | ||
| 1200 | while (it.next()) |info| { | ||
| 1201 | // Skip directories | ||
| 1202 | if (info.FileAttributes.DIRECTORY) continue; | ||
| 1203 | const filename = @as([*]u16, @ptrCast(&info.FileName))[0 .. info.FileNameLength / 2]; | ||
| 1204 | // Because all results start with the app_name since we're using the wildcard `app_name*`, | ||
| 1205 | // if the length is equal to app_name then this is an exact match | ||
| 1206 | if (filename.len == app_name_len) { | ||
| 1207 | // Note: We can't break early here because it's possible that the unappended version | ||
| 1208 | // fails to spawn, in which case we still want to try the PATHEXT appended versions. | ||
| 1209 | unappended_exists = true; | ||
| 1210 | } else if (windowsCreateProcessSupportsExtension(filename[app_name_len..])) |pathext_ext| { | ||
| 1211 | pathext_seen[@intFromEnum(pathext_ext)] = true; | ||
| 1212 | any_pathext_seen = true; | ||
| 1213 | } | ||
| 1214 | } | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | const unappended_err = unappended: { | ||
| 1218 | if (unappended_exists) { | ||
| 1219 | if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) { | ||
| 1220 | '/', '\\' => {}, | ||
| 1221 | else => try dir_buf.append(allocator, fs.path.sep), | ||
| 1222 | }; | ||
| 1223 | try dir_buf.appendSlice(allocator, app_buf.items[0..app_name_len]); | ||
| 1224 | try dir_buf.append(allocator, 0); | ||
| 1225 | const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; | ||
| 1226 | |||
| 1227 | const is_bat_or_cmd = bat_or_cmd: { | ||
| 1228 | const app_name = app_buf.items[0..app_name_len]; | ||
| 1229 | const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :bat_or_cmd false; | ||
| 1230 | const ext = app_name[ext_start..]; | ||
| 1231 | const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse break :bat_or_cmd false; | ||
| 1232 | switch (ext_enum) { | ||
| 1233 | .cmd, .bat => break :bat_or_cmd true, | ||
| 1234 | else => break :bat_or_cmd false, | ||
| 1235 | } | ||
| 1236 | }; | ||
| 1237 | const cmd_line_w = if (is_bat_or_cmd) | ||
| 1238 | try cmd_line_cache.scriptCommandLine(full_app_name) | ||
| 1239 | else | ||
| 1240 | try cmd_line_cache.commandLine(); | ||
| 1241 | const app_name_w = if (is_bat_or_cmd) | ||
| 1242 | try cmd_line_cache.cmdExePath() | ||
| 1243 | else | ||
| 1244 | full_app_name; | ||
| 1245 | |||
| 1246 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | ||
| 1247 | return; | ||
| 1248 | } else |err| switch (err) { | ||
| 1249 | error.FileNotFound, | ||
| 1250 | error.AccessDenied, | ||
| 1251 | => break :unappended err, | ||
| 1252 | error.InvalidExe => { | ||
| 1253 | // On InvalidExe, if the extension of the app name is .exe then | ||
| 1254 | // it's treated as an unrecoverable error. Otherwise, it'll be | ||
| 1255 | // skipped as normal. | ||
| 1256 | const app_name = app_buf.items[0..app_name_len]; | ||
| 1257 | const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :unappended err; | ||
| 1258 | const ext = app_name[ext_start..]; | ||
| 1259 | if (windows.eqlIgnoreCaseWtf16(ext, unicode.utf8ToUtf16LeStringLiteral(".EXE"))) { | ||
| 1260 | return error.UnrecoverableInvalidExe; | ||
| 1261 | } | ||
| 1262 | break :unappended err; | ||
| 1263 | }, | ||
| 1264 | else => return err, | ||
| 1265 | } | ||
| 1266 | } | ||
| 1267 | break :unappended error.FileNotFound; | ||
| 1268 | }; | ||
| 1269 | |||
| 1270 | if (!any_pathext_seen) return unappended_err; | ||
| 1271 | |||
| 1272 | // Now try any PATHEXT appended versions that we've seen | ||
| 1273 | var ext_it = mem.tokenizeScalar(u16, pathext, ';'); | ||
| 1274 | while (ext_it.next()) |ext| { | ||
| 1275 | const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse continue; | ||
| 1276 | if (!pathext_seen[@intFromEnum(ext_enum)]) continue; | ||
| 1277 | |||
| 1278 | dir_buf.shrinkRetainingCapacity(dir_path_len); | ||
| 1279 | if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) { | ||
| 1280 | '/', '\\' => {}, | ||
| 1281 | else => try dir_buf.append(allocator, fs.path.sep), | ||
| 1282 | }; | ||
| 1283 | try dir_buf.appendSlice(allocator, app_buf.items[0..app_name_len]); | ||
| 1284 | try dir_buf.appendSlice(allocator, ext); | ||
| 1285 | try dir_buf.append(allocator, 0); | ||
| 1286 | const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; | ||
| 1287 | |||
| 1288 | const is_bat_or_cmd = switch (ext_enum) { | ||
| 1289 | .cmd, .bat => true, | ||
| 1290 | else => false, | ||
| 1291 | }; | ||
| 1292 | const cmd_line_w = if (is_bat_or_cmd) | ||
| 1293 | try cmd_line_cache.scriptCommandLine(full_app_name) | ||
| 1294 | else | ||
| 1295 | try cmd_line_cache.commandLine(); | ||
| 1296 | const app_name_w = if (is_bat_or_cmd) | ||
| 1297 | try cmd_line_cache.cmdExePath() | ||
| 1298 | else | ||
| 1299 | full_app_name; | ||
| 1300 | |||
| 1301 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | ||
| 1302 | return; | ||
| 1303 | } else |err| switch (err) { | ||
| 1304 | error.FileNotFound => continue, | ||
| 1305 | error.AccessDenied => continue, | ||
| 1306 | error.InvalidExe => { | ||
| 1307 | // On InvalidExe, if the extension of the app name is .exe then | ||
| 1308 | // it's treated as an unrecoverable error. Otherwise, it'll be | ||
| 1309 | // skipped as normal. | ||
| 1310 | if (windows.eqlIgnoreCaseWtf16(ext, unicode.utf8ToUtf16LeStringLiteral(".EXE"))) { | ||
| 1311 | return error.UnrecoverableInvalidExe; | ||
| 1312 | } | ||
| 1313 | continue; | ||
| 1314 | }, | ||
| 1315 | else => return err, | ||
| 1316 | } | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | return unappended_err; | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | fn windowsCreateProcess( | ||
| 1323 | app_name: [*:0]u16, | ||
| 1324 | cmd_line: [*:0]u16, | ||
| 1325 | envp_ptr: ?[*]u16, | ||
| 1326 | cwd_ptr: ?[*:0]u16, | ||
| 1327 | flags: windows.CreateProcessFlags, | ||
| 1328 | lpStartupInfo: *windows.STARTUPINFOW, | ||
| 1329 | lpProcessInformation: *windows.PROCESS_INFORMATION, | ||
| 1330 | ) !void { | ||
| 1331 | // TODO the docs for environment pointer say: | ||
| 1332 | // > A pointer to the environment block for the new process. If this parameter | ||
| 1333 | // > is NULL, the new process uses the environment of the calling process. | ||
| 1334 | // > ... | ||
| 1335 | // > An environment block can contain either Unicode or ANSI characters. If | ||
| 1336 | // > the environment block pointed to by lpEnvironment contains Unicode | ||
| 1337 | // > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT. | ||
| 1338 | // > If this parameter is NULL and the environment block of the parent process | ||
| 1339 | // > contains Unicode characters, you must also ensure that dwCreationFlags | ||
| 1340 | // > includes CREATE_UNICODE_ENVIRONMENT. | ||
| 1341 | // This seems to imply that we have to somehow know whether our process parent passed | ||
| 1342 | // CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter. | ||
| 1343 | // Since we do not know this information that would imply that we must not pass NULL | ||
| 1344 | // for the parameter. | ||
| 1345 | // However this would imply that programs compiled with -DUNICODE could not pass | ||
| 1346 | // environment variables to programs that were not, which seems unlikely. | ||
| 1347 | // More investigation is needed. | ||
| 1348 | return windows.CreateProcessW( | ||
| 1349 | app_name, | ||
| 1350 | cmd_line, | ||
| 1351 | null, | ||
| 1352 | null, | ||
| 1353 | windows.TRUE, | ||
| 1354 | flags, | ||
| 1355 | @as(?*anyopaque, @ptrCast(envp_ptr)), | ||
| 1356 | cwd_ptr, | ||
| 1357 | lpStartupInfo, | ||
| 1358 | lpProcessInformation, | ||
| 1359 | ); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | ||
| 1363 | var rd_h: windows.HANDLE = undefined; | ||
| 1364 | var wr_h: windows.HANDLE = undefined; | ||
| 1365 | try windows.CreatePipe(&rd_h, &wr_h, sattr); | ||
| 1366 | errdefer windowsDestroyPipe(rd_h, wr_h); | ||
| 1367 | try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0); | ||
| 1368 | rd.* = rd_h; | ||
| 1369 | wr.* = wr_h; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { | ||
| 1373 | if (rd) |h| posix.close(h); | ||
| 1374 | if (wr) |h| posix.close(h); | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | ||
| 1378 | var tmp_bufw: [128]u16 = undefined; | ||
| 1379 | |||
| 1380 | // Anonymous pipes are built upon Named pipes. | ||
| 1381 | // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe | ||
| 1382 | // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. | ||
| 1383 | // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations | ||
| 1384 | const pipe_path = blk: { | ||
| 1385 | var tmp_buf: [128]u8 = undefined; | ||
| 1386 | // Forge a random path for the pipe. | ||
| 1387 | const pipe_path = std.fmt.bufPrintSentinel( | ||
| 1388 | &tmp_buf, | ||
| 1389 | "\\\\.\\pipe\\zig-childprocess-{d}-{d}", | ||
| 1390 | .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) }, | ||
| 1391 | 0, | ||
| 1392 | ) catch unreachable; | ||
| 1393 | const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable; | ||
| 1394 | tmp_bufw[len] = 0; | ||
| 1395 | break :blk tmp_bufw[0..len :0]; | ||
| 1396 | }; | ||
| 1397 | |||
| 1398 | // Create the read handle that can be used with overlapped IO ops. | ||
| 1399 | const read_handle = windows.kernel32.CreateNamedPipeW( | ||
| 1400 | pipe_path.ptr, | ||
| 1401 | windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED, | ||
| 1402 | windows.PIPE_TYPE_BYTE, | ||
| 1403 | 1, | ||
| 1404 | 4096, | ||
| 1405 | 4096, | ||
| 1406 | 0, | ||
| 1407 | sattr, | ||
| 1408 | ); | ||
| 1409 | if (read_handle == windows.INVALID_HANDLE_VALUE) { | ||
| 1410 | switch (windows.GetLastError()) { | ||
| 1411 | else => |err| return windows.unexpectedError(err), | ||
| 1412 | } | ||
| 1413 | } | ||
| 1414 | errdefer posix.close(read_handle); | ||
| 1415 | |||
| 1416 | var sattr_copy = sattr.*; | ||
| 1417 | const write_handle = windows.kernel32.CreateFileW( | ||
| 1418 | pipe_path.ptr, | ||
| 1419 | .{ .GENERIC = .{ .WRITE = true } }, | ||
| 1420 | 0, | ||
| 1421 | &sattr_copy, | ||
| 1422 | windows.OPEN_EXISTING, | ||
| 1423 | @bitCast(windows.FILE.ATTRIBUTE{ .NORMAL = true }), | ||
| 1424 | null, | ||
| 1425 | ); | ||
| 1426 | if (write_handle == windows.INVALID_HANDLE_VALUE) { | ||
| 1427 | switch (windows.GetLastError()) { | ||
| 1428 | else => |err| return windows.unexpectedError(err), | ||
| 1429 | } | ||
| 1430 | } | ||
| 1431 | errdefer posix.close(write_handle); | ||
| 1432 | |||
| 1433 | try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0); | ||
| 1434 | |||
| 1435 | rd.* = read_handle; | ||
| 1436 | wr.* = write_handle; | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | var pipe_name_counter = std.atomic.Value(u32).init(1); | ||
| 1440 | |||
| 1441 | /// File name extensions supported natively by `CreateProcess()` on Windows. | ||
| 1442 | // Should be kept in sync with `windowsCreateProcessSupportsExtension`. | ||
| 1443 | pub const WindowsExtension = enum { | ||
| 1444 | bat, | ||
| 1445 | cmd, | ||
| 1446 | com, | ||
| 1447 | exe, | ||
| 1448 | }; | ||
| 1449 | |||
| 1450 | /// Case-insensitive WTF-16 lookup | ||
| 1451 | fn windowsCreateProcessSupportsExtension(ext: []const u16) ?WindowsExtension { | ||
| 1452 | if (ext.len != 4) return null; | ||
| 1453 | const State = enum { | ||
| 1454 | start, | ||
| 1455 | dot, | ||
| 1456 | b, | ||
| 1457 | ba, | ||
| 1458 | c, | ||
| 1459 | cm, | ||
| 1460 | co, | ||
| 1461 | e, | ||
| 1462 | ex, | ||
| 1463 | }; | ||
| 1464 | var state: State = .start; | ||
| 1465 | for (ext) |c| switch (state) { | ||
| 1466 | .start => switch (c) { | ||
| 1467 | '.' => state = .dot, | ||
| 1468 | else => return null, | ||
| 1469 | }, | ||
| 1470 | .dot => switch (c) { | ||
| 1471 | 'b', 'B' => state = .b, | ||
| 1472 | 'c', 'C' => state = .c, | ||
| 1473 | 'e', 'E' => state = .e, | ||
| 1474 | else => return null, | ||
| 1475 | }, | ||
| 1476 | .b => switch (c) { | ||
| 1477 | 'a', 'A' => state = .ba, | ||
| 1478 | else => return null, | ||
| 1479 | }, | ||
| 1480 | .c => switch (c) { | ||
| 1481 | 'm', 'M' => state = .cm, | ||
| 1482 | 'o', 'O' => state = .co, | ||
| 1483 | else => return null, | ||
| 1484 | }, | ||
| 1485 | .e => switch (c) { | ||
| 1486 | 'x', 'X' => state = .ex, | ||
| 1487 | else => return null, | ||
| 1488 | }, | ||
| 1489 | .ba => switch (c) { | ||
| 1490 | 't', 'T' => return .bat, | ||
| 1491 | else => return null, | ||
| 1492 | }, | ||
| 1493 | .cm => switch (c) { | ||
| 1494 | 'd', 'D' => return .cmd, | ||
| 1495 | else => return null, | ||
| 1496 | }, | ||
| 1497 | .co => switch (c) { | ||
| 1498 | 'm', 'M' => return .com, | ||
| 1499 | else => return null, | ||
| 1500 | }, | ||
| 1501 | .ex => switch (c) { | ||
| 1502 | 'e', 'E' => return .exe, | ||
| 1503 | else => return null, | ||
| 1504 | }, | ||
| 1505 | }; | ||
| 1506 | return null; | ||
| 1507 | } | ||
| 1508 | |||
| 1509 | test windowsCreateProcessSupportsExtension { | ||
| 1510 | try std.testing.expectEqual(WindowsExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?); | ||
| 1511 | try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null); | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | /// Serializes argv into a WTF-16 encoded command-line string for use with CreateProcessW. | ||
| 1515 | /// | ||
| 1516 | /// Serialization is done on-demand and the result is cached in order to allow for: | ||
| 1517 | /// - Only serializing the particular type of command line needed (`.bat`/`.cmd` | ||
| 1518 | /// command line serialization is different from `.exe`/etc) | ||
| 1519 | /// - Reusing the serialized command lines if necessary (i.e. if the execution | ||
| 1520 | /// of a command fails and the PATH is going to be continued to be searched | ||
| 1521 | /// for more candidates) | ||
| 1522 | const WindowsCommandLineCache = struct { | ||
| 1523 | cmd_line: ?[:0]u16 = null, | ||
| 1524 | script_cmd_line: ?[:0]u16 = null, | ||
| 1525 | cmd_exe_path: ?[:0]u16 = null, | ||
| 1526 | argv: []const []const u8, | ||
| 1527 | allocator: Allocator, | ||
| 1528 | |||
| 1529 | fn init(allocator: Allocator, argv: []const []const u8) WindowsCommandLineCache { | ||
| 1530 | return .{ | ||
| 1531 | .allocator = allocator, | ||
| 1532 | .argv = argv, | ||
| 1533 | }; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | fn deinit(self: *WindowsCommandLineCache) void { | ||
| 1537 | if (self.cmd_line) |cmd_line| self.allocator.free(cmd_line); | ||
| 1538 | if (self.script_cmd_line) |script_cmd_line| self.allocator.free(script_cmd_line); | ||
| 1539 | if (self.cmd_exe_path) |cmd_exe_path| self.allocator.free(cmd_exe_path); | ||
| 1540 | } | ||
| 1541 | |||
| 1542 | fn commandLine(self: *WindowsCommandLineCache) ![:0]u16 { | ||
| 1543 | if (self.cmd_line == null) { | ||
| 1544 | self.cmd_line = try argvToCommandLineWindows(self.allocator, self.argv); | ||
| 1545 | } | ||
| 1546 | return self.cmd_line.?; | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | /// Not cached, since the path to the batch script will change during PATH searching. | ||
| 1550 | /// `script_path` should be as qualified as possible, e.g. if the PATH is being searched, | ||
| 1551 | /// then script_path should include both the search path and the script filename | ||
| 1552 | /// (this allows avoiding cmd.exe having to search the PATH again). | ||
| 1553 | fn scriptCommandLine(self: *WindowsCommandLineCache, script_path: []const u16) ![:0]u16 { | ||
| 1554 | if (self.script_cmd_line) |v| self.allocator.free(v); | ||
| 1555 | self.script_cmd_line = try argvToScriptCommandLineWindows( | ||
| 1556 | self.allocator, | ||
| 1557 | script_path, | ||
| 1558 | self.argv[1..], | ||
| 1559 | ); | ||
| 1560 | return self.script_cmd_line.?; | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | fn cmdExePath(self: *WindowsCommandLineCache) ![:0]u16 { | ||
| 1564 | if (self.cmd_exe_path == null) { | ||
| 1565 | self.cmd_exe_path = try windowsCmdExePath(self.allocator); | ||
| 1566 | } | ||
| 1567 | return self.cmd_exe_path.?; | ||
| 1568 | } | ||
| 1569 | }; | ||
| 1570 | |||
| 1571 | /// Returns the absolute path of `cmd.exe` within the Windows system directory. | ||
| 1572 | /// The caller owns the returned slice. | ||
| 1573 | fn windowsCmdExePath(allocator: Allocator) error{ OutOfMemory, Unexpected }![:0]u16 { | ||
| 1574 | var buf = try ArrayList(u16).initCapacity(allocator, 128); | ||
| 1575 | errdefer buf.deinit(allocator); | ||
| 1576 | while (true) { | ||
| 1577 | const unused_slice = buf.unusedCapacitySlice(); | ||
| 1578 | // TODO: Get the system directory from PEB.ReadOnlyStaticServerData | ||
| 1579 | const len = windows.kernel32.GetSystemDirectoryW(@ptrCast(unused_slice), @intCast(unused_slice.len)); | ||
| 1580 | if (len == 0) { | ||
| 1581 | switch (windows.GetLastError()) { | ||
| 1582 | else => |err| return windows.unexpectedError(err), | ||
| 1583 | } | ||
| 1584 | } | ||
| 1585 | if (len > unused_slice.len) { | ||
| 1586 | try buf.ensureUnusedCapacity(allocator, len); | ||
| 1587 | } else { | ||
| 1588 | buf.items.len = len; | ||
| 1589 | break; | ||
| 1590 | } | ||
| 1591 | } | ||
| 1592 | switch (buf.items[buf.items.len - 1]) { | ||
| 1593 | '/', '\\' => {}, | ||
| 1594 | else => try buf.append(allocator, fs.path.sep), | ||
| 1595 | } | ||
| 1596 | try buf.appendSlice(allocator, unicode.utf8ToUtf16LeStringLiteral("cmd.exe")); | ||
| 1597 | return try buf.toOwnedSliceSentinel(allocator, 0); | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | const ArgvToCommandLineError = error{ OutOfMemory, InvalidWtf8, InvalidArg0 }; | ||
| 1601 | |||
| 1602 | /// Serializes `argv` to a Windows command-line string suitable for passing to a child process and | ||
| 1603 | /// parsing by the `CommandLineToArgvW` algorithm. The caller owns the returned slice. | ||
| 1604 | /// | ||
| 1605 | /// To avoid arbitrary command execution, this function should not be used when spawning `.bat`/`.cmd` scripts. | ||
| 1606 | /// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/ | ||
| 1607 | /// | ||
| 1608 | /// When executing `.bat`/`.cmd` scripts, use `argvToScriptCommandLineWindows` instead. | ||
| 1609 | fn argvToCommandLineWindows( | ||
| 1610 | allocator: Allocator, | ||
| 1611 | argv: []const []const u8, | ||
| 1612 | ) ArgvToCommandLineError![:0]u16 { | ||
| 1613 | var buf = std.array_list.Managed(u8).init(allocator); | ||
| 1614 | defer buf.deinit(); | ||
| 1615 | |||
| 1616 | if (argv.len != 0) { | ||
| 1617 | const arg0 = argv[0]; | ||
| 1618 | |||
| 1619 | // The first argument must be quoted if it contains spaces or ASCII control characters | ||
| 1620 | // (excluding DEL). It also follows special quoting rules where backslashes have no special | ||
| 1621 | // interpretation, which makes it impossible to pass certain first arguments containing | ||
| 1622 | // double quotes to a child process without characters from the first argument leaking into | ||
| 1623 | // subsequent ones (which could have security implications). | ||
| 1624 | // | ||
| 1625 | // Empty arguments technically don't need quotes, but we quote them anyway for maximum | ||
| 1626 | // compatibility with different implementations of the 'CommandLineToArgvW' algorithm. | ||
| 1627 | // | ||
| 1628 | // Double quotes are illegal in paths on Windows, so for the sake of simplicity we reject | ||
| 1629 | // all first arguments containing double quotes, even ones that we could theoretically | ||
| 1630 | // serialize in unquoted form. | ||
| 1631 | var needs_quotes = arg0.len == 0; | ||
| 1632 | for (arg0) |c| { | ||
| 1633 | if (c <= ' ') { | ||
| 1634 | needs_quotes = true; | ||
| 1635 | } else if (c == '"') { | ||
| 1636 | return error.InvalidArg0; | ||
| 1637 | } | ||
| 1638 | } | ||
| 1639 | if (needs_quotes) { | ||
| 1640 | try buf.append('"'); | ||
| 1641 | try buf.appendSlice(arg0); | ||
| 1642 | try buf.append('"'); | ||
| 1643 | } else { | ||
| 1644 | try buf.appendSlice(arg0); | ||
| 1645 | } | ||
| 1646 | |||
| 1647 | for (argv[1..]) |arg| { | ||
| 1648 | try buf.append(' '); | ||
| 1649 | |||
| 1650 | // Subsequent arguments must be quoted if they contain spaces, tabs or double quotes, | ||
| 1651 | // or if they are empty. For simplicity and for maximum compatibility with different | ||
| 1652 | // implementations of the 'CommandLineToArgvW' algorithm, we also quote all ASCII | ||
| 1653 | // control characters (again, excluding DEL). | ||
| 1654 | needs_quotes = for (arg) |c| { | ||
| 1655 | if (c <= ' ' or c == '"') { | ||
| 1656 | break true; | ||
| 1657 | } | ||
| 1658 | } else arg.len == 0; | ||
| 1659 | if (!needs_quotes) { | ||
| 1660 | try buf.appendSlice(arg); | ||
| 1661 | continue; | ||
| 1662 | } | ||
| 1663 | |||
| 1664 | try buf.append('"'); | ||
| 1665 | var backslash_count: usize = 0; | ||
| 1666 | for (arg) |byte| { | ||
| 1667 | switch (byte) { | ||
| 1668 | '\\' => { | ||
| 1669 | backslash_count += 1; | ||
| 1670 | }, | ||
| 1671 | '"' => { | ||
| 1672 | try buf.appendNTimes('\\', backslash_count * 2 + 1); | ||
| 1673 | try buf.append('"'); | ||
| 1674 | backslash_count = 0; | ||
| 1675 | }, | ||
| 1676 | else => { | ||
| 1677 | try buf.appendNTimes('\\', backslash_count); | ||
| 1678 | try buf.append(byte); | ||
| 1679 | backslash_count = 0; | ||
| 1680 | }, | ||
| 1681 | } | ||
| 1682 | } | ||
| 1683 | try buf.appendNTimes('\\', backslash_count * 2); | ||
| 1684 | try buf.append('"'); | ||
| 1685 | } | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | return try unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items); | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | test argvToCommandLineWindows { | ||
| 1692 | const t = testArgvToCommandLineWindows; | ||
| 1693 | |||
| 1694 | try t(&.{ | ||
| 1695 | \\C:\Program Files\zig\zig.exe | ||
| 1696 | , | ||
| 1697 | \\run | ||
| 1698 | , | ||
| 1699 | \\.\src\main.zig | ||
| 1700 | , | ||
| 1701 | \\-target | ||
| 1702 | , | ||
| 1703 | \\x86_64-windows-gnu | ||
| 1704 | , | ||
| 1705 | \\-O | ||
| 1706 | , | ||
| 1707 | \\ReleaseSafe | ||
| 1708 | , | ||
| 1709 | \\-- | ||
| 1710 | , | ||
| 1711 | \\--emoji=🗿 | ||
| 1712 | , | ||
| 1713 | \\--eval=new Regex("Dwayne \"The Rock\" Johnson") | ||
| 1714 | , | ||
| 1715 | }, | ||
| 1716 | \\"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\")" | ||
| 1717 | ); | ||
| 1718 | |||
| 1719 | try t(&.{}, ""); | ||
| 1720 | try t(&.{""}, "\"\""); | ||
| 1721 | try t(&.{" "}, "\" \""); | ||
| 1722 | try t(&.{"\t"}, "\"\t\""); | ||
| 1723 | try t(&.{"\x07"}, "\"\x07\""); | ||
| 1724 | try t(&.{"🦎"}, "🦎"); | ||
| 1725 | |||
| 1726 | try t( | ||
| 1727 | &.{ "zig", "aa aa", "bb\tbb", "cc\ncc", "dd\r\ndd", "ee\x7Fee" }, | ||
| 1728 | "zig \"aa aa\" \"bb\tbb\" \"cc\ncc\" \"dd\r\ndd\" ee\x7Fee", | ||
| 1729 | ); | ||
| 1730 | |||
| 1731 | try t( | ||
| 1732 | &.{ "\\\\foo bar\\foo bar\\", "\\\\zig zag\\zig zag\\" }, | ||
| 1733 | "\"\\\\foo bar\\foo bar\\\" \"\\\\zig zag\\zig zag\\\\\"", | ||
| 1734 | ); | ||
| 1735 | |||
| 1736 | try std.testing.expectError( | ||
| 1737 | error.InvalidArg0, | ||
| 1738 | argvToCommandLineWindows(std.testing.allocator, &.{"\"quotes\"quotes\""}), | ||
| 1739 | ); | ||
| 1740 | try std.testing.expectError( | ||
| 1741 | error.InvalidArg0, | ||
| 1742 | argvToCommandLineWindows(std.testing.allocator, &.{"quotes\"quotes"}), | ||
| 1743 | ); | ||
| 1744 | try std.testing.expectError( | ||
| 1745 | error.InvalidArg0, | ||
| 1746 | argvToCommandLineWindows(std.testing.allocator, &.{"q u o t e s \" q u o t e s"}), | ||
| 1747 | ); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []const u8) !void { | ||
| 1751 | const cmd_line_w = try argvToCommandLineWindows(std.testing.allocator, argv); | ||
| 1752 | defer std.testing.allocator.free(cmd_line_w); | ||
| 1753 | |||
| 1754 | const cmd_line = try unicode.wtf16LeToWtf8Alloc(std.testing.allocator, cmd_line_w); | ||
| 1755 | defer std.testing.allocator.free(cmd_line); | ||
| 1756 | |||
| 1757 | try std.testing.expectEqualStrings(expected_cmd_line, cmd_line); | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | const ArgvToScriptCommandLineError = error{ | ||
| 1761 | OutOfMemory, | ||
| 1762 | InvalidWtf8, | ||
| 1763 | /// NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed | ||
| 1764 | /// within arguments when executing a `.bat`/`.cmd` script. | ||
| 1765 | /// - NUL/LF signifiies end of arguments, so anything afterwards | ||
| 1766 | /// would be lost after execution. | ||
| 1767 | /// - CR is stripped by `cmd.exe`, so any CR codepoints | ||
| 1768 | /// would be lost after execution. | ||
| 1769 | InvalidBatchScriptArg, | ||
| 1770 | }; | ||
| 1771 | |||
| 1772 | /// Serializes `argv` to a Windows command-line string that uses `cmd.exe /c` and `cmd.exe`-specific | ||
| 1773 | /// escaping rules. The caller owns the returned slice. | ||
| 1774 | /// | ||
| 1775 | /// Escapes `argv` using the suggested mitigation against arbitrary command execution from: | ||
| 1776 | /// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/ | ||
| 1777 | /// | ||
| 1778 | /// The return of this function will look like | ||
| 1779 | /// `cmd.exe /d /e:ON /v:OFF /c "<escaped command line>"` | ||
| 1780 | /// and should be used as the `lpCommandLine` of `CreateProcessW`, while the | ||
| 1781 | /// return of `windowsCmdExePath` should be used as `lpApplicationName`. | ||
| 1782 | /// | ||
| 1783 | /// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise. | ||
| 1784 | /// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem. | ||
| 1785 | fn argvToScriptCommandLineWindows( | ||
| 1786 | allocator: Allocator, | ||
| 1787 | /// Path to the `.bat`/`.cmd` script. If this path is relative, it is assumed to be relative to the CWD. | ||
| 1788 | /// The script must have been verified to exist at this path before calling this function. | ||
| 1789 | script_path: []const u16, | ||
| 1790 | /// Arguments, not including the script name itself. Expected to be encoded as WTF-8. | ||
| 1791 | script_args: []const []const u8, | ||
| 1792 | ) ArgvToScriptCommandLineError![:0]u16 { | ||
| 1793 | var buf = try std.array_list.Managed(u8).initCapacity(allocator, 64); | ||
| 1794 | defer buf.deinit(); | ||
| 1795 | |||
| 1796 | // `/d` disables execution of AutoRun commands. | ||
| 1797 | // `/e:ON` and `/v:OFF` are needed for BatBadBut mitigation: | ||
| 1798 | // > If delayed expansion is enabled via the registry value DelayedExpansion, | ||
| 1799 | // > it must be disabled by explicitly calling cmd.exe with the /V:OFF option. | ||
| 1800 | // > Escaping for % requires the command extension to be enabled. | ||
| 1801 | // > If it’s disabled via the registry value EnableExtensions, it must be enabled with the /E:ON option. | ||
| 1802 | // https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/ | ||
| 1803 | buf.appendSliceAssumeCapacity("cmd.exe /d /e:ON /v:OFF /c \""); | ||
| 1804 | |||
| 1805 | // Always quote the path to the script arg | ||
| 1806 | buf.appendAssumeCapacity('"'); | ||
| 1807 | // We always want the path to the batch script to include a path separator in order to | ||
| 1808 | // avoid cmd.exe searching the PATH for the script. This is not part of the arbitrary | ||
| 1809 | // command execution mitigation, we just know exactly what script we want to execute | ||
| 1810 | // at this point, and potentially making cmd.exe re-find it is unnecessary. | ||
| 1811 | // | ||
| 1812 | // If the script path does not have a path separator, then we know its relative to CWD and | ||
| 1813 | // we can just put `.\` in the front. | ||
| 1814 | if (mem.findAny(u16, script_path, &[_]u16{ mem.nativeToLittle(u16, '\\'), mem.nativeToLittle(u16, '/') }) == null) { | ||
| 1815 | try buf.appendSlice(".\\"); | ||
| 1816 | } | ||
| 1817 | // Note that we don't do any escaping/mitigations for this argument, since the relevant | ||
| 1818 | // characters (", %, etc) are illegal in file paths and this function should only be called | ||
| 1819 | // with script paths that have been verified to exist. | ||
| 1820 | try unicode.wtf16LeToWtf8ArrayList(&buf, script_path); | ||
| 1821 | buf.appendAssumeCapacity('"'); | ||
| 1822 | |||
| 1823 | for (script_args) |arg| { | ||
| 1824 | // Literal carriage returns get stripped when run through cmd.exe | ||
| 1825 | // and NUL/newlines act as 'end of command.' Because of this, it's basically | ||
| 1826 | // always a mistake to include these characters in argv, so it's | ||
| 1827 | // an error condition in order to ensure that the return of this | ||
| 1828 | // function can always roundtrip through cmd.exe. | ||
| 1829 | if (std.mem.findAny(u8, arg, "\x00\r\n") != null) { | ||
| 1830 | return error.InvalidBatchScriptArg; | ||
| 1831 | } | ||
| 1832 | |||
| 1833 | // Separate args with a space. | ||
| 1834 | try buf.append(' '); | ||
| 1835 | |||
| 1836 | // Need to quote if the argument is empty (otherwise the arg would just be lost) | ||
| 1837 | // or if the last character is a `\`, since then something like "%~2" in a .bat | ||
| 1838 | // script would cause the closing " to be escaped which we don't want. | ||
| 1839 | var needs_quotes = arg.len == 0 or arg[arg.len - 1] == '\\'; | ||
| 1840 | if (!needs_quotes) { | ||
| 1841 | for (arg) |c| { | ||
| 1842 | switch (c) { | ||
| 1843 | // Known good characters that don't need to be quoted | ||
| 1844 | 'A'...'Z', 'a'...'z', '0'...'9', '#', '$', '*', '+', '-', '.', '/', ':', '?', '@', '\\', '_' => {}, | ||
| 1845 | // When in doubt, quote | ||
| 1846 | else => { | ||
| 1847 | needs_quotes = true; | ||
| 1848 | break; | ||
| 1849 | }, | ||
| 1850 | } | ||
| 1851 | } | ||
| 1852 | } | ||
| 1853 | if (needs_quotes) { | ||
| 1854 | try buf.append('"'); | ||
| 1855 | } | ||
| 1856 | var backslashes: usize = 0; | ||
| 1857 | for (arg) |c| { | ||
| 1858 | switch (c) { | ||
| 1859 | '\\' => { | ||
| 1860 | backslashes += 1; | ||
| 1861 | }, | ||
| 1862 | '"' => { | ||
| 1863 | try buf.appendNTimes('\\', backslashes); | ||
| 1864 | try buf.append('"'); | ||
| 1865 | backslashes = 0; | ||
| 1866 | }, | ||
| 1867 | // Replace `%` with `%%cd:~,%`. | ||
| 1868 | // | ||
| 1869 | // cmd.exe allows extracting a substring from an environment | ||
| 1870 | // variable with the syntax: `%foo:~<start_index>,<end_index>%`. | ||
| 1871 | // Therefore, `%cd:~,%` will always expand to an empty string | ||
| 1872 | // since both the start and end index are blank, and it is assumed | ||
| 1873 | // that `%cd%` is always available since it is a built-in variable | ||
| 1874 | // that corresponds to the current directory. | ||
| 1875 | // | ||
| 1876 | // This means that replacing `%foo%` with `%%cd:~,%foo%%cd:~,%` | ||
| 1877 | // will stop `%foo%` from being expanded and *after* expansion | ||
| 1878 | // we'll still be left with `%foo%` (the literal string). | ||
| 1879 | '%' => { | ||
| 1880 | // the trailing `%` is appended outside the switch | ||
| 1881 | try buf.appendSlice("%%cd:~,"); | ||
| 1882 | backslashes = 0; | ||
| 1883 | }, | ||
| 1884 | else => { | ||
| 1885 | backslashes = 0; | ||
| 1886 | }, | ||
| 1887 | } | ||
| 1888 | try buf.append(c); | ||
| 1889 | } | ||
| 1890 | if (needs_quotes) { | ||
| 1891 | try buf.appendNTimes('\\', backslashes); | ||
| 1892 | try buf.append('"'); | ||
| 1893 | } | ||
| 1894 | } | ||
| 1895 | |||
| 1896 | try buf.append('"'); | ||
| 1897 | |||
| 1898 | return try unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items); | ||
| 1899 | } |
lib/std/process/Environ.zig created+822| ... | @@ -0,0 +1,822 @@ | ||
| 1 | const Environ = @This(); | ||
| 2 | |||
| 3 | const builtin = @import("builtin"); | ||
| 4 | const native_os = builtin.os.tag; | ||
| 5 | |||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | const testing = std.testing; | ||
| 10 | const unicode = std.unicode; | ||
| 11 | const posix = std.posix; | ||
| 12 | const mem = std.mem; | ||
| 13 | |||
| 14 | /// Unmodified, unprocessed data provided by the operating system. | ||
| 15 | block: Block, | ||
| 16 | |||
| 17 | pub const empty: Environ = .{ | ||
| 18 | .block = switch (Block) { | ||
| 19 | void => {}, | ||
| 20 | else => &.{}, | ||
| 21 | }, | ||
| 22 | }; | ||
| 23 | |||
| 24 | /// On WASI without libc, this is `void` because the environment has to be | ||
| 25 | /// queried and heap-allocated at runtime. | ||
| 26 | /// | ||
| 27 | /// On Windows, the memory pointed at by the PEB changes when the environment | ||
| 28 | /// is modified, so a long-lived pointer cannot be used. Therefore, on this | ||
| 29 | /// operating system `void` is also used. | ||
| 30 | pub const Block = switch (native_os) { | ||
| 31 | .windows => void, | ||
| 32 | .wasi => switch (builtin.link_libc) { | ||
| 33 | false => void, | ||
| 34 | true => [:null]const ?[*:0]const u8, | ||
| 35 | }, | ||
| 36 | .freestanding, .other => void, | ||
| 37 | else => [:null]const ?[*:0]const u8, | ||
| 38 | }; | ||
| 39 | |||
| 40 | pub const Map = struct { | ||
| 41 | array_hash_map: ArrayHashMap, | ||
| 42 | allocator: Allocator, | ||
| 43 | |||
| 44 | const ArrayHashMap = std.ArrayHashMapUnmanaged([]const u8, []const u8, EnvNameHashContext, false); | ||
| 45 | |||
| 46 | pub const Size = usize; | ||
| 47 | |||
| 48 | pub const EnvNameHashContext = struct { | ||
| 49 | fn upcase(c: u21) u21 { | ||
| 50 | if (c <= std.math.maxInt(u16)) | ||
| 51 | return std.os.windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c))); | ||
| 52 | return c; | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn hash(self: @This(), s: []const u8) u32 { | ||
| 56 | _ = self; | ||
| 57 | if (native_os == .windows) { | ||
| 58 | var h = std.hash.Wyhash.init(0); | ||
| 59 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); | ||
| 60 | while (it.nextCodepoint()) |cp| { | ||
| 61 | const cp_upper = upcase(cp); | ||
| 62 | h.update(&[_]u8{ | ||
| 63 | @as(u8, @intCast((cp_upper >> 16) & 0xff)), | ||
| 64 | @as(u8, @intCast((cp_upper >> 8) & 0xff)), | ||
| 65 | @as(u8, @intCast((cp_upper >> 0) & 0xff)), | ||
| 66 | }); | ||
| 67 | } | ||
| 68 | return @truncate(h.final()); | ||
| 69 | } | ||
| 70 | return std.array_hash_map.hashString(s); | ||
| 71 | } | ||
| 72 | |||
| 73 | pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool { | ||
| 74 | _ = self; | ||
| 75 | _ = b_index; | ||
| 76 | if (native_os == .windows) { | ||
| 77 | var it_a = unicode.Wtf8View.initUnchecked(a).iterator(); | ||
| 78 | var it_b = unicode.Wtf8View.initUnchecked(b).iterator(); | ||
| 79 | while (true) { | ||
| 80 | const c_a = it_a.nextCodepoint() orelse break; | ||
| 81 | const c_b = it_b.nextCodepoint() orelse return false; | ||
| 82 | if (upcase(c_a) != upcase(c_b)) | ||
| 83 | return false; | ||
| 84 | } | ||
| 85 | return if (it_b.nextCodepoint()) |_| false else true; | ||
| 86 | } | ||
| 87 | return std.array_hash_map.eqlString(a, b); | ||
| 88 | } | ||
| 89 | }; | ||
| 90 | |||
| 91 | /// Create a Map backed by a specific allocator. | ||
| 92 | /// That allocator will be used for both backing allocations | ||
| 93 | /// and string deduplication. | ||
| 94 | pub fn init(allocator: Allocator) Map { | ||
| 95 | return .{ .array_hash_map = .empty, .allocator = allocator }; | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Free the backing storage of the map, as well as all | ||
| 99 | /// of the stored keys and values. | ||
| 100 | pub fn deinit(self: *Map) void { | ||
| 101 | const gpa = self.allocator; | ||
| 102 | var it = self.array_hash_map.iterator(); | ||
| 103 | while (it.next()) |entry| { | ||
| 104 | gpa.free(entry.key_ptr.*); | ||
| 105 | gpa.free(entry.value_ptr.*); | ||
| 106 | } | ||
| 107 | self.array_hash_map.deinit(gpa); | ||
| 108 | self.* = undefined; | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn keys(m: *const Map) [][]const u8 { | ||
| 112 | return m.array_hash_map.keys(); | ||
| 113 | } | ||
| 114 | |||
| 115 | pub fn values(m: *const Map) [][]const u8 { | ||
| 116 | return m.array_hash_map.values(); | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Same as `put` but the key and value become owned by the Map rather | ||
| 120 | /// than being copied. | ||
| 121 | /// If `putMove` fails, the ownership of key and value does not transfer. | ||
| 122 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 123 | pub fn putMove(self: *Map, key: []u8, value: []u8) !void { | ||
| 124 | const gpa = self.allocator; | ||
| 125 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 126 | const get_or_put = try self.array_hash_map.getOrPut(gpa, key); | ||
| 127 | if (get_or_put.found_existing) { | ||
| 128 | gpa.free(get_or_put.key_ptr.*); | ||
| 129 | gpa.free(get_or_put.value_ptr.*); | ||
| 130 | get_or_put.key_ptr.* = key; | ||
| 131 | } | ||
| 132 | get_or_put.value_ptr.* = value; | ||
| 133 | } | ||
| 134 | |||
| 135 | /// `key` and `value` are copied into the Map. | ||
| 136 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 137 | pub fn put(self: *Map, key: []const u8, value: []const u8) !void { | ||
| 138 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 139 | const gpa = self.allocator; | ||
| 140 | const value_copy = try gpa.dupe(u8, value); | ||
| 141 | errdefer gpa.free(value_copy); | ||
| 142 | const get_or_put = try self.array_hash_map.getOrPut(gpa, key); | ||
| 143 | errdefer { | ||
| 144 | if (!get_or_put.found_existing) assert(self.array_hash_map.pop() != null); | ||
| 145 | } | ||
| 146 | if (get_or_put.found_existing) { | ||
| 147 | gpa.free(get_or_put.value_ptr.*); | ||
| 148 | } else { | ||
| 149 | get_or_put.key_ptr.* = try gpa.dupe(u8, key); | ||
| 150 | } | ||
| 151 | get_or_put.value_ptr.* = value_copy; | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Find the address of the value associated with a key. | ||
| 155 | /// The returned pointer is invalidated if the map resizes. | ||
| 156 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 157 | pub fn getPtr(self: Map, key: []const u8) ?*[]const u8 { | ||
| 158 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 159 | return self.array_hash_map.getPtr(key); | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Return the map's copy of the value associated with | ||
| 163 | /// a key. The returned string is invalidated if this | ||
| 164 | /// key is removed from the map. | ||
| 165 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 166 | pub fn get(self: Map, key: []const u8) ?[]const u8 { | ||
| 167 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 168 | return self.array_hash_map.get(key); | ||
| 169 | } | ||
| 170 | |||
| 171 | pub fn contains(m: *const Map, key: []const u8) bool { | ||
| 172 | return m.array_hash_map.contains(key); | ||
| 173 | } | ||
| 174 | |||
| 175 | /// If there is an entry with a matching key, it is deleted from the hash | ||
| 176 | /// map. The entry is removed from the underlying array by swapping it with | ||
| 177 | /// the last element. | ||
| 178 | /// | ||
| 179 | /// Returns true if an entry was removed, false otherwise. | ||
| 180 | /// | ||
| 181 | /// This invalidates the value returned by get() for this key. | ||
| 182 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 183 | pub fn swapRemove(self: *Map, key: []const u8) bool { | ||
| 184 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 185 | const kv = self.array_hash_map.fetchSwapRemove(key) orelse return false; | ||
| 186 | const gpa = self.allocator; | ||
| 187 | gpa.free(kv.key); | ||
| 188 | gpa.free(kv.value); | ||
| 189 | return true; | ||
| 190 | } | ||
| 191 | |||
| 192 | /// If there is an entry with a matching key, it is deleted from the map. | ||
| 193 | /// The entry is removed from the underlying array by shifting all elements | ||
| 194 | /// forward, thereby maintaining the current ordering. | ||
| 195 | /// | ||
| 196 | /// Returns true if an entry was removed, false otherwise. | ||
| 197 | /// | ||
| 198 | /// This invalidates the value returned by get() for this key. | ||
| 199 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | ||
| 200 | pub fn orderedRemove(self: *Map, key: []const u8) bool { | ||
| 201 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 202 | const kv = self.array_hash_map.fetchOrderedRemove(key) orelse return false; | ||
| 203 | const gpa = self.allocator; | ||
| 204 | gpa.free(kv.key); | ||
| 205 | gpa.free(kv.value); | ||
| 206 | return true; | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Returns the number of KV pairs stored in the map. | ||
| 210 | pub fn count(self: Map) Size { | ||
| 211 | return self.array_hash_map.count(); | ||
| 212 | } | ||
| 213 | |||
| 214 | /// Returns an iterator over entries in the map. | ||
| 215 | pub fn iterator(self: *const Map) ArrayHashMap.Iterator { | ||
| 216 | return self.array_hash_map.iterator(); | ||
| 217 | } | ||
| 218 | |||
| 219 | /// Returns a full copy of `em` allocated with `gpa`, which is not necessarily | ||
| 220 | /// the same allocator used to allocate `em`. | ||
| 221 | pub fn clone(m: *const Map, gpa: Allocator) Allocator.Error!Map { | ||
| 222 | // Since we need to dupe the keys and values, the only way for error handling to not be a | ||
| 223 | // nightmare is to add keys to an empty map one-by-one. This could be avoided if this | ||
| 224 | // abstraction were a bit less... OOP-esque. | ||
| 225 | var new: Map = .init(gpa); | ||
| 226 | errdefer new.deinit(); | ||
| 227 | try new.array_hash_map.ensureUnusedCapacity(gpa, m.array_hash_map.count()); | ||
| 228 | for (m.array_hash_map.keys(), m.array_hash_map.values()) |key, value| { | ||
| 229 | try new.put(key, value); | ||
| 230 | } | ||
| 231 | return new; | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Creates a null-delimited environment variable block in the format | ||
| 235 | /// expected by POSIX, from a hash map plus options. | ||
| 236 | pub fn createBlockPosix( | ||
| 237 | map: *const Map, | ||
| 238 | arena: Allocator, | ||
| 239 | options: CreateBlockPosixOptions, | ||
| 240 | ) Allocator.Error![:null]?[*:0]u8 { | ||
| 241 | const ZigProgressAction = enum { nothing, edit, delete, add }; | ||
| 242 | const zig_progress_action: ZigProgressAction = a: { | ||
| 243 | const fd = options.zig_progress_fd orelse break :a .nothing; | ||
| 244 | const exists = map.get("ZIG_PROGRESS") != null; | ||
| 245 | if (fd >= 0) { | ||
| 246 | break :a if (exists) .edit else .add; | ||
| 247 | } else { | ||
| 248 | if (exists) break :a .delete; | ||
| 249 | } | ||
| 250 | break :a .nothing; | ||
| 251 | }; | ||
| 252 | |||
| 253 | const envp_count: usize = c: { | ||
| 254 | var c: usize = map.count(); | ||
| 255 | switch (zig_progress_action) { | ||
| 256 | .add => c += 1, | ||
| 257 | .delete => c -= 1, | ||
| 258 | .nothing, .edit => {}, | ||
| 259 | } | ||
| 260 | break :c c; | ||
| 261 | }; | ||
| 262 | |||
| 263 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | ||
| 264 | var i: usize = 0; | ||
| 265 | |||
| 266 | if (zig_progress_action == .add) { | ||
| 267 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | ||
| 268 | i += 1; | ||
| 269 | } | ||
| 270 | |||
| 271 | { | ||
| 272 | var it = map.iterator(); | ||
| 273 | while (it.next()) |pair| { | ||
| 274 | if (mem.eql(u8, pair.key_ptr.*, "ZIG_PROGRESS")) switch (zig_progress_action) { | ||
| 275 | .add => unreachable, | ||
| 276 | .delete => continue, | ||
| 277 | .edit => { | ||
| 278 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{ | ||
| 279 | pair.key_ptr.*, options.zig_progress_fd.?, | ||
| 280 | }, 0); | ||
| 281 | i += 1; | ||
| 282 | continue; | ||
| 283 | }, | ||
| 284 | .nothing => {}, | ||
| 285 | }; | ||
| 286 | |||
| 287 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); | ||
| 288 | i += 1; | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | assert(i == envp_count); | ||
| 293 | return envp_buf; | ||
| 294 | } | ||
| 295 | |||
| 296 | /// Caller owns result. | ||
| 297 | pub fn createBlockWindows(map: *const Map, gpa: Allocator) error{ OutOfMemory, InvalidWtf8 }![:0]u16 { | ||
| 298 | // count bytes needed | ||
| 299 | const max_chars_needed = x: { | ||
| 300 | // Only need 2 trailing NUL code units for an empty environment | ||
| 301 | var max_chars_needed: usize = if (map.count() == 0) 2 else 1; | ||
| 302 | var it = map.iterator(); | ||
| 303 | while (it.next()) |pair| { | ||
| 304 | // +1 for '=' | ||
| 305 | // +1 for null byte | ||
| 306 | max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; | ||
| 307 | } | ||
| 308 | break :x max_chars_needed; | ||
| 309 | }; | ||
| 310 | const result = try gpa.alloc(u16, max_chars_needed); | ||
| 311 | errdefer gpa.free(result); | ||
| 312 | |||
| 313 | var it = map.iterator(); | ||
| 314 | var i: usize = 0; | ||
| 315 | while (it.next()) |pair| { | ||
| 316 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*); | ||
| 317 | result[i] = '='; | ||
| 318 | i += 1; | ||
| 319 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*); | ||
| 320 | result[i] = 0; | ||
| 321 | i += 1; | ||
| 322 | } | ||
| 323 | result[i] = 0; | ||
| 324 | i += 1; | ||
| 325 | // An empty environment is a special case that requires a redundant | ||
| 326 | // NUL terminator. CreateProcess will read the second code unit even | ||
| 327 | // though theoretically the first should be enough to recognize that the | ||
| 328 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) | ||
| 329 | if (map.count() == 0) { | ||
| 330 | result[i] = 0; | ||
| 331 | i += 1; | ||
| 332 | } | ||
| 333 | const reallocated = try gpa.realloc(result, i); | ||
| 334 | return reallocated[0 .. i - 1 :0]; | ||
| 335 | } | ||
| 336 | }; | ||
| 337 | |||
| 338 | pub const CreateMapError = error{ | ||
| 339 | OutOfMemory, | ||
| 340 | /// WASI-only. `environ_sizes_get` or `environ_get` failed for an | ||
| 341 | /// unanticipated, undocumented reason. | ||
| 342 | Unexpected, | ||
| 343 | }; | ||
| 344 | |||
| 345 | /// Allocates a `Map` and copies environment block into it. | ||
| 346 | pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | ||
| 347 | if (native_os == .windows) | ||
| 348 | return createMapWide(std.os.windows.peb().ProcessParameters.Environment, allocator); | ||
| 349 | |||
| 350 | var result = Map.init(allocator); | ||
| 351 | errdefer result.deinit(); | ||
| 352 | |||
| 353 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 354 | var environ_count: usize = undefined; | ||
| 355 | var environ_buf_size: usize = undefined; | ||
| 356 | |||
| 357 | const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size); | ||
| 358 | if (environ_sizes_get_ret != .SUCCESS) { | ||
| 359 | return posix.unexpectedErrno(environ_sizes_get_ret); | ||
| 360 | } | ||
| 361 | |||
| 362 | if (environ_count == 0) { | ||
| 363 | return result; | ||
| 364 | } | ||
| 365 | |||
| 366 | const environ = try allocator.alloc([*:0]u8, environ_count); | ||
| 367 | defer allocator.free(environ); | ||
| 368 | const environ_buf = try allocator.alloc(u8, environ_buf_size); | ||
| 369 | defer allocator.free(environ_buf); | ||
| 370 | |||
| 371 | const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr); | ||
| 372 | if (environ_get_ret != .SUCCESS) { | ||
| 373 | return posix.unexpectedErrno(environ_get_ret); | ||
| 374 | } | ||
| 375 | |||
| 376 | for (environ) |line| { | ||
| 377 | const pair = mem.sliceTo(line, 0); | ||
| 378 | var parts = mem.splitScalar(u8, pair, '='); | ||
| 379 | const key = parts.first(); | ||
| 380 | const value = parts.rest(); | ||
| 381 | try result.put(key, value); | ||
| 382 | } | ||
| 383 | return result; | ||
| 384 | } else { | ||
| 385 | for (env.block) |opt_line| { | ||
| 386 | const line = opt_line.?; | ||
| 387 | var line_i: usize = 0; | ||
| 388 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | ||
| 389 | const key = line[0..line_i]; | ||
| 390 | |||
| 391 | var end_i: usize = line_i; | ||
| 392 | while (line[end_i] != 0) : (end_i += 1) {} | ||
| 393 | const value = line[line_i + 1 .. end_i]; | ||
| 394 | |||
| 395 | try result.put(key, value); | ||
| 396 | } | ||
| 397 | return result; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | pub fn createMapWide(ptr: [*:0]u16, gpa: Allocator) CreateMapError!Map { | ||
| 402 | var result = Map.init(gpa); | ||
| 403 | errdefer result.deinit(); | ||
| 404 | |||
| 405 | var i: usize = 0; | ||
| 406 | while (ptr[i] != 0) { | ||
| 407 | const key_start = i; | ||
| 408 | |||
| 409 | // There are some special environment variables that start with =, | ||
| 410 | // so we need a special case to not treat = as a key/value separator | ||
| 411 | // if it's the first character. | ||
| 412 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 413 | if (ptr[key_start] == '=') i += 1; | ||
| 414 | |||
| 415 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | ||
| 416 | const key_w = ptr[key_start..i]; | ||
| 417 | const key = try unicode.wtf16LeToWtf8Alloc(gpa, key_w); | ||
| 418 | errdefer gpa.free(key); | ||
| 419 | |||
| 420 | if (ptr[i] == '=') i += 1; | ||
| 421 | |||
| 422 | const value_start = i; | ||
| 423 | while (ptr[i] != 0) : (i += 1) {} | ||
| 424 | const value_w = ptr[value_start..i]; | ||
| 425 | const value = try unicode.wtf16LeToWtf8Alloc(gpa, value_w); | ||
| 426 | errdefer gpa.free(value); | ||
| 427 | |||
| 428 | i += 1; // skip over null byte | ||
| 429 | |||
| 430 | try result.putMove(key, value); | ||
| 431 | } | ||
| 432 | return result; | ||
| 433 | } | ||
| 434 | |||
| 435 | pub const ContainsError = error{ | ||
| 436 | OutOfMemory, | ||
| 437 | /// On Windows, environment variable keys provided by the user must be | ||
| 438 | /// valid [WTF-8](https://wtf-8.codeberg.page/). This error is unreachable | ||
| 439 | /// if the key is statically known to be valid. | ||
| 440 | InvalidWtf8, | ||
| 441 | /// WASI-only. `environ_sizes_get` or `environ_get` failed for an | ||
| 442 | /// unexpected reason. | ||
| 443 | Unexpected, | ||
| 444 | }; | ||
| 445 | |||
| 446 | /// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/), | ||
| 447 | /// then `error.InvalidWtf8` is returned. | ||
| 448 | /// | ||
| 449 | /// See also: | ||
| 450 | /// * `createMap` | ||
| 451 | /// * `containsConstant` | ||
| 452 | /// * `containsUnempty` | ||
| 453 | pub fn contains(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool { | ||
| 454 | var map = try createMap(environ, gpa); | ||
| 455 | defer map.deinit(); | ||
| 456 | return map.contains(key); | ||
| 457 | } | ||
| 458 | |||
| 459 | /// On Windows, if `key` is not valid [WTF-8](https://wtf-8.codeberg.page/), | ||
| 460 | /// then `error.InvalidWtf8` is returned. | ||
| 461 | /// | ||
| 462 | /// See also: | ||
| 463 | /// * `createMap` | ||
| 464 | /// * `containsUnemptyConstant` | ||
| 465 | /// * `contains` | ||
| 466 | pub fn containsUnempty(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool { | ||
| 467 | var map = try createMap(environ, gpa); | ||
| 468 | defer map.deinit(); | ||
| 469 | const value = map.get(key) orelse return false; | ||
| 470 | return value.len != 0; | ||
| 471 | } | ||
| 472 | |||
| 473 | /// This function is unavailable on WASI without libc due to the memory | ||
| 474 | /// allocation requirement. | ||
| 475 | /// | ||
| 476 | /// On Windows, `key` must be valid [WTF-8](https://wtf-8.codeberg.page/), | ||
| 477 | /// | ||
| 478 | /// See also: | ||
| 479 | /// * `contains` | ||
| 480 | /// * `containsUnemptyConstant` | ||
| 481 | /// * `createMap` | ||
| 482 | pub inline fn containsConstant(environ: Environ, comptime key: []const u8) bool { | ||
| 483 | if (native_os == .windows) { | ||
| 484 | const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key); | ||
| 485 | return getWindows(environ, key_w) != null; | ||
| 486 | } else { | ||
| 487 | return getPosix(environ, key) != null; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | /// This function is unavailable on WASI without libc due to the memory | ||
| 492 | /// allocation requirement. | ||
| 493 | /// | ||
| 494 | /// On Windows, `key` must be valid [WTF-8](https://wtf-8.codeberg.page/), | ||
| 495 | /// | ||
| 496 | /// See also: | ||
| 497 | /// * `containsUnempty` | ||
| 498 | /// * `containsConstant` | ||
| 499 | /// * `createMap` | ||
| 500 | pub inline fn containsUnemptyConstant(environ: Environ, comptime key: []const u8) bool { | ||
| 501 | if (native_os == .windows) { | ||
| 502 | const key_w = comptime unicode.wtf8ToWtf16LeStringLiteral(key); | ||
| 503 | const value = getWindows(environ, key_w) orelse return false; | ||
| 504 | return value.len != 0; | ||
| 505 | } else { | ||
| 506 | const value = getPosix(environ, key) orelse return false; | ||
| 507 | return value.len != 0; | ||
| 508 | } | ||
| 509 | } | ||
| 510 | |||
| 511 | /// This function is unavailable on WASI without libc due to the memory | ||
| 512 | /// allocation requirement. | ||
| 513 | /// | ||
| 514 | /// See also: | ||
| 515 | /// * `getWindows` | ||
| 516 | /// * `createMap` | ||
| 517 | pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { | ||
| 518 | if (mem.findScalar(u8, key, '=') != null) return null; | ||
| 519 | for (environ.block) |opt_line| { | ||
| 520 | const line = opt_line.?; | ||
| 521 | var line_i: usize = 0; | ||
| 522 | while (line[line_i] != 0) : (line_i += 1) { | ||
| 523 | if (line_i == key.len) break; | ||
| 524 | if (line[line_i] != key[line_i]) break; | ||
| 525 | } | ||
| 526 | if ((line_i != key.len) or (line[line_i] != '=')) continue; | ||
| 527 | |||
| 528 | return mem.sliceTo(line + line_i + 1, 0); | ||
| 529 | } | ||
| 530 | return null; | ||
| 531 | } | ||
| 532 | |||
| 533 | /// Windows-only. Get an environment variable with a null-terminated, WTF-16 | ||
| 534 | /// encoded name. | ||
| 535 | /// | ||
| 536 | /// This function performs a Unicode-aware case-insensitive lookup using | ||
| 537 | /// RtlEqualUnicodeString. | ||
| 538 | /// | ||
| 539 | /// See also: | ||
| 540 | /// * `createMap` | ||
| 541 | /// * `containsConstant` | ||
| 542 | /// * `contains` | ||
| 543 | pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { | ||
| 544 | comptime assert(native_os == .windows); | ||
| 545 | comptime assert(@TypeOf(environ.block) == void); | ||
| 546 | |||
| 547 | // '=' anywhere but the start makes this an invalid environment variable name. | ||
| 548 | const key_slice = mem.sliceTo(key, 0); | ||
| 549 | if (key_slice.len > 0 and mem.findScalar(u16, key_slice[1..], '=') != null) return null; | ||
| 550 | |||
| 551 | const ptr = std.os.windows.peb().ProcessParameters.Environment; | ||
| 552 | |||
| 553 | var i: usize = 0; | ||
| 554 | while (ptr[i] != 0) { | ||
| 555 | const key_value = mem.sliceTo(ptr[i..], 0); | ||
| 556 | |||
| 557 | // There are some special environment variables that start with =, | ||
| 558 | // so we need a special case to not treat = as a key/value separator | ||
| 559 | // if it's the first character. | ||
| 560 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 561 | const equal_search_start: usize = if (key_value[0] == '=') 1 else 0; | ||
| 562 | const equal_index = mem.findScalarPos(u16, key_value, equal_search_start, '=') orelse { | ||
| 563 | // This is enforced by CreateProcess. | ||
| 564 | // If violated, CreateProcess will fail with INVALID_PARAMETER. | ||
| 565 | unreachable; // must contain a = | ||
| 566 | }; | ||
| 567 | |||
| 568 | const this_key = key_value[0..equal_index]; | ||
| 569 | if (std.os.windows.eqlIgnoreCaseWtf16(key_slice, this_key)) { | ||
| 570 | return key_value[equal_index + 1 ..]; | ||
| 571 | } | ||
| 572 | |||
| 573 | // skip past the NUL terminator | ||
| 574 | i += key_value.len + 1; | ||
| 575 | } | ||
| 576 | return null; | ||
| 577 | } | ||
| 578 | |||
| 579 | pub const GetAllocError = error{ | ||
| 580 | OutOfMemory, | ||
| 581 | EnvironmentVariableMissing, | ||
| 582 | /// On Windows, environment variable keys provided by the user must be | ||
| 583 | /// valid [WTF-8](https://wtf-8.codeberg.page/). This error is unreachable | ||
| 584 | /// if the key is statically known to be valid. | ||
| 585 | InvalidWtf8, | ||
| 586 | }; | ||
| 587 | |||
| 588 | /// Caller owns returned memory. | ||
| 589 | /// | ||
| 590 | /// On Windows: | ||
| 591 | /// * If `key` is not valid [WTF-8](https://wtf-8.codeberg.page/), then | ||
| 592 | /// `error.InvalidWtf8` is returned. | ||
| 593 | /// * The returned value is encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 594 | /// | ||
| 595 | /// On other platforms, the value is an opaque sequence of bytes with no | ||
| 596 | /// particular encoding. | ||
| 597 | /// | ||
| 598 | /// See also: | ||
| 599 | /// * `createMap` | ||
| 600 | pub fn getAlloc(environ: Environ, gpa: Allocator, key: []const u8) GetAllocError![]u8 { | ||
| 601 | var map = createMap(environ, gpa) catch return error.OutOfMemory; | ||
| 602 | defer map.deinit(); | ||
| 603 | const val = map.get(key) orelse return error.EnvironmentVariableMissing; | ||
| 604 | return gpa.dupe(u8, val); | ||
| 605 | } | ||
| 606 | |||
| 607 | pub const CreateBlockPosixOptions = struct { | ||
| 608 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. | ||
| 609 | /// If non-null, negative means to remove the environment variable, and >= 0 | ||
| 610 | /// means to provide it with the given integer. | ||
| 611 | zig_progress_fd: ?i32 = null, | ||
| 612 | }; | ||
| 613 | |||
| 614 | /// Creates a null-delimited environment variable block in the format expected | ||
| 615 | /// by POSIX, from a different one. | ||
| 616 | pub fn createBlockPosix( | ||
| 617 | existing: Environ, | ||
| 618 | arena: Allocator, | ||
| 619 | options: CreateBlockPosixOptions, | ||
| 620 | ) Allocator.Error![:null]?[*:0]u8 { | ||
| 621 | const contains_zig_progress = for (existing.block) |opt_line| { | ||
| 622 | if (mem.eql(u8, mem.sliceTo(opt_line.?, '='), "ZIG_PROGRESS")) break true; | ||
| 623 | } else false; | ||
| 624 | |||
| 625 | const ZigProgressAction = enum { nothing, edit, delete, add }; | ||
| 626 | const zig_progress_action: ZigProgressAction = a: { | ||
| 627 | const fd = options.zig_progress_fd orelse break :a .nothing; | ||
| 628 | if (fd >= 0) { | ||
| 629 | break :a if (contains_zig_progress) .edit else .add; | ||
| 630 | } else { | ||
| 631 | if (contains_zig_progress) break :a .delete; | ||
| 632 | } | ||
| 633 | break :a .nothing; | ||
| 634 | }; | ||
| 635 | |||
| 636 | const envp_count: usize = c: { | ||
| 637 | var count: usize = existing.block.len; | ||
| 638 | switch (zig_progress_action) { | ||
| 639 | .add => count += 1, | ||
| 640 | .delete => count -= 1, | ||
| 641 | .nothing, .edit => {}, | ||
| 642 | } | ||
| 643 | break :c count; | ||
| 644 | }; | ||
| 645 | |||
| 646 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | ||
| 647 | var i: usize = 0; | ||
| 648 | var existing_index: usize = 0; | ||
| 649 | |||
| 650 | if (zig_progress_action == .add) { | ||
| 651 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | ||
| 652 | i += 1; | ||
| 653 | } | ||
| 654 | |||
| 655 | while (existing.block[existing_index]) |line| : (existing_index += 1) { | ||
| 656 | if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | ||
| 657 | .add => unreachable, | ||
| 658 | .delete => continue, | ||
| 659 | .edit => { | ||
| 660 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | ||
| 661 | i += 1; | ||
| 662 | continue; | ||
| 663 | }, | ||
| 664 | .nothing => {}, | ||
| 665 | }; | ||
| 666 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); | ||
| 667 | i += 1; | ||
| 668 | } | ||
| 669 | |||
| 670 | assert(i == envp_count); | ||
| 671 | return envp_buf; | ||
| 672 | } | ||
| 673 | |||
| 674 | test "Map.createBlock" { | ||
| 675 | const allocator = testing.allocator; | ||
| 676 | var envmap = Map.init(allocator); | ||
| 677 | defer envmap.deinit(); | ||
| 678 | |||
| 679 | try envmap.put("HOME", "/home/ifreund"); | ||
| 680 | try envmap.put("WAYLAND_DISPLAY", "wayland-1"); | ||
| 681 | try envmap.put("DISPLAY", ":1"); | ||
| 682 | try envmap.put("DEBUGINFOD_URLS", " "); | ||
| 683 | try envmap.put("XCURSOR_SIZE", "24"); | ||
| 684 | |||
| 685 | var arena = std.heap.ArenaAllocator.init(allocator); | ||
| 686 | defer arena.deinit(); | ||
| 687 | const environ = try envmap.createBlockPosix(arena.allocator(), .{}); | ||
| 688 | |||
| 689 | try testing.expectEqual(@as(usize, 5), environ.len); | ||
| 690 | |||
| 691 | inline for (.{ | ||
| 692 | "HOME=/home/ifreund", | ||
| 693 | "WAYLAND_DISPLAY=wayland-1", | ||
| 694 | "DISPLAY=:1", | ||
| 695 | "DEBUGINFOD_URLS= ", | ||
| 696 | "XCURSOR_SIZE=24", | ||
| 697 | }) |target| { | ||
| 698 | for (environ) |variable| { | ||
| 699 | if (mem.eql(u8, mem.span(variable orelse continue), target)) break; | ||
| 700 | } else { | ||
| 701 | try testing.expect(false); // Environment variable not found | ||
| 702 | } | ||
| 703 | } | ||
| 704 | } | ||
| 705 | |||
| 706 | test Map { | ||
| 707 | var env = Map.init(testing.allocator); | ||
| 708 | defer env.deinit(); | ||
| 709 | |||
| 710 | try env.put("SOMETHING_NEW", "hello"); | ||
| 711 | try testing.expectEqualStrings("hello", env.get("SOMETHING_NEW").?); | ||
| 712 | try testing.expectEqual(@as(Map.Size, 1), env.count()); | ||
| 713 | |||
| 714 | // overwrite | ||
| 715 | try env.put("SOMETHING_NEW", "something"); | ||
| 716 | try testing.expectEqualStrings("something", env.get("SOMETHING_NEW").?); | ||
| 717 | try testing.expectEqual(@as(Map.Size, 1), env.count()); | ||
| 718 | |||
| 719 | // a new longer name to test the Windows-specific conversion buffer | ||
| 720 | try env.put("SOMETHING_NEW_AND_LONGER", "1"); | ||
| 721 | try testing.expectEqualStrings("1", env.get("SOMETHING_NEW_AND_LONGER").?); | ||
| 722 | try testing.expectEqual(@as(Map.Size, 2), env.count()); | ||
| 723 | |||
| 724 | // case insensitivity on Windows only | ||
| 725 | if (native_os == .windows) { | ||
| 726 | try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?); | ||
| 727 | } else { | ||
| 728 | try testing.expect(null == env.get("something_New_aNd_LONGER")); | ||
| 729 | } | ||
| 730 | |||
| 731 | var it = env.iterator(); | ||
| 732 | var count: Map.Size = 0; | ||
| 733 | while (it.next()) |entry| { | ||
| 734 | const is_an_expected_name = mem.eql(u8, "SOMETHING_NEW", entry.key_ptr.*) or mem.eql(u8, "SOMETHING_NEW_AND_LONGER", entry.key_ptr.*); | ||
| 735 | try testing.expect(is_an_expected_name); | ||
| 736 | count += 1; | ||
| 737 | } | ||
| 738 | try testing.expectEqual(@as(Map.Size, 2), count); | ||
| 739 | |||
| 740 | try testing.expect(env.swapRemove("SOMETHING_NEW")); | ||
| 741 | try testing.expect(!env.swapRemove("SOMETHING_NEW")); | ||
| 742 | try testing.expect(env.get("SOMETHING_NEW") == null); | ||
| 743 | |||
| 744 | try testing.expectEqual(@as(Map.Size, 1), env.count()); | ||
| 745 | |||
| 746 | if (native_os == .windows) { | ||
| 747 | // test Unicode case-insensitivity on Windows | ||
| 748 | try env.put("КИРиллИЦА", "something else"); | ||
| 749 | try testing.expectEqualStrings("something else", env.get("кириллица").?); | ||
| 750 | |||
| 751 | // and WTF-8 that's not valid UTF-8 | ||
| 752 | const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{ | ||
| 753 | mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate | ||
| 754 | }); | ||
| 755 | defer testing.allocator.free(wtf8_with_surrogate_pair); | ||
| 756 | |||
| 757 | try env.put(wtf8_with_surrogate_pair, wtf8_with_surrogate_pair); | ||
| 758 | try testing.expectEqualSlices(u8, wtf8_with_surrogate_pair, env.get(wtf8_with_surrogate_pair).?); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | |||
| 762 | test "convert from Environ to Map and back again" { | ||
| 763 | if (native_os == .windows) return; | ||
| 764 | if (native_os == .wasi and !builtin.link_libc) return; | ||
| 765 | |||
| 766 | const gpa = testing.allocator; | ||
| 767 | |||
| 768 | var map: Map = .init(gpa); | ||
| 769 | defer map.deinit(); | ||
| 770 | try map.put("FOO", "BAR"); | ||
| 771 | try map.put("A", ""); | ||
| 772 | try map.put("", "B"); | ||
| 773 | |||
| 774 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | ||
| 775 | defer arena_allocator.deinit(); | ||
| 776 | const arena = arena_allocator.allocator(); | ||
| 777 | |||
| 778 | const environ: Environ = .{ .block = try map.createBlockPosix(arena, .{}) }; | ||
| 779 | |||
| 780 | try testing.expectEqual(true, environ.contains(gpa, "FOO")); | ||
| 781 | try testing.expectEqual(false, environ.contains(gpa, "BAR")); | ||
| 782 | try testing.expectEqual(true, environ.contains(gpa, "A")); | ||
| 783 | try testing.expectEqual(true, environ.containsConstant("A")); | ||
| 784 | try testing.expectEqual(false, environ.containsUnempty(gpa, "A")); | ||
| 785 | try testing.expectEqual(false, environ.containsUnemptyConstant("A")); | ||
| 786 | try testing.expectEqual(true, environ.contains(gpa, "")); | ||
| 787 | try testing.expectEqual(false, environ.contains(gpa, "B")); | ||
| 788 | |||
| 789 | try testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(gpa, "BOGUS")); | ||
| 790 | { | ||
| 791 | const value = try environ.getAlloc(gpa, "FOO"); | ||
| 792 | defer gpa.free(value); | ||
| 793 | try testing.expectEqualStrings("BAR", value); | ||
| 794 | } | ||
| 795 | |||
| 796 | var map2 = try environ.createMap(gpa); | ||
| 797 | defer map2.deinit(); | ||
| 798 | |||
| 799 | try testing.expectEqualDeep(map.keys(), map2.keys()); | ||
| 800 | try testing.expectEqualDeep(map.values(), map2.values()); | ||
| 801 | } | ||
| 802 | |||
| 803 | test createMapWide { | ||
| 804 | if (builtin.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO | ||
| 805 | |||
| 806 | const gpa = testing.allocator; | ||
| 807 | |||
| 808 | var map: Map = .init(gpa); | ||
| 809 | defer map.deinit(); | ||
| 810 | try map.put("FOO", "BAR"); | ||
| 811 | try map.put("A", ""); | ||
| 812 | try map.put("", "B"); | ||
| 813 | |||
| 814 | const environ: [:0]u16 = try map.createBlockWindows(gpa); | ||
| 815 | defer gpa.free(environ); | ||
| 816 | |||
| 817 | var map2 = try createMapWide(environ, gpa); | ||
| 818 | defer map2.deinit(); | ||
| 819 | |||
| 820 | try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "=B" }, map2.keys()); | ||
| 821 | try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "" }, map2.values()); | ||
| 822 | } | ||
lib/std/start.zig+165-168| ... | @@ -1,128 +1,76 @@ | ... | @@ -1,128 +1,76 @@ |
| 1 | // This file is included in the compilation unit when exporting an executable. | 1 | // This file is included in the compilation unit when exporting an executable. |
| 2 | 2 | ||
| 3 | const root = @import("root"); | ||
| 4 | const std = @import("std.zig"); | ||
| 5 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const native_arch = builtin.cpu.arch; | ||
| 5 | const native_os = builtin.os.tag; | ||
| 6 | const is_wasm = native_arch.isWasm(); | ||
| 7 | |||
| 8 | const std = @import("std.zig"); | ||
| 6 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 7 | const uefi = std.os.uefi; | 10 | const uefi = std.os.uefi; |
| 8 | const elf = std.elf; | 11 | const elf = std.elf; |
| 9 | const native_arch = builtin.cpu.arch; | ||
| 10 | const native_os = builtin.os.tag; | ||
| 11 | 12 | ||
| 12 | const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start"; | 13 | const root = @import("root"); |
| 13 | 14 | ||
| 14 | // The self-hosted compiler is not fully capable of handling all of this start.zig file. | 15 | const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start"; |
| 15 | // Until then, we have simplified logic here for self-hosted. TODO remove this once | ||
| 16 | // self-hosted is capable enough to handle all of the real start.zig logic. | ||
| 17 | pub const simplified_logic = switch (builtin.zig_backend) { | ||
| 18 | .stage2_aarch64, | ||
| 19 | .stage2_arm, | ||
| 20 | .stage2_powerpc, | ||
| 21 | .stage2_sparc64, | ||
| 22 | .stage2_spirv, | ||
| 23 | .stage2_x86, | ||
| 24 | => true, | ||
| 25 | else => false, | ||
| 26 | }; | ||
| 27 | 16 | ||
| 28 | comptime { | 17 | comptime { |
| 29 | // No matter what, we import the root file, so that any export, test, comptime | 18 | // No matter what, we import the root file, so that any export, test, comptime |
| 30 | // decls there get run. | 19 | // decls there get run. |
| 31 | _ = root; | 20 | _ = root; |
| 32 | 21 | ||
| 33 | if (simplified_logic) { | 22 | if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { |
| 34 | if (builtin.output_mode == .Exe) { | 23 | if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { |
| 35 | if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) { | 24 | @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); |
| 36 | if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { | ||
| 37 | @export(&main2, .{ .name = "main" }); | ||
| 38 | } | ||
| 39 | } else if (builtin.os.tag == .windows) { | ||
| 40 | if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) { | ||
| 41 | @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); | ||
| 42 | } | ||
| 43 | } else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) { | ||
| 44 | if (@hasDecl(root, "main")) | ||
| 45 | @export(&spirvMain2, .{ .name = "main" }); | ||
| 46 | } else { | ||
| 47 | if (!@hasDecl(root, "_start")) { | ||
| 48 | @export(&_start2, .{ .name = "_start" }); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | 25 | } |
| 52 | } else { | 26 | } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { |
| 53 | if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { | 27 | if (builtin.link_libc and @hasDecl(root, "main")) { |
| 54 | if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { | 28 | if (is_wasm) { |
| 55 | @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); | 29 | @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" }); |
| 30 | } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { | ||
| 31 | @export(&main, .{ .name = "main" }); | ||
| 56 | } | 32 | } |
| 57 | } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { | 33 | } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { |
| 58 | if (builtin.link_libc and @hasDecl(root, "main")) { | 34 | if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) { |
| 59 | if (native_arch.isWasm()) { | 35 | @export(&wWinMain, .{ .name = "wWinMain" }); |
| 60 | @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" }); | 36 | } |
| 61 | } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { | 37 | } else if (native_os == .windows) { |
| 62 | @export(&main, .{ .name = "main" }); | 38 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and |
| 63 | } | 39 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) |
| 64 | } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { | 40 | { |
| 65 | if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) { | 41 | @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); |
| 66 | @export(&wWinMain, .{ .name = "wWinMain" }); | 42 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and |
| 67 | } | 43 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) |
| 68 | } else if (native_os == .windows) { | 44 | { |
| 69 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | 45 | @compileError("WinMain not supported; declare wWinMain or main instead"); |
| 70 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | 46 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and |
| 71 | { | 47 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) |
| 72 | @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); | 48 | { |
| 73 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | 49 | @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); |
| 74 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | 50 | } |
| 75 | { | 51 | } else if (native_os == .uefi) { |
| 76 | @compileError("WinMain not supported; declare wWinMain or main instead"); | 52 | if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" }); |
| 77 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and | 53 | } else if (native_os == .wasi) { |
| 78 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) | 54 | const wasm_start_sym = switch (builtin.wasi_exec_model) { |
| 79 | { | 55 | .reactor => "_initialize", |
| 80 | @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); | 56 | .command => "_start", |
| 81 | } | 57 | }; |
| 82 | } else if (native_os == .uefi) { | 58 | if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { |
| 83 | if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" }); | ||
| 84 | } else if (native_os == .wasi) { | ||
| 85 | const wasm_start_sym = switch (builtin.wasi_exec_model) { | ||
| 86 | .reactor => "_initialize", | ||
| 87 | .command => "_start", | ||
| 88 | }; | ||
| 89 | if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { | ||
| 90 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | ||
| 91 | // case it's not required to provide an entrypoint such as main. | ||
| 92 | @export(&wasi_start, .{ .name = wasm_start_sym }); | ||
| 93 | } | ||
| 94 | } else if (native_arch.isWasm() and native_os == .freestanding) { | ||
| 95 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | 59 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which |
| 96 | // case it's not required to provide an entrypoint such as main. | 60 | // case it's not required to provide an entrypoint such as main. |
| 97 | if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name }); | 61 | @export(&startWasi, .{ .name = wasm_start_sym }); |
| 98 | } else switch (native_os) { | ||
| 99 | .other, .freestanding, .@"3ds", .vita => {}, | ||
| 100 | else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }), | ||
| 101 | } | 62 | } |
| 63 | } else if (is_wasm and native_os == .freestanding) { | ||
| 64 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | ||
| 65 | // case it's not required to provide an entrypoint such as main. | ||
| 66 | if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name }); | ||
| 67 | } else switch (native_os) { | ||
| 68 | .other, .freestanding, .@"3ds", .vita => {}, | ||
| 69 | else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }), | ||
| 102 | } | 70 | } |
| 103 | } | 71 | } |
| 104 | } | 72 | } |
| 105 | 73 | ||
| 106 | // Simplified start code for stage2 until it supports more language features /// | ||
| 107 | |||
| 108 | fn main2() callconv(.c) c_int { | ||
| 109 | return callMain(); | ||
| 110 | } | ||
| 111 | |||
| 112 | fn _start2() callconv(.withStackAlign(.c, 1)) noreturn { | ||
| 113 | std.process.exit(callMain()); | ||
| 114 | } | ||
| 115 | |||
| 116 | fn spirvMain2() callconv(.kernel) void { | ||
| 117 | root.main(); | ||
| 118 | } | ||
| 119 | |||
| 120 | fn wWinMainCRTStartup2() callconv(.c) noreturn { | ||
| 121 | std.process.exit(callMain()); | ||
| 122 | } | ||
| 123 | |||
| 124 | //////////////////////////////////////////////////////////////////////////////// | ||
| 125 | |||
| 126 | fn _DllMainCRTStartup( | 74 | fn _DllMainCRTStartup( |
| 127 | hinstDLL: std.os.windows.HINSTANCE, | 75 | hinstDLL: std.os.windows.HINSTANCE, |
| 128 | fdwReason: std.os.windows.DWORD, | 76 | fdwReason: std.os.windows.DWORD, |
| ... | @@ -142,15 +90,15 @@ fn _DllMainCRTStartup( | ... | @@ -142,15 +90,15 @@ fn _DllMainCRTStartup( |
| 142 | fn wasm_freestanding_start() callconv(.c) void { | 90 | fn wasm_freestanding_start() callconv(.c) void { |
| 143 | // This is marked inline because for some reason LLVM in | 91 | // This is marked inline because for some reason LLVM in |
| 144 | // release mode fails to inline it, and we want fewer call frames in stack traces. | 92 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 145 | _ = @call(.always_inline, callMain, .{}); | 93 | _ = @call(.always_inline, callMain, .{ {}, {} }); |
| 146 | } | 94 | } |
| 147 | 95 | ||
| 148 | fn wasi_start() callconv(.c) void { | 96 | fn startWasi() callconv(.c) void { |
| 149 | // The function call is marked inline because for some reason LLVM in | 97 | // The function call is marked inline because for some reason LLVM in |
| 150 | // release mode fails to inline it, and we want fewer call frames in stack traces. | 98 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 151 | switch (builtin.wasi_exec_model) { | 99 | switch (builtin.wasi_exec_model) { |
| 152 | .reactor => _ = @call(.always_inline, callMain, .{}), | 100 | .reactor => _ = @call(.always_inline, callMain, .{ {}, {} }), |
| 153 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{})), | 101 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{ {}, {} })), |
| 154 | } | 102 | } |
| 155 | } | 103 | } |
| 156 | 104 | ||
| ... | @@ -524,7 +472,10 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { | ... | @@ -524,7 +472,10 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| 524 | 472 | ||
| 525 | std.debug.maybeEnableSegfaultHandler(); | 473 | std.debug.maybeEnableSegfaultHandler(); |
| 526 | 474 | ||
| 527 | std.os.windows.ntdll.RtlExitUserProcess(callMain()); | 475 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 476 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; | ||
| 477 | |||
| 478 | std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, {})); | ||
| 528 | } | 479 | } |
| 529 | 480 | ||
| 530 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { | 481 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| ... | @@ -556,7 +507,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { | ... | @@ -556,7 +507,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { |
| 556 | const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1)); | 507 | const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1)); |
| 557 | var envp_count: usize = 0; | 508 | var envp_count: usize = 0; |
| 558 | while (envp_optional[envp_count]) |_| : (envp_count += 1) {} | 509 | while (envp_optional[envp_count]) |_| : (envp_count += 1) {} |
| 559 | const envp = @as([*][*:0]u8, @ptrCast(envp_optional))[0..envp_count]; | 510 | const envp = envp_optional[0..envp_count :null]; |
| 560 | 511 | ||
| 561 | // Find the beginning of the auxiliary vector | 512 | // Find the beginning of the auxiliary vector |
| 562 | const auxv: [*]elf.Auxv = @ptrCast(@alignCast(envp.ptr + envp_count + 1)); | 513 | const auxv: [*]elf.Auxv = @ptrCast(@alignCast(envp.ptr + envp_count + 1)); |
| ... | @@ -631,6 +582,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { | ... | @@ -631,6 +582,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { |
| 631 | } | 582 | } |
| 632 | 583 | ||
| 633 | fn expandStackSize(phdrs: []elf.Phdr) void { | 584 | fn expandStackSize(phdrs: []elf.Phdr) void { |
| 585 | @disableInstrumentation(); | ||
| 634 | for (phdrs) |*phdr| { | 586 | for (phdrs) |*phdr| { |
| 635 | switch (phdr.p_type) { | 587 | switch (phdr.p_type) { |
| 636 | elf.PT_GNU_STACK => { | 588 | elf.PT_GNU_STACK => { |
| ... | @@ -665,92 +617,137 @@ fn expandStackSize(phdrs: []elf.Phdr) void { | ... | @@ -665,92 +617,137 @@ fn expandStackSize(phdrs: []elf.Phdr) void { |
| 665 | } | 617 | } |
| 666 | } | 618 | } |
| 667 | 619 | ||
| 668 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { | 620 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [:null]?[*:0]u8) u8 { |
| 669 | std.os.argv = argv[0..argc]; | ||
| 670 | std.os.environ = envp; | ||
| 671 | |||
| 672 | if (std.Options.debug_threaded_io) |t| { | 621 | if (std.Options.debug_threaded_io) |t| { |
| 673 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; | 622 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; |
| 674 | t.environ = .{ .block = envp }; | 623 | t.environ = .{ .process_environ = .{ .block = envp } }; |
| 675 | } | 624 | } |
| 676 | |||
| 677 | std.debug.maybeEnableSegfaultHandler(); | 625 | std.debug.maybeEnableSegfaultHandler(); |
| 678 | 626 | return callMain(argv[0..argc], envp); | |
| 679 | return callMain(); | ||
| 680 | } | 627 | } |
| 681 | 628 | ||
| 682 | fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int { | 629 | fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int { |
| 683 | var env_count: usize = 0; | 630 | var env_count: usize = 0; |
| 684 | while (c_envp[env_count] != null) : (env_count += 1) {} | 631 | while (c_envp[env_count] != null) : (env_count += 1) {} |
| 685 | const envp = @as([*][*:0]u8, @ptrCast(c_envp))[0..env_count]; | 632 | const envp = c_envp[0..env_count :null]; |
| 686 | 633 | ||
| 687 | if (builtin.os.tag == .linux) { | 634 | switch (builtin.os.tag) { |
| 688 | const at_phdr = std.c.getauxval(elf.AT_PHDR); | 635 | .linux => { |
| 689 | const at_phnum = std.c.getauxval(elf.AT_PHNUM); | 636 | const at_phdr = std.c.getauxval(elf.AT_PHDR); |
| 690 | const phdrs = (@as([*]elf.Phdr, @ptrFromInt(at_phdr)))[0..at_phnum]; | 637 | const at_phnum = std.c.getauxval(elf.AT_PHNUM); |
| 691 | expandStackSize(phdrs); | 638 | const phdrs = (@as([*]elf.Phdr, @ptrFromInt(at_phdr)))[0..at_phnum]; |
| 639 | expandStackSize(phdrs); | ||
| 640 | }, | ||
| 641 | .windows => { | ||
| 642 | // On Windows, we ignore libc environment and argv and get those | ||
| 643 | // values in their intended encoding from the PEB instead. | ||
| 644 | std.debug.maybeEnableSegfaultHandler(); | ||
| 645 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; | ||
| 646 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; | ||
| 647 | return callMain(cmd_line_w, {}); | ||
| 648 | }, | ||
| 649 | else => {}, | ||
| 692 | } | 650 | } |
| 693 | 651 | ||
| 694 | return callMainWithArgs(@as(usize, @intCast(c_argc)), @as([*][*:0]u8, @ptrCast(c_argv)), envp); | 652 | return callMainWithArgs(@as(usize, @intCast(c_argc)), @as([*][*:0]u8, @ptrCast(c_argv)), @ptrCast(envp)); |
| 695 | } | 653 | } |
| 696 | 654 | ||
| 697 | fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { | 655 | fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { |
| 698 | std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; | 656 | const argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; |
| 699 | |||
| 700 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) { | 657 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) { |
| 701 | if (std.Options.debug_threaded_io) |t| t.argv0.value = std.os.argv[0]; | 658 | if (std.Options.debug_threaded_io) |t| t.argv0.value = argv[0]; |
| 702 | } | 659 | } |
| 703 | 660 | return callMain(argv, &.{}); | |
| 704 | return callMain(); | ||
| 705 | } | 661 | } |
| 706 | 662 | ||
| 707 | // General error message for a malformed return type | 663 | /// General error message for a malformed return type |
| 708 | const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'"; | 664 | const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'"; |
| 709 | 665 | ||
| 710 | pub inline fn callMain() u8 { | 666 | const use_debug_allocator = !is_wasm and switch (builtin.mode) { |
| 711 | const ReturnType = @typeInfo(@TypeOf(root.main)).@"fn".return_type.?; | 667 | .Debug => true, |
| 668 | .ReleaseSafe => !builtin.link_libc, // Not ideal, but the best we have for now. | ||
| 669 | .ReleaseFast, .ReleaseSmall => !builtin.link_libc and builtin.single_threaded, // Also not ideal. | ||
| 670 | }; | ||
| 671 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | ||
| 672 | |||
| 673 | inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.Block) u8 { | ||
| 674 | const fn_info = @typeInfo(@TypeOf(root.main)).@"fn"; | ||
| 675 | if (fn_info.params.len == 0) return wrapMain(root.main()); | ||
| 676 | if (fn_info.params[0].type.? == std.process.Init.Minimal) return wrapMain(root.main(.{ | ||
| 677 | .args = .{ .vector = args }, | ||
| 678 | .environ = .{ .block = environ }, | ||
| 679 | })); | ||
| 680 | |||
| 681 | const gpa = if (use_debug_allocator) | ||
| 682 | debug_allocator.allocator() | ||
| 683 | else if (builtin.link_libc) | ||
| 684 | std.heap.c_allocator | ||
| 685 | else if (is_wasm) | ||
| 686 | std.heap.wasm_allocator | ||
| 687 | else if (!builtin.single_threaded) | ||
| 688 | std.heap.smp_allocator | ||
| 689 | else | ||
| 690 | comptime unreachable; | ||
| 691 | |||
| 692 | defer if (use_debug_allocator) { | ||
| 693 | _ = debug_allocator.deinit(); // Leaks do not affect return code. | ||
| 694 | }; | ||
| 712 | 695 | ||
| 713 | switch (ReturnType) { | 696 | const arena_backing_allocator = if (is_wasm) gpa else std.heap.page_allocator; |
| 714 | void => { | ||
| 715 | root.main(); | ||
| 716 | return 0; | ||
| 717 | }, | ||
| 718 | noreturn, u8 => { | ||
| 719 | return root.main(); | ||
| 720 | }, | ||
| 721 | else => { | ||
| 722 | if (@typeInfo(ReturnType) != .error_union) @compileError(bad_main_ret); | ||
| 723 | |||
| 724 | const result = root.main() catch |err| { | ||
| 725 | switch (builtin.zig_backend) { | ||
| 726 | .stage2_powerpc, | ||
| 727 | .stage2_riscv64, | ||
| 728 | => { | ||
| 729 | _ = std.posix.write(std.posix.STDERR_FILENO, "error: failed with error\n") catch {}; | ||
| 730 | return 1; | ||
| 731 | }, | ||
| 732 | else => {}, | ||
| 733 | } | ||
| 734 | std.log.err("{s}", .{@errorName(err)}); | ||
| 735 | switch (native_os) { | ||
| 736 | .freestanding, .other => {}, | ||
| 737 | else => if (@errorReturnTrace()) |trace| { | ||
| 738 | std.debug.dumpStackTrace(trace); | ||
| 739 | }, | ||
| 740 | } | ||
| 741 | return 1; | ||
| 742 | }; | ||
| 743 | 697 | ||
| 744 | return switch (@TypeOf(result)) { | 698 | var arena_allocator = std.heap.ArenaAllocator.init(arena_backing_allocator); |
| 745 | void => 0, | 699 | defer arena_allocator.deinit(); |
| 746 | u8 => result, | 700 | |
| 747 | else => @compileError(bad_main_ret), | 701 | var threaded: std.Io.Threaded = .init(gpa, .{ |
| 748 | }; | 702 | .argv0 = .init(.{ .vector = args }), |
| 703 | .environ = .{ .block = environ }, | ||
| 704 | }); | ||
| 705 | defer threaded.deinit(); | ||
| 706 | |||
| 707 | var environ_map = std.process.Environ.createMap(.{ .block = environ }, gpa) catch |err| | ||
| 708 | std.process.fatal("failed to parse environment variables: {t}", .{err}); | ||
| 709 | defer environ_map.deinit(); | ||
| 710 | |||
| 711 | return wrapMain(root.main(.{ | ||
| 712 | .minimal = .{ | ||
| 713 | .args = .{ .vector = args }, | ||
| 714 | .environ = .{ .block = environ }, | ||
| 749 | }, | 715 | }, |
| 716 | .arena = &arena_allocator, | ||
| 717 | .gpa = gpa, | ||
| 718 | .io = threaded.io(), | ||
| 719 | .environ_map = &environ_map, | ||
| 720 | })); | ||
| 721 | } | ||
| 722 | |||
| 723 | inline fn wrapMain(result: anytype) u8 { | ||
| 724 | const ReturnType = @TypeOf(result); | ||
| 725 | switch (ReturnType) { | ||
| 726 | void => return 0, | ||
| 727 | noreturn => unreachable, | ||
| 728 | u8 => return result, | ||
| 729 | else => {}, | ||
| 750 | } | 730 | } |
| 731 | if (@typeInfo(ReturnType) != .error_union) @compileError(bad_main_ret); | ||
| 732 | |||
| 733 | const unwrapped_result = result catch |err| { | ||
| 734 | std.log.err("{t}", .{err}); | ||
| 735 | switch (native_os) { | ||
| 736 | .freestanding, .other => {}, | ||
| 737 | else => if (@errorReturnTrace()) |trace| std.debug.dumpStackTrace(trace), | ||
| 738 | } | ||
| 739 | return 1; | ||
| 740 | }; | ||
| 741 | |||
| 742 | return switch (@TypeOf(unwrapped_result)) { | ||
| 743 | noreturn => unreachable, | ||
| 744 | void => 0, | ||
| 745 | u8 => unwrapped_result, | ||
| 746 | else => @compileError(bad_main_ret), | ||
| 747 | }; | ||
| 751 | } | 748 | } |
| 752 | 749 | ||
| 753 | pub fn call_wWinMain() std.os.windows.INT { | 750 | fn call_wWinMain() std.os.windows.INT { |
| 754 | const peb = std.os.windows.peb(); | 751 | const peb = std.os.windows.peb(); |
| 755 | const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).@"fn".params[0].type.?; | 752 | const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).@"fn".params[0].type.?; |
| 756 | const hInstance: MAIN_HINSTANCE = @ptrCast(peb.ImageBaseAddress); | 753 | const hInstance: MAIN_HINSTANCE = @ptrCast(peb.ImageBaseAddress); |
lib/std/std.zig+14-3| ... | @@ -114,9 +114,6 @@ pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options | ... | @@ -114,9 +114,6 @@ pub const options: Options = if (@hasDecl(root, "std_options")) root.std_options |
| 114 | pub const Options = struct { | 114 | pub const Options = struct { |
| 115 | enable_segfault_handler: bool = debug.default_enable_segfault_handler, | 115 | enable_segfault_handler: bool = debug.default_enable_segfault_handler, |
| 116 | 116 | ||
| 117 | /// Function used to implement `std.Io.Dir.cwd` for WASI. | ||
| 118 | wasiCwd: fn () os.wasi.fd_t = os.defaultWasiCwd, | ||
| 119 | |||
| 120 | /// The current log level. | 117 | /// The current log level. |
| 121 | log_level: log.Level = log.default_level, | 118 | log_level: log.Level = log.default_level, |
| 122 | 119 | ||
| ... | @@ -176,10 +173,21 @@ pub const Options = struct { | ... | @@ -176,10 +173,21 @@ pub const Options = struct { |
| 176 | /// stack traces will just print an error to the relevant `Io.Writer` and return. | 173 | /// stack traces will just print an error to the relevant `Io.Writer` and return. |
| 177 | allow_stack_tracing: bool = !@import("builtin").strip_debug_info, | 174 | allow_stack_tracing: bool = !@import("builtin").strip_debug_info, |
| 178 | 175 | ||
| 176 | /// TODO This is a separate decl instead of a field as a workaround around | ||
| 177 | /// compilation errors due to zig not being lazy enough. | ||
| 178 | pub const elf_debug_info_search_paths: ?fn (exe_path: []const u8) switch (@import("builtin").object_format) { | ||
| 179 | .elf => debug.ElfFile.DebugInfoSearchPaths, | ||
| 180 | else => void, | ||
| 181 | } = if (@hasDecl(root, "std_options_elf_debug_info_search_paths")) | ||
| 182 | root.std_options_elf_debug_info_search_paths | ||
| 183 | else | ||
| 184 | null; | ||
| 185 | |||
| 179 | pub const debug_threaded_io: ?*Io.Threaded = if (@hasDecl(root, "std_options_debug_threaded_io")) | 186 | pub const debug_threaded_io: ?*Io.Threaded = if (@hasDecl(root, "std_options_debug_threaded_io")) |
| 180 | root.std_options_debug_threaded_io | 187 | root.std_options_debug_threaded_io |
| 181 | else | 188 | else |
| 182 | Io.Threaded.global_single_threaded; | 189 | Io.Threaded.global_single_threaded; |
| 190 | |||
| 183 | /// The `Io` instance that `std.debug` uses for `std.debug.print`, | 191 | /// The `Io` instance that `std.debug` uses for `std.debug.print`, |
| 184 | /// capturing stack traces, loading debug info, finding the executable's | 192 | /// capturing stack traces, loading debug info, finding the executable's |
| 185 | /// own path, and environment variables that affect terminal mode | 193 | /// own path, and environment variables that affect terminal mode |
| ... | @@ -193,6 +201,9 @@ pub const Options = struct { | ... | @@ -193,6 +201,9 @@ pub const Options = struct { |
| 193 | 201 | ||
| 194 | /// Overrides `std.Io.File.Permissions`. | 202 | /// Overrides `std.Io.File.Permissions`. |
| 195 | pub const FilePermissions: ?type = if (@hasDecl(root, "std_options_FilePermissions")) root.std_options_FilePermissions else null; | 203 | pub const FilePermissions: ?type = if (@hasDecl(root, "std_options_FilePermissions")) root.std_options_FilePermissions else null; |
| 204 | |||
| 205 | /// Overrides `std.Io.Dir.cwd`. | ||
| 206 | pub const cwd: ?fn () Io.Dir = if (@hasDecl(root, "std_options_cwd")) root.std_options_cwd else null; | ||
| 196 | }; | 207 | }; |
| 197 | 208 | ||
| 198 | // This forces the start.zig file to be imported, and the comptime logic inside that | 209 | // This forces the start.zig file to be imported, and the comptime logic inside that |
lib/std/zig.zig+23-12| ... | @@ -739,28 +739,39 @@ pub const EnvVar = enum { | ... | @@ -739,28 +739,39 @@ pub const EnvVar = enum { |
| 739 | ZIG_VERBOSE_CC, | 739 | ZIG_VERBOSE_CC, |
| 740 | ZIG_BTRFS_WORKAROUND, | 740 | ZIG_BTRFS_WORKAROUND, |
| 741 | ZIG_DEBUG_CMD, | 741 | ZIG_DEBUG_CMD, |
| 742 | ZIG_IS_DETECTING_LIBC_PATHS, | ||
| 743 | ZIG_IS_TRYING_TO_NOT_CALL_ITSELF, | ||
| 744 | |||
| 745 | // C toolchain integration | ||
| 746 | NIX_CFLAGS_COMPILE, | ||
| 747 | NIX_CFLAGS_LINK, | ||
| 748 | NIX_LDFLAGS, | ||
| 749 | C_INCLUDE_PATH, | ||
| 750 | CPLUS_INCLUDE_PATH, | ||
| 751 | LIBRARY_PATH, | ||
| 742 | CC, | 752 | CC, |
| 753 | |||
| 754 | // Terminal integration | ||
| 743 | NO_COLOR, | 755 | NO_COLOR, |
| 744 | CLICOLOR_FORCE, | 756 | CLICOLOR_FORCE, |
| 757 | |||
| 758 | // Debug info integration | ||
| 745 | XDG_CACHE_HOME, | 759 | XDG_CACHE_HOME, |
| 746 | LOCALAPPDATA, | 760 | LOCALAPPDATA, |
| 747 | HOME, | 761 | HOME, |
| 748 | 762 | ||
| 749 | pub fn isSet(comptime ev: EnvVar) bool { | 763 | // Windows SDK integration |
| 750 | return std.process.hasNonEmptyEnvVarConstant(@tagName(ev)); | 764 | PROGRAMDATA, |
| 751 | } | ||
| 752 | 765 | ||
| 753 | pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 { | 766 | // Homebrew integration |
| 754 | if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| { | 767 | HOMEBREW_PREFIX, |
| 755 | return value; | 768 | |
| 756 | } else |err| switch (err) { | 769 | pub fn isSet(ev: EnvVar, map: *const std.process.Environ.Map) bool { |
| 757 | error.EnvironmentVariableNotFound => return null, | 770 | return map.contains(@tagName(ev)); |
| 758 | else => |e| return e, | ||
| 759 | } | ||
| 760 | } | 771 | } |
| 761 | 772 | ||
| 762 | pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 { | 773 | pub fn get(ev: EnvVar, map: *const std.process.Environ.Map) ?[]const u8 { |
| 763 | return std.posix.getenvZ(@tagName(ev)); | 774 | return map.get(@tagName(ev)); |
| 764 | } | 775 | } |
| 765 | }; | 776 | }; |
| 766 | 777 |
lib/std/zig/LibCDirs.zig+11-3| ... | @@ -28,6 +28,7 @@ pub fn detect( | ... | @@ -28,6 +28,7 @@ pub fn detect( |
| 28 | is_native_abi: bool, | 28 | is_native_abi: bool, |
| 29 | link_libc: bool, | 29 | link_libc: bool, |
| 30 | libc_installation: ?*const LibCInstallation, | 30 | libc_installation: ?*const LibCInstallation, |
| 31 | environ_map: *const std.process.Environ.Map, | ||
| 31 | ) LibCInstallation.FindError!LibCDirs { | 32 | ) LibCInstallation.FindError!LibCDirs { |
| 32 | if (!link_libc) { | 33 | if (!link_libc) { |
| 33 | return .{ | 34 | return .{ |
| ... | @@ -47,7 +48,10 @@ pub fn detect( | ... | @@ -47,7 +48,10 @@ pub fn detect( |
| 47 | // using the system libc installation. | 48 | // using the system libc installation. |
| 48 | if (is_native_abi and !target.isMinGW()) { | 49 | if (is_native_abi and !target.isMinGW()) { |
| 49 | const libc = try arena.create(LibCInstallation); | 50 | const libc = try arena.create(LibCInstallation); |
| 50 | libc.* = LibCInstallation.findNative(arena, io, .{ .target = target }) catch |err| switch (err) { | 51 | libc.* = LibCInstallation.findNative(arena, io, .{ |
| 52 | .target = target, | ||
| 53 | .environ_map = environ_map, | ||
| 54 | }) catch |err| switch (err) { | ||
| 51 | error.CCompilerExitCode, | 55 | error.CCompilerExitCode, |
| 52 | error.CCompilerCrashed, | 56 | error.CCompilerCrashed, |
| 53 | error.CCompilerCannotFindHeaders, | 57 | error.CCompilerCannotFindHeaders, |
| ... | @@ -84,12 +88,16 @@ pub fn detect( | ... | @@ -84,12 +88,16 @@ pub fn detect( |
| 84 | 88 | ||
| 85 | if (use_system_abi) { | 89 | if (use_system_abi) { |
| 86 | const libc = try arena.create(LibCInstallation); | 90 | const libc = try arena.create(LibCInstallation); |
| 87 | libc.* = try LibCInstallation.findNative(arena, io, .{ .verbose = true, .target = target }); | 91 | libc.* = try LibCInstallation.findNative(arena, io, .{ |
| 92 | .verbose = true, | ||
| 93 | .target = target, | ||
| 94 | .environ_map = environ_map, | ||
| 95 | }); | ||
| 88 | return detectFromInstallation(arena, target, libc); | 96 | return detectFromInstallation(arena, target, libc); |
| 89 | } | 97 | } |
| 90 | 98 | ||
| 91 | return .{ | 99 | return .{ |
| 92 | .libc_include_dir_list = &[0][]u8{}, | 100 | .libc_include_dir_list = &.{}, |
| 93 | .libc_installation = null, | 101 | .libc_installation = null, |
| 94 | .libc_framework_dir_list = &.{}, | 102 | .libc_framework_dir_list = &.{}, |
| 95 | .sysroot = null, | 103 | .sysroot = null, |
lib/std/zig/LibCInstallation.zig+41-39| ... | @@ -13,6 +13,7 @@ const fs = std.fs; | ... | @@ -13,6 +13,7 @@ const fs = std.fs; |
| 13 | const Allocator = std.mem.Allocator; | 13 | const Allocator = std.mem.Allocator; |
| 14 | const Path = std.Build.Cache.Path; | 14 | const Path = std.Build.Cache.Path; |
| 15 | const log = std.log.scoped(.libc_installation); | 15 | const log = std.log.scoped(.libc_installation); |
| 16 | const Environ = std.process.Environ; | ||
| 16 | 17 | ||
| 17 | include_dir: ?[]const u8 = null, | 18 | include_dir: ?[]const u8 = null, |
| 18 | sys_include_dir: ?[]const u8 = null, | 19 | sys_include_dir: ?[]const u8 = null, |
| ... | @@ -167,6 +168,7 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { | ... | @@ -167,6 +168,7 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { |
| 167 | 168 | ||
| 168 | pub const FindNativeOptions = struct { | 169 | pub const FindNativeOptions = struct { |
| 169 | target: *const std.Target, | 170 | target: *const std.Target, |
| 171 | environ_map: *const Environ.Map, | ||
| 170 | 172 | ||
| 171 | /// If enabled, will print human-friendly errors to stderr. | 173 | /// If enabled, will print human-friendly errors to stderr. |
| 172 | verbose: bool = false, | 174 | verbose: bool = false, |
| ... | @@ -191,7 +193,7 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib | ... | @@ -191,7 +193,7 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib |
| 191 | }); | 193 | }); |
| 192 | return self; | 194 | return self; |
| 193 | } else if (is_windows) { | 195 | } else if (is_windows) { |
| 194 | const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch) catch |err| switch (err) { | 196 | const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch, args.environ_map) catch |err| switch (err) { |
| 195 | error.NotFound => return error.WindowsSdkNotFound, | 197 | error.NotFound => return error.WindowsSdkNotFound, |
| 196 | error.PathTooLong => return error.WindowsSdkNotFound, | 198 | error.PathTooLong => return error.WindowsSdkNotFound, |
| 197 | error.OutOfMemory => return error.OutOfMemory, | 199 | error.OutOfMemory => return error.OutOfMemory, |
| ... | @@ -206,16 +208,16 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib | ... | @@ -206,16 +208,16 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib |
| 206 | } else if (is_haiku) { | 208 | } else if (is_haiku) { |
| 207 | try self.findNativeIncludeDirPosix(gpa, io, args); | 209 | try self.findNativeIncludeDirPosix(gpa, io, args); |
| 208 | try self.findNativeGccDirHaiku(gpa, io, args); | 210 | try self.findNativeGccDirHaiku(gpa, io, args); |
| 209 | self.crt_dir = try gpa.dupeZ(u8, "/system/develop/lib"); | 211 | self.crt_dir = try gpa.dupe(u8, "/system/develop/lib"); |
| 210 | } else if (builtin.target.os.tag == .illumos) { | 212 | } else if (builtin.target.os.tag == .illumos) { |
| 211 | // There is only one libc, and its headers/libraries are always in the same spot. | 213 | // There is only one libc, and its headers/libraries are always in the same spot. |
| 212 | self.include_dir = try gpa.dupeZ(u8, "/usr/include"); | 214 | self.include_dir = try gpa.dupe(u8, "/usr/include"); |
| 213 | self.sys_include_dir = try gpa.dupeZ(u8, "/usr/include"); | 215 | self.sys_include_dir = try gpa.dupe(u8, "/usr/include"); |
| 214 | self.crt_dir = try gpa.dupeZ(u8, "/usr/lib/64"); | 216 | self.crt_dir = try gpa.dupe(u8, "/usr/lib/64"); |
| 215 | } else if (std.process.can_spawn) { | 217 | } else if (std.process.can_spawn) { |
| 216 | try self.findNativeIncludeDirPosix(gpa, io, args); | 218 | try self.findNativeIncludeDirPosix(gpa, io, args); |
| 217 | switch (builtin.target.os.tag) { | 219 | switch (builtin.target.os.tag) { |
| 218 | .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try gpa.dupeZ(u8, "/usr/lib"), | 220 | .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try gpa.dupe(u8, "/usr/lib"), |
| 219 | .linux => try self.findNativeCrtDirPosix(gpa, io, args), | 221 | .linux => try self.findNativeCrtDirPosix(gpa, io, args), |
| 220 | else => {}, | 222 | else => {}, |
| 221 | } | 223 | } |
| ... | @@ -238,20 +240,17 @@ pub fn deinit(self: *LibCInstallation, allocator: Allocator) void { | ... | @@ -238,20 +240,17 @@ pub fn deinit(self: *LibCInstallation, allocator: Allocator) void { |
| 238 | 240 | ||
| 239 | fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void { | 241 | fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void { |
| 240 | // Detect infinite loops. | 242 | // Detect infinite loops. |
| 241 | var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) { | 243 | var environ_map = try args.environ_map.clone(gpa); |
| 242 | error.Unexpected => unreachable, // WASI-only | 244 | defer environ_map.deinit(); |
| 243 | else => |e| return e, | 245 | const skip_cc_env_var = if (environ_map.get(inf_loop_env_key)) |phase| blk: { |
| 244 | }; | ||
| 245 | defer env_map.deinit(); | ||
| 246 | const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: { | ||
| 247 | if (std.mem.eql(u8, phase, "1")) { | 246 | if (std.mem.eql(u8, phase, "1")) { |
| 248 | try env_map.put(inf_loop_env_key, "2"); | 247 | try environ_map.put(inf_loop_env_key, "2"); |
| 249 | break :blk true; | 248 | break :blk true; |
| 250 | } else { | 249 | } else { |
| 251 | return error.ZigIsTheCCompiler; | 250 | return error.ZigIsTheCCompiler; |
| 252 | } | 251 | } |
| 253 | } else blk: { | 252 | } else blk: { |
| 254 | try env_map.put(inf_loop_env_key, "1"); | 253 | try environ_map.put(inf_loop_env_key, "1"); |
| 255 | break :blk false; | 254 | break :blk false; |
| 256 | }; | 255 | }; |
| 257 | 256 | ||
| ... | @@ -260,7 +259,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar | ... | @@ -260,7 +259,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar |
| 260 | var argv = std.array_list.Managed([]const u8).init(gpa); | 259 | var argv = std.array_list.Managed([]const u8).init(gpa); |
| 261 | defer argv.deinit(); | 260 | defer argv.deinit(); |
| 262 | 261 | ||
| 263 | try appendCcExe(&argv, skip_cc_env_var); | 262 | try appendCcExe(&argv, skip_cc_env_var, &environ_map); |
| 264 | try argv.appendSlice(&.{ | 263 | try argv.appendSlice(&.{ |
| 265 | "-E", | 264 | "-E", |
| 266 | "-Wp,-v", | 265 | "-Wp,-v", |
| ... | @@ -268,10 +267,10 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar | ... | @@ -268,10 +267,10 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar |
| 268 | dev_null, | 267 | dev_null, |
| 269 | }); | 268 | }); |
| 270 | 269 | ||
| 271 | const run_res = std.process.Child.run(gpa, io, .{ | 270 | const run_res = std.process.run(gpa, io, .{ |
| 272 | .argv = argv.items, | ||
| 273 | .max_output_bytes = 1024 * 1024, | 271 | .max_output_bytes = 1024 * 1024, |
| 274 | .env_map = &env_map, | 272 | .argv = argv.items, |
| 273 | .environ_map = &environ_map, | ||
| 275 | // Some C compilers, such as Clang, are known to rely on argv[0] to find the path | 274 | // Some C compilers, such as Clang, are known to rely on argv[0] to find the path |
| 276 | // to their own executable, without even bothering to resolve PATH. This results in the message: | 275 | // to their own executable, without even bothering to resolve PATH. This results in the message: |
| 277 | // error: unable to execute command: Executable "" doesn't exist! | 276 | // error: unable to execute command: Executable "" doesn't exist! |
| ... | @@ -289,7 +288,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar | ... | @@ -289,7 +288,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar |
| 289 | gpa.free(run_res.stderr); | 288 | gpa.free(run_res.stderr); |
| 290 | } | 289 | } |
| 291 | switch (run_res.term) { | 290 | switch (run_res.term) { |
| 292 | .Exited => |code| if (code != 0) { | 291 | .exited => |code| if (code != 0) { |
| 293 | printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr); | 292 | printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr); |
| 294 | return error.CCompilerExitCode; | 293 | return error.CCompilerExitCode; |
| 295 | }, | 294 | }, |
| ... | @@ -336,7 +335,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar | ... | @@ -336,7 +335,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar |
| 336 | 335 | ||
| 337 | if (self.include_dir == null) { | 336 | if (self.include_dir == null) { |
| 338 | if (search_dir.access(io, include_dir_example_file, .{})) |_| { | 337 | if (search_dir.access(io, include_dir_example_file, .{})) |_| { |
| 339 | self.include_dir = try gpa.dupeZ(u8, search_path); | 338 | self.include_dir = try gpa.dupe(u8, search_path); |
| 340 | } else |err| switch (err) { | 339 | } else |err| switch (err) { |
| 341 | error.FileNotFound => {}, | 340 | error.FileNotFound => {}, |
| 342 | else => return error.FileSystem, | 341 | else => return error.FileSystem, |
| ... | @@ -345,7 +344,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar | ... | @@ -345,7 +344,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar |
| 345 | 344 | ||
| 346 | if (self.sys_include_dir == null) { | 345 | if (self.sys_include_dir == null) { |
| 347 | if (search_dir.access(io, sys_include_dir_example_file, .{})) |_| { | 346 | if (search_dir.access(io, sys_include_dir_example_file, .{})) |_| { |
| 348 | self.sys_include_dir = try gpa.dupeZ(u8, search_path); | 347 | self.sys_include_dir = try gpa.dupe(u8, search_path); |
| 349 | } else |err| switch (err) { | 348 | } else |err| switch (err) { |
| 350 | error.FileNotFound => {}, | 349 | error.FileNotFound => {}, |
| 351 | else => return error.FileSystem, | 350 | else => return error.FileSystem, |
| ... | @@ -447,6 +446,7 @@ fn findNativeCrtDirWindows( | ... | @@ -447,6 +446,7 @@ fn findNativeCrtDirWindows( |
| 447 | 446 | ||
| 448 | fn findNativeCrtDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void { | 447 | fn findNativeCrtDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void { |
| 449 | self.crt_dir = try ccPrintFileName(gpa, io, .{ | 448 | self.crt_dir = try ccPrintFileName(gpa, io, .{ |
| 449 | .environ_map = args.environ_map, | ||
| 450 | .search_basename = switch (args.target.os.tag) { | 450 | .search_basename = switch (args.target.os.tag) { |
| 451 | .linux => if (args.target.abi.isAndroid()) "crtbegin_dynamic.o" else "crt1.o", | 451 | .linux => if (args.target.abi.isAndroid()) "crtbegin_dynamic.o" else "crt1.o", |
| 452 | else => "crt1.o", | 452 | else => "crt1.o", |
| ... | @@ -551,28 +551,26 @@ fn findNativeMsvcLibDir( | ... | @@ -551,28 +551,26 @@ fn findNativeMsvcLibDir( |
| 551 | } | 551 | } |
| 552 | 552 | ||
| 553 | pub const CCPrintFileNameOptions = struct { | 553 | pub const CCPrintFileNameOptions = struct { |
| 554 | environ_map: *const Environ.Map, | ||
| 554 | search_basename: []const u8, | 555 | search_basename: []const u8, |
| 555 | want_dirname: enum { full_path, only_dir }, | 556 | want_dirname: enum { full_path, only_dir }, |
| 556 | verbose: bool = false, | 557 | verbose: bool = false, |
| 557 | }; | 558 | }; |
| 558 | 559 | ||
| 559 | /// caller owns returned memory | 560 | /// caller owns returned memory |
| 560 | fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 { | 561 | fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![]u8 { |
| 561 | // Detect infinite loops. | 562 | // Detect infinite loops. |
| 562 | var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) { | 563 | var environ_map = try args.environ_map.clone(gpa); |
| 563 | error.Unexpected => unreachable, // WASI-only | 564 | defer environ_map.deinit(); |
| 564 | else => |e| return e, | 565 | const skip_cc_env_var = if (environ_map.get(inf_loop_env_key)) |phase| blk: { |
| 565 | }; | ||
| 566 | defer env_map.deinit(); | ||
| 567 | const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: { | ||
| 568 | if (std.mem.eql(u8, phase, "1")) { | 566 | if (std.mem.eql(u8, phase, "1")) { |
| 569 | try env_map.put(inf_loop_env_key, "2"); | 567 | try environ_map.put(inf_loop_env_key, "2"); |
| 570 | break :blk true; | 568 | break :blk true; |
| 571 | } else { | 569 | } else { |
| 572 | return error.ZigIsTheCCompiler; | 570 | return error.ZigIsTheCCompiler; |
| 573 | } | 571 | } |
| 574 | } else blk: { | 572 | } else blk: { |
| 575 | try env_map.put(inf_loop_env_key, "1"); | 573 | try environ_map.put(inf_loop_env_key, "1"); |
| 576 | break :blk false; | 574 | break :blk false; |
| 577 | }; | 575 | }; |
| 578 | 576 | ||
| ... | @@ -582,13 +580,13 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 | ... | @@ -582,13 +580,13 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 |
| 582 | const arg1 = try std.fmt.allocPrint(gpa, "-print-file-name={s}", .{args.search_basename}); | 580 | const arg1 = try std.fmt.allocPrint(gpa, "-print-file-name={s}", .{args.search_basename}); |
| 583 | defer gpa.free(arg1); | 581 | defer gpa.free(arg1); |
| 584 | 582 | ||
| 585 | try appendCcExe(&argv, skip_cc_env_var); | 583 | try appendCcExe(&argv, skip_cc_env_var, &environ_map); |
| 586 | try argv.append(arg1); | 584 | try argv.append(arg1); |
| 587 | 585 | ||
| 588 | const run_res = std.process.Child.run(gpa, io, .{ | 586 | const run_res = std.process.run(gpa, io, .{ |
| 589 | .argv = argv.items, | ||
| 590 | .max_output_bytes = 1024 * 1024, | 587 | .max_output_bytes = 1024 * 1024, |
| 591 | .env_map = &env_map, | 588 | .argv = argv.items, |
| 589 | .environ_map = &environ_map, | ||
| 592 | // Some C compilers, such as Clang, are known to rely on argv[0] to find the path | 590 | // Some C compilers, such as Clang, are known to rely on argv[0] to find the path |
| 593 | // to their own executable, without even bothering to resolve PATH. This results in the message: | 591 | // to their own executable, without even bothering to resolve PATH. This results in the message: |
| 594 | // error: unable to execute command: Executable "" doesn't exist! | 592 | // error: unable to execute command: Executable "" doesn't exist! |
| ... | @@ -603,7 +601,7 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 | ... | @@ -603,7 +601,7 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 |
| 603 | gpa.free(run_res.stderr); | 601 | gpa.free(run_res.stderr); |
| 604 | } | 602 | } |
| 605 | switch (run_res.term) { | 603 | switch (run_res.term) { |
| 606 | .Exited => |code| if (code != 0) { | 604 | .exited => |code| if (code != 0) { |
| 607 | printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr); | 605 | printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr); |
| 608 | return error.CCompilerExitCode; | 606 | return error.CCompilerExitCode; |
| 609 | }, | 607 | }, |
| ... | @@ -619,10 +617,10 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 | ... | @@ -619,10 +617,10 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 |
| 619 | // So we detect failure by checking if the output matches exactly the input. | 617 | // So we detect failure by checking if the output matches exactly the input. |
| 620 | if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound; | 618 | if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound; |
| 621 | switch (args.want_dirname) { | 619 | switch (args.want_dirname) { |
| 622 | .full_path => return gpa.dupeZ(u8, line), | 620 | .full_path => return gpa.dupe(u8, line), |
| 623 | .only_dir => { | 621 | .only_dir => { |
| 624 | const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; | 622 | const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; |
| 625 | return gpa.dupeZ(u8, dirname); | 623 | return gpa.dupe(u8, dirname); |
| 626 | }, | 624 | }, |
| 627 | } | 625 | } |
| 628 | } | 626 | } |
| ... | @@ -668,14 +666,18 @@ fn fillInstallations( | ... | @@ -668,14 +666,18 @@ fn fillInstallations( |
| 668 | 666 | ||
| 669 | const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS"; | 667 | const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS"; |
| 670 | 668 | ||
| 671 | fn appendCcExe(args: *std.array_list.Managed([]const u8), skip_cc_env_var: bool) !void { | 669 | fn appendCcExe( |
| 670 | args: *std.array_list.Managed([]const u8), | ||
| 671 | skip_cc_env_var: bool, | ||
| 672 | environ_map: *const Environ.Map, | ||
| 673 | ) !void { | ||
| 672 | const default_cc_exe = if (is_windows) "cc.exe" else "cc"; | 674 | const default_cc_exe = if (is_windows) "cc.exe" else "cc"; |
| 673 | try args.ensureUnusedCapacity(1); | 675 | try args.ensureUnusedCapacity(1); |
| 674 | if (skip_cc_env_var) { | 676 | if (skip_cc_env_var) { |
| 675 | args.appendAssumeCapacity(default_cc_exe); | 677 | args.appendAssumeCapacity(default_cc_exe); |
| 676 | return; | 678 | return; |
| 677 | } | 679 | } |
| 678 | const cc_env_var = std.zig.EnvVar.CC.getPosix() orelse { | 680 | const cc_env_var = std.zig.EnvVar.CC.get(environ_map) orelse { |
| 679 | args.appendAssumeCapacity(default_cc_exe); | 681 | args.appendAssumeCapacity(default_cc_exe); |
| 680 | return; | 682 | return; |
| 681 | }; | 683 | }; |
lib/std/zig/WindowsSdk.zig+47-30| ... | @@ -6,6 +6,7 @@ const Io = std.Io; | ... | @@ -6,6 +6,7 @@ const Io = std.Io; |
| 6 | const Dir = std.Io.Dir; | 6 | const Dir = std.Io.Dir; |
| 7 | const Writer = std.Io.Writer; | 7 | const Writer = std.Io.Writer; |
| 8 | const Allocator = std.mem.Allocator; | 8 | const Allocator = std.mem.Allocator; |
| 9 | const Environ = std.process.Environ; | ||
| 9 | 10 | ||
| 10 | windows10sdk: ?Installation, | 11 | windows10sdk: ?Installation, |
| 11 | windows81sdk: ?Installation, | 12 | windows81sdk: ?Installation, |
| ... | @@ -24,7 +25,12 @@ const product_version_max_length = version_major_minor_max_length + ".65535".len | ... | @@ -24,7 +25,12 @@ const product_version_max_length = version_major_minor_max_length + ".65535".len |
| 24 | /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory. | 25 | /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory. |
| 25 | /// Caller owns the result's fields. | 26 | /// Caller owns the result's fields. |
| 26 | /// Returns memory allocated by `gpa` | 27 | /// Returns memory allocated by `gpa` |
| 27 | pub fn find(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk { | 28 | pub fn find( |
| 29 | gpa: Allocator, | ||
| 30 | io: Io, | ||
| 31 | arch: std.Target.Cpu.Arch, | ||
| 32 | environ_map: *const Environ.Map, | ||
| 33 | ) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk { | ||
| 28 | if (builtin.os.tag != .windows) return error.NotFound; | 34 | if (builtin.os.tag != .windows) return error.NotFound; |
| 29 | 35 | ||
| 30 | //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed | 36 | //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 | ... | @@ -49,7 +55,7 @@ pub fn find(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemor |
| 49 | }; | 55 | }; |
| 50 | errdefer if (windows81sdk) |*w| w.free(gpa); | 56 | errdefer if (windows81sdk) |*w| w.free(gpa); |
| 51 | 57 | ||
| 52 | const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(gpa, io, arch) catch |err| switch (err) { | 58 | const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(gpa, io, arch, environ_map) catch |err| switch (err) { |
| 53 | error.MsvcLibDirNotFound => null, | 59 | error.MsvcLibDirNotFound => null, |
| 54 | error.OutOfMemory => return error.OutOfMemory, | 60 | error.OutOfMemory => return error.OutOfMemory, |
| 55 | }; | 61 | }; |
| ... | @@ -671,7 +677,11 @@ const MsvcLibDir = struct { | ... | @@ -671,7 +677,11 @@ const MsvcLibDir = struct { |
| 671 | return Dir.openDirAbsolute(io, instances_path, .{ .iterate = true }) catch return error.PathNotFound; | 677 | return Dir.openDirAbsolute(io, instances_path, .{ .iterate = true }) catch return error.PathNotFound; |
| 672 | } | 678 | } |
| 673 | 679 | ||
| 674 | fn findInstancesDir(gpa: Allocator, io: Io) error{ OutOfMemory, PathNotFound }!Dir { | 680 | fn findInstancesDir( |
| 681 | gpa: Allocator, | ||
| 682 | io: Io, | ||
| 683 | environ_map: *const Environ.Map, | ||
| 684 | ) error{ OutOfMemory, PathNotFound }!Dir { | ||
| 675 | // First, try getting the packages cache path from the registry. | 685 | // First, try getting the packages cache path from the registry. |
| 676 | // This only seems to exist when the path is different from the default. | 686 | // This only seems to exist when the path is different from the default. |
| 677 | method1: { | 687 | method1: { |
| ... | @@ -691,16 +701,13 @@ const MsvcLibDir = struct { | ... | @@ -691,16 +701,13 @@ const MsvcLibDir = struct { |
| 691 | // If that can't be found, fall back to manually appending | 701 | // If that can't be found, fall back to manually appending |
| 692 | // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA% | 702 | // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA% |
| 693 | method3: { | 703 | method3: { |
| 694 | const program_data = std.process.getEnvVarOwned(gpa, "PROGRAMDATA") catch |err| switch (err) { | 704 | const program_data = std.zig.EnvVar.PROGRAMDATA.get(environ_map) orelse break :method3; |
| 695 | error.OutOfMemory => |e| return e, | ||
| 696 | error.InvalidWtf8 => unreachable, | ||
| 697 | error.EnvironmentVariableNotFound => break :method3, | ||
| 698 | }; | ||
| 699 | defer gpa.free(program_data); | ||
| 700 | 705 | ||
| 701 | if (!Dir.path.isAbsolute(program_data)) break :method3; | 706 | if (!Dir.path.isAbsolute(program_data)) break :method3; |
| 702 | 707 | ||
| 703 | const instances_path = try Dir.path.join(gpa, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" }); | 708 | const instances_path = try Dir.path.join(gpa, &.{ |
| 709 | program_data, "Microsoft", "VisualStudio", "Packages", "_Instances", | ||
| 710 | }); | ||
| 704 | defer gpa.free(instances_path); | 711 | defer gpa.free(instances_path); |
| 705 | 712 | ||
| 706 | return Dir.openDirAbsolute(io, instances_path, .{ .iterate = true }) catch break :method3; | 713 | return Dir.openDirAbsolute(io, instances_path, .{ .iterate = true }) catch break :method3; |
| ... | @@ -754,12 +761,17 @@ const MsvcLibDir = struct { | ... | @@ -754,12 +761,17 @@ const MsvcLibDir = struct { |
| 754 | /// | 761 | /// |
| 755 | /// The logic in this function is intended to match what ISetupConfiguration does | 762 | /// The logic in this function is intended to match what ISetupConfiguration does |
| 756 | /// under-the-hood, as verified using Procmon. | 763 | /// under-the-hood, as verified using Procmon. |
| 757 | fn findViaCOM(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { | 764 | fn findViaCOM( |
| 765 | gpa: Allocator, | ||
| 766 | io: Io, | ||
| 767 | arch: std.Target.Cpu.Arch, | ||
| 768 | environ_map: *const Environ.Map, | ||
| 769 | ) error{ OutOfMemory, PathNotFound }![]const u8 { | ||
| 758 | // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances` | 770 | // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances` |
| 759 | // This will contain directories with names of instance IDs like 80a758ca, | 771 | // This will contain directories with names of instance IDs like 80a758ca, |
| 760 | // which will contain `state.json` files that have the version and | 772 | // which will contain `state.json` files that have the version and |
| 761 | // installation directory. | 773 | // installation directory. |
| 762 | var instances_dir = try findInstancesDir(gpa, io); | 774 | var instances_dir = try findInstancesDir(gpa, io, environ_map); |
| 763 | defer instances_dir.close(io); | 775 | defer instances_dir.close(io); |
| 764 | 776 | ||
| 765 | var state_subpath_buf: [Dir.max_name_bytes + 32]u8 = undefined; | 777 | var state_subpath_buf: [Dir.max_name_bytes + 32]u8 = undefined; |
| ... | @@ -856,15 +868,16 @@ const MsvcLibDir = struct { | ... | @@ -856,15 +868,16 @@ const MsvcLibDir = struct { |
| 856 | } | 868 | } |
| 857 | 869 | ||
| 858 | // 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 | 870 | // 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 |
| 859 | fn findViaRegistry(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { | 871 | fn findViaRegistry( |
| 872 | gpa: Allocator, | ||
| 873 | io: Io, | ||
| 874 | arch: std.Target.Cpu.Arch, | ||
| 875 | environ_map: *const Environ.Map, | ||
| 876 | ) error{ OutOfMemory, PathNotFound }![]const u8 { | ||
| 860 | 877 | ||
| 861 | // %localappdata%\Microsoft\VisualStudio\ | 878 | // %localappdata%\Microsoft\VisualStudio\ |
| 862 | // %appdata%\Local\Microsoft\VisualStudio\ | 879 | // %appdata%\Local\Microsoft\VisualStudio\ |
| 863 | const local_app_data_path = (std.zig.EnvVar.LOCALAPPDATA.get(gpa) catch |err| switch (err) { | 880 | const local_app_data_path = std.zig.EnvVar.LOCALAPPDATA.get(environ_map) orelse return error.PathNotFound; |
| 864 | error.OutOfMemory => |e| return e, | ||
| 865 | error.InvalidWtf8 => return error.PathNotFound, | ||
| 866 | }) orelse return error.PathNotFound; | ||
| 867 | defer gpa.free(local_app_data_path); | ||
| 868 | const visualstudio_folder_path = try Dir.path.join(gpa, &.{ | 881 | const visualstudio_folder_path = try Dir.path.join(gpa, &.{ |
| 869 | local_app_data_path, "Microsoft\\VisualStudio\\", | 882 | local_app_data_path, "Microsoft\\VisualStudio\\", |
| 870 | }); | 883 | }); |
| ... | @@ -951,16 +964,15 @@ const MsvcLibDir = struct { | ... | @@ -951,16 +964,15 @@ const MsvcLibDir = struct { |
| 951 | return msvc_dir; | 964 | return msvc_dir; |
| 952 | } | 965 | } |
| 953 | 966 | ||
| 954 | fn findViaVs7Key(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { | 967 | fn findViaVs7Key( |
| 968 | gpa: Allocator, | ||
| 969 | io: Io, | ||
| 970 | arch: std.Target.Cpu.Arch, | ||
| 971 | environ_map: *const Environ.Map, | ||
| 972 | ) error{ OutOfMemory, PathNotFound }![]const u8 { | ||
| 955 | var base_path: std.array_list.Managed(u8) = base_path: { | 973 | var base_path: std.array_list.Managed(u8) = base_path: { |
| 956 | try_env: { | 974 | try_env: { |
| 957 | var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) { | 975 | if (environ_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| { |
| 958 | error.OutOfMemory => return error.OutOfMemory, | ||
| 959 | else => break :try_env, | ||
| 960 | }; | ||
| 961 | defer env_map.deinit(); | ||
| 962 | |||
| 963 | if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| { | ||
| 964 | if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env; | 976 | if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env; |
| 965 | if (!Dir.path.isAbsolute(VS140COMNTOOLS)) break :try_env; | 977 | if (!Dir.path.isAbsolute(VS140COMNTOOLS)) break :try_env; |
| 966 | var list = std.array_list.Managed(u8).init(gpa); | 978 | var list = std.array_list.Managed(u8).init(gpa); |
| ... | @@ -1030,12 +1042,17 @@ const MsvcLibDir = struct { | ... | @@ -1030,12 +1042,17 @@ const MsvcLibDir = struct { |
| 1030 | 1042 | ||
| 1031 | /// Find path to MSVC's `lib/` directory. | 1043 | /// Find path to MSVC's `lib/` directory. |
| 1032 | /// Caller owns the result. | 1044 | /// Caller owns the result. |
| 1033 | pub fn find(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 { | 1045 | pub fn find( |
| 1034 | const full_path = MsvcLibDir.findViaCOM(gpa, io, arch) catch |err1| switch (err1) { | 1046 | gpa: Allocator, |
| 1047 | io: Io, | ||
| 1048 | arch: std.Target.Cpu.Arch, | ||
| 1049 | environ_map: *const Environ.Map, | ||
| 1050 | ) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 { | ||
| 1051 | const full_path = MsvcLibDir.findViaCOM(gpa, io, arch, environ_map) catch |err1| switch (err1) { | ||
| 1035 | error.OutOfMemory => return error.OutOfMemory, | 1052 | error.OutOfMemory => return error.OutOfMemory, |
| 1036 | error.PathNotFound => MsvcLibDir.findViaRegistry(gpa, io, arch) catch |err2| switch (err2) { | 1053 | error.PathNotFound => MsvcLibDir.findViaRegistry(gpa, io, arch, environ_map) catch |err2| switch (err2) { |
| 1037 | error.OutOfMemory => return error.OutOfMemory, | 1054 | error.OutOfMemory => return error.OutOfMemory, |
| 1038 | error.PathNotFound => MsvcLibDir.findViaVs7Key(gpa, io, arch) catch |err3| switch (err3) { | 1055 | error.PathNotFound => MsvcLibDir.findViaVs7Key(gpa, io, arch, environ_map) catch |err3| switch (err3) { |
| 1039 | error.OutOfMemory => return error.OutOfMemory, | 1056 | error.OutOfMemory => return error.OutOfMemory, |
| 1040 | error.PathNotFound => return error.MsvcLibDirNotFound, | 1057 | error.PathNotFound => return error.MsvcLibDirNotFound, |
| 1041 | }, | 1058 | }, |
lib/std/zig/system/NativePaths.zig+17-22| ... | @@ -14,10 +14,16 @@ framework_dirs: std.ArrayList([]const u8) = .empty, | ... | @@ -14,10 +14,16 @@ framework_dirs: std.ArrayList([]const u8) = .empty, |
| 14 | rpaths: std.ArrayList([]const u8) = .empty, | 14 | rpaths: std.ArrayList([]const u8) = .empty, |
| 15 | warnings: std.ArrayList([]const u8) = .empty, | 15 | warnings: std.ArrayList([]const u8) = .empty, |
| 16 | 16 | ||
| 17 | pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !NativePaths { | 17 | pub fn detect( |
| 18 | arena: Allocator, | ||
| 19 | io: Io, | ||
| 20 | native_target: *const std.Target, | ||
| 21 | environ_map: *process.Environ.Map, | ||
| 22 | ) !NativePaths { | ||
| 18 | var self: NativePaths = .{ .arena = arena }; | 23 | var self: NativePaths = .{ .arena = arena }; |
| 19 | var is_nix = false; | 24 | var is_nix = false; |
| 20 | if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { | 25 | |
| 26 | if (std.zig.EnvVar.NIX_CFLAGS_COMPILE.get(environ_map)) |nix_cflags_compile| { | ||
| 21 | is_nix = true; | 27 | is_nix = true; |
| 22 | var it = mem.tokenizeScalar(u8, nix_cflags_compile, ' '); | 28 | var it = mem.tokenizeScalar(u8, nix_cflags_compile, ' '); |
| 23 | while (true) { | 29 | while (true) { |
| ... | @@ -41,12 +47,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ | ... | @@ -41,12 +47,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ |
| 41 | try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word}); | 47 | try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word}); |
| 42 | } | 48 | } |
| 43 | } | 49 | } |
| 44 | } else |err| switch (err) { | ||
| 45 | error.InvalidWtf8 => unreachable, | ||
| 46 | error.EnvironmentVariableNotFound => {}, | ||
| 47 | error.OutOfMemory => |e| return e, | ||
| 48 | } | 50 | } |
| 49 | if (process.getEnvVarOwned(arena, "NIX_LDFLAGS")) |nix_ldflags| { | 51 | |
| 52 | if (std.zig.EnvVar.NIX_LDFLAGS.get(environ_map)) |nix_ldflags| { | ||
| 50 | is_nix = true; | 53 | is_nix = true; |
| 51 | var it = mem.tokenizeScalar(u8, nix_ldflags, ' '); | 54 | var it = mem.tokenizeScalar(u8, nix_ldflags, ' '); |
| 52 | while (true) { | 55 | while (true) { |
| ... | @@ -73,12 +76,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ | ... | @@ -73,12 +76,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ |
| 73 | break; | 76 | break; |
| 74 | } | 77 | } |
| 75 | } | 78 | } |
| 76 | } else |err| switch (err) { | ||
| 77 | error.InvalidWtf8 => unreachable, | ||
| 78 | error.EnvironmentVariableNotFound => {}, | ||
| 79 | error.OutOfMemory => |e| return e, | ||
| 80 | } | 79 | } |
| 81 | if (process.getEnvVarOwned(arena, "NIX_CFLAGS_LINK")) |nix_cflags_link| { | 80 | |
| 81 | if (std.zig.EnvVar.NIX_CFLAGS_LINK.get(environ_map)) |nix_cflags_link| { | ||
| 82 | is_nix = true; | 82 | is_nix = true; |
| 83 | var it = mem.tokenizeScalar(u8, nix_cflags_link, ' '); | 83 | var it = mem.tokenizeScalar(u8, nix_cflags_link, ' '); |
| 84 | while (true) { | 84 | while (true) { |
| ... | @@ -105,11 +105,8 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ | ... | @@ -105,11 +105,8 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ |
| 105 | break; | 105 | break; |
| 106 | } | 106 | } |
| 107 | } | 107 | } |
| 108 | } else |err| switch (err) { | ||
| 109 | error.InvalidWtf8 => unreachable, | ||
| 110 | error.EnvironmentVariableNotFound => {}, | ||
| 111 | error.OutOfMemory => |e| return e, | ||
| 112 | } | 108 | } |
| 109 | |||
| 113 | if (is_nix) { | 110 | if (is_nix) { |
| 114 | return self; | 111 | return self; |
| 115 | } | 112 | } |
| ... | @@ -124,7 +121,7 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ | ... | @@ -124,7 +121,7 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ |
| 124 | } | 121 | } |
| 125 | 122 | ||
| 126 | // Check for homebrew paths | 123 | // Check for homebrew paths |
| 127 | if (std.posix.getenv("HOMEBREW_PREFIX")) |prefix| { | 124 | if (std.zig.EnvVar.HOMEBREW_PREFIX.get(environ_map)) |prefix| { |
| 128 | try self.addLibDir(try std.fs.path.join(arena, &.{ prefix, "/lib" })); | 125 | try self.addLibDir(try std.fs.path.join(arena, &.{ prefix, "/lib" })); |
| 129 | try self.addIncludeDir(try std.fs.path.join(arena, &.{ prefix, "/include" })); | 126 | try self.addIncludeDir(try std.fs.path.join(arena, &.{ prefix, "/include" })); |
| 130 | } | 127 | } |
| ... | @@ -180,23 +177,21 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ | ... | @@ -180,23 +177,21 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ |
| 180 | 177 | ||
| 181 | // Distros like guix don't use FHS, so they rely on environment | 178 | // Distros like guix don't use FHS, so they rely on environment |
| 182 | // variables to search for headers and libraries. | 179 | // variables to search for headers and libraries. |
| 183 | // We use os.getenv here since this part won't be executed on | 180 | if (std.zig.EnvVar.C_INCLUDE_PATH.get(environ_map)) |c_include_path| { |
| 184 | // windows, to get rid of unnecessary error handling. | ||
| 185 | if (std.posix.getenv("C_INCLUDE_PATH")) |c_include_path| { | ||
| 186 | var it = mem.tokenizeScalar(u8, c_include_path, ':'); | 181 | var it = mem.tokenizeScalar(u8, c_include_path, ':'); |
| 187 | while (it.next()) |dir| { | 182 | while (it.next()) |dir| { |
| 188 | try self.addIncludeDir(dir); | 183 | try self.addIncludeDir(dir); |
| 189 | } | 184 | } |
| 190 | } | 185 | } |
| 191 | 186 | ||
| 192 | if (std.posix.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| { | 187 | if (std.zig.EnvVar.CPLUS_INCLUDE_PATH.get(environ_map)) |cplus_include_path| { |
| 193 | var it = mem.tokenizeScalar(u8, cplus_include_path, ':'); | 188 | var it = mem.tokenizeScalar(u8, cplus_include_path, ':'); |
| 194 | while (it.next()) |dir| { | 189 | while (it.next()) |dir| { |
| 195 | try self.addIncludeDir(dir); | 190 | try self.addIncludeDir(dir); |
| 196 | } | 191 | } |
| 197 | } | 192 | } |
| 198 | 193 | ||
| 199 | if (std.posix.getenv("LIBRARY_PATH")) |library_path| { | 194 | if (std.zig.EnvVar.LIBRARY_PATH.get(environ_map)) |library_path| { |
| 200 | var it = mem.tokenizeScalar(u8, library_path, ':'); | 195 | var it = mem.tokenizeScalar(u8, library_path, ':'); |
| 201 | while (it.next()) |dir| { | 196 | while (it.next()) |dir| { |
| 202 | try self.addLibDir(dir); | 197 | try self.addLibDir(dir); |
lib/std/zig/system/darwin.zig+4-4| ... | @@ -17,7 +17,7 @@ pub const macos = @import("darwin/macos.zig"); | ... | @@ -17,7 +17,7 @@ pub const macos = @import("darwin/macos.zig"); |
| 17 | /// | 17 | /// |
| 18 | /// If error.OutOfMemory occurs in Allocator, this function returns null. | 18 | /// If error.OutOfMemory occurs in Allocator, this function returns null. |
| 19 | pub fn isSdkInstalled(gpa: Allocator, io: Io) bool { | 19 | pub fn isSdkInstalled(gpa: Allocator, io: Io) bool { |
| 20 | const result = std.process.Child.run(gpa, io, .{ | 20 | const result = std.process.run(gpa, io, .{ |
| 21 | .argv = &.{ "xcode-select", "--print-path" }, | 21 | .argv = &.{ "xcode-select", "--print-path" }, |
| 22 | }) catch return false; | 22 | }) catch return false; |
| 23 | defer { | 23 | defer { |
| ... | @@ -25,7 +25,7 @@ pub fn isSdkInstalled(gpa: Allocator, io: Io) bool { | ... | @@ -25,7 +25,7 @@ pub fn isSdkInstalled(gpa: Allocator, io: Io) bool { |
| 25 | gpa.free(result.stdout); | 25 | gpa.free(result.stdout); |
| 26 | } | 26 | } |
| 27 | return switch (result.term) { | 27 | return switch (result.term) { |
| 28 | .Exited => |code| if (code == 0) result.stdout.len > 0 else false, | 28 | .exited => |code| if (code == 0) result.stdout.len > 0 else false, |
| 29 | else => false, | 29 | else => false, |
| 30 | }; | 30 | }; |
| 31 | } | 31 | } |
| ... | @@ -47,13 +47,13 @@ pub fn getSdk(gpa: Allocator, io: Io, target: *const Target) ?[]const u8 { | ... | @@ -47,13 +47,13 @@ pub fn getSdk(gpa: Allocator, io: Io, target: *const Target) ?[]const u8 { |
| 47 | else => return null, | 47 | else => return null, |
| 48 | }; | 48 | }; |
| 49 | const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" }; | 49 | const argv = &[_][]const u8{ "xcrun", "--sdk", sdk, "--show-sdk-path" }; |
| 50 | const result = std.process.Child.run(gpa, io, .{ .argv = argv }) catch return null; | 50 | const result = std.process.run(gpa, io, .{ .argv = argv }) catch return null; |
| 51 | defer { | 51 | defer { |
| 52 | gpa.free(result.stderr); | 52 | gpa.free(result.stderr); |
| 53 | gpa.free(result.stdout); | 53 | gpa.free(result.stdout); |
| 54 | } | 54 | } |
| 55 | switch (result.term) { | 55 | switch (result.term) { |
| 56 | .Exited => |code| if (code != 0) return null, | 56 | .exited => |code| if (code != 0) return null, |
| 57 | else => return null, | 57 | else => return null, |
| 58 | } | 58 | } |
| 59 | return gpa.dupe(u8, mem.trimEnd(u8, result.stdout, "\r\n")) catch null; | 59 | return gpa.dupe(u8, mem.trimEnd(u8, result.stdout, "\r\n")) catch null; |
src/Compilation.zig+64-35| ... | @@ -54,6 +54,7 @@ gpa: Allocator, | ... | @@ -54,6 +54,7 @@ gpa: Allocator, |
| 54 | /// threads at once. | 54 | /// threads at once. |
| 55 | arena: Allocator, | 55 | arena: Allocator, |
| 56 | io: Io, | 56 | io: Io, |
| 57 | environ_map: *const std.process.Environ.Map, | ||
| 57 | thread_limit: usize, | 58 | thread_limit: usize, |
| 58 | /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. | 59 | /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. |
| 59 | zcu: ?*Zcu, | 60 | zcu: ?*Zcu, |
| ... | @@ -761,11 +762,12 @@ pub const Directories = struct { | ... | @@ -761,11 +762,12 @@ pub const Directories = struct { |
| 761 | .wasi => void, | 762 | .wasi => void, |
| 762 | else => []const u8, | 763 | else => []const u8, |
| 763 | }, | 764 | }, |
| 765 | environ_map: *const std.process.Environ.Map, | ||
| 764 | ) Directories { | 766 | ) Directories { |
| 765 | const wasi = builtin.target.os.tag == .wasi; | 767 | const wasi = builtin.target.os.tag == .wasi; |
| 766 | 768 | ||
| 767 | const cwd = introspect.getResolvedCwd(arena) catch |err| { | 769 | const cwd = introspect.getResolvedCwd(arena) catch |err| { |
| 768 | fatal("unable to get cwd: {s}", .{@errorName(err)}); | 770 | fatal("unable to get cwd: {t}", .{err}); |
| 769 | }; | 771 | }; |
| 770 | 772 | ||
| 771 | const zig_lib: Cache.Directory = d: { | 773 | const zig_lib: Cache.Directory = d: { |
| ... | @@ -779,7 +781,7 @@ pub const Directories = struct { | ... | @@ -779,7 +781,7 @@ pub const Directories = struct { |
| 779 | const global_cache: Cache.Directory = d: { | 781 | const global_cache: Cache.Directory = d: { |
| 780 | if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache"); | 782 | if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache"); |
| 781 | if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache"); | 783 | if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache"); |
| 782 | const path = introspect.resolveGlobalCacheDir(arena) catch |err| { | 784 | const path = introspect.resolveGlobalCacheDir(arena, environ_map) catch |err| { |
| 783 | fatal("unable to resolve zig cache directory: {t}", .{err}); | 785 | fatal("unable to resolve zig cache directory: {t}", .{err}); |
| 784 | }; | 786 | }; |
| 785 | break :d openUnresolved(arena, io, cwd, path, .@"global cache"); | 787 | break :d openUnresolved(arena, io, cwd, path, .@"global cache"); |
| ... | @@ -1797,6 +1799,8 @@ pub const CreateOptions = struct { | ... | @@ -1797,6 +1799,8 @@ pub const CreateOptions = struct { |
| 1797 | 1799 | ||
| 1798 | parent_whole_cache: ?ParentWholeCache = null, | 1800 | parent_whole_cache: ?ParentWholeCache = null, |
| 1799 | 1801 | ||
| 1802 | environ_map: *const std.process.Environ.Map, | ||
| 1803 | |||
| 1800 | pub const Entry = link.File.OpenOptions.Entry; | 1804 | pub const Entry = link.File.OpenOptions.Entry; |
| 1801 | 1805 | ||
| 1802 | /// Which fields are valid depends on the `cache_mode` given. | 1806 | /// 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, | ... | @@ -1967,6 +1971,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, |
| 1967 | options.root_mod.resolved_target.is_native_abi, | 1971 | options.root_mod.resolved_target.is_native_abi, |
| 1968 | link_libc, | 1972 | link_libc, |
| 1969 | options.libc_installation, | 1973 | options.libc_installation, |
| 1974 | options.environ_map, | ||
| 1970 | ) catch |err| switch (err) { | 1975 | ) catch |err| switch (err) { |
| 1971 | error.OutOfMemory => |e| return e, | 1976 | error.OutOfMemory => |e| return e, |
| 1972 | // Every other error is specifically related to finding the native installation | 1977 | // 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, | ... | @@ -2123,6 +2128,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, |
| 2123 | .manifest_dir = options.dirs.local_cache.handle.createDirPathOpen(io, "h", .{}) catch |err| { | 2128 | .manifest_dir = options.dirs.local_cache.handle.createDirPathOpen(io, "h", .{}) catch |err| { |
| 2124 | return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = "h", .err = err } }); | 2129 | return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = "h", .err = err } }); |
| 2125 | }, | 2130 | }, |
| 2131 | .cwd = options.dirs.cwd, | ||
| 2126 | }; | 2132 | }; |
| 2127 | // These correspond to std.zig.Server.Message.PathPrefix. | 2133 | // These correspond to std.zig.Server.Message.PathPrefix. |
| 2128 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); | 2134 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); |
| ... | @@ -2306,6 +2312,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, | ... | @@ -2306,6 +2312,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, |
| 2306 | .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir), | 2312 | .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir), |
| 2307 | .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc), | 2313 | .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc), |
| 2308 | .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), | 2314 | .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), |
| 2315 | .environ_map = options.environ_map, | ||
| 2309 | }; | 2316 | }; |
| 2310 | 2317 | ||
| 2311 | errdefer { | 2318 | errdefer { |
| ... | @@ -5503,6 +5510,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU | ... | @@ -5503,6 +5510,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU |
| 5503 | .verbose_llvm_bc = comp.verbose_llvm_bc, | 5510 | .verbose_llvm_bc = comp.verbose_llvm_bc, |
| 5504 | .verbose_cimport = comp.verbose_cimport, | 5511 | .verbose_cimport = comp.verbose_cimport, |
| 5505 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, | 5512 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, |
| 5513 | .environ_map = comp.environ_map, | ||
| 5506 | }) catch |err| switch (err) { | 5514 | }) catch |err| switch (err) { |
| 5507 | error.CreateFail => { | 5515 | error.CreateFail => { |
| 5508 | comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: {f}", .{sub_create_diag}); | 5516 | comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: {f}", .{sub_create_diag}); |
| ... | @@ -5705,6 +5713,7 @@ pub fn translateC( | ... | @@ -5705,6 +5713,7 @@ pub fn translateC( |
| 5705 | translated_basename: []const u8, | 5713 | translated_basename: []const u8, |
| 5706 | owner_mod: *Package.Module, | 5714 | owner_mod: *Package.Module, |
| 5707 | prog_node: std.Progress.Node, | 5715 | prog_node: std.Progress.Node, |
| 5716 | environ_map: *const std.process.Environ.Map, | ||
| 5708 | ) !CImportResult { | 5717 | ) !CImportResult { |
| 5709 | dev.check(.translate_c_command); | 5718 | dev.check(.translate_c_command); |
| 5710 | 5719 | ||
| ... | @@ -5774,7 +5783,7 @@ pub fn translateC( | ... | @@ -5774,7 +5783,7 @@ pub fn translateC( |
| 5774 | } | 5783 | } |
| 5775 | 5784 | ||
| 5776 | var stdout: []u8 = undefined; | 5785 | var stdout: []u8 = undefined; |
| 5777 | try @import("main.zig").translateC(gpa, arena, io, argv.items, prog_node, &stdout); | 5786 | try @import("main.zig").translateC(gpa, arena, io, argv.items, environ_map, prog_node, &stdout); |
| 5778 | 5787 | ||
| 5779 | if (out_dep_path) |dep_file_path| add_deps: { | 5788 | if (out_dep_path) |dep_file_path| add_deps: { |
| 5780 | if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_file_path}); | 5789 | if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_file_path}); |
| ... | @@ -5861,7 +5870,8 @@ pub fn cImport( | ... | @@ -5861,7 +5870,8 @@ pub fn cImport( |
| 5861 | defer arena_allocator.deinit(); | 5870 | defer arena_allocator.deinit(); |
| 5862 | const arena = arena_allocator.allocator(); | 5871 | const arena = arena_allocator.allocator(); |
| 5863 | 5872 | ||
| 5864 | break :result try comp.translateC( | 5873 | break :result try translateC( |
| 5874 | comp, | ||
| 5865 | arena, | 5875 | arena, |
| 5866 | &man, | 5876 | &man, |
| 5867 | .c, | 5877 | .c, |
| ... | @@ -5869,6 +5879,7 @@ pub fn cImport( | ... | @@ -5869,6 +5879,7 @@ pub fn cImport( |
| 5869 | translated_basename, | 5879 | translated_basename, |
| 5870 | owner_mod, | 5880 | owner_mod, |
| 5871 | prog_node, | 5881 | prog_node, |
| 5882 | comp.environ_map, | ||
| 5872 | ); | 5883 | ); |
| 5873 | }; | 5884 | }; |
| 5874 | 5885 | ||
| ... | @@ -6249,7 +6260,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr | ... | @@ -6249,7 +6260,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 6249 | // that we could "tail call" clang by doing an execve, and any use of | 6260 | // that we could "tail call" clang by doing an execve, and any use of |
| 6250 | // the caching system would actually be problematic since the user is | 6261 | // the caching system would actually be problematic since the user is |
| 6251 | // presumably doing their own caching by using dep file flags. | 6262 | // presumably doing their own caching by using dep file flags. |
| 6252 | if (std.process.can_execv and direct_o and | 6263 | if (std.process.can_replace and direct_o and |
| 6253 | comp.disable_c_depfile and comp.clang_passthrough_mode) | 6264 | comp.disable_c_depfile and comp.clang_passthrough_mode) |
| 6254 | { | 6265 | { |
| 6255 | try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner); | 6266 | 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 | ... | @@ -6281,8 +6292,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 6281 | try dumpArgv(io, argv.items); | 6292 | try dumpArgv(io, argv.items); |
| 6282 | } | 6293 | } |
| 6283 | 6294 | ||
| 6284 | const err = std.process.execv(arena, argv.items); | 6295 | const err = std.process.replace(io, .{ .argv = argv.items }); |
| 6285 | fatal("unable to execv clang: {s}", .{@errorName(err)}); | 6296 | fatal("unable to replace process with clang: {t}", .{err}); |
| 6286 | } | 6297 | } |
| 6287 | 6298 | ||
| 6288 | // We can't know the digest until we do the C compiler invocation, | 6299 | // 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 | ... | @@ -6337,17 +6348,24 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 6337 | else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }), | 6348 | else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }), |
| 6338 | }; | 6349 | }; |
| 6339 | if (std.process.can_spawn) { | 6350 | if (std.process.can_spawn) { |
| 6340 | var child = std.process.Child.init(argv.items, arena); | ||
| 6341 | if (comp.clang_passthrough_mode) { | 6351 | if (comp.clang_passthrough_mode) { |
| 6342 | child.stdin_behavior = .Inherit; | 6352 | var child = std.process.spawn(io, .{ |
| 6343 | child.stdout_behavior = .Inherit; | 6353 | .argv = argv.items, |
| 6344 | child.stderr_behavior = .Inherit; | 6354 | .stdin = .inherit, |
| 6345 | 6355 | .stdout = .inherit, | |
| 6346 | const term = child.spawnAndWait(io) catch |err| { | 6356 | .stderr = .inherit, |
| 6347 | return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {s}", .{ argv.items[0], @errorName(err) }); | 6357 | }) catch |err| { |
| 6358 | return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {t}", .{ | ||
| 6359 | argv.items[0], err, | ||
| 6360 | }); | ||
| 6361 | }; | ||
| 6362 | const term = child.wait(io) catch |err| { | ||
| 6363 | return comp.failCObj(c_object, "failed to wait zig clang (passthrough mode) {s}: {t}", .{ | ||
| 6364 | argv.items[0], err, | ||
| 6365 | }); | ||
| 6348 | }; | 6366 | }; |
| 6349 | switch (term) { | 6367 | switch (term) { |
| 6350 | .Exited => |code| { | 6368 | .exited => |code| { |
| 6351 | if (code != 0) { | 6369 | if (code != 0) { |
| 6352 | std.process.exit(code); | 6370 | std.process.exit(code); |
| 6353 | } | 6371 | } |
| ... | @@ -6357,21 +6375,21 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr | ... | @@ -6357,21 +6375,21 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 6357 | else => std.process.abort(), | 6375 | else => std.process.abort(), |
| 6358 | } | 6376 | } |
| 6359 | } else { | 6377 | } else { |
| 6360 | child.stdin_behavior = .Ignore; | 6378 | var child = try std.process.spawn(io, .{ |
| 6361 | child.stdout_behavior = .Ignore; | 6379 | .argv = argv.items, |
| 6362 | child.stderr_behavior = .Pipe; | 6380 | .stdin = .ignore, |
| 6363 | 6381 | .stdout = .ignore, | |
| 6364 | try child.spawn(io); | 6382 | .stderr = .pipe, |
| 6383 | }); | ||
| 6365 | 6384 | ||
| 6366 | var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); | 6385 | var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); |
| 6367 | const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32))); | 6386 | const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32))); |
| 6368 | 6387 | ||
| 6369 | const term = child.wait(io) catch |err| { | 6388 | const term = child.wait(io) catch |err| |
| 6370 | return comp.failCObj(c_object, "failed to spawn zig clang {s}: {s}", .{ argv.items[0], @errorName(err) }); | 6389 | return comp.failCObj(c_object, "failed to spawn zig clang {s}: {t}", .{ argv.items[0], err }); |
| 6371 | }; | ||
| 6372 | 6390 | ||
| 6373 | switch (term) { | 6391 | switch (term) { |
| 6374 | .Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { | 6392 | .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { |
| 6375 | const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| { | 6393 | const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| { |
| 6376 | log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); | 6394 | log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); |
| 6377 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); | 6395 | 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 | ... | @@ -6381,6 +6399,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr |
| 6381 | log.err("clang failed with stderr: {s}", .{stderr}); | 6399 | log.err("clang failed with stderr: {s}", .{stderr}); |
| 6382 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); | 6400 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); |
| 6383 | }, | 6401 | }, |
| 6402 | .signal => |sig| { | ||
| 6403 | log.err("clang failed with stderr: {s}", .{stderr}); | ||
| 6404 | return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig}); | ||
| 6405 | }, | ||
| 6384 | else => { | 6406 | else => { |
| 6385 | log.err("clang terminated with stderr: {s}", .{stderr}); | 6407 | log.err("clang terminated with stderr: {s}", .{stderr}); |
| 6386 | return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); | 6408 | return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); |
| ... | @@ -6741,15 +6763,16 @@ fn spawnZigRc( | ... | @@ -6741,15 +6763,16 @@ fn spawnZigRc( |
| 6741 | var node_name: std.ArrayList(u8) = .empty; | 6763 | var node_name: std.ArrayList(u8) = .empty; |
| 6742 | defer node_name.deinit(arena); | 6764 | defer node_name.deinit(arena); |
| 6743 | 6765 | ||
| 6744 | var child = std.process.Child.init(argv, arena); | 6766 | var child = std.process.spawn(io, .{ |
| 6745 | child.stdin_behavior = .Ignore; | 6767 | .argv = argv, |
| 6746 | child.stdout_behavior = .Pipe; | 6768 | .stdin = .ignore, |
| 6747 | child.stderr_behavior = .Pipe; | 6769 | .stdout = .pipe, |
| 6748 | child.progress_node = child_progress_node; | 6770 | .stderr = .pipe, |
| 6749 | 6771 | .progress_node = child_progress_node, | |
| 6750 | child.spawn(io) catch |err| { | 6772 | }) catch |err| return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{ |
| 6751 | return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{ argv[0], err }); | 6773 | argv[0], err, |
| 6752 | }; | 6774 | }); |
| 6775 | defer child.kill(io); | ||
| 6753 | 6776 | ||
| 6754 | var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{ | 6777 | var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{ |
| 6755 | .stdout = child.stdout.?, | 6778 | .stdout = child.stdout.?, |
| ... | @@ -6781,16 +6804,20 @@ fn spawnZigRc( | ... | @@ -6781,16 +6804,20 @@ fn spawnZigRc( |
| 6781 | const stderr = poller.reader(.stderr); | 6804 | const stderr = poller.reader(.stderr); |
| 6782 | 6805 | ||
| 6783 | const term = child.wait(io) catch |err| { | 6806 | const term = child.wait(io) catch |err| { |
| 6784 | return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) }); | 6807 | return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {t}", .{ argv[0], err }); |
| 6785 | }; | 6808 | }; |
| 6786 | 6809 | ||
| 6787 | switch (term) { | 6810 | switch (term) { |
| 6788 | .Exited => |code| { | 6811 | .exited => |code| { |
| 6789 | if (code != 0) { | 6812 | if (code != 0) { |
| 6790 | log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()}); | 6813 | log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()}); |
| 6791 | return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); | 6814 | return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); |
| 6792 | } | 6815 | } |
| 6793 | }, | 6816 | }, |
| 6817 | .signal => |sig| { | ||
| 6818 | log.err("zig rc signaled {t} with stderr:\n{s}", .{ sig, stderr.buffered() }); | ||
| 6819 | return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); | ||
| 6820 | }, | ||
| 6794 | else => { | 6821 | else => { |
| 6795 | log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()}); | 6822 | log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()}); |
| 6796 | return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); | 6823 | return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); |
| ... | @@ -7959,6 +7986,7 @@ fn buildOutputFromZig( | ... | @@ -7959,6 +7986,7 @@ fn buildOutputFromZig( |
| 7959 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, | 7986 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, |
| 7960 | .clang_passthrough_mode = comp.clang_passthrough_mode, | 7987 | .clang_passthrough_mode = comp.clang_passthrough_mode, |
| 7961 | .skip_linker_dependencies = true, | 7988 | .skip_linker_dependencies = true, |
| 7989 | .environ_map = comp.environ_map, | ||
| 7962 | }) catch |err| switch (err) { | 7990 | }) catch |err| switch (err) { |
| 7963 | error.CreateFail => { | 7991 | error.CreateFail => { |
| 7964 | comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag }); | 7992 | 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( | ... | @@ -8096,6 +8124,7 @@ pub fn build_crt_file( |
| 8096 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, | 8124 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, |
| 8097 | .clang_passthrough_mode = comp.clang_passthrough_mode, | 8125 | .clang_passthrough_mode = comp.clang_passthrough_mode, |
| 8098 | .skip_linker_dependencies = true, | 8126 | .skip_linker_dependencies = true, |
| 8127 | .environ_map = comp.environ_map, | ||
| 8099 | }) catch |err| switch (err) { | 8128 | }) catch |err| switch (err) { |
| 8100 | error.CreateFail => { | 8129 | error.CreateFail => { |
| 8101 | comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag }); | 8130 | comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag }); |
src/DarwinPosixSpawn.zig deleted-225| ... | @@ -1,225 +0,0 @@ | ||
| 1 | const errno = std.posix.errno; | ||
| 2 | const unexpectedErrno = std.posix.unexpectedErrno; | ||
| 3 | |||
| 4 | pub const Error = error{ | ||
| 5 | SystemResources, | ||
| 6 | InvalidFileDescriptor, | ||
| 7 | NameTooLong, | ||
| 8 | TooBig, | ||
| 9 | AccessDenied, | ||
| 10 | PermissionDenied, | ||
| 11 | InputOutput, | ||
| 12 | FileSystem, | ||
| 13 | FileNotFound, | ||
| 14 | InvalidExe, | ||
| 15 | NotDir, | ||
| 16 | FileBusy, | ||
| 17 | /// Returned when the child fails to execute either in the pre-exec() initialization step, or | ||
| 18 | /// when exec(3) is invoked. | ||
| 19 | ChildExecFailed, | ||
| 20 | } || std.posix.UnexpectedError; | ||
| 21 | |||
| 22 | pub const Attr = struct { | ||
| 23 | attr: std.c.posix_spawnattr_t, | ||
| 24 | |||
| 25 | pub fn init() Error!Attr { | ||
| 26 | var attr: std.c.posix_spawnattr_t = undefined; | ||
| 27 | switch (errno(std.c.posix_spawnattr_init(&attr))) { | ||
| 28 | .SUCCESS => return Attr{ .attr = attr }, | ||
| 29 | .NOMEM => return error.SystemResources, | ||
| 30 | .INVAL => unreachable, | ||
| 31 | else => |err| return unexpectedErrno(err), | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | pub fn deinit(self: *Attr) void { | ||
| 36 | defer self.* = undefined; | ||
| 37 | switch (errno(std.c.posix_spawnattr_destroy(&self.attr))) { | ||
| 38 | .SUCCESS => return, | ||
| 39 | .INVAL => unreachable, // Invalid parameters. | ||
| 40 | else => unreachable, | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn get(self: Attr) Error!std.c.POSIX_SPAWN { | ||
| 45 | var flags: std.c.POSIX_SPAWN = undefined; | ||
| 46 | switch (errno(std.c.posix_spawnattr_getflags(&self.attr, &flags))) { | ||
| 47 | .SUCCESS => return flags, | ||
| 48 | .INVAL => unreachable, | ||
| 49 | else => |err| return unexpectedErrno(err), | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | pub fn set(self: *Attr, flags: std.c.POSIX_SPAWN) Error!void { | ||
| 54 | switch (errno(std.c.posix_spawnattr_setflags(&self.attr, flags))) { | ||
| 55 | .SUCCESS => return, | ||
| 56 | .INVAL => unreachable, | ||
| 57 | else => |err| return unexpectedErrno(err), | ||
| 58 | } | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | |||
| 62 | pub const Actions = struct { | ||
| 63 | actions: std.c.posix_spawn_file_actions_t, | ||
| 64 | |||
| 65 | pub fn init() Error!Actions { | ||
| 66 | var actions: std.c.posix_spawn_file_actions_t = undefined; | ||
| 67 | switch (errno(std.c.posix_spawn_file_actions_init(&actions))) { | ||
| 68 | .SUCCESS => return Actions{ .actions = actions }, | ||
| 69 | .NOMEM => return error.SystemResources, | ||
| 70 | .INVAL => unreachable, | ||
| 71 | else => |err| return unexpectedErrno(err), | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn deinit(self: *Actions) void { | ||
| 76 | defer self.* = undefined; | ||
| 77 | switch (errno(std.c.posix_spawn_file_actions_destroy(&self.actions))) { | ||
| 78 | .SUCCESS => return, | ||
| 79 | .INVAL => unreachable, // Invalid parameters. | ||
| 80 | else => unreachable, | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn open(self: *Actions, fd: std.c.fd_t, path: []const u8, flags: u32, mode: std.c.mode_t) Error!void { | ||
| 85 | const posix_path = try std.posix.toPosixPath(path); | ||
| 86 | return self.openZ(fd, &posix_path, flags, mode); | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn openZ(self: *Actions, fd: std.c.fd_t, path: [*:0]const u8, flags: u32, mode: std.c.mode_t) Error!void { | ||
| 90 | switch (errno(std.c.posix_spawn_file_actions_addopen(&self.actions, fd, path, @as(c_int, @bitCast(flags)), mode))) { | ||
| 91 | .SUCCESS => return, | ||
| 92 | .BADF => return error.InvalidFileDescriptor, | ||
| 93 | .NOMEM => return error.SystemResources, | ||
| 94 | .NAMETOOLONG => return error.NameTooLong, | ||
| 95 | .INVAL => unreachable, // the value of file actions is invalid | ||
| 96 | else => |err| return unexpectedErrno(err), | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | pub fn close(self: *Actions, fd: std.c.fd_t) Error!void { | ||
| 101 | switch (errno(std.c.posix_spawn_file_actions_addclose(&self.actions, fd))) { | ||
| 102 | .SUCCESS => return, | ||
| 103 | .BADF => return error.InvalidFileDescriptor, | ||
| 104 | .NOMEM => return error.SystemResources, | ||
| 105 | .INVAL => unreachable, // the value of file actions is invalid | ||
| 106 | .NAMETOOLONG => unreachable, | ||
| 107 | else => |err| return unexpectedErrno(err), | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn dup2(self: *Actions, fd: std.c.fd_t, newfd: std.c.fd_t) Error!void { | ||
| 112 | switch (errno(std.c.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) { | ||
| 113 | .SUCCESS => return, | ||
| 114 | .BADF => return error.InvalidFileDescriptor, | ||
| 115 | .NOMEM => return error.SystemResources, | ||
| 116 | .INVAL => unreachable, // the value of file actions is invalid | ||
| 117 | .NAMETOOLONG => unreachable, | ||
| 118 | else => |err| return unexpectedErrno(err), | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn inherit(self: *Actions, fd: std.c.fd_t) Error!void { | ||
| 123 | switch (errno(std.c.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) { | ||
| 124 | .SUCCESS => return, | ||
| 125 | .BADF => return error.InvalidFileDescriptor, | ||
| 126 | .NOMEM => return error.SystemResources, | ||
| 127 | .INVAL => unreachable, // the value of file actions is invalid | ||
| 128 | .NAMETOOLONG => unreachable, | ||
| 129 | else => |err| return unexpectedErrno(err), | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | pub fn chdir(self: *Actions, path: []const u8) Error!void { | ||
| 134 | const posix_path = try std.posix.toPosixPath(path); | ||
| 135 | return self.chdirZ(&posix_path); | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void { | ||
| 139 | switch (errno(std.c.posix_spawn_file_actions_addchdir_np(&self.actions, path))) { | ||
| 140 | .SUCCESS => return, | ||
| 141 | .NOMEM => return error.SystemResources, | ||
| 142 | .NAMETOOLONG => return error.NameTooLong, | ||
| 143 | .BADF => unreachable, | ||
| 144 | .INVAL => unreachable, // the value of file actions is invalid | ||
| 145 | else => |err| return unexpectedErrno(err), | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | pub fn fchdir(self: *Actions, fd: std.c.fd_t) Error!void { | ||
| 150 | switch (errno(std.c.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) { | ||
| 151 | .SUCCESS => return, | ||
| 152 | .BADF => return error.InvalidFileDescriptor, | ||
| 153 | .NOMEM => return error.SystemResources, | ||
| 154 | .INVAL => unreachable, // the value of file actions is invalid | ||
| 155 | .NAMETOOLONG => unreachable, | ||
| 156 | else => |err| return unexpectedErrno(err), | ||
| 157 | } | ||
| 158 | } | ||
| 159 | }; | ||
| 160 | |||
| 161 | pub fn spawn( | ||
| 162 | path: []const u8, | ||
| 163 | actions: ?Actions, | ||
| 164 | attr: ?Attr, | ||
| 165 | argv: [*:null]const ?[*:0]const u8, | ||
| 166 | envp: [*:null]const ?[*:0]const u8, | ||
| 167 | ) Error!std.c.pid_t { | ||
| 168 | const posix_path = try std.posix.toPosixPath(path); | ||
| 169 | return spawnZ(&posix_path, actions, attr, argv, envp); | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn spawnZ( | ||
| 173 | path: [*:0]const u8, | ||
| 174 | actions: ?Actions, | ||
| 175 | attr: ?Attr, | ||
| 176 | argv: [*:null]const ?[*:0]const u8, | ||
| 177 | envp: [*:null]const ?[*:0]const u8, | ||
| 178 | ) Error!std.c.pid_t { | ||
| 179 | var pid: std.c.pid_t = undefined; | ||
| 180 | switch (errno(std.c.posix_spawn( | ||
| 181 | &pid, | ||
| 182 | path, | ||
| 183 | if (actions) |a| &a.actions else null, | ||
| 184 | if (attr) |a| &a.attr else null, | ||
| 185 | argv, | ||
| 186 | envp, | ||
| 187 | ))) { | ||
| 188 | .SUCCESS => return pid, | ||
| 189 | .@"2BIG" => return error.TooBig, | ||
| 190 | .NOMEM => return error.SystemResources, | ||
| 191 | .BADF => return error.InvalidFileDescriptor, | ||
| 192 | .ACCES => return error.AccessDenied, | ||
| 193 | .IO => return error.InputOutput, | ||
| 194 | .LOOP => return error.FileSystem, | ||
| 195 | .NAMETOOLONG => return error.NameTooLong, | ||
| 196 | .NOENT => return error.FileNotFound, | ||
| 197 | .NOEXEC => return error.InvalidExe, | ||
| 198 | .NOTDIR => return error.NotDir, | ||
| 199 | .TXTBSY => return error.FileBusy, | ||
| 200 | .BADARCH => return error.InvalidExe, | ||
| 201 | .BADEXEC => return error.InvalidExe, | ||
| 202 | .FAULT => unreachable, | ||
| 203 | .INVAL => unreachable, | ||
| 204 | else => |err| return unexpectedErrno(err), | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | pub fn waitpid(pid: std.c.pid_t, flags: u32) Error!std.posix.WaitPidResult { | ||
| 209 | var status: c_int = undefined; | ||
| 210 | while (true) { | ||
| 211 | const rc = waitpid(pid, &status, @as(c_int, @intCast(flags))); | ||
| 212 | switch (errno(rc)) { | ||
| 213 | .SUCCESS => return std.posix.WaitPidResult{ | ||
| 214 | .pid = @as(std.c.pid_t, @intCast(rc)), | ||
| 215 | .status = @as(u32, @bitCast(status)), | ||
| 216 | }, | ||
| 217 | .INTR => continue, | ||
| 218 | .CHILD => return error.ChildExecFailed, | ||
| 219 | .INVAL => unreachable, // Invalid flags. | ||
| 220 | else => unreachable, | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | const std = @import("std"); | ||
src/Zcu/PerThread.zig+1-4| ... | @@ -2571,10 +2571,7 @@ fn newEmbedFile( | ... | @@ -2571,10 +2571,7 @@ fn newEmbedFile( |
| 2571 | try whole.cache_manifest_mutex.lock(io); | 2571 | try whole.cache_manifest_mutex.lock(io); |
| 2572 | defer whole.cache_manifest_mutex.unlock(io); | 2572 | defer whole.cache_manifest_mutex.unlock(io); |
| 2573 | 2573 | ||
| 2574 | man.addFilePostContents(path_str, contents, new_file.stat) catch |err| switch (err) { | 2574 | try man.addFilePostContents(path_str, contents, new_file.stat); |
| 2575 | error.Unexpected => unreachable, | ||
| 2576 | else => |e| return e, | ||
| 2577 | }; | ||
| 2578 | } | 2575 | } |
| 2579 | 2576 | ||
| 2580 | return new_file; | 2577 | return new_file; |
src/introspect.zig+17-20| ... | @@ -6,6 +6,7 @@ const Dir = std.Io.Dir; | ... | @@ -6,6 +6,7 @@ const Dir = std.Io.Dir; |
| 6 | const mem = std.mem; | 6 | const mem = std.mem; |
| 7 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 8 | const Cache = std.Build.Cache; | 8 | const Cache = std.Build.Cache; |
| 9 | const assert = std.debug.assert; | ||
| 9 | 10 | ||
| 10 | const build_options = @import("build_options"); | 11 | const build_options = @import("build_options"); |
| 11 | 12 | ||
| ... | @@ -62,14 +63,14 @@ pub fn getResolvedCwd(gpa: Allocator) error{ | ... | @@ -62,14 +63,14 @@ pub fn getResolvedCwd(gpa: Allocator) error{ |
| 62 | if (std.debug.runtime_safety) { | 63 | if (std.debug.runtime_safety) { |
| 63 | const cwd = try std.process.getCwdAlloc(gpa); | 64 | const cwd = try std.process.getCwdAlloc(gpa); |
| 64 | defer gpa.free(cwd); | 65 | defer gpa.free(cwd); |
| 65 | std.debug.assert(mem.eql(u8, cwd, ".")); | 66 | assert(mem.eql(u8, cwd, ".")); |
| 66 | } | 67 | } |
| 67 | return ""; | 68 | return ""; |
| 68 | } | 69 | } |
| 69 | const cwd = try std.process.getCwdAlloc(gpa); | 70 | const cwd = try std.process.getCwdAlloc(gpa); |
| 70 | defer gpa.free(cwd); | 71 | defer gpa.free(cwd); |
| 71 | const resolved = try Dir.path.resolve(gpa, &.{cwd}); | 72 | const resolved = try Dir.path.resolve(gpa, &.{cwd}); |
| 72 | std.debug.assert(Dir.path.isAbsolute(resolved)); | 73 | assert(Dir.path.isAbsolute(resolved)); |
| 73 | return resolved; | 74 | return resolved; |
| 74 | } | 75 | } |
| 75 | 76 | ||
| ... | @@ -101,31 +102,27 @@ pub fn findZigLibDirFromSelfExe( | ... | @@ -101,31 +102,27 @@ pub fn findZigLibDirFromSelfExe( |
| 101 | return error.FileNotFound; | 102 | return error.FileNotFound; |
| 102 | } | 103 | } |
| 103 | 104 | ||
| 104 | /// Caller owns returned memory. | 105 | pub fn resolveGlobalCacheDir(arena: Allocator, environ_map: *const std.process.Environ.Map) ![]const u8 { |
| 105 | pub fn resolveGlobalCacheDir(gpa: Allocator) ![]u8 { | 106 | if (std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map)) |value| return value; |
| 106 | if (try std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(gpa)) |value| return value; | ||
| 107 | 107 | ||
| 108 | const app_name = "zig"; | 108 | const app_name = "zig"; |
| 109 | 109 | ||
| 110 | switch (builtin.os.tag) { | 110 | switch (builtin.os.tag) { |
| 111 | .wasi => @compileError("on WASI the global cache dir must be resolved with preopens"), | 111 | .wasi => @compileError("on WASI the global cache dir must be resolved with preopens"), |
| 112 | .windows => { | 112 | .windows => { |
| 113 | const local_app_data_dir = (std.zig.EnvVar.LOCALAPPDATA.get(gpa) catch |err| switch (err) { | 113 | const local_app_data_dir = std.zig.EnvVar.LOCALAPPDATA.get(environ_map) orelse |
| 114 | error.OutOfMemory => |e| return e, | 114 | return error.AppDataDirUnavailable; |
| 115 | error.InvalidWtf8 => return error.AppDataDirUnavailable, | 115 | return Dir.path.join(arena, &.{ local_app_data_dir, app_name }); |
| 116 | }) orelse return error.AppDataDirUnavailable; | ||
| 117 | defer gpa.free(local_app_data_dir); | ||
| 118 | return Dir.path.join(gpa, &.{ local_app_data_dir, app_name }); | ||
| 119 | }, | 116 | }, |
| 120 | else => { | 117 | else => { |
| 121 | if (std.zig.EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| { | 118 | if (std.zig.EnvVar.XDG_CACHE_HOME.get(environ_map)) |cache_root| { |
| 122 | if (cache_root.len > 0) { | 119 | if (cache_root.len > 0) { |
| 123 | return Dir.path.join(gpa, &.{ cache_root, app_name }); | 120 | return Dir.path.join(arena, &.{ cache_root, app_name }); |
| 124 | } | 121 | } |
| 125 | } | 122 | } |
| 126 | if (std.zig.EnvVar.HOME.getPosix()) |home| { | 123 | if (std.zig.EnvVar.HOME.get(environ_map)) |home| { |
| 127 | if (home.len > 0) { | 124 | if (home.len > 0) { |
| 128 | return Dir.path.join(gpa, &.{ home, ".cache", app_name }); | 125 | return Dir.path.join(arena, &.{ home, ".cache", app_name }); |
| 129 | } | 126 | } |
| 130 | } | 127 | } |
| 131 | return error.AppDataDirUnavailable; | 128 | return error.AppDataDirUnavailable; |
| ... | @@ -144,7 +141,7 @@ pub fn resolvePath( | ... | @@ -144,7 +141,7 @@ pub fn resolvePath( |
| 144 | paths: []const []const u8, | 141 | paths: []const []const u8, |
| 145 | ) Allocator.Error![]u8 { | 142 | ) Allocator.Error![]u8 { |
| 146 | if (builtin.target.os.tag == .wasi) { | 143 | if (builtin.target.os.tag == .wasi) { |
| 147 | std.debug.assert(mem.eql(u8, cwd_resolved, "")); | 144 | assert(mem.eql(u8, cwd_resolved, "")); |
| 148 | const res = try Dir.path.resolve(gpa, paths); | 145 | const res = try Dir.path.resolve(gpa, paths); |
| 149 | if (mem.eql(u8, res, ".")) { | 146 | if (mem.eql(u8, res, ".")) { |
| 150 | gpa.free(res); | 147 | gpa.free(res); |
| ... | @@ -164,8 +161,8 @@ pub fn resolvePath( | ... | @@ -164,8 +161,8 @@ pub fn resolvePath( |
| 164 | gpa.free(res); | 161 | gpa.free(res); |
| 165 | return ""; | 162 | return ""; |
| 166 | } | 163 | } |
| 167 | std.debug.assert(!Dir.path.isAbsolute(res)); | 164 | assert(!Dir.path.isAbsolute(res)); |
| 168 | std.debug.assert(!isUpDir(res)); | 165 | assert(!isUpDir(res)); |
| 169 | return res; | 166 | return res; |
| 170 | } | 167 | } |
| 171 | 168 | ||
| ... | @@ -184,8 +181,8 @@ pub fn resolvePath( | ... | @@ -184,8 +181,8 @@ pub fn resolvePath( |
| 184 | }; | 181 | }; |
| 185 | errdefer gpa.free(path_resolved); | 182 | errdefer gpa.free(path_resolved); |
| 186 | 183 | ||
| 187 | std.debug.assert(Dir.path.isAbsolute(path_resolved)); | 184 | assert(Dir.path.isAbsolute(path_resolved)); |
| 188 | std.debug.assert(Dir.path.isAbsolute(cwd_resolved)); | 185 | assert(Dir.path.isAbsolute(cwd_resolved)); |
| 189 | 186 | ||
| 190 | if (!std.mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd | 187 | if (!std.mem.startsWith(u8, path_resolved, cwd_resolved)) return path_resolved; // not in cwd |
| 191 | if (path_resolved.len == cwd_resolved.len) { | 188 | if (path_resolved.len == cwd_resolved.len) { |
src/libs/freebsd.zig+2| ... | @@ -445,6 +445,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye | ... | @@ -445,6 +445,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 445 | .gpa = gpa, | 445 | .gpa = gpa, |
| 446 | .io = io, | 446 | .io = io, |
| 447 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), | 447 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), |
| 448 | .cwd = comp.dirs.cwd, | ||
| 448 | }; | 449 | }; |
| 449 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); | 450 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); |
| 450 | cache.addPrefix(comp.dirs.zig_lib); | 451 | cache.addPrefix(comp.dirs.zig_lib); |
| ... | @@ -1119,6 +1120,7 @@ fn buildSharedLib( | ... | @@ -1119,6 +1120,7 @@ fn buildSharedLib( |
| 1119 | .soname = soname, | 1120 | .soname = soname, |
| 1120 | .c_source_files = &c_source_files, | 1121 | .c_source_files = &c_source_files, |
| 1121 | .skip_linker_dependencies = true, | 1122 | .skip_linker_dependencies = true, |
| 1123 | .environ_map = comp.environ_map, | ||
| 1122 | }) catch |err| switch (err) { | 1124 | }) catch |err| switch (err) { |
| 1123 | error.CreateFail => { | 1125 | error.CreateFail => { |
| 1124 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); | 1126 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); |
src/libs/glibc.zig+2| ... | @@ -680,6 +680,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye | ... | @@ -680,6 +680,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 680 | .gpa = gpa, | 680 | .gpa = gpa, |
| 681 | .io = io, | 681 | .io = io, |
| 682 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), | 682 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), |
| 683 | .cwd = comp.dirs.cwd, | ||
| 683 | }; | 684 | }; |
| 684 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); | 685 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); |
| 685 | cache.addPrefix(comp.dirs.zig_lib); | 686 | cache.addPrefix(comp.dirs.zig_lib); |
| ... | @@ -1258,6 +1259,7 @@ fn buildSharedLib( | ... | @@ -1258,6 +1259,7 @@ fn buildSharedLib( |
| 1258 | .soname = soname, | 1259 | .soname = soname, |
| 1259 | .c_source_files = &c_source_files, | 1260 | .c_source_files = &c_source_files, |
| 1260 | .skip_linker_dependencies = true, | 1261 | .skip_linker_dependencies = true, |
| 1262 | .environ_map = comp.environ_map, | ||
| 1261 | }) catch |err| switch (err) { | 1263 | }) catch |err| switch (err) { |
| 1262 | error.CreateFail => { | 1264 | error.CreateFail => { |
| 1263 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); | 1265 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); |
src/libs/libcxx.zig+2| ... | @@ -275,6 +275,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! | ... | @@ -275,6 +275,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! |
| 275 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, | 275 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, |
| 276 | .clang_passthrough_mode = comp.clang_passthrough_mode, | 276 | .clang_passthrough_mode = comp.clang_passthrough_mode, |
| 277 | .skip_linker_dependencies = true, | 277 | .skip_linker_dependencies = true, |
| 278 | .environ_map = comp.environ_map, | ||
| 278 | }) catch |err| { | 279 | }) catch |err| { |
| 279 | switch (err) { | 280 | switch (err) { |
| 280 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {t}", .{err}), | 281 | 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 | ... | @@ -468,6 +469,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr |
| 468 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, | 469 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, |
| 469 | .clang_passthrough_mode = comp.clang_passthrough_mode, | 470 | .clang_passthrough_mode = comp.clang_passthrough_mode, |
| 470 | .skip_linker_dependencies = true, | 471 | .skip_linker_dependencies = true, |
| 472 | .environ_map = comp.environ_map, | ||
| 471 | }) catch |err| { | 473 | }) catch |err| { |
| 472 | switch (err) { | 474 | switch (err) { |
| 473 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}), | 475 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}), |
src/libs/libtsan.zig+1| ... | @@ -301,6 +301,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo | ... | @@ -301,6 +301,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo |
| 301 | .linker_allow_shlib_undefined = linker_allow_shlib_undefined, | 301 | .linker_allow_shlib_undefined = linker_allow_shlib_undefined, |
| 302 | .install_name = install_name, | 302 | .install_name = install_name, |
| 303 | .headerpad_size = headerpad_size, | 303 | .headerpad_size = headerpad_size, |
| 304 | .environ_map = comp.environ_map, | ||
| 304 | }) catch |err| { | 305 | }) catch |err| { |
| 305 | switch (err) { | 306 | switch (err) { |
| 306 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), | 307 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), |
src/libs/libunwind.zig+1| ... | @@ -166,6 +166,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr | ... | @@ -166,6 +166,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr |
| 166 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, | 166 | .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, |
| 167 | .clang_passthrough_mode = comp.clang_passthrough_mode, | 167 | .clang_passthrough_mode = comp.clang_passthrough_mode, |
| 168 | .skip_linker_dependencies = true, | 168 | .skip_linker_dependencies = true, |
| 169 | .environ_map = comp.environ_map, | ||
| 169 | }) catch |err| { | 170 | }) catch |err| { |
| 170 | switch (err) { | 171 | switch (err) { |
| 171 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), | 172 | else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), |
src/libs/mingw.zig+1| ... | @@ -259,6 +259,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { | ... | @@ -259,6 +259,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 259 | .gpa = gpa, | 259 | .gpa = gpa, |
| 260 | .io = io, | 260 | .io = io, |
| 261 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), | 261 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), |
| 262 | .cwd = comp.dirs.cwd, | ||
| 262 | }; | 263 | }; |
| 263 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); | 264 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); |
| 264 | cache.addPrefix(comp.dirs.zig_lib); | 265 | cache.addPrefix(comp.dirs.zig_lib); |
src/libs/musl.zig+1| ... | @@ -272,6 +272,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro | ... | @@ -272,6 +272,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro |
| 272 | }, | 272 | }, |
| 273 | .skip_linker_dependencies = true, | 273 | .skip_linker_dependencies = true, |
| 274 | .soname = "libc.so", | 274 | .soname = "libc.so", |
| 275 | .environ_map = comp.environ_map, | ||
| 275 | }) catch |err| switch (err) { | 276 | }) catch |err| switch (err) { |
| 276 | error.CreateFail => { | 277 | error.CreateFail => { |
| 277 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); | 278 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); |
src/libs/netbsd.zig+2| ... | @@ -386,6 +386,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye | ... | @@ -386,6 +386,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 386 | .gpa = gpa, | 386 | .gpa = gpa, |
| 387 | .io = io, | 387 | .io = io, |
| 388 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), | 388 | .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), |
| 389 | .cwd = comp.dirs.cwd, | ||
| 389 | }; | 390 | }; |
| 390 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); | 391 | cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); |
| 391 | cache.addPrefix(comp.dirs.zig_lib); | 392 | cache.addPrefix(comp.dirs.zig_lib); |
| ... | @@ -761,6 +762,7 @@ fn buildSharedLib( | ... | @@ -761,6 +762,7 @@ fn buildSharedLib( |
| 761 | .soname = soname, | 762 | .soname = soname, |
| 762 | .c_source_files = &c_source_files, | 763 | .c_source_files = &c_source_files, |
| 763 | .skip_linker_dependencies = true, | 764 | .skip_linker_dependencies = true, |
| 765 | .environ_map = comp.environ_map, | ||
| 764 | }) catch |err| switch (err) { | 766 | }) catch |err| switch (err) { |
| 765 | error.CreateFail => { | 767 | error.CreateFail => { |
| 766 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); | 768 | comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); |
src/link/Lld.zig+29-26| ... | @@ -1604,19 +1604,24 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi | ... | @@ -1604,19 +1604,24 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi |
| 1604 | var stderr: []u8 = &.{}; | 1604 | var stderr: []u8 = &.{}; |
| 1605 | defer gpa.free(stderr); | 1605 | defer gpa.free(stderr); |
| 1606 | 1606 | ||
| 1607 | var child = std.process.Child.init(argv, arena); | 1607 | // TODO rework this awkward logic to call child.kill() in the failure case |
| 1608 | const term = (if (comp.clang_passthrough_mode) term: { | 1608 | const term = (if (comp.clang_passthrough_mode) term: { |
| 1609 | child.stdin_behavior = .Inherit; | 1609 | var child = std.process.spawn(io, .{ |
| 1610 | child.stdout_behavior = .Inherit; | 1610 | .argv = argv, |
| 1611 | child.stderr_behavior = .Inherit; | 1611 | .stdin = .inherit, |
| 1612 | .stdout = .inherit, | ||
| 1613 | .stderr = .inherit, | ||
| 1614 | }) catch |err| break :term err; | ||
| 1612 | 1615 | ||
| 1613 | break :term child.spawnAndWait(io); | 1616 | break :term child.wait(io); |
| 1614 | } else term: { | 1617 | } else term: { |
| 1615 | child.stdin_behavior = .Ignore; | 1618 | var child = std.process.spawn(io, .{ |
| 1616 | child.stdout_behavior = .Ignore; | 1619 | .argv = argv, |
| 1617 | child.stderr_behavior = .Pipe; | 1620 | .stdin = .ignore, |
| 1621 | .stdout = .ignore, | ||
| 1622 | .stderr = .pipe, | ||
| 1623 | }) catch |err| break :term err; | ||
| 1618 | 1624 | ||
| 1619 | child.spawn(io) catch |err| break :term err; | ||
| 1620 | var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); | 1625 | var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); |
| 1621 | stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited); | 1626 | stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited); |
| 1622 | break :term child.wait(io); | 1627 | break :term child.wait(io); |
| ... | @@ -1650,23 +1655,21 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi | ... | @@ -1650,23 +1655,21 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi |
| 1650 | try rsp_writer.flush(); | 1655 | try rsp_writer.flush(); |
| 1651 | } | 1656 | } |
| 1652 | 1657 | ||
| 1653 | var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint( | 1658 | var rsp_child = std.process.spawn(io, .{ |
| 1654 | arena, | 1659 | .argv = &.{ |
| 1655 | "@{s}", | 1660 | argv[0], |
| 1656 | .{try comp.dirs.local_cache.join(arena, &.{rsp_path})}, | 1661 | argv[1], |
| 1657 | ) }, arena); | 1662 | try std.fmt.allocPrint(arena, "@{s}", .{ |
| 1663 | try comp.dirs.local_cache.join(arena, &.{rsp_path}), | ||
| 1664 | }), | ||
| 1665 | }, | ||
| 1666 | .stdin = if (comp.clang_passthrough_mode) .inherit else .ignore, | ||
| 1667 | .stdout = if (comp.clang_passthrough_mode) .inherit else .ignore, | ||
| 1668 | .stderr = if (comp.clang_passthrough_mode) .inherit else .pipe, | ||
| 1669 | }) catch |err| break :err err; | ||
| 1658 | if (comp.clang_passthrough_mode) { | 1670 | if (comp.clang_passthrough_mode) { |
| 1659 | rsp_child.stdin_behavior = .Inherit; | 1671 | break :term rsp_child.wait(io) catch |err| break :err err; |
| 1660 | rsp_child.stdout_behavior = .Inherit; | ||
| 1661 | rsp_child.stderr_behavior = .Inherit; | ||
| 1662 | |||
| 1663 | break :term rsp_child.spawnAndWait(io) catch |err| break :err err; | ||
| 1664 | } else { | 1672 | } else { |
| 1665 | rsp_child.stdin_behavior = .Ignore; | ||
| 1666 | rsp_child.stdout_behavior = .Ignore; | ||
| 1667 | rsp_child.stderr_behavior = .Pipe; | ||
| 1668 | |||
| 1669 | rsp_child.spawn(io) catch |err| break :err err; | ||
| 1670 | var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{}); | 1673 | var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{}); |
| 1671 | stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited); | 1674 | stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited); |
| 1672 | break :term rsp_child.wait(io) catch |err| break :err err; | 1675 | 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 | ... | @@ -1674,13 +1677,13 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi |
| 1674 | }, | 1677 | }, |
| 1675 | else => first_err, | 1678 | else => first_err, |
| 1676 | }; | 1679 | }; |
| 1677 | log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) }); | 1680 | log.err("unable to spawn LLD {s}: {t}", .{ argv[0], err }); |
| 1678 | return error.UnableToSpawnSelf; | 1681 | return error.UnableToSpawnSelf; |
| 1679 | }; | 1682 | }; |
| 1680 | 1683 | ||
| 1681 | const diags = &comp.link_diags; | 1684 | const diags = &comp.link_diags; |
| 1682 | switch (term) { | 1685 | switch (term) { |
| 1683 | .Exited => |code| if (code != 0) { | 1686 | .exited => |code| if (code != 0) { |
| 1684 | if (comp.clang_passthrough_mode) std.process.exit(code); | 1687 | if (comp.clang_passthrough_mode) std.process.exit(code); |
| 1685 | diags.lockAndParseLldStderr(argv[1], stderr); | 1688 | diags.lockAndParseLldStderr(argv[1], stderr); |
| 1686 | return error.LinkFailure; | 1689 | return error.LinkFailure; |
src/main.zig+281-268| ... | @@ -42,7 +42,6 @@ test { | ... | @@ -42,7 +42,6 @@ test { |
| 42 | const thread_stack_size = 60 << 20; | 42 | const thread_stack_size = 60 << 20; |
| 43 | 43 | ||
| 44 | pub const std_options: std.Options = .{ | 44 | pub const std_options: std.Options = .{ |
| 45 | .wasiCwd = wasi_cwd, | ||
| 46 | .logFn = log, | 45 | .logFn = log, |
| 47 | 46 | ||
| 48 | .log_level = switch (builtin.mode) { | 47 | .log_level = switch (builtin.mode) { |
| ... | @@ -51,16 +50,17 @@ pub const std_options: std.Options = .{ | ... | @@ -51,16 +50,17 @@ pub const std_options: std.Options = .{ |
| 51 | .ReleaseSmall => .err, | 50 | .ReleaseSmall => .err, |
| 52 | }, | 51 | }, |
| 53 | }; | 52 | }; |
| 53 | pub const std_options_cwd = if (native_os == .wasi) wasi_cwd else null; | ||
| 54 | 54 | ||
| 55 | pub const panic = crash_report.panic; | 55 | pub const panic = crash_report.panic; |
| 56 | pub const debug = crash_report.debug; | 56 | pub const debug = crash_report.debug; |
| 57 | 57 | ||
| 58 | var wasi_preopens: fs.wasi.Preopens = undefined; | 58 | var wasi_preopens: fs.wasi.Preopens = undefined; |
| 59 | pub fn wasi_cwd() std.os.wasi.fd_t { | 59 | pub fn wasi_cwd() Io.Dir { |
| 60 | // Expect the first preopen to be current working directory. | 60 | // Expect the first preopen to be current working directory. |
| 61 | const cwd_fd: std.posix.fd_t = 3; | 61 | const cwd_fd: std.posix.fd_t = 3; |
| 62 | assert(mem.eql(u8, wasi_preopens.names[cwd_fd], ".")); | 62 | assert(mem.eql(u8, wasi_preopens.names[cwd_fd], ".")); |
| 63 | return cwd_fd; | 63 | return .{ .handle = cwd_fd }; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | const fatal = std.process.fatal; | 66 | const fatal = std.process.fatal; |
| ... | @@ -168,7 +168,7 @@ const use_debug_allocator = build_options.debug_gpa or | ... | @@ -168,7 +168,7 @@ const use_debug_allocator = build_options.debug_gpa or |
| 168 | .ReleaseFast, .ReleaseSmall => false, | 168 | .ReleaseFast, .ReleaseSmall => false, |
| 169 | }); | 169 | }); |
| 170 | 170 | ||
| 171 | pub fn main() anyerror!void { | 171 | pub fn main(init: std.process.Init.Minimal) anyerror!void { |
| 172 | const gpa = gpa: { | 172 | const gpa = gpa: { |
| 173 | if (use_debug_allocator) break :gpa debug_allocator.allocator(); | 173 | if (use_debug_allocator) break :gpa debug_allocator.allocator(); |
| 174 | if (native_os == .wasi) break :gpa std.heap.wasm_allocator; | 174 | if (native_os == .wasi) break :gpa std.heap.wasm_allocator; |
| ... | @@ -182,34 +182,48 @@ pub fn main() anyerror!void { | ... | @@ -182,34 +182,48 @@ pub fn main() anyerror!void { |
| 182 | defer arena_instance.deinit(); | 182 | defer arena_instance.deinit(); |
| 183 | const arena = arena_instance.allocator(); | 183 | const arena = arena_instance.allocator(); |
| 184 | 184 | ||
| 185 | const args = try process.argsAlloc(arena); | 185 | const args = try init.args.toSlice(arena); |
| 186 | 186 | ||
| 187 | if (args.len > 0) crash_report.zig_argv0 = args[0]; | 187 | if (args.len > 0) crash_report.zig_argv0 = args[0]; |
| 188 | 188 | ||
| 189 | if (args.len <= 1) { | ||
| 190 | std.log.info("{s}", .{usage}); | ||
| 191 | fatal("expected command argument", .{}); | ||
| 192 | } | ||
| 193 | |||
| 194 | var environ_map = init.environ.createMap(arena) catch |err| fatal("failed to parse environment: {t}", .{err}); | ||
| 195 | |||
| 196 | Compilation.setMainThread(); | ||
| 197 | |||
| 198 | var threaded: Io.Threaded = .init(gpa, .{ | ||
| 199 | .argv0 = .init(init.args), | ||
| 200 | .environ = init.environ, | ||
| 201 | }); | ||
| 202 | defer threaded.deinit(); | ||
| 203 | threaded_impl_ptr = &threaded; | ||
| 204 | threaded.stack_size = thread_stack_size; | ||
| 205 | const io = threaded.io(); | ||
| 206 | |||
| 189 | if (tracy.enable_allocation) { | 207 | if (tracy.enable_allocation) { |
| 190 | var gpa_tracy = tracy.tracyAllocator(gpa); | 208 | var gpa_tracy = tracy.tracyAllocator(gpa); |
| 191 | return mainArgs(gpa_tracy.allocator(), arena, args); | 209 | return mainArgs(gpa_tracy.allocator(), arena, io, args, &environ_map); |
| 192 | } | 210 | } |
| 193 | 211 | ||
| 194 | if (native_os == .wasi) { | 212 | if (native_os == .wasi) { |
| 195 | wasi_preopens = try fs.wasi.preopensAlloc(arena); | 213 | wasi_preopens = try fs.wasi.preopensAlloc(arena); |
| 196 | } | 214 | } |
| 197 | 215 | ||
| 198 | return mainArgs(gpa, arena, args); | 216 | return mainArgs(gpa, arena, io, args, &environ_map); |
| 199 | } | 217 | } |
| 200 | 218 | ||
| 201 | fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void { | 219 | fn mainArgs( |
| 202 | const tr = tracy.trace(@src()); | 220 | gpa: Allocator, |
| 203 | defer tr.end(); | 221 | arena: Allocator, |
| 204 | 222 | io: Io, | |
| 205 | Compilation.setMainThread(); | 223 | args: []const [:0]const u8, |
| 206 | 224 | environ_map: *process.Environ.Map, | |
| 207 | if (args.len <= 1) { | 225 | ) !void { |
| 208 | std.log.info("{s}", .{usage}); | 226 | if (process.can_replace and EnvVar.ZIG_IS_DETECTING_LIBC_PATHS.isSet(environ_map)) { |
| 209 | fatal("expected command argument", .{}); | ||
| 210 | } | ||
| 211 | |||
| 212 | if (process.can_execv and std.posix.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) { | ||
| 213 | dev.check(.cc_command); | 227 | dev.check(.cc_command); |
| 214 | // In this case we have accidentally invoked ourselves as "the system C compiler" | 228 | // In this case we have accidentally invoked ourselves as "the system C compiler" |
| 215 | // to figure out where libc is installed. This is essentially infinite recursion | 229 | // 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 | ... | @@ -217,58 +231,51 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void |
| 217 | // Here we ignore the CC environment variable and exec `cc` as a child process. | 231 | // Here we ignore the CC environment variable and exec `cc` as a child process. |
| 218 | // However it's possible Zig is installed as *that* C compiler as well, which is | 232 | // However it's possible Zig is installed as *that* C compiler as well, which is |
| 219 | // why we have this additional environment variable here to check. | 233 | // why we have this additional environment variable here to check. |
| 220 | var env_map = try process.getEnvMap(arena); | 234 | |
| 221 | 235 | const inf_loop_env_key: EnvVar = .ZIG_IS_TRYING_TO_NOT_CALL_ITSELF; | |
| 222 | const inf_loop_env_key = "ZIG_IS_TRYING_TO_NOT_CALL_ITSELF"; | 236 | if (inf_loop_env_key.isSet(environ_map)) { |
| 223 | if (env_map.get(inf_loop_env_key) != null) { | 237 | fatal("{s}", .{ |
| 224 | fatal("The compilation links against libc, but Zig is unable to provide a libc " ++ | 238 | "The compilation links against libc, but Zig is unable to provide a libc " ++ |
| 225 | "for this operating system, and no --libc " ++ | 239 | "for this operating system, and no --libc " ++ |
| 226 | "parameter was provided, so Zig attempted to invoke the system C compiler " ++ | 240 | "parameter was provided, so Zig attempted to invoke the system C compiler " ++ |
| 227 | "in order to determine where libc is installed. However the system C " ++ | 241 | "in order to determine where libc is installed. However the system C " ++ |
| 228 | "compiler is `zig cc`, so no libc installation was found.", .{}); | 242 | "compiler is `zig cc`, so no libc installation was found.", |
| 243 | }); | ||
| 229 | } | 244 | } |
| 230 | try env_map.put(inf_loop_env_key, "1"); | 245 | try environ_map.put(@tagName(inf_loop_env_key), "1"); |
| 231 | 246 | ||
| 232 | // Some programs such as CMake will strip the `cc` and subsequent args from the | 247 | // Some programs such as CMake will strip the `cc` and subsequent args from the |
| 233 | // CC environment variable. We detect and support this scenario here because of | 248 | // CC environment variable. We detect and support this scenario here because of |
| 234 | // the ZIG_IS_DETECTING_LIBC_PATHS environment variable. | 249 | // the ZIG_IS_DETECTING_LIBC_PATHS environment variable. |
| 235 | if (mem.eql(u8, args[1], "cc")) { | 250 | if (mem.eql(u8, args[1], "cc")) { |
| 236 | return process.execve(arena, args[1..], &env_map); | 251 | return process.replace(io, .{ .argv = args[1..], .environ_map = environ_map }); |
| 237 | } else { | 252 | } else { |
| 238 | const modified_args = try arena.dupe([]const u8, args); | 253 | const modified_args = try arena.dupe([]const u8, args); |
| 239 | modified_args[0] = "cc"; | 254 | modified_args[0] = "cc"; |
| 240 | return process.execve(arena, modified_args, &env_map); | 255 | return process.replace(io, .{ .argv = modified_args, .environ_map = environ_map }); |
| 241 | } | 256 | } |
| 242 | } | 257 | } |
| 243 | 258 | ||
| 244 | var threaded: Io.Threaded = .init(gpa, .{ | ||
| 245 | .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{}, | ||
| 246 | }); | ||
| 247 | defer threaded.deinit(); | ||
| 248 | threaded_impl_ptr = &threaded; | ||
| 249 | threaded.stack_size = thread_stack_size; | ||
| 250 | const io = threaded.io(); | ||
| 251 | |||
| 252 | const cmd = args[1]; | 259 | const cmd = args[1]; |
| 253 | const cmd_args = args[2..]; | 260 | const cmd_args = args[2..]; |
| 254 | if (mem.eql(u8, cmd, "build-exe")) { | 261 | if (mem.eql(u8, cmd, "build-exe")) { |
| 255 | dev.check(.build_exe_command); | 262 | dev.check(.build_exe_command); |
| 256 | return buildOutputType(gpa, arena, io, args, .{ .build = .Exe }); | 263 | return buildOutputType(gpa, arena, io, args, .{ .build = .Exe }, environ_map); |
| 257 | } else if (mem.eql(u8, cmd, "build-lib")) { | 264 | } else if (mem.eql(u8, cmd, "build-lib")) { |
| 258 | dev.check(.build_lib_command); | 265 | dev.check(.build_lib_command); |
| 259 | return buildOutputType(gpa, arena, io, args, .{ .build = .Lib }); | 266 | return buildOutputType(gpa, arena, io, args, .{ .build = .Lib }, environ_map); |
| 260 | } else if (mem.eql(u8, cmd, "build-obj")) { | 267 | } else if (mem.eql(u8, cmd, "build-obj")) { |
| 261 | dev.check(.build_obj_command); | 268 | dev.check(.build_obj_command); |
| 262 | return buildOutputType(gpa, arena, io, args, .{ .build = .Obj }); | 269 | return buildOutputType(gpa, arena, io, args, .{ .build = .Obj }, environ_map); |
| 263 | } else if (mem.eql(u8, cmd, "test")) { | 270 | } else if (mem.eql(u8, cmd, "test")) { |
| 264 | dev.check(.test_command); | 271 | dev.check(.test_command); |
| 265 | return buildOutputType(gpa, arena, io, args, .zig_test); | 272 | return buildOutputType(gpa, arena, io, args, .zig_test, environ_map); |
| 266 | } else if (mem.eql(u8, cmd, "test-obj")) { | 273 | } else if (mem.eql(u8, cmd, "test-obj")) { |
| 267 | dev.check(.test_command); | 274 | dev.check(.test_command); |
| 268 | return buildOutputType(gpa, arena, io, args, .zig_test_obj); | 275 | return buildOutputType(gpa, arena, io, args, .zig_test_obj, environ_map); |
| 269 | } else if (mem.eql(u8, cmd, "run")) { | 276 | } else if (mem.eql(u8, cmd, "run")) { |
| 270 | dev.check(.run_command); | 277 | dev.check(.run_command); |
| 271 | return buildOutputType(gpa, arena, io, args, .run); | 278 | return buildOutputType(gpa, arena, io, args, .run, environ_map); |
| 272 | } else if (mem.eql(u8, cmd, "dlltool") or | 279 | } else if (mem.eql(u8, cmd, "dlltool") or |
| 273 | mem.eql(u8, cmd, "ranlib") or | 280 | mem.eql(u8, cmd, "ranlib") or |
| 274 | mem.eql(u8, cmd, "lib") or | 281 | mem.eql(u8, cmd, "lib") or |
| ... | @@ -278,7 +285,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void | ... | @@ -278,7 +285,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void |
| 278 | return process.exit(try llvmArMain(arena, args)); | 285 | return process.exit(try llvmArMain(arena, args)); |
| 279 | } else if (mem.eql(u8, cmd, "build")) { | 286 | } else if (mem.eql(u8, cmd, "build")) { |
| 280 | dev.check(.build_command); | 287 | dev.check(.build_command); |
| 281 | return cmdBuild(gpa, arena, io, cmd_args); | 288 | return cmdBuild(gpa, arena, io, cmd_args, environ_map); |
| 282 | } else if (mem.eql(u8, cmd, "clang") or | 289 | } else if (mem.eql(u8, cmd, "clang") or |
| 283 | mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) | 290 | mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) |
| 284 | { | 291 | { |
| ... | @@ -292,16 +299,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void | ... | @@ -292,16 +299,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void |
| 292 | return process.exit(try lldMain(arena, args, true)); | 299 | return process.exit(try lldMain(arena, args, true)); |
| 293 | } else if (mem.eql(u8, cmd, "cc")) { | 300 | } else if (mem.eql(u8, cmd, "cc")) { |
| 294 | dev.check(.cc_command); | 301 | dev.check(.cc_command); |
| 295 | return buildOutputType(gpa, arena, io, args, .cc); | 302 | return buildOutputType(gpa, arena, io, args, .cc, environ_map); |
| 296 | } else if (mem.eql(u8, cmd, "c++")) { | 303 | } else if (mem.eql(u8, cmd, "c++")) { |
| 297 | dev.check(.cc_command); | 304 | dev.check(.cc_command); |
| 298 | return buildOutputType(gpa, arena, io, args, .cpp); | 305 | return buildOutputType(gpa, arena, io, args, .cpp, environ_map); |
| 299 | } else if (mem.eql(u8, cmd, "translate-c")) { | 306 | } else if (mem.eql(u8, cmd, "translate-c")) { |
| 300 | dev.check(.translate_c_command); | 307 | dev.check(.translate_c_command); |
| 301 | return buildOutputType(gpa, arena, io, args, .translate_c); | 308 | return buildOutputType(gpa, arena, io, args, .translate_c, environ_map); |
| 302 | } else if (mem.eql(u8, cmd, "rc")) { | 309 | } else if (mem.eql(u8, cmd, "rc")) { |
| 303 | const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration"); | 310 | const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration"); |
| 304 | return jitCmd(gpa, arena, io, cmd_args, .{ | 311 | return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ |
| 305 | .cmd_name = "resinator", | 312 | .cmd_name = "resinator", |
| 306 | .root_src_path = "resinator/main.zig", | 313 | .root_src_path = "resinator/main.zig", |
| 307 | .depend_on_aro = true, | 314 | .depend_on_aro = true, |
| ... | @@ -312,20 +319,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void | ... | @@ -312,20 +319,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void |
| 312 | dev.check(.fmt_command); | 319 | dev.check(.fmt_command); |
| 313 | return @import("fmt.zig").run(gpa, arena, io, cmd_args); | 320 | return @import("fmt.zig").run(gpa, arena, io, cmd_args); |
| 314 | } else if (mem.eql(u8, cmd, "objcopy")) { | 321 | } else if (mem.eql(u8, cmd, "objcopy")) { |
| 315 | return jitCmd(gpa, arena, io, cmd_args, .{ | 322 | return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ |
| 316 | .cmd_name = "objcopy", | 323 | .cmd_name = "objcopy", |
| 317 | .root_src_path = "objcopy.zig", | 324 | .root_src_path = "objcopy.zig", |
| 318 | }); | 325 | }); |
| 319 | } else if (mem.eql(u8, cmd, "fetch")) { | 326 | } else if (mem.eql(u8, cmd, "fetch")) { |
| 320 | return cmdFetch(gpa, arena, io, cmd_args); | 327 | return cmdFetch(gpa, arena, io, cmd_args, environ_map); |
| 321 | } else if (mem.eql(u8, cmd, "libc")) { | 328 | } else if (mem.eql(u8, cmd, "libc")) { |
| 322 | return jitCmd(gpa, arena, io, cmd_args, .{ | 329 | return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ |
| 323 | .cmd_name = "libc", | 330 | .cmd_name = "libc", |
| 324 | .root_src_path = "libc.zig", | 331 | .root_src_path = "libc.zig", |
| 325 | .prepend_zig_lib_dir_path = true, | 332 | .prepend_zig_lib_dir_path = true, |
| 326 | }); | 333 | }); |
| 327 | } else if (mem.eql(u8, cmd, "std")) { | 334 | } else if (mem.eql(u8, cmd, "std")) { |
| 328 | return jitCmd(gpa, arena, io, cmd_args, .{ | 335 | return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ |
| 329 | .cmd_name = "std", | 336 | .cmd_name = "std", |
| 330 | .root_src_path = "std-docs.zig", | 337 | .root_src_path = "std-docs.zig", |
| 331 | .prepend_zig_lib_dir_path = true, | 338 | .prepend_zig_lib_dir_path = true, |
| ... | @@ -355,10 +362,11 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void | ... | @@ -355,10 +362,11 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8) !void |
| 355 | args, | 362 | args, |
| 356 | if (native_os == .wasi) wasi_preopens, | 363 | if (native_os == .wasi) wasi_preopens, |
| 357 | &host, | 364 | &host, |
| 365 | environ_map, | ||
| 358 | ); | 366 | ); |
| 359 | return stdout_writer.interface.flush(); | 367 | return stdout_writer.interface.flush(); |
| 360 | } else if (mem.eql(u8, cmd, "reduce")) { | 368 | } else if (mem.eql(u8, cmd, "reduce")) { |
| 361 | return jitCmd(gpa, arena, io, cmd_args, .{ | 369 | return jitCmd(gpa, arena, io, cmd_args, environ_map, .{ |
| 362 | .cmd_name = "reduce", | 370 | .cmd_name = "reduce", |
| 363 | .root_src_path = "reduce.zig", | 371 | .root_src_path = "reduce.zig", |
| 364 | }); | 372 | }); |
| ... | @@ -803,6 +811,7 @@ fn buildOutputType( | ... | @@ -803,6 +811,7 @@ fn buildOutputType( |
| 803 | io: Io, | 811 | io: Io, |
| 804 | all_args: []const []const u8, | 812 | all_args: []const []const u8, |
| 805 | arg_mode: ArgMode, | 813 | arg_mode: ArgMode, |
| 814 | environ_map: *process.Environ.Map, | ||
| 806 | ) !void { | 815 | ) !void { |
| 807 | var provided_name: ?[]const u8 = null; | 816 | var provided_name: ?[]const u8 = null; |
| 808 | var root_src_file: ?[]const u8 = null; | 817 | var root_src_file: ?[]const u8 = null; |
| ... | @@ -815,9 +824,9 @@ fn buildOutputType( | ... | @@ -815,9 +824,9 @@ fn buildOutputType( |
| 815 | var debug_compile_errors = false; | 824 | var debug_compile_errors = false; |
| 816 | var debug_incremental = false; | 825 | var debug_incremental = false; |
| 817 | var verbose_link = (native_os != .wasi or builtin.link_libc) and | 826 | var verbose_link = (native_os != .wasi or builtin.link_libc) and |
| 818 | EnvVar.ZIG_VERBOSE_LINK.isSet(); | 827 | EnvVar.ZIG_VERBOSE_LINK.isSet(environ_map); |
| 819 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and | 828 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and |
| 820 | EnvVar.ZIG_VERBOSE_CC.isSet(); | 829 | EnvVar.ZIG_VERBOSE_CC.isSet(environ_map); |
| 821 | var verbose_air = false; | 830 | var verbose_air = false; |
| 822 | var verbose_intern_pool = false; | 831 | var verbose_intern_pool = false; |
| 823 | var verbose_generic_instances = false; | 832 | var verbose_generic_instances = false; |
| ... | @@ -889,9 +898,9 @@ fn buildOutputType( | ... | @@ -889,9 +898,9 @@ fn buildOutputType( |
| 889 | var runtime_args_start: ?usize = null; | 898 | var runtime_args_start: ?usize = null; |
| 890 | var test_filters: std.ArrayList([]const u8) = .empty; | 899 | var test_filters: std.ArrayList([]const u8) = .empty; |
| 891 | var test_runner_path: ?[]const u8 = null; | 900 | var test_runner_path: ?[]const u8 = null; |
| 892 | var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena); | 901 | var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map); |
| 893 | var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); | 902 | var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); |
| 894 | var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); | 903 | var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map); |
| 895 | var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; | 904 | var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; |
| 896 | var subsystem: ?std.zig.Subsystem = null; | 905 | var subsystem: ?std.zig.Subsystem = null; |
| 897 | var major_subsystem_version: ?u16 = null; | 906 | var major_subsystem_version: ?u16 = null; |
| ... | @@ -988,7 +997,7 @@ fn buildOutputType( | ... | @@ -988,7 +997,7 @@ fn buildOutputType( |
| 988 | .framework_dirs = .{}, | 997 | .framework_dirs = .{}, |
| 989 | .rpath_list = .{}, | 998 | .rpath_list = .{}, |
| 990 | .each_lib_rpath = null, | 999 | .each_lib_rpath = null, |
| 991 | .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena), | 1000 | .libc_paths_file = EnvVar.ZIG_LIBC.get(environ_map), |
| 992 | .native_system_include_paths = &.{}, | 1001 | .native_system_include_paths = &.{}, |
| 993 | }; | 1002 | }; |
| 994 | defer create_module.link_inputs.deinit(gpa); | 1003 | defer create_module.link_inputs.deinit(gpa); |
| ... | @@ -997,9 +1006,9 @@ fn buildOutputType( | ... | @@ -997,9 +1006,9 @@ fn buildOutputType( |
| 997 | // if set, default the color setting to .off or .on, respectively | 1006 | // if set, default the color setting to .off or .on, respectively |
| 998 | // explicit --color arguments will still override this setting. | 1007 | // explicit --color arguments will still override this setting. |
| 999 | // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162 | 1008 | // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162 |
| 1000 | var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet()) | 1009 | var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet(environ_map)) |
| 1001 | .off | 1010 | .off |
| 1002 | else if (EnvVar.CLICOLOR_FORCE.isSet()) | 1011 | else if (EnvVar.CLICOLOR_FORCE.isSet(environ_map)) |
| 1003 | .on | 1012 | .on |
| 1004 | else | 1013 | else |
| 1005 | .auto; | 1014 | .auto; |
| ... | @@ -3097,6 +3106,7 @@ fn buildOutputType( | ... | @@ -3097,6 +3106,7 @@ fn buildOutputType( |
| 3097 | }, | 3106 | }, |
| 3098 | if (native_os == .wasi) wasi_preopens, | 3107 | if (native_os == .wasi) wasi_preopens, |
| 3099 | self_exe_path, | 3108 | self_exe_path, |
| 3109 | environ_map, | ||
| 3100 | ); | 3110 | ); |
| 3101 | defer dirs.deinit(io); | 3111 | defer dirs.deinit(io); |
| 3102 | 3112 | ||
| ... | @@ -3108,7 +3118,7 @@ fn buildOutputType( | ... | @@ -3108,7 +3118,7 @@ fn buildOutputType( |
| 3108 | create_module.opts.emit_bin = emit_bin != .no; | 3118 | create_module.opts.emit_bin = emit_bin != .no; |
| 3109 | create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0; | 3119 | create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0; |
| 3110 | 3120 | ||
| 3111 | const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color); | 3121 | const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color, environ_map); |
| 3112 | for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| { | 3122 | for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| { |
| 3113 | if (cli_mod.resolved == null) | 3123 | if (cli_mod.resolved == null) |
| 3114 | fatal("module '{s}' declared but not used", .{key}); | 3124 | fatal("module '{s}' declared but not used", .{key}); |
| ... | @@ -3585,6 +3595,7 @@ fn buildOutputType( | ... | @@ -3585,6 +3595,7 @@ fn buildOutputType( |
| 3585 | .global_cc_argv = try cc_argv.toOwnedSlice(arena), | 3595 | .global_cc_argv = try cc_argv.toOwnedSlice(arena), |
| 3586 | .file_system_inputs = &file_system_inputs, | 3596 | .file_system_inputs = &file_system_inputs, |
| 3587 | .debug_compiler_runtime_libs = debug_compiler_runtime_libs, | 3597 | .debug_compiler_runtime_libs = debug_compiler_runtime_libs, |
| 3598 | .environ_map = environ_map, | ||
| 3588 | }) catch |err| switch (err) { | 3599 | }) catch |err| switch (err) { |
| 3589 | error.CreateFail => switch (create_diag) { | 3600 | error.CreateFail => switch (create_diag) { |
| 3590 | .cross_libc_unavailable => { | 3601 | .cross_libc_unavailable => { |
| ... | @@ -3648,6 +3659,7 @@ fn buildOutputType( | ... | @@ -3648,6 +3659,7 @@ fn buildOutputType( |
| 3648 | arg_mode, | 3659 | arg_mode, |
| 3649 | all_args, | 3660 | all_args, |
| 3650 | runtime_args_start, | 3661 | runtime_args_start, |
| 3662 | environ_map, | ||
| 3651 | ); | 3663 | ); |
| 3652 | return cleanExit(io); | 3664 | return cleanExit(io); |
| 3653 | }, | 3665 | }, |
| ... | @@ -3674,6 +3686,7 @@ fn buildOutputType( | ... | @@ -3674,6 +3686,7 @@ fn buildOutputType( |
| 3674 | arg_mode, | 3686 | arg_mode, |
| 3675 | all_args, | 3687 | all_args, |
| 3676 | runtime_args_start, | 3688 | runtime_args_start, |
| 3689 | environ_map, | ||
| 3677 | ); | 3690 | ); |
| 3678 | return cleanExit(io); | 3691 | return cleanExit(io); |
| 3679 | }, | 3692 | }, |
| ... | @@ -3686,7 +3699,7 @@ fn buildOutputType( | ... | @@ -3686,7 +3699,7 @@ fn buildOutputType( |
| 3686 | defer root_prog_node.end(); | 3699 | defer root_prog_node.end(); |
| 3687 | 3700 | ||
| 3688 | if (arg_mode == .translate_c) { | 3701 | if (arg_mode == .translate_c) { |
| 3689 | return cmdTranslateC(comp, arena, null, null, root_prog_node); | 3702 | return cmdTranslateC(comp, arena, null, null, root_prog_node, environ_map); |
| 3690 | } | 3703 | } |
| 3691 | 3704 | ||
| 3692 | updateModule(comp, color, root_prog_node) catch |err| switch (err) { | 3705 | updateModule(comp, color, root_prog_node) catch |err| switch (err) { |
| ... | @@ -3754,6 +3767,7 @@ fn buildOutputType( | ... | @@ -3754,6 +3767,7 @@ fn buildOutputType( |
| 3754 | all_args, | 3767 | all_args, |
| 3755 | runtime_args_start, | 3768 | runtime_args_start, |
| 3756 | create_module.resolved_options.link_libc, | 3769 | create_module.resolved_options.link_libc, |
| 3770 | environ_map, | ||
| 3757 | ); | 3771 | ); |
| 3758 | } | 3772 | } |
| 3759 | 3773 | ||
| ... | @@ -3809,6 +3823,7 @@ fn createModule( | ... | @@ -3809,6 +3823,7 @@ fn createModule( |
| 3809 | index: usize, | 3823 | index: usize, |
| 3810 | parent: ?*Package.Module, | 3824 | parent: ?*Package.Module, |
| 3811 | color: std.zig.Color, | 3825 | color: std.zig.Color, |
| 3826 | environ_map: *process.Environ.Map, | ||
| 3812 | ) Allocator.Error!*Package.Module { | 3827 | ) Allocator.Error!*Package.Module { |
| 3813 | const cli_mod = &create_module.modules.values()[index]; | 3828 | const cli_mod = &create_module.modules.values()[index]; |
| 3814 | if (cli_mod.resolved) |m| return m; | 3829 | if (cli_mod.resolved) |m| return m; |
| ... | @@ -3988,7 +4003,7 @@ fn createModule( | ... | @@ -3988,7 +4003,7 @@ fn createModule( |
| 3988 | resolved_target.is_native_os and resolved_target.is_native_abi and | 4003 | resolved_target.is_native_os and resolved_target.is_native_abi and |
| 3989 | create_module.want_native_include_dirs) | 4004 | create_module.want_native_include_dirs) |
| 3990 | { | 4005 | { |
| 3991 | var paths = std.zig.system.NativePaths.detect(arena, io, target) catch |err| | 4006 | var paths = std.zig.system.NativePaths.detect(arena, io, target, environ_map) catch |err| |
| 3992 | fatal("unable to detect native system paths: {t}", .{err}); | 4007 | fatal("unable to detect native system paths: {t}", .{err}); |
| 3993 | for (paths.warnings.items) |warning| { | 4008 | for (paths.warnings.items) |warning| { |
| 3994 | warn("{s}", .{warning}); | 4009 | warn("{s}", .{warning}); |
| ... | @@ -4015,6 +4030,7 @@ fn createModule( | ... | @@ -4015,6 +4030,7 @@ fn createModule( |
| 4015 | create_module.libc_installation = LibCInstallation.findNative(arena, io, .{ | 4030 | create_module.libc_installation = LibCInstallation.findNative(arena, io, .{ |
| 4016 | .verbose = true, | 4031 | .verbose = true, |
| 4017 | .target = target, | 4032 | .target = target, |
| 4033 | .environ_map = environ_map, | ||
| 4018 | }) catch |err| { | 4034 | }) catch |err| { |
| 4019 | fatal("unable to find native libc installation: {t}", .{err}); | 4035 | fatal("unable to find native libc installation: {t}", .{err}); |
| 4020 | }; | 4036 | }; |
| ... | @@ -4119,7 +4135,7 @@ fn createModule( | ... | @@ -4119,7 +4135,7 @@ fn createModule( |
| 4119 | for (cli_mod.deps) |dep| { | 4135 | for (cli_mod.deps) |dep| { |
| 4120 | const dep_index = create_module.modules.getIndex(dep.value) orelse | 4136 | const dep_index = create_module.modules.getIndex(dep.value) orelse |
| 4121 | fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key }); | 4137 | fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key }); |
| 4122 | const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color); | 4138 | const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color, environ_map); |
| 4123 | try mod.deps.put(arena, dep.key, dep_mod); | 4139 | try mod.deps.put(arena, dep.key, dep_mod); |
| 4124 | } | 4140 | } |
| 4125 | 4141 | ||
| ... | @@ -4128,9 +4144,7 @@ fn createModule( | ... | @@ -4128,9 +4144,7 @@ fn createModule( |
| 4128 | 4144 | ||
| 4129 | fn saveState(comp: *Compilation, incremental: bool) void { | 4145 | fn saveState(comp: *Compilation, incremental: bool) void { |
| 4130 | if (incremental) { | 4146 | if (incremental) { |
| 4131 | comp.saveState() catch |err| { | 4147 | comp.saveState() catch |err| warn("unable to save incremental compilation state: {t}", .{err}); |
| 4132 | warn("unable to save incremental compilation state: {s}", .{@errorName(err)}); | ||
| 4133 | }; | ||
| 4134 | } | 4148 | } |
| 4135 | } | 4149 | } |
| 4136 | 4150 | ||
| ... | @@ -4143,6 +4157,7 @@ fn serve( | ... | @@ -4143,6 +4157,7 @@ fn serve( |
| 4143 | arg_mode: ArgMode, | 4157 | arg_mode: ArgMode, |
| 4144 | all_args: []const []const u8, | 4158 | all_args: []const []const u8, |
| 4145 | runtime_args_start: ?usize, | 4159 | runtime_args_start: ?usize, |
| 4160 | environ_map: *process.Environ.Map, | ||
| 4146 | ) !void { | 4161 | ) !void { |
| 4147 | const gpa = comp.gpa; | 4162 | const gpa = comp.gpa; |
| 4148 | const io = comp.io; | 4163 | const io = comp.io; |
| ... | @@ -4190,7 +4205,7 @@ fn serve( | ... | @@ -4190,7 +4205,7 @@ fn serve( |
| 4190 | defer arena_instance.deinit(); | 4205 | defer arena_instance.deinit(); |
| 4191 | const arena = arena_instance.allocator(); | 4206 | const arena = arena_instance.allocator(); |
| 4192 | var output: Compilation.CImportResult = undefined; | 4207 | var output: Compilation.CImportResult = undefined; |
| 4193 | try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node); | 4208 | try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node, environ_map); |
| 4194 | defer output.deinit(gpa); | 4209 | defer output.deinit(gpa); |
| 4195 | 4210 | ||
| 4196 | if (file_system_inputs.items.len != 0) { | 4211 | if (file_system_inputs.items.len != 0) { |
| ... | @@ -4390,6 +4405,7 @@ fn runOrTest( | ... | @@ -4390,6 +4405,7 @@ fn runOrTest( |
| 4390 | all_args: []const []const u8, | 4405 | all_args: []const []const u8, |
| 4391 | runtime_args_start: ?usize, | 4406 | runtime_args_start: ?usize, |
| 4392 | link_libc: bool, | 4407 | link_libc: bool, |
| 4408 | environ_map: *process.Environ.Map, | ||
| 4393 | ) !void { | 4409 | ) !void { |
| 4394 | const raw_emit_bin = comp.emit_bin orelse return; | 4410 | const raw_emit_bin = comp.emit_bin orelse return; |
| 4395 | const exe_path = switch (comp.cache_use) { | 4411 | const exe_path = switch (comp.cache_use) { |
| ... | @@ -4426,77 +4442,90 @@ fn runOrTest( | ... | @@ -4426,77 +4442,90 @@ fn runOrTest( |
| 4426 | if (runtime_args_start) |i| { | 4442 | if (runtime_args_start) |i| { |
| 4427 | try argv.appendSlice(all_args[i..]); | 4443 | try argv.appendSlice(all_args[i..]); |
| 4428 | } | 4444 | } |
| 4429 | var env_map = try process.getEnvMap(arena); | 4445 | try environ_map.put("ZIG_EXE", self_exe_path); |
| 4430 | try env_map.put("ZIG_EXE", self_exe_path); | ||
| 4431 | 4446 | ||
| 4432 | // We do not execve for tests because if the test fails we want to print | 4447 | // We do not execve for tests because if the test fails we want to print |
| 4433 | // the error message and invocation below. | 4448 | // the error message and invocation below. |
| 4434 | if (process.can_execv and arg_mode == .run) { | 4449 | if (process.can_replace and arg_mode == .run) { |
| 4435 | // execv releases the locks; no need to destroy the Compilation here. | 4450 | // process replacement releases the locks; no need to destroy the Compilation here. |
| 4436 | _ = try io.lockStderr(&.{}, .no_color); | 4451 | _ = try io.lockStderr(&.{}, .no_color); |
| 4437 | const err = process.execve(gpa, argv.items, &env_map); | 4452 | const err = process.replace(io, .{ .argv = argv.items, .environ_map = environ_map }); |
| 4438 | io.unlockStderr(); | 4453 | io.unlockStderr(); |
| 4439 | try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); | 4454 | try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); |
| 4440 | const cmd = try std.mem.join(arena, " ", argv.items); | 4455 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 4441 | fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); | 4456 | fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); |
| 4442 | } else if (process.can_spawn) { | 4457 | } else if (!process.can_spawn) { |
| 4443 | var child = std.process.Child.init(argv.items, gpa); | 4458 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 4444 | child.env_map = &env_map; | 4459 | fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ |
| 4445 | child.stdin_behavior = .Inherit; | 4460 | native_os, cmd, |
| 4446 | child.stdout_behavior = .Inherit; | 4461 | }); |
| 4447 | child.stderr_behavior = .Inherit; | 4462 | } |
| 4448 | 4463 | const term_result = (term: { | |
| 4449 | // Here we release all the locks associated with the Compilation so | 4464 | // Here we release all the locks associated with the Compilation so |
| 4450 | // that whatever this child process wants to do won't deadlock. | 4465 | // that whatever this child process wants to do won't deadlock. |
| 4451 | comp.destroy(); | 4466 | comp.destroy(); |
| 4452 | comp_destroyed.* = true; | 4467 | comp_destroyed.* = true; |
| 4453 | 4468 | ||
| 4454 | const term_result = t: { | 4469 | _ = try io.lockStderr(&.{}, .no_color); |
| 4455 | _ = try io.lockStderr(&.{}, .no_color); | 4470 | defer io.unlockStderr(); |
| 4456 | defer io.unlockStderr(); | 4471 | |
| 4457 | break :t child.spawnAndWait(io); | 4472 | var child = std.process.spawn(io, .{ |
| 4458 | }; | 4473 | .argv = argv.items, |
| 4459 | const term = term_result catch |err| { | 4474 | .environ_map = environ_map, |
| 4460 | try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); | 4475 | .stdin = .inherit, |
| 4461 | const cmd = try std.mem.join(arena, " ", argv.items); | 4476 | .stdout = .inherit, |
| 4462 | fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); | 4477 | .stderr = .inherit, |
| 4463 | }; | 4478 | }) catch |err| break :term err; |
| 4464 | switch (arg_mode) { | 4479 | defer child.kill(io); |
| 4465 | .run, .build => { | 4480 | |
| 4466 | switch (term) { | 4481 | break :term child.wait(io); |
| 4467 | .Exited => |code| { | 4482 | }); |
| 4468 | if (code == 0) { | 4483 | |
| 4469 | return cleanExit(io); | 4484 | const term = term_result catch |err| { |
| 4470 | } else { | 4485 | try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); |
| 4471 | process.exit(code); | ||
| 4472 | } | ||
| 4473 | }, | ||
| 4474 | else => { | ||
| 4475 | process.exit(1); | ||
| 4476 | }, | ||
| 4477 | } | ||
| 4478 | }, | ||
| 4479 | .zig_test => { | ||
| 4480 | switch (term) { | ||
| 4481 | .Exited => |code| { | ||
| 4482 | if (code == 0) { | ||
| 4483 | return cleanExit(io); | ||
| 4484 | } else { | ||
| 4485 | const cmd = try std.mem.join(arena, " ", argv.items); | ||
| 4486 | fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); | ||
| 4487 | } | ||
| 4488 | }, | ||
| 4489 | else => { | ||
| 4490 | const cmd = try std.mem.join(arena, " ", argv.items); | ||
| 4491 | fatal("the following test command crashed:\n{s}", .{cmd}); | ||
| 4492 | }, | ||
| 4493 | } | ||
| 4494 | }, | ||
| 4495 | else => unreachable, | ||
| 4496 | } | ||
| 4497 | } else { | ||
| 4498 | const cmd = try std.mem.join(arena, " ", argv.items); | 4486 | const cmd = try std.mem.join(arena, " ", argv.items); |
| 4499 | fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd }); | 4487 | fatal("the following command failed with {t}:\n{s}", .{ err, cmd }); |
| 4488 | }; | ||
| 4489 | switch (arg_mode) { | ||
| 4490 | .run, .build => { | ||
| 4491 | switch (term) { | ||
| 4492 | .exited => |code| { | ||
| 4493 | if (code == 0) { | ||
| 4494 | return cleanExit(io); | ||
| 4495 | } else { | ||
| 4496 | process.exit(code); | ||
| 4497 | } | ||
| 4498 | }, | ||
| 4499 | .signal => |sig| { | ||
| 4500 | const cmd = try std.mem.join(arena, " ", argv.items); | ||
| 4501 | fatal("the following command terminated with signal {t}:\n{s}", .{ sig, cmd }); | ||
| 4502 | }, | ||
| 4503 | else => { | ||
| 4504 | process.exit(1); | ||
| 4505 | }, | ||
| 4506 | } | ||
| 4507 | }, | ||
| 4508 | .zig_test => { | ||
| 4509 | switch (term) { | ||
| 4510 | .exited => |code| { | ||
| 4511 | if (code == 0) { | ||
| 4512 | return cleanExit(io); | ||
| 4513 | } else { | ||
| 4514 | const cmd = try std.mem.join(arena, " ", argv.items); | ||
| 4515 | fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); | ||
| 4516 | } | ||
| 4517 | }, | ||
| 4518 | .signal => |sig| { | ||
| 4519 | const cmd = try std.mem.join(arena, " ", argv.items); | ||
| 4520 | fatal("the following test command terminated with signal {t}:\n{s}", .{ sig, cmd }); | ||
| 4521 | }, | ||
| 4522 | else => { | ||
| 4523 | const cmd = try std.mem.join(arena, " ", argv.items); | ||
| 4524 | fatal("the following test command crashed:\n{s}", .{cmd}); | ||
| 4525 | }, | ||
| 4526 | } | ||
| 4527 | }, | ||
| 4528 | else => unreachable, | ||
| 4500 | } | 4529 | } |
| 4501 | } | 4530 | } |
| 4502 | 4531 | ||
| ... | @@ -4559,43 +4588,13 @@ fn runOrTestHotSwap( | ... | @@ -4559,43 +4588,13 @@ fn runOrTestHotSwap( |
| 4559 | try argv.appendSlice(all_args[i..]); | 4588 | try argv.appendSlice(all_args[i..]); |
| 4560 | } | 4589 | } |
| 4561 | 4590 | ||
| 4562 | switch (builtin.target.os.tag) { | 4591 | var child = try std.process.spawn(io, .{ |
| 4563 | .macos => { | 4592 | .argv = argv.items, |
| 4564 | const PosixSpawn = @import("DarwinPosixSpawn.zig"); | 4593 | .stdin = .inherit, |
| 4565 | 4594 | .stdout = .inherit, | |
| 4566 | var attr = try PosixSpawn.Attr.init(); | 4595 | .stderr = .inherit, |
| 4567 | defer attr.deinit(); | 4596 | }); |
| 4568 | 4597 | return child.id.?; | |
| 4569 | // ASLR is probably a good default for better debugging experience/programming | ||
| 4570 | // with hot-code updates in mind. However, we can also make it work with ASLR on. | ||
| 4571 | try attr.set(.{ | ||
| 4572 | .SETSIGDEF = true, | ||
| 4573 | .SETSIGMASK = true, | ||
| 4574 | .DISABLE_ASLR = true, | ||
| 4575 | }); | ||
| 4576 | |||
| 4577 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | ||
| 4578 | defer arena_allocator.deinit(); | ||
| 4579 | const arena = arena_allocator.allocator(); | ||
| 4580 | |||
| 4581 | const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null); | ||
| 4582 | for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | ||
| 4583 | |||
| 4584 | const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ); | ||
| 4585 | return pid; | ||
| 4586 | }, | ||
| 4587 | else => { | ||
| 4588 | var child = std.process.Child.init(argv.items, gpa); | ||
| 4589 | |||
| 4590 | child.stdin_behavior = .Inherit; | ||
| 4591 | child.stdout_behavior = .Inherit; | ||
| 4592 | child.stderr_behavior = .Inherit; | ||
| 4593 | |||
| 4594 | try child.spawn(io); | ||
| 4595 | |||
| 4596 | return child.id; | ||
| 4597 | }, | ||
| 4598 | } | ||
| 4599 | } | 4598 | } |
| 4600 | 4599 | ||
| 4601 | const UpdateModuleError = Compilation.UpdateError || error{ | 4600 | const UpdateModuleError = Compilation.UpdateError || error{ |
| ... | @@ -4627,6 +4626,7 @@ fn cmdTranslateC( | ... | @@ -4627,6 +4626,7 @@ fn cmdTranslateC( |
| 4627 | fancy_output: ?*Compilation.CImportResult, | 4626 | fancy_output: ?*Compilation.CImportResult, |
| 4628 | file_system_inputs: ?*std.ArrayList(u8), | 4627 | file_system_inputs: ?*std.ArrayList(u8), |
| 4629 | prog_node: std.Progress.Node, | 4628 | prog_node: std.Progress.Node, |
| 4629 | environ_map: *process.Environ.Map, | ||
| 4630 | ) !void { | 4630 | ) !void { |
| 4631 | dev.check(.translate_c_command); | 4631 | dev.check(.translate_c_command); |
| 4632 | 4632 | ||
| ... | @@ -4660,6 +4660,7 @@ fn cmdTranslateC( | ... | @@ -4660,6 +4660,7 @@ fn cmdTranslateC( |
| 4660 | translated_basename, | 4660 | translated_basename, |
| 4661 | comp.root_mod, | 4661 | comp.root_mod, |
| 4662 | prog_node, | 4662 | prog_node, |
| 4663 | environ_map, | ||
| 4663 | ); | 4664 | ); |
| 4664 | 4665 | ||
| 4665 | if (result.errors.errorMessageCount() != 0) { | 4666 | if (result.errors.errorMessageCount() != 0) { |
| ... | @@ -4707,10 +4708,11 @@ pub fn translateC( | ... | @@ -4707,10 +4708,11 @@ pub fn translateC( |
| 4707 | arena: Allocator, | 4708 | arena: Allocator, |
| 4708 | io: Io, | 4709 | io: Io, |
| 4709 | argv: []const []const u8, | 4710 | argv: []const []const u8, |
| 4711 | environ_map: *const process.Environ.Map, | ||
| 4710 | prog_node: std.Progress.Node, | 4712 | prog_node: std.Progress.Node, |
| 4711 | capture: ?*[]u8, | 4713 | capture: ?*[]u8, |
| 4712 | ) !void { | 4714 | ) !void { |
| 4713 | try jitCmd(gpa, arena, io, argv, .{ | 4715 | try jitCmd(gpa, arena, io, argv, environ_map, .{ |
| 4714 | .cmd_name = "translate-c", | 4716 | .cmd_name = "translate-c", |
| 4715 | .root_src_path = "translate-c/main.zig", | 4717 | .root_src_path = "translate-c/main.zig", |
| 4716 | .depend_on_aro = true, | 4718 | .depend_on_aro = true, |
| ... | @@ -4867,21 +4869,21 @@ test sanitizeExampleName { | ... | @@ -4867,21 +4869,21 @@ test sanitizeExampleName { |
| 4867 | try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project")); | 4869 | try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project")); |
| 4868 | } | 4870 | } |
| 4869 | 4871 | ||
| 4870 | fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { | 4872 | fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, environ_map: *process.Environ.Map) !void { |
| 4871 | dev.check(.build_command); | 4873 | dev.check(.build_command); |
| 4872 | 4874 | ||
| 4873 | var build_file: ?[]const u8 = null; | 4875 | var build_file: ?[]const u8 = null; |
| 4874 | var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); | 4876 | var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map); |
| 4875 | var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); | 4877 | var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); |
| 4876 | var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena); | 4878 | var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map); |
| 4877 | var override_build_runner: ?[]const u8 = try EnvVar.ZIG_BUILD_RUNNER.get(arena); | 4879 | var override_build_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map); |
| 4878 | var child_argv = std.array_list.Managed([]const u8).init(arena); | 4880 | var child_argv = std.array_list.Managed([]const u8).init(arena); |
| 4879 | var reference_trace: ?u32 = null; | 4881 | var reference_trace: ?u32 = null; |
| 4880 | var debug_compile_errors = false; | 4882 | var debug_compile_errors = false; |
| 4881 | var verbose_link = (native_os != .wasi or builtin.link_libc) and | 4883 | var verbose_link = (native_os != .wasi or builtin.link_libc) and |
| 4882 | EnvVar.ZIG_VERBOSE_LINK.isSet(); | 4884 | EnvVar.ZIG_VERBOSE_LINK.isSet(environ_map); |
| 4883 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and | 4885 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and |
| 4884 | EnvVar.ZIG_VERBOSE_CC.isSet(); | 4886 | EnvVar.ZIG_VERBOSE_CC.isSet(environ_map); |
| 4885 | var verbose_air = false; | 4887 | var verbose_air = false; |
| 4886 | var verbose_intern_pool = false; | 4888 | var verbose_intern_pool = false; |
| 4887 | var verbose_generic_instances = false; | 4889 | var verbose_generic_instances = false; |
| ... | @@ -5078,7 +5080,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) | ... | @@ -5078,7 +5080,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) |
| 5078 | } | 5080 | } |
| 5079 | 5081 | ||
| 5080 | const work_around_btrfs_bug = native_os == .linux and | 5082 | const work_around_btrfs_bug = native_os == .linux and |
| 5081 | EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); | 5083 | EnvVar.ZIG_BTRFS_WORKAROUND.isSet(environ_map); |
| 5082 | const root_prog_node = std.Progress.start(io, .{ | 5084 | const root_prog_node = std.Progress.start(io, .{ |
| 5083 | .disable_printing = (color == .off), | 5085 | .disable_printing = (color == .off), |
| 5084 | .root_name = "Compile Build Script", | 5086 | .root_name = "Compile Build Script", |
| ... | @@ -5138,6 +5140,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) | ... | @@ -5138,6 +5140,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) |
| 5138 | } }, | 5140 | } }, |
| 5139 | {}, | 5141 | {}, |
| 5140 | self_exe_path, | 5142 | self_exe_path, |
| 5143 | environ_map, | ||
| 5141 | ); | 5144 | ); |
| 5142 | defer dirs.deinit(io); | 5145 | defer dirs.deinit(io); |
| 5143 | 5146 | ||
| ... | @@ -5240,7 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) | ... | @@ -5240,7 +5243,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) |
| 5240 | job_queue.read_only = true; | 5243 | job_queue.read_only = true; |
| 5241 | cleanup_build_dir = job_queue.global_cache.handle; | 5244 | cleanup_build_dir = job_queue.global_cache.handle; |
| 5242 | } else { | 5245 | } else { |
| 5243 | try http_client.initDefaultProxies(arena); | 5246 | try http_client.initDefaultProxies(arena, environ_map); |
| 5244 | } | 5247 | } |
| 5245 | 5248 | ||
| 5246 | try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); | 5249 | try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); |
| ... | @@ -5394,6 +5397,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) | ... | @@ -5394,6 +5397,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) |
| 5394 | .cache_mode = .whole, | 5397 | .cache_mode = .whole, |
| 5395 | .reference_trace = reference_trace, | 5398 | .reference_trace = reference_trace, |
| 5396 | .debug_compile_errors = debug_compile_errors, | 5399 | .debug_compile_errors = debug_compile_errors, |
| 5400 | .environ_map = environ_map, | ||
| 5397 | }) catch |err| switch (err) { | 5401 | }) catch |err| switch (err) { |
| 5398 | error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), | 5402 | error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), |
| 5399 | else => fatal("failed to create compilation: {s}", .{@errorName(err)}), | 5403 | 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) | ... | @@ -5415,81 +5419,81 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) |
| 5415 | }); | 5419 | }); |
| 5416 | } | 5420 | } |
| 5417 | 5421 | ||
| 5418 | if (process.can_spawn) { | 5422 | if (!process.can_spawn) { |
| 5419 | var child = std.process.Child.init(child_argv.items, gpa); | 5423 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5420 | child.stdin_behavior = .Inherit; | 5424 | fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd }); |
| 5421 | child.stdout_behavior = .Inherit; | 5425 | } |
| 5422 | child.stderr_behavior = .Inherit; | 5426 | switch (term: { |
| 5423 | 5427 | _ = try io.lockStderr(&.{}, .no_color); | |
| 5424 | const term = t: { | 5428 | defer io.unlockStderr(); |
| 5425 | _ = try io.lockStderr(&.{}, .no_color); | 5429 | var child = std.process.spawn(io, .{ |
| 5426 | defer io.unlockStderr(); | 5430 | .argv = child_argv.items, |
| 5427 | break :t child.spawnAndWait(io) catch |err| | 5431 | }) catch |err| fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err }); |
| 5428 | fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err }); | 5432 | defer child.kill(io); |
| 5429 | }; | 5433 | break :term child.wait(io) catch |err| |
| 5430 | 5434 | fatal("failed to wait build runner {s}: {t}", .{ child_argv.items[0], err }); | |
| 5431 | switch (term) { | 5435 | }) { |
| 5432 | .Exited => |code| { | 5436 | .exited => |code| { |
| 5433 | if (code == 0) return cleanExit(io); | 5437 | if (code == 0) return cleanExit(io); |
| 5434 | // Indicates that the build runner has reported compile errors | 5438 | // Indicates that the build runner has reported compile errors |
| 5435 | // and this parent process does not need to report any further | 5439 | // and this parent process does not need to report any further |
| 5436 | // diagnostics. | 5440 | // diagnostics. |
| 5437 | if (code == 2) process.exit(2); | 5441 | if (code == 2) process.exit(2); |
| 5438 | 5442 | ||
| 5439 | if (code == 3) { | 5443 | if (code == 3) { |
| 5440 | if (!dev.env.supports(.fetch_command)) process.exit(3); | 5444 | if (!dev.env.supports(.fetch_command)) process.exit(3); |
| 5441 | // Indicates the configure phase failed due to missing lazy | 5445 | // Indicates the configure phase failed due to missing lazy |
| 5442 | // dependencies and stdout contains the hashes of the ones | 5446 | // dependencies and stdout contains the hashes of the ones |
| 5443 | // that are missing. | 5447 | // that are missing. |
| 5444 | const s = fs.path.sep_str; | 5448 | const s = fs.path.sep_str; |
| 5445 | const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce; | 5449 | const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce; |
| 5446 | const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| { | 5450 | const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| { |
| 5447 | fatal("unable to read results of configure phase from '{f}{s}': {s}", .{ | 5451 | fatal("unable to read results of configure phase from '{f}{s}': {t}", .{ |
| 5448 | dirs.local_cache, tmp_sub_path, @errorName(err), | 5452 | dirs.local_cache, tmp_sub_path, err, |
| 5453 | }); | ||
| 5454 | }; | ||
| 5455 | dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {}; | ||
| 5456 | |||
| 5457 | var it = mem.splitScalar(u8, stdout, '\n'); | ||
| 5458 | var any_errors = false; | ||
| 5459 | while (it.next()) |hash| { | ||
| 5460 | if (hash.len == 0) continue; | ||
| 5461 | if (hash.len > Package.Hash.max_len) { | ||
| 5462 | std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ | ||
| 5463 | hash.len, hash, | ||
| 5449 | }); | 5464 | }); |
| 5450 | }; | 5465 | any_errors = true; |
| 5451 | dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {}; | 5466 | continue; |
| 5452 | |||
| 5453 | var it = mem.splitScalar(u8, stdout, '\n'); | ||
| 5454 | var any_errors = false; | ||
| 5455 | while (it.next()) |hash| { | ||
| 5456 | if (hash.len == 0) continue; | ||
| 5457 | if (hash.len > Package.Hash.max_len) { | ||
| 5458 | std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ | ||
| 5459 | hash.len, hash, | ||
| 5460 | }); | ||
| 5461 | any_errors = true; | ||
| 5462 | continue; | ||
| 5463 | } | ||
| 5464 | try unlazy_set.put(arena, .fromSlice(hash), {}); | ||
| 5465 | } | 5467 | } |
| 5466 | if (any_errors) process.exit(3); | 5468 | try unlazy_set.put(arena, .fromSlice(hash), {}); |
| 5467 | if (system_pkg_dir_path) |p| { | 5469 | } |
| 5468 | // In this mode, the system needs to provide these packages; they | 5470 | if (any_errors) process.exit(3); |
| 5469 | // cannot be fetched by Zig. | 5471 | if (system_pkg_dir_path) |p| { |
| 5470 | for (unlazy_set.keys()) |*hash| { | 5472 | // In this mode, the system needs to provide these packages; they |
| 5471 | std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ | 5473 | // cannot be fetched by Zig. |
| 5472 | p, hash.toSlice(), | 5474 | for (unlazy_set.keys()) |*hash| { |
| 5473 | }); | 5475 | std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ |
| 5474 | } | 5476 | p, hash.toSlice(), |
| 5475 | std.log.info("remote package fetching disabled due to --system mode", .{}); | 5477 | }); |
| 5476 | std.log.info("dependencies might be avoidable depending on build configuration", .{}); | ||
| 5477 | process.exit(3); | ||
| 5478 | } | 5478 | } |
| 5479 | continue; | 5479 | std.log.info("remote package fetching disabled due to --system mode", .{}); |
| 5480 | std.log.info("dependencies might be avoidable depending on build configuration", .{}); | ||
| 5481 | process.exit(3); | ||
| 5480 | } | 5482 | } |
| 5483 | continue; | ||
| 5484 | } | ||
| 5481 | 5485 | ||
| 5482 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5486 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5483 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); | 5487 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); |
| 5484 | }, | 5488 | }, |
| 5485 | else => { | 5489 | .signal => |sig| { |
| 5486 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5490 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5487 | fatal("the following build command crashed:\n{s}", .{cmd}); | 5491 | fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd }); |
| 5488 | }, | 5492 | }, |
| 5489 | } | 5493 | else => { |
| 5490 | } else { | 5494 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5491 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5495 | fatal("the following build command crashed:\n{s}", .{cmd}); |
| 5492 | fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd }); | 5496 | }, |
| 5493 | } | 5497 | } |
| 5494 | } | 5498 | } |
| 5495 | } | 5499 | } |
| ... | @@ -5512,6 +5516,7 @@ fn jitCmd( | ... | @@ -5512,6 +5516,7 @@ fn jitCmd( |
| 5512 | arena: Allocator, | 5516 | arena: Allocator, |
| 5513 | io: Io, | 5517 | io: Io, |
| 5514 | args: []const []const u8, | 5518 | args: []const []const u8, |
| 5519 | environ_map: *const process.Environ.Map, | ||
| 5515 | options: JitCmdOptions, | 5520 | options: JitCmdOptions, |
| 5516 | ) !void { | 5521 | ) !void { |
| 5517 | dev.check(.jit_command); | 5522 | dev.check(.jit_command); |
| ... | @@ -5533,13 +5538,13 @@ fn jitCmd( | ... | @@ -5533,13 +5538,13 @@ fn jitCmd( |
| 5533 | const self_exe_path = process.executablePathAlloc(io, arena) catch |err| | 5538 | const self_exe_path = process.executablePathAlloc(io, arena) catch |err| |
| 5534 | fatal("unable to find self exe path: {t}", .{err}); | 5539 | fatal("unable to find self exe path: {t}", .{err}); |
| 5535 | 5540 | ||
| 5536 | const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet()) | 5541 | const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) |
| 5537 | .Debug | 5542 | .Debug |
| 5538 | else | 5543 | else |
| 5539 | .ReleaseFast; | 5544 | .ReleaseFast; |
| 5540 | const strip = optimize_mode != .Debug; | 5545 | const strip = optimize_mode != .Debug; |
| 5541 | const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); | 5546 | const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map); |
| 5542 | const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); | 5547 | const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); |
| 5543 | 5548 | ||
| 5544 | // This `init` calls `fatal` on error. | 5549 | // This `init` calls `fatal` on error. |
| 5545 | var dirs: Compilation.Directories = .init( | 5550 | var dirs: Compilation.Directories = .init( |
| ... | @@ -5550,6 +5555,7 @@ fn jitCmd( | ... | @@ -5550,6 +5555,7 @@ fn jitCmd( |
| 5550 | .global, | 5555 | .global, |
| 5551 | if (native_os == .wasi) wasi_preopens, | 5556 | if (native_os == .wasi) wasi_preopens, |
| 5552 | self_exe_path, | 5557 | self_exe_path, |
| 5558 | environ_map, | ||
| 5553 | ); | 5559 | ); |
| 5554 | defer dirs.deinit(io); | 5560 | defer dirs.deinit(io); |
| 5555 | 5561 | ||
| ... | @@ -5623,6 +5629,7 @@ fn jitCmd( | ... | @@ -5623,6 +5629,7 @@ fn jitCmd( |
| 5623 | .self_exe_path = self_exe_path, | 5629 | .self_exe_path = self_exe_path, |
| 5624 | .thread_limit = thread_limit, | 5630 | .thread_limit = thread_limit, |
| 5625 | .cache_mode = .whole, | 5631 | .cache_mode = .whole, |
| 5632 | .environ_map = environ_map, | ||
| 5626 | }) catch |err| switch (err) { | 5633 | }) catch |err| switch (err) { |
| 5627 | error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), | 5634 | error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), |
| 5628 | else => fatal("failed to create compilation: {s}", .{@errorName(err)}), | 5635 | else => fatal("failed to create compilation: {s}", .{@errorName(err)}), |
| ... | @@ -5668,32 +5675,34 @@ fn jitCmd( | ... | @@ -5668,32 +5675,34 @@ fn jitCmd( |
| 5668 | 5675 | ||
| 5669 | child_argv.appendSliceAssumeCapacity(args); | 5676 | child_argv.appendSliceAssumeCapacity(args); |
| 5670 | 5677 | ||
| 5671 | if (process.can_execv and options.capture == null) { | 5678 | if (process.can_replace and options.capture == null) { |
| 5672 | if (EnvVar.ZIG_DEBUG_CMD.isSet()) { | 5679 | if (EnvVar.ZIG_DEBUG_CMD.isSet(environ_map)) { |
| 5673 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5680 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5674 | std.debug.print("{s}\n", .{cmd}); | 5681 | std.debug.print("{s}\n", .{cmd}); |
| 5675 | } | 5682 | } |
| 5676 | const err = process.execv(gpa, child_argv.items); | 5683 | const err = process.replace(io, .{ .argv = child_argv.items, .environ_map = environ_map }); |
| 5677 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5684 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5678 | fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); | 5685 | fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); |
| 5679 | } | 5686 | } |
| 5680 | 5687 | ||
| 5681 | if (!process.can_spawn) { | 5688 | if (!process.can_spawn) { |
| 5682 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5689 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5683 | fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ | 5690 | fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ |
| 5684 | @tagName(native_os), cmd, | 5691 | native_os, cmd, |
| 5685 | }); | 5692 | }); |
| 5686 | } | 5693 | } |
| 5687 | 5694 | ||
| 5688 | var child = std.process.Child.init(child_argv.items, gpa); | 5695 | switch (t: { |
| 5689 | child.stdin_behavior = .Inherit; | ||
| 5690 | child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe; | ||
| 5691 | child.stderr_behavior = .Inherit; | ||
| 5692 | |||
| 5693 | const term = t: { | ||
| 5694 | _ = try io.lockStderr(&.{}, .no_color); | 5696 | _ = try io.lockStderr(&.{}, .no_color); |
| 5695 | defer io.unlockStderr(); | 5697 | defer io.unlockStderr(); |
| 5696 | try child.spawn(io); | 5698 | |
| 5699 | var child = std.process.spawn(io, .{ | ||
| 5700 | .argv = child_argv.items, | ||
| 5701 | .stdin = .inherit, | ||
| 5702 | .stdout = if (options.capture == null) .inherit else .pipe, | ||
| 5703 | .stderr = .inherit, | ||
| 5704 | }) catch |err| fatal("failed to spawn {s}: {t}", .{ child_argv.items[0], err }); | ||
| 5705 | defer child.kill(io); | ||
| 5697 | 5706 | ||
| 5698 | if (options.capture) |ptr| { | 5707 | if (options.capture) |ptr| { |
| 5699 | var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); | 5708 | var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); |
| ... | @@ -5701,9 +5710,8 @@ fn jitCmd( | ... | @@ -5701,9 +5710,8 @@ fn jitCmd( |
| 5701 | } | 5710 | } |
| 5702 | 5711 | ||
| 5703 | break :t try child.wait(io); | 5712 | break :t try child.wait(io); |
| 5704 | }; | 5713 | }) { |
| 5705 | switch (term) { | 5714 | .exited => |code| { |
| 5706 | .Exited => |code| { | ||
| 5707 | if (code == 0) { | 5715 | if (code == 0) { |
| 5708 | if (options.capture != null) return; | 5716 | if (options.capture != null) return; |
| 5709 | return cleanExit(io); | 5717 | return cleanExit(io); |
| ... | @@ -5711,6 +5719,10 @@ fn jitCmd( | ... | @@ -5711,6 +5719,10 @@ fn jitCmd( |
| 5711 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5719 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5712 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); | 5720 | fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); |
| 5713 | }, | 5721 | }, |
| 5722 | .signal => |sig| { | ||
| 5723 | const cmd = try std.mem.join(arena, " ", child_argv.items); | ||
| 5724 | fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd }); | ||
| 5725 | }, | ||
| 5714 | else => { | 5726 | else => { |
| 5715 | const cmd = try std.mem.join(arena, " ", child_argv.items); | 5727 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5716 | fatal("the following build command crashed:\n{s}", .{cmd}); | 5728 | fatal("the following build command crashed:\n{s}", .{cmd}); |
| ... | @@ -5826,7 +5838,7 @@ pub fn lldMain( | ... | @@ -5826,7 +5838,7 @@ pub fn lldMain( |
| 5826 | return @intFromBool(!ok); | 5838 | return @intFromBool(!ok); |
| 5827 | } | 5839 | } |
| 5828 | 5840 | ||
| 5829 | const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }); | 5841 | const ArgIteratorResponseFile = process.Args.IteratorGeneral(.{ .comments = true, .single_quotes = true }); |
| 5830 | 5842 | ||
| 5831 | /// Initialize the arguments from a Response File. "*.rsp" | 5843 | /// Initialize the arguments from a Response File. "*.rsp" |
| 5832 | fn initArgIteratorResponseFile(allocator: Allocator, io: Io, resp_file_path: []const u8) !ArgIteratorResponseFile { | 5844 | fn initArgIteratorResponseFile(allocator: Allocator, io: Io, resp_file_path: []const u8) !ArgIteratorResponseFile { |
| ... | @@ -6902,14 +6914,15 @@ fn cmdFetch( | ... | @@ -6902,14 +6914,15 @@ fn cmdFetch( |
| 6902 | arena: Allocator, | 6914 | arena: Allocator, |
| 6903 | io: Io, | 6915 | io: Io, |
| 6904 | args: []const []const u8, | 6916 | args: []const []const u8, |
| 6917 | environ_map: *process.Environ.Map, | ||
| 6905 | ) !void { | 6918 | ) !void { |
| 6906 | dev.check(.fetch_command); | 6919 | dev.check(.fetch_command); |
| 6907 | 6920 | ||
| 6908 | const color: Color = .auto; | 6921 | const color: Color = .auto; |
| 6909 | const work_around_btrfs_bug = native_os == .linux and | 6922 | const work_around_btrfs_bug = native_os == .linux and |
| 6910 | EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); | 6923 | EnvVar.ZIG_BTRFS_WORKAROUND.isSet(environ_map); |
| 6911 | var opt_path_or_url: ?[]const u8 = null; | 6924 | var opt_path_or_url: ?[]const u8 = null; |
| 6912 | var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); | 6925 | var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); |
| 6913 | var debug_hash: bool = false; | 6926 | var debug_hash: bool = false; |
| 6914 | var save: union(enum) { | 6927 | var save: union(enum) { |
| 6915 | no, | 6928 | no, |
| ... | @@ -6955,7 +6968,7 @@ fn cmdFetch( | ... | @@ -6955,7 +6968,7 @@ fn cmdFetch( |
| 6955 | var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; | 6968 | var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; |
| 6956 | defer http_client.deinit(); | 6969 | defer http_client.deinit(); |
| 6957 | 6970 | ||
| 6958 | try http_client.initDefaultProxies(arena); | 6971 | try http_client.initDefaultProxies(arena, environ_map); |
| 6959 | 6972 | ||
| 6960 | var root_prog_node = std.Progress.start(io, .{ | 6973 | var root_prog_node = std.Progress.start(io, .{ |
| 6961 | .root_name = "Fetch", | 6974 | .root_name = "Fetch", |
| ... | @@ -6963,7 +6976,7 @@ fn cmdFetch( | ... | @@ -6963,7 +6976,7 @@ fn cmdFetch( |
| 6963 | defer root_prog_node.end(); | 6976 | defer root_prog_node.end(); |
| 6964 | 6977 | ||
| 6965 | var global_cache_directory: Directory = l: { | 6978 | var global_cache_directory: Directory = l: { |
| 6966 | const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); | 6979 | const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena, environ_map); |
| 6967 | break :l .{ | 6980 | break :l .{ |
| 6968 | .handle = try Io.Dir.cwd().createDirPathOpen(io, p, .{}), | 6981 | .handle = try Io.Dir.cwd().createDirPathOpen(io, p, .{}), |
| 6969 | .path = p, | 6982 | .path = p, |
src/print_env.zig+6-4| ... | @@ -19,9 +19,10 @@ pub fn cmdEnv( | ... | @@ -19,9 +19,10 @@ pub fn cmdEnv( |
| 19 | else => void, | 19 | else => void, |
| 20 | }, | 20 | }, |
| 21 | host: *const std.Target, | 21 | host: *const std.Target, |
| 22 | environ_map: *std.process.Environ.Map, | ||
| 22 | ) !void { | 23 | ) !void { |
| 23 | const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); | 24 | const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(environ_map); |
| 24 | const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); | 25 | const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); |
| 25 | 26 | ||
| 26 | const self_exe_path = switch (builtin.target.os.tag) { | 27 | const self_exe_path = switch (builtin.target.os.tag) { |
| 27 | .wasi => args[0], | 28 | .wasi => args[0], |
| ... | @@ -38,6 +39,7 @@ pub fn cmdEnv( | ... | @@ -38,6 +39,7 @@ pub fn cmdEnv( |
| 38 | .global, | 39 | .global, |
| 39 | if (builtin.target.os.tag == .wasi) wasi_preopens, | 40 | if (builtin.target.os.tag == .wasi) wasi_preopens, |
| 40 | if (builtin.target.os.tag != .wasi) self_exe_path, | 41 | if (builtin.target.os.tag != .wasi) self_exe_path, |
| 42 | environ_map, | ||
| 41 | ); | 43 | ); |
| 42 | defer dirs.deinit(io); | 44 | defer dirs.deinit(io); |
| 43 | 45 | ||
| ... | @@ -56,8 +58,8 @@ pub fn cmdEnv( | ... | @@ -56,8 +58,8 @@ pub fn cmdEnv( |
| 56 | try root.field("version", build_options.version, .{}); | 58 | try root.field("version", build_options.version, .{}); |
| 57 | try root.field("target", triple, .{}); | 59 | try root.field("target", triple, .{}); |
| 58 | var env = try root.beginStructField("env", .{}); | 60 | var env = try root.beginStructField("env", .{}); |
| 59 | inline for (@typeInfo(std.zig.EnvVar).@"enum".fields) |field| { | 61 | inline for (@typeInfo(EnvVar).@"enum".fields) |field| { |
| 60 | try env.field(field.name, try @field(std.zig.EnvVar, field.name).get(arena), .{}); | 62 | try env.field(field.name, @field(EnvVar, field.name).get(environ_map), .{}); |
| 61 | } | 63 | } |
| 62 | try env.end(); | 64 | try env.end(); |
| 63 | try root.end(); | 65 | try root.end(); |
test/src/Debugger.zig+1-1| ... | @@ -2306,7 +2306,7 @@ fn addTest( | ... | @@ -2306,7 +2306,7 @@ fn addTest( |
| 2306 | run.addArgs(db_argv2); | 2306 | run.addArgs(db_argv2); |
| 2307 | run.addArtifactArg(exe); | 2307 | run.addArtifactArg(exe); |
| 2308 | for (expected_output) |expected| run.addCheck(.{ .expect_stdout_match = db.b.fmt("{s}\n", .{expected}) }); | 2308 | for (expected_output) |expected| run.addCheck(.{ .expect_stdout_match = db.b.fmt("{s}\n", .{expected}) }); |
| 2309 | run.addCheck(.{ .expect_term = .{ .Exited = success } }); | 2309 | run.addCheck(.{ .expect_term = .{ .exited = success } }); |
| 2310 | run.setStdIn(.{ .bytes = "" }); | 2310 | run.setStdIn(.{ .bytes = "" }); |
| 2311 | db.root_step.dependOn(&run.step); | 2311 | db.root_step.dependOn(&run.step); |
| 2312 | } | 2312 | } |
test/src/StackTrace.zig+3-3| ... | @@ -193,9 +193,9 @@ fn addCaseInstance( | ... | @@ -193,9 +193,9 @@ fn addCaseInstance( |
| 193 | run.removeEnvironmentVariable("CLICOLOR_FORCE"); | 193 | run.removeEnvironmentVariable("CLICOLOR_FORCE"); |
| 194 | run.setEnvironmentVariable("NO_COLOR", "1"); | 194 | run.setEnvironmentVariable("NO_COLOR", "1"); |
| 195 | run.addCheck(.{ .expect_term = term: { | 195 | run.addCheck(.{ .expect_term = term: { |
| 196 | if (!expect_panic) break :term .{ .Exited = 0 }; | 196 | if (!expect_panic) break :term .{ .exited = 0 }; |
| 197 | if (target.result.os.tag == .windows) break :term .{ .Exited = 3 }; | 197 | if (target.result.os.tag == .windows) break :term .{ .exited = 3 }; |
| 198 | break :term .{ .Signal = 6 }; | 198 | break :term .{ .signal = @enumFromInt(6) }; |
| 199 | } }); | 199 | } }); |
| 200 | run.expectStdOutEqual(""); | 200 | run.expectStdOutEqual(""); |
| 201 | 201 |
test/src/convert-stack-trace.zig+4-11| ... | @@ -24,20 +24,13 @@ | ... | @@ -24,20 +24,13 @@ |
| 24 | //! | 24 | //! |
| 25 | //! With these transformations, the test harness can safely do string comparisons. | 25 | //! With these transformations, the test harness can safely do string comparisons. |
| 26 | 26 | ||
| 27 | pub fn main() !void { | 27 | pub fn main(init: std.process.Init) !void { |
| 28 | var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator); | 28 | const arena = init.arena.allocator(); |
| 29 | defer arena_instance.deinit(); | 29 | const io = init.io; |
| 30 | const arena = arena_instance.allocator(); | 30 | const args = try init.minimal.args.toSlice(arena); |
| 31 | 31 | ||
| 32 | const args = try std.process.argsAlloc(arena); | ||
| 33 | if (args.len != 2) std.process.fatal("usage: convert-stack-trace path/to/test/output", .{}); | 32 | if (args.len != 2) std.process.fatal("usage: convert-stack-trace path/to/test/output", .{}); |
| 34 | 33 | ||
| 35 | const gpa = arena; | ||
| 36 | |||
| 37 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 38 | defer threaded.deinit(); | ||
| 39 | const io = threaded.io(); | ||
| 40 | |||
| 41 | var read_buf: [1024]u8 = undefined; | 34 | var read_buf: [1024]u8 = undefined; |
| 42 | var write_buf: [1024]u8 = undefined; | 35 | var write_buf: [1024]u8 = undefined; |
| 43 | 36 |
test/standalone/child_process/child.zig+8-16| ... | @@ -4,31 +4,23 @@ const Io = std.Io; | ... | @@ -4,31 +4,23 @@ const Io = std.Io; |
| 4 | // 42 is expected by parent; other values result in test failure | 4 | // 42 is expected by parent; other values result in test failure |
| 5 | var exit_code: u8 = 42; | 5 | var exit_code: u8 = 42; |
| 6 | 6 | ||
| 7 | pub fn main() !void { | 7 | pub fn main(init: std.process.Init) !void { |
| 8 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 8 | try run(init.arena.allocator(), init.io, init.minimal.args); |
| 9 | const arena = arena_state.allocator(); | ||
| 10 | |||
| 11 | var threaded: std.Io.Threaded = .init(arena, .{}); | ||
| 12 | defer threaded.deinit(); | ||
| 13 | const io = threaded.io(); | ||
| 14 | |||
| 15 | try run(arena, io); | ||
| 16 | arena_state.deinit(); | ||
| 17 | std.process.exit(exit_code); | 9 | std.process.exit(exit_code); |
| 18 | } | 10 | } |
| 19 | 11 | ||
| 20 | fn run(allocator: std.mem.Allocator, io: Io) !void { | 12 | fn run(arena: std.mem.Allocator, io: Io, args: std.process.Args) !void { |
| 21 | var args = try std.process.argsWithAllocator(allocator); | 13 | var it = try args.iterateAllocator(arena); |
| 22 | defer args.deinit(); | 14 | defer it.deinit(); |
| 23 | _ = args.next() orelse unreachable; // skip binary name | 15 | _ = it.next() orelse unreachable; // skip binary name |
| 24 | 16 | ||
| 25 | // test cmd args | 17 | // test cmd args |
| 26 | const hello_arg = "hello arg"; | 18 | const hello_arg = "hello arg"; |
| 27 | const a1 = args.next() orelse unreachable; | 19 | const a1 = it.next() orelse unreachable; |
| 28 | if (!std.mem.eql(u8, a1, hello_arg)) { | 20 | if (!std.mem.eql(u8, a1, hello_arg)) { |
| 29 | testError(io, "first arg: '{s}'; want '{s}'", .{ a1, hello_arg }); | 21 | testError(io, "first arg: '{s}'; want '{s}'", .{ a1, hello_arg }); |
| 30 | } | 22 | } |
| 31 | if (args.next()) |a2| { | 23 | if (it.next()) |a2| { |
| 32 | testError(io, "expected only one arg; got more: {s}", .{a2}); | 24 | testError(io, "expected only one arg; got more: {s}", .{a2}); |
| 33 | } | 25 | } |
| 34 | 26 |
test/standalone/child_process/main.zig+23-12| ... | @@ -1,15 +1,21 @@ | ... | @@ -1,15 +1,21 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Io = std.Io; | 2 | const Io = std.Io; |
| 3 | 3 | ||
| 4 | pub fn main() !void { | 4 | pub fn main(init: std.process.Init.Minimal) !void { |
| 5 | // make sure safety checks are enabled even in release modes | 5 | // make sure safety checks are enabled even in release modes |
| 6 | var gpa_state = std.heap.GeneralPurposeAllocator(.{ .safety = true }){}; | 6 | var gpa_state: std.heap.GeneralPurposeAllocator(.{ .safety = true }) = .{}; |
| 7 | defer if (gpa_state.deinit() != .ok) { | 7 | defer if (gpa_state.deinit() != .ok) { |
| 8 | @panic("found memory leaks"); | 8 | @panic("found memory leaks"); |
| 9 | }; | 9 | }; |
| 10 | const gpa = gpa_state.allocator(); | 10 | const gpa = gpa_state.allocator(); |
| 11 | 11 | ||
| 12 | var it = try std.process.argsWithAllocator(gpa); | 12 | const process_cwd_path = try std.process.getCwdAlloc(gpa); |
| 13 | defer gpa.free(process_cwd_path); | ||
| 14 | |||
| 15 | var environ_map = try init.environ.createMap(gpa); | ||
| 16 | defer environ_map.deinit(); | ||
| 17 | |||
| 18 | var it = try init.args.iterateAllocator(gpa); | ||
| 13 | defer it.deinit(); | 19 | defer it.deinit(); |
| 14 | _ = it.next() orelse unreachable; // skip binary name | 20 | _ = it.next() orelse unreachable; // skip binary name |
| 15 | const child_path, const needs_free = child_path: { | 21 | const child_path, const needs_free = child_path: { |
| ... | @@ -17,19 +23,24 @@ pub fn main() !void { | ... | @@ -17,19 +23,24 @@ pub fn main() !void { |
| 17 | const cwd_path = it.next() orelse break :child_path .{ child_path, false }; | 23 | const cwd_path = it.next() orelse break :child_path .{ child_path, false }; |
| 18 | // If there is a third argument, it is the current CWD somewhere within the cache directory. | 24 | // If there is a third argument, it is the current CWD somewhere within the cache directory. |
| 19 | // In that case, modify the child path in order to test spawning a path with a leading `..` component. | 25 | // In that case, modify the child path in order to test spawning a path with a leading `..` component. |
| 20 | break :child_path .{ try std.fs.path.relative(gpa, cwd_path, child_path), true }; | 26 | break :child_path .{ try std.fs.path.relative(gpa, process_cwd_path, &environ_map, cwd_path, child_path), true }; |
| 21 | }; | 27 | }; |
| 22 | defer if (needs_free) gpa.free(child_path); | 28 | defer if (needs_free) gpa.free(child_path); |
| 23 | 29 | ||
| 24 | var threaded: Io.Threaded = .init(gpa, .{}); | 30 | var threaded: Io.Threaded = .init(gpa, .{ |
| 31 | .argv0 = .init(init.args), | ||
| 32 | .environ = init.environ, | ||
| 33 | }); | ||
| 25 | defer threaded.deinit(); | 34 | defer threaded.deinit(); |
| 26 | const io = threaded.io(); | 35 | const io = threaded.io(); |
| 27 | 36 | ||
| 28 | var child = std.process.Child.init(&.{ child_path, "hello arg" }, gpa); | 37 | var child = try std.process.spawn(io, .{ |
| 29 | child.stdin_behavior = .Pipe; | 38 | .argv = &.{ child_path, "hello arg" }, |
| 30 | child.stdout_behavior = .Pipe; | 39 | .stdin = .pipe, |
| 31 | child.stderr_behavior = .Inherit; | 40 | .stdout = .pipe, |
| 32 | try child.spawn(io); | 41 | .stderr = .inherit, |
| 42 | }); | ||
| 43 | |||
| 33 | const child_stdin = child.stdin.?; | 44 | const child_stdin = child.stdin.?; |
| 34 | try child_stdin.writeStreamingAll(io, "hello from stdin"); // verified in child | 45 | try child_stdin.writeStreamingAll(io, "hello from stdin"); // verified in child |
| 35 | child_stdin.close(io); | 46 | child_stdin.close(io); |
| ... | @@ -44,7 +55,7 @@ pub fn main() !void { | ... | @@ -44,7 +55,7 @@ pub fn main() !void { |
| 44 | } | 55 | } |
| 45 | 56 | ||
| 46 | switch (try child.wait(io)) { | 57 | switch (try child.wait(io)) { |
| 47 | .Exited => |code| { | 58 | .exited => |code| { |
| 48 | const child_ok_code = 42; // set by child if no test errors | 59 | const child_ok_code = 42; // set by child if no test errors |
| 49 | if (code != child_ok_code) { | 60 | if (code != child_ok_code) { |
| 50 | testError(io, "child exit code: {d}; want {d}", .{ code, child_ok_code }); | 61 | testError(io, "child exit code: {d}; want {d}", .{ code, child_ok_code }); |
| ... | @@ -57,7 +68,7 @@ pub fn main() !void { | ... | @@ -57,7 +68,7 @@ pub fn main() !void { |
| 57 | // Check that FileNotFound is consistent across platforms when trying to spawn an executable that doesn't exist | 68 | // Check that FileNotFound is consistent across platforms when trying to spawn an executable that doesn't exist |
| 58 | const missing_child_path = try std.mem.concat(gpa, u8, &.{ child_path, "_intentionally_missing" }); | 69 | const missing_child_path = try std.mem.concat(gpa, u8, &.{ child_path, "_intentionally_missing" }); |
| 59 | defer gpa.free(missing_child_path); | 70 | defer gpa.free(missing_child_path); |
| 60 | try std.testing.expectError(error.FileNotFound, std.process.Child.run(gpa, io, .{ .argv = &.{missing_child_path} })); | 71 | try std.testing.expectError(error.FileNotFound, std.process.run(gpa, io, .{ .argv = &.{missing_child_path} })); |
| 61 | } | 72 | } |
| 62 | 73 | ||
| 63 | var parent_test_error = false; | 74 | var parent_test_error = false; |
test/standalone/cmakedefine/check.zig+4-8| ... | @@ -1,16 +1,12 @@ | ... | @@ -1,16 +1,12 @@ |
| 1 | pub fn main() !void { | 1 | pub fn main(init: std.process.Init) !void { |
| 2 | var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator); | 2 | const arena = init.arena.allocator(); |
| 3 | defer arena_state.deinit(); | 3 | const io = init.io; |
| 4 | const arena = arena_state.allocator(); | 4 | const args = try init.minimal.args.toSlice(arena); |
| 5 | |||
| 6 | const args = try std.process.argsAlloc(arena); | ||
| 7 | 5 | ||
| 8 | if (args.len != 3) return error.BadUsage; | 6 | if (args.len != 3) return error.BadUsage; |
| 9 | const actual_path = args[1]; | 7 | const actual_path = args[1]; |
| 10 | const expected_path = args[2]; | 8 | const expected_path = args[2]; |
| 11 | 9 | ||
| 12 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | ||
| 13 | |||
| 14 | const actual = try std.Io.Dir.cwd().readFileAlloc(io, actual_path, arena, .limited(1024 * 1024)); | 10 | const actual = try std.Io.Dir.cwd().readFileAlloc(io, actual_path, arena, .limited(1024 * 1024)); |
| 15 | const expected = try std.Io.Dir.cwd().readFileAlloc(io, expected_path, arena, .limited(1024 * 1024)); | 11 | const expected = try std.Io.Dir.cwd().readFileAlloc(io, expected_path, arena, .limited(1024 * 1024)); |
| 16 | 12 |
test/standalone/coff_dwarf/main.zig+3-8| ... | @@ -3,18 +3,13 @@ const fatal = std.process.fatal; | ... | @@ -3,18 +3,13 @@ const fatal = std.process.fatal; |
| 3 | 3 | ||
| 4 | extern fn add(a: u32, b: u32, addr: *usize) u32; | 4 | extern fn add(a: u32, b: u32, addr: *usize) u32; |
| 5 | 5 | ||
| 6 | pub fn main() void { | 6 | pub fn main(init: std.process.Init) void { |
| 7 | var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init; | 7 | const gpa = init.gpa; |
| 8 | defer std.debug.assert(debug_alloc_inst.deinit() == .ok); | 8 | const io = init.io; |
| 9 | const gpa = debug_alloc_inst.allocator(); | ||
| 10 | 9 | ||
| 11 | var di: std.debug.SelfInfo = .init; | 10 | var di: std.debug.SelfInfo = .init; |
| 12 | defer di.deinit(gpa); | 11 | defer di.deinit(gpa); |
| 13 | 12 | ||
| 14 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 15 | defer threaded.deinit(); | ||
| 16 | const io = threaded.io(); | ||
| 17 | |||
| 18 | var add_addr: usize = undefined; | 13 | var add_addr: usize = undefined; |
| 19 | _ = add(1, 2, &add_addr); | 14 | _ = add(1, 2, &add_addr); |
| 20 | 15 |
test/standalone/dirname/exists_in.zig+2-10| ... | @@ -11,16 +11,8 @@ | ... | @@ -11,16 +11,8 @@ |
| 11 | 11 | ||
| 12 | const std = @import("std"); | 12 | const std = @import("std"); |
| 13 | 13 | ||
| 14 | pub fn main() !void { | 14 | pub fn main(init: std.process.Init) !void { |
| 15 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 15 | var args = try init.minimal.args.iterateAllocator(init.gpa); |
| 16 | const arena = arena_state.allocator(); | ||
| 17 | defer arena_state.deinit(); | ||
| 18 | |||
| 19 | try run(arena); | ||
| 20 | } | ||
| 21 | |||
| 22 | fn run(allocator: std.mem.Allocator) !void { | ||
| 23 | var args = try std.process.argsWithAllocator(allocator); | ||
| 24 | defer args.deinit(); | 16 | defer args.deinit(); |
| 25 | _ = args.next() orelse unreachable; // skip binary name | 17 | _ = args.next() orelse unreachable; // skip binary name |
| 26 | 18 |
test/standalone/dirname/has_basename.zig+2-10| ... | @@ -13,16 +13,8 @@ | ... | @@ -13,16 +13,8 @@ |
| 13 | 13 | ||
| 14 | const std = @import("std"); | 14 | const std = @import("std"); |
| 15 | 15 | ||
| 16 | pub fn main() !void { | 16 | pub fn main(init: std.process.Init) !void { |
| 17 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 17 | var args = try init.minimal.args.iterateAllocator(init.gpa); |
| 18 | const arena = arena_state.allocator(); | ||
| 19 | defer arena_state.deinit(); | ||
| 20 | |||
| 21 | try run(arena); | ||
| 22 | } | ||
| 23 | |||
| 24 | fn run(allocator: std.mem.Allocator) !void { | ||
| 25 | var args = try std.process.argsWithAllocator(allocator); | ||
| 26 | defer args.deinit(); | 18 | defer args.deinit(); |
| 27 | _ = args.next() orelse unreachable; // skip binary name | 19 | _ = args.next() orelse unreachable; // skip binary name |
| 28 | 20 |
test/standalone/dirname/touch.zig+2-10| ... | @@ -8,16 +8,8 @@ | ... | @@ -8,16 +8,8 @@ |
| 8 | 8 | ||
| 9 | const std = @import("std"); | 9 | const std = @import("std"); |
| 10 | 10 | ||
| 11 | pub fn main() !void { | 11 | pub fn main(init: std.process.Init) !void { |
| 12 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 12 | var args = try init.minimal.args.iterateAllocator(init.gpa); |
| 13 | const arena = arena_state.allocator(); | ||
| 14 | defer arena_state.deinit(); | ||
| 15 | |||
| 16 | try run(arena); | ||
| 17 | } | ||
| 18 | |||
| 19 | fn run(allocator: std.mem.Allocator) !void { | ||
| 20 | var args = try std.process.argsWithAllocator(allocator); | ||
| 21 | defer args.deinit(); | 13 | defer args.deinit(); |
| 22 | _ = args.next() orelse unreachable; // skip binary name | 14 | _ = args.next() orelse unreachable; // skip binary name |
| 23 | 15 |
test/standalone/empty_env/build.zig+1-1| ... | @@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void { |
| 7 | 7 | ||
| 8 | const optimize: std.builtin.OptimizeMode = .Debug; | 8 | const optimize: std.builtin.OptimizeMode = .Debug; |
| 9 | 9 | ||
| 10 | if (builtin.os.tag == .windows and std.process.hasEnvVarConstant("ConEmuHWND")) { | 10 | if (builtin.os.tag == .windows and b.graph.environ_map.contains("ConEmuHWND")) { |
| 11 | // ConEmu injects environment variables into processes before they are executed | 11 | // ConEmu injects environment variables into processes before they are executed |
| 12 | // depending on user settings. This obviously invalidates the test, so skipping | 12 | // depending on user settings. This obviously invalidates the test, so skipping |
| 13 | // it is the best option. | 13 | // it is the best option. |
test/standalone/empty_env/main.zig+2-5| ... | @@ -1,8 +1,5 @@ | ... | @@ -1,8 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 4 | try std.testing.expectEqual(0, init.environ_map.count()); |
| 5 | defer _ = gpa.deinit(); | ||
| 6 | const env_map = std.process.getEnvMap(gpa.allocator()) catch @panic("unable to get env map"); | ||
| 7 | try std.testing.expect(env_map.count() == 0); | ||
| 8 | } | 5 | } |
test/standalone/entry_point/check_differ.zig+6-8| ... | @@ -1,12 +1,11 @@ | ... | @@ -1,12 +1,11 @@ |
| 1 | pub fn main() !void { | 1 | const std = @import("std"); |
| 2 | var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator); | ||
| 3 | defer arena_state.deinit(); | ||
| 4 | const arena = arena_state.allocator(); | ||
| 5 | 2 | ||
| 6 | const args = try std.process.argsAlloc(arena); | 3 | pub fn main(init: std.process.Init) !void { |
| 7 | if (args.len != 3) return error.BadUsage; // usage: 'check_differ <path a> <path b>' | 4 | const arena = init.arena.allocator(); |
| 5 | const io = init.io; | ||
| 6 | const args = try init.minimal.args.toSlice(arena); | ||
| 8 | 7 | ||
| 9 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | 8 | if (args.len != 3) return error.BadUsage; // usage: 'check_differ <path a> <path b>' |
| 10 | 9 | ||
| 11 | const contents_1 = try std.Io.Dir.cwd().readFileAlloc(io, args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty | 10 | const contents_1 = try std.Io.Dir.cwd().readFileAlloc(io, args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty |
| 12 | const contents_2 = try std.Io.Dir.cwd().readFileAlloc(io, args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty | 11 | 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 { | ... | @@ -16,4 +15,3 @@ pub fn main() !void { |
| 16 | } | 15 | } |
| 17 | // success, files differ | 16 | // success, files differ |
| 18 | } | 17 | } |
| 19 | const std = @import("std"); |
test/standalone/env_vars/main.zig+89-113| ... | @@ -2,174 +2,150 @@ const std = @import("std"); | ... | @@ -2,174 +2,150 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | // Note: the environment variables under test are set by the build.zig | 4 | // Note: the environment variables under test are set by the build.zig |
| 5 | pub fn main() !void { | 5 | pub fn main(init: std.process.Init) !void { |
| 6 | @setEvalBranchQuota(10000); | 6 | @setEvalBranchQuota(10000); |
| 7 | 7 | ||
| 8 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 8 | const allocator = init.gpa; |
| 9 | defer _ = gpa.deinit(); | 9 | const arena = init.arena.allocator(); |
| 10 | const allocator = gpa.allocator(); | 10 | const environ = init.minimal.environ; |
| 11 | 11 | ||
| 12 | var arena_state = std.heap.ArenaAllocator.init(allocator); | 12 | // containsUnempty |
| 13 | defer arena_state.deinit(); | ||
| 14 | const arena = arena_state.allocator(); | ||
| 15 | |||
| 16 | // hasNonEmptyEnvVar | ||
| 17 | { | 13 | { |
| 18 | try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "FOO")); | 14 | try std.testing.expect(try environ.containsUnempty(allocator, "FOO")); |
| 19 | try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "FOO="))); | 15 | try std.testing.expect(!(try environ.containsUnempty(allocator, "FOO="))); |
| 20 | try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "FO"))); | 16 | try std.testing.expect(!(try environ.containsUnempty(allocator, "FO"))); |
| 21 | try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "FOOO"))); | 17 | try std.testing.expect(!(try environ.containsUnempty(allocator, "FOOO"))); |
| 22 | if (builtin.os.tag == .windows) { | 18 | if (builtin.os.tag == .windows) { |
| 23 | try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "foo")); | 19 | try std.testing.expect(try environ.containsUnempty(allocator, "foo")); |
| 24 | } | 20 | } |
| 25 | try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "EQUALS")); | 21 | try std.testing.expect(try environ.containsUnempty(allocator, "EQUALS")); |
| 26 | try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "EQUALS=ABC"))); | 22 | try std.testing.expect(!(try environ.containsUnempty(allocator, "EQUALS=ABC"))); |
| 27 | try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "КИРиллИЦА")); | 23 | try std.testing.expect(try environ.containsUnempty(allocator, "КИРиллИЦА")); |
| 28 | if (builtin.os.tag == .windows) { | 24 | if (builtin.os.tag == .windows) { |
| 29 | try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "кирИЛЛица")); | 25 | try std.testing.expect(try environ.containsUnempty(allocator, "кирИЛЛица")); |
| 30 | } | 26 | } |
| 31 | try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "NO_VALUE"))); | 27 | try std.testing.expect(!(try environ.containsUnempty(allocator, "NO_VALUE"))); |
| 32 | try std.testing.expect(!(try std.process.hasNonEmptyEnvVar(allocator, "NOT_SET"))); | 28 | try std.testing.expect(!(try environ.containsUnempty(allocator, "NOT_SET"))); |
| 33 | if (builtin.os.tag == .windows) { | 29 | if (builtin.os.tag == .windows) { |
| 34 | try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "=HIDDEN")); | 30 | try std.testing.expect(try environ.containsUnempty(allocator, "=HIDDEN")); |
| 35 | try std.testing.expect(try std.process.hasNonEmptyEnvVar(allocator, "INVALID_UTF16_\xed\xa0\x80")); | 31 | try std.testing.expect(try environ.containsUnempty(allocator, "INVALID_UTF16_\xed\xa0\x80")); |
| 36 | } | 32 | } |
| 37 | } | 33 | } |
| 38 | 34 | ||
| 39 | // hasNonEmptyEnvVarContstant | 35 | // containsUnemptyConstant |
| 40 | { | 36 | { |
| 41 | try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("FOO")); | 37 | try std.testing.expect(environ.containsUnemptyConstant("FOO")); |
| 42 | try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("FOO=")); | 38 | try std.testing.expect(!environ.containsUnemptyConstant("FOO=")); |
| 43 | try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("FO")); | 39 | try std.testing.expect(!environ.containsUnemptyConstant("FO")); |
| 44 | try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("FOOO")); | 40 | try std.testing.expect(!environ.containsUnemptyConstant("FOOO")); |
| 45 | if (builtin.os.tag == .windows) { | 41 | if (builtin.os.tag == .windows) { |
| 46 | try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("foo")); | 42 | try std.testing.expect(environ.containsUnemptyConstant("foo")); |
| 47 | } | 43 | } |
| 48 | try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("EQUALS")); | 44 | try std.testing.expect(environ.containsUnemptyConstant("EQUALS")); |
| 49 | try std.testing.expect(!std.process.hasNonEmptyEnvVarConstant("EQUALS=ABC")); | 45 | try std.testing.expect(!environ.containsUnemptyConstant("EQUALS=ABC")); |
| 50 | try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("КИРиллИЦА")); | 46 | try std.testing.expect(environ.containsUnemptyConstant("КИРиллИЦА")); |
| 51 | if (builtin.os.tag == .windows) { | 47 | if (builtin.os.tag == .windows) { |
| 52 | try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("кирИЛЛица")); | 48 | try std.testing.expect(environ.containsUnemptyConstant("кирИЛЛица")); |
| 53 | } | 49 | } |
| 54 | try std.testing.expect(!(std.process.hasNonEmptyEnvVarConstant("NO_VALUE"))); | 50 | try std.testing.expect(!(environ.containsUnemptyConstant("NO_VALUE"))); |
| 55 | try std.testing.expect(!(std.process.hasNonEmptyEnvVarConstant("NOT_SET"))); | 51 | try std.testing.expect(!(environ.containsUnemptyConstant("NOT_SET"))); |
| 56 | if (builtin.os.tag == .windows) { | 52 | if (builtin.os.tag == .windows) { |
| 57 | try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("=HIDDEN")); | 53 | try std.testing.expect(environ.containsUnemptyConstant("=HIDDEN")); |
| 58 | try std.testing.expect(std.process.hasNonEmptyEnvVarConstant("INVALID_UTF16_\xed\xa0\x80")); | 54 | try std.testing.expect(environ.containsUnemptyConstant("INVALID_UTF16_\xed\xa0\x80")); |
| 59 | } | 55 | } |
| 60 | } | 56 | } |
| 61 | 57 | ||
| 62 | // hasEnvVar | 58 | // contains |
| 63 | { | 59 | { |
| 64 | try std.testing.expect(try std.process.hasEnvVar(allocator, "FOO")); | 60 | try std.testing.expect(try environ.contains(allocator, "FOO")); |
| 65 | try std.testing.expect(!(try std.process.hasEnvVar(allocator, "FOO="))); | 61 | try std.testing.expect(!(try environ.contains(allocator, "FOO="))); |
| 66 | try std.testing.expect(!(try std.process.hasEnvVar(allocator, "FO"))); | 62 | try std.testing.expect(!(try environ.contains(allocator, "FO"))); |
| 67 | try std.testing.expect(!(try std.process.hasEnvVar(allocator, "FOOO"))); | 63 | try std.testing.expect(!(try environ.contains(allocator, "FOOO"))); |
| 68 | if (builtin.os.tag == .windows) { | 64 | if (builtin.os.tag == .windows) { |
| 69 | try std.testing.expect(try std.process.hasEnvVar(allocator, "foo")); | 65 | try std.testing.expect(try environ.contains(allocator, "foo")); |
| 70 | } | 66 | } |
| 71 | try std.testing.expect(try std.process.hasEnvVar(allocator, "EQUALS")); | 67 | try std.testing.expect(try environ.contains(allocator, "EQUALS")); |
| 72 | try std.testing.expect(!(try std.process.hasEnvVar(allocator, "EQUALS=ABC"))); | 68 | try std.testing.expect(!(try environ.contains(allocator, "EQUALS=ABC"))); |
| 73 | try std.testing.expect(try std.process.hasEnvVar(allocator, "КИРиллИЦА")); | 69 | try std.testing.expect(try environ.contains(allocator, "КИРиллИЦА")); |
| 74 | if (builtin.os.tag == .windows) { | 70 | if (builtin.os.tag == .windows) { |
| 75 | try std.testing.expect(try std.process.hasEnvVar(allocator, "кирИЛЛица")); | 71 | try std.testing.expect(try environ.contains(allocator, "кирИЛЛица")); |
| 76 | } | 72 | } |
| 77 | try std.testing.expect(try std.process.hasEnvVar(allocator, "NO_VALUE")); | 73 | try std.testing.expect(try environ.contains(allocator, "NO_VALUE")); |
| 78 | try std.testing.expect(!(try std.process.hasEnvVar(allocator, "NOT_SET"))); | 74 | try std.testing.expect(!(try environ.contains(allocator, "NOT_SET"))); |
| 79 | if (builtin.os.tag == .windows) { | 75 | if (builtin.os.tag == .windows) { |
| 80 | try std.testing.expect(try std.process.hasEnvVar(allocator, "=HIDDEN")); | 76 | try std.testing.expect(try environ.contains(allocator, "=HIDDEN")); |
| 81 | try std.testing.expect(try std.process.hasEnvVar(allocator, "INVALID_UTF16_\xed\xa0\x80")); | 77 | try std.testing.expect(try environ.contains(allocator, "INVALID_UTF16_\xed\xa0\x80")); |
| 82 | } | 78 | } |
| 83 | } | 79 | } |
| 84 | 80 | ||
| 85 | // hasEnvVarConstant | 81 | // containsConstant |
| 86 | { | 82 | { |
| 87 | try std.testing.expect(std.process.hasEnvVarConstant("FOO")); | 83 | try std.testing.expect(environ.containsConstant("FOO")); |
| 88 | try std.testing.expect(!std.process.hasEnvVarConstant("FOO=")); | 84 | try std.testing.expect(!environ.containsConstant("FOO=")); |
| 89 | try std.testing.expect(!std.process.hasEnvVarConstant("FO")); | 85 | try std.testing.expect(!environ.containsConstant("FO")); |
| 90 | try std.testing.expect(!std.process.hasEnvVarConstant("FOOO")); | 86 | try std.testing.expect(!environ.containsConstant("FOOO")); |
| 91 | if (builtin.os.tag == .windows) { | 87 | if (builtin.os.tag == .windows) { |
| 92 | try std.testing.expect(std.process.hasEnvVarConstant("foo")); | 88 | try std.testing.expect(environ.containsConstant("foo")); |
| 93 | } | 89 | } |
| 94 | try std.testing.expect(std.process.hasEnvVarConstant("EQUALS")); | 90 | try std.testing.expect(environ.containsConstant("EQUALS")); |
| 95 | try std.testing.expect(!std.process.hasEnvVarConstant("EQUALS=ABC")); | 91 | try std.testing.expect(!environ.containsConstant("EQUALS=ABC")); |
| 96 | try std.testing.expect(std.process.hasEnvVarConstant("КИРиллИЦА")); | 92 | try std.testing.expect(environ.containsConstant("КИРиллИЦА")); |
| 97 | if (builtin.os.tag == .windows) { | 93 | if (builtin.os.tag == .windows) { |
| 98 | try std.testing.expect(std.process.hasEnvVarConstant("кирИЛЛица")); | 94 | try std.testing.expect(environ.containsConstant("кирИЛЛица")); |
| 99 | } | 95 | } |
| 100 | try std.testing.expect(std.process.hasEnvVarConstant("NO_VALUE")); | 96 | try std.testing.expect(environ.containsConstant("NO_VALUE")); |
| 101 | try std.testing.expect(!(std.process.hasEnvVarConstant("NOT_SET"))); | 97 | try std.testing.expect(!(environ.containsConstant("NOT_SET"))); |
| 102 | if (builtin.os.tag == .windows) { | 98 | if (builtin.os.tag == .windows) { |
| 103 | try std.testing.expect(std.process.hasEnvVarConstant("=HIDDEN")); | 99 | try std.testing.expect(environ.containsConstant("=HIDDEN")); |
| 104 | try std.testing.expect(std.process.hasEnvVarConstant("INVALID_UTF16_\xed\xa0\x80")); | 100 | try std.testing.expect(environ.containsConstant("INVALID_UTF16_\xed\xa0\x80")); |
| 105 | } | 101 | } |
| 106 | } | 102 | } |
| 107 | 103 | ||
| 108 | // getEnvVarOwned | 104 | // getAlloc |
| 109 | { | 105 | { |
| 110 | try std.testing.expectEqualSlices(u8, "123", try std.process.getEnvVarOwned(arena, "FOO")); | 106 | try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "FOO")); |
| 111 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "FOO=")); | 107 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FOO=")); |
| 112 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "FO")); | 108 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FO")); |
| 113 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "FOOO")); | 109 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FOOO")); |
| 114 | if (builtin.os.tag == .windows) { | ||
| 115 | try std.testing.expectEqualSlices(u8, "123", try std.process.getEnvVarOwned(arena, "foo")); | ||
| 116 | } | ||
| 117 | try std.testing.expectEqualSlices(u8, "ABC=123", try std.process.getEnvVarOwned(arena, "EQUALS")); | ||
| 118 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "EQUALS=ABC")); | ||
| 119 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try std.process.getEnvVarOwned(arena, "КИРиллИЦА")); | ||
| 120 | if (builtin.os.tag == .windows) { | ||
| 121 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try std.process.getEnvVarOwned(arena, "кирИЛЛица")); | ||
| 122 | } | ||
| 123 | try std.testing.expectEqualSlices(u8, "", try std.process.getEnvVarOwned(arena, "NO_VALUE")); | ||
| 124 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.getEnvVarOwned(arena, "NOT_SET")); | ||
| 125 | if (builtin.os.tag == .windows) { | 110 | if (builtin.os.tag == .windows) { |
| 126 | try std.testing.expectEqualSlices(u8, "hi", try std.process.getEnvVarOwned(arena, "=HIDDEN")); | 111 | try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "foo")); |
| 127 | try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", try std.process.getEnvVarOwned(arena, "INVALID_UTF16_\xed\xa0\x80")); | ||
| 128 | } | 112 | } |
| 129 | } | 113 | try std.testing.expectEqualSlices(u8, "ABC=123", try environ.getAlloc(arena, "EQUALS")); |
| 130 | 114 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "EQUALS=ABC")); | |
| 131 | // parseEnvVarInt | 115 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "КИРиллИЦА")); |
| 132 | { | ||
| 133 | try std.testing.expectEqual(123, try std.process.parseEnvVarInt("FOO", u32, 10)); | ||
| 134 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("FO", u32, 10)); | ||
| 135 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("FOOO", u32, 10)); | ||
| 136 | try std.testing.expectEqual(0x123, try std.process.parseEnvVarInt("FOO", u32, 16)); | ||
| 137 | if (builtin.os.tag == .windows) { | 116 | if (builtin.os.tag == .windows) { |
| 138 | try std.testing.expectEqual(123, try std.process.parseEnvVarInt("foo", u32, 10)); | 117 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "кирИЛЛица")); |
| 139 | } | 118 | } |
| 140 | try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("EQUALS", u32, 10)); | 119 | try std.testing.expectEqualSlices(u8, "", try environ.getAlloc(arena, "NO_VALUE")); |
| 141 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("EQUALS=ABC", u32, 10)); | 120 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "NOT_SET")); |
| 142 | try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("КИРиллИЦА", u32, 10)); | ||
| 143 | try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("NO_VALUE", u32, 10)); | ||
| 144 | try std.testing.expectError(error.EnvironmentVariableNotFound, std.process.parseEnvVarInt("NOT_SET", u32, 10)); | ||
| 145 | if (builtin.os.tag == .windows) { | 121 | if (builtin.os.tag == .windows) { |
| 146 | try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("=HIDDEN", u32, 10)); | 122 | try std.testing.expectEqualSlices(u8, "hi", try environ.getAlloc(arena, "=HIDDEN")); |
| 147 | try std.testing.expectError(error.InvalidCharacter, std.process.parseEnvVarInt("INVALID_UTF16_\xed\xa0\x80", u32, 10)); | 123 | try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", try environ.getAlloc(arena, "INVALID_UTF16_\xed\xa0\x80")); |
| 148 | } | 124 | } |
| 149 | } | 125 | } |
| 150 | 126 | ||
| 151 | // EnvMap | 127 | // Environ.Map |
| 152 | { | 128 | { |
| 153 | var env_map = try std.process.getEnvMap(allocator); | 129 | var environ_map = try environ.createMap(allocator); |
| 154 | defer env_map.deinit(); | 130 | defer environ_map.deinit(); |
| 155 | 131 | ||
| 156 | try std.testing.expectEqualSlices(u8, "123", env_map.get("FOO").?); | 132 | try std.testing.expectEqualSlices(u8, "123", environ_map.get("FOO").?); |
| 157 | try std.testing.expectEqual(null, env_map.get("FO")); | 133 | try std.testing.expectEqual(null, environ_map.get("FO")); |
| 158 | try std.testing.expectEqual(null, env_map.get("FOOO")); | 134 | try std.testing.expectEqual(null, environ_map.get("FOOO")); |
| 159 | if (builtin.os.tag == .windows) { | 135 | if (builtin.os.tag == .windows) { |
| 160 | try std.testing.expectEqualSlices(u8, "123", env_map.get("foo").?); | 136 | try std.testing.expectEqualSlices(u8, "123", environ_map.get("foo").?); |
| 161 | } | 137 | } |
| 162 | try std.testing.expectEqualSlices(u8, "ABC=123", env_map.get("EQUALS").?); | 138 | try std.testing.expectEqualSlices(u8, "ABC=123", environ_map.get("EQUALS").?); |
| 163 | try std.testing.expectEqual(null, env_map.get("EQUALS=ABC")); | 139 | try std.testing.expectEqual(null, environ_map.get("EQUALS=ABC")); |
| 164 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", env_map.get("КИРиллИЦА").?); | 140 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("КИРиллИЦА").?); |
| 165 | if (builtin.os.tag == .windows) { | 141 | if (builtin.os.tag == .windows) { |
| 166 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", env_map.get("кирИЛЛица").?); | 142 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("кирИЛЛица").?); |
| 167 | } | 143 | } |
| 168 | try std.testing.expectEqualSlices(u8, "", env_map.get("NO_VALUE").?); | 144 | try std.testing.expectEqualSlices(u8, "", environ_map.get("NO_VALUE").?); |
| 169 | try std.testing.expectEqual(null, env_map.get("NOT_SET")); | 145 | try std.testing.expectEqual(null, environ_map.get("NOT_SET")); |
| 170 | if (builtin.os.tag == .windows) { | 146 | if (builtin.os.tag == .windows) { |
| 171 | try std.testing.expectEqualSlices(u8, "hi", env_map.get("=HIDDEN").?); | 147 | try std.testing.expectEqualSlices(u8, "hi", environ_map.get("=HIDDEN").?); |
| 172 | try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", env_map.get("INVALID_UTF16_\xed\xa0\x80").?); | 148 | try std.testing.expectEqualSlices(u8, "\xed\xa0\x80", environ_map.get("INVALID_UTF16_\xed\xa0\x80").?); |
| 173 | } | 149 | } |
| 174 | } | 150 | } |
| 175 | } | 151 | } |
test/standalone/install_headers/check_exists.zig+4-8| ... | @@ -2,17 +2,13 @@ const std = @import("std"); | ... | @@ -2,17 +2,13 @@ const std = @import("std"); |
| 2 | 2 | ||
| 3 | /// Checks the existence of files relative to cwd. | 3 | /// Checks the existence of files relative to cwd. |
| 4 | /// A path starting with ! should not exist. | 4 | /// A path starting with ! should not exist. |
| 5 | pub fn main() !void { | 5 | pub fn main(init: std.process.Init) !void { |
| 6 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 6 | const arena = init.arena.allocator(); |
| 7 | defer arena_state.deinit(); | 7 | const io = init.io; |
| 8 | 8 | ||
| 9 | const arena = arena_state.allocator(); | 9 | var arg_it = try init.minimal.args.iterateAllocator(arena); |
| 10 | |||
| 11 | var arg_it = try std.process.argsWithAllocator(arena); | ||
| 12 | _ = arg_it.next(); | 10 | _ = arg_it.next(); |
| 13 | 11 | ||
| 14 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | ||
| 15 | |||
| 16 | const cwd = std.Io.Dir.cwd(); | 12 | const cwd = std.Io.Dir.cwd(); |
| 17 | const cwd_realpath = try cwd.realPathFileAlloc(io, ".", arena); | 13 | const cwd_realpath = try cwd.realPathFileAlloc(io, ".", arena); |
| 18 | 14 |
test/standalone/libfuzzer/main.zig+4-9| ... | @@ -6,19 +6,14 @@ fn testOne(in: abi.Slice) callconv(.c) void { | ... | @@ -6,19 +6,14 @@ fn testOne(in: abi.Slice) callconv(.c) void { |
| 6 | std.debug.assertReadable(in.toSlice()); | 6 | std.debug.assertReadable(in.toSlice()); |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | pub fn main() !void { | 9 | pub fn main(init: std.process.Init) !void { |
| 10 | var debug_gpa_ctx: std.heap.DebugAllocator(.{}) = .init; | 10 | const gpa = init.gpa; |
| 11 | defer _ = debug_gpa_ctx.deinit(); | 11 | const io = init.io; |
| 12 | const gpa = debug_gpa_ctx.allocator(); | ||
| 13 | 12 | ||
| 14 | var args = try std.process.argsWithAllocator(gpa); | 13 | var args = try init.minimal.args.iterateAllocator(gpa); |
| 15 | defer args.deinit(); | 14 | defer args.deinit(); |
| 16 | _ = args.skip(); // executable name | 15 | _ = args.skip(); // executable name |
| 17 | 16 | ||
| 18 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 19 | defer threaded.deinit(); | ||
| 20 | const io = threaded.io(); | ||
| 21 | |||
| 22 | const cache_dir_path = args.next() orelse @panic("expected cache directory path argument"); | 17 | const cache_dir_path = args.next() orelse @panic("expected cache directory path argument"); |
| 23 | var cache_dir = try std.Io.Dir.cwd().openDir(io, cache_dir_path, .{}); | 18 | var cache_dir = try std.Io.Dir.cwd().openDir(io, cache_dir_path, .{}); |
| 24 | defer cache_dir.close(io); | 19 | defer cache_dir.close(io); |
test/standalone/load_dynamic_library/main.zig+2-5| ... | @@ -1,10 +1,7 @@ | ... | @@ -1,10 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 4 | const args = try init.minimal.args.toSlice(init.arena.allocator()); |
| 5 | defer _ = gpa.deinit(); | ||
| 6 | const args = try std.process.argsAlloc(gpa.allocator()); | ||
| 7 | defer std.process.argsFree(gpa.allocator(), args); | ||
| 8 | 5 | ||
| 9 | const dynlib_name = args[1]; | 6 | const dynlib_name = args[1]; |
| 10 | 7 |
test/standalone/posix/cwd.zig+5-13| ... | @@ -7,24 +7,16 @@ const assert = std.debug.assert; | ... | @@ -7,24 +7,16 @@ const assert = std.debug.assert; |
| 7 | 7 | ||
| 8 | const path_max = std.fs.max_path_bytes; | 8 | const path_max = std.fs.max_path_bytes; |
| 9 | 9 | ||
| 10 | pub fn main() !void { | 10 | pub fn main(init: std.process.Init) !void { |
| 11 | switch (builtin.target.os.tag) { | 11 | switch (builtin.target.os.tag) { |
| 12 | .wasi => return, // WASI doesn't support changing the working directory at all. | 12 | .wasi => return, // WASI doesn't support changing the working directory at all. |
| 13 | .windows => return, // POSIX is not implemented by Windows | 13 | .windows => return, // POSIX is not implemented by Windows |
| 14 | else => {}, | 14 | else => {}, |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | var debug_allocator: std.heap.DebugAllocator(.{}) = .{}; | ||
| 18 | defer assert(debug_allocator.deinit() == .ok); | ||
| 19 | const gpa = debug_allocator.allocator(); | ||
| 20 | |||
| 21 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 22 | defer threaded.deinit(); | ||
| 23 | const io = threaded.io(); | ||
| 24 | |||
| 25 | try test_chdir_self(); | 17 | try test_chdir_self(); |
| 26 | try test_chdir_absolute(); | 18 | try test_chdir_absolute(); |
| 27 | try test_chdir_relative(gpa, io); | 19 | try test_chdir_relative(init.gpa, init.io); |
| 28 | } | 20 | } |
| 29 | 21 | ||
| 30 | // get current working directory and expect it to match given path | 22 | // get current working directory and expect it to match given path |
| ... | @@ -39,7 +31,7 @@ fn test_chdir_self() !void { | ... | @@ -39,7 +31,7 @@ fn test_chdir_self() !void { |
| 39 | const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]); | 31 | const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]); |
| 40 | 32 | ||
| 41 | // Try changing to the current directory | 33 | // Try changing to the current directory |
| 42 | try std.posix.chdir(old_cwd); | 34 | try std.Io.Threaded.chdir(old_cwd); |
| 43 | try expect_cwd(old_cwd); | 35 | try expect_cwd(old_cwd); |
| 44 | } | 36 | } |
| 45 | 37 | ||
| ... | @@ -50,7 +42,7 @@ fn test_chdir_absolute() !void { | ... | @@ -50,7 +42,7 @@ fn test_chdir_absolute() !void { |
| 50 | const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute | 42 | const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute |
| 51 | 43 | ||
| 52 | // Try changing to the parent via a full path | 44 | // Try changing to the parent via a full path |
| 53 | try std.posix.chdir(parent); | 45 | try std.Io.Threaded.chdir(parent); |
| 54 | 46 | ||
| 55 | try expect_cwd(parent); | 47 | try expect_cwd(parent); |
| 56 | } | 48 | } |
| ... | @@ -71,7 +63,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io) !void { | ... | @@ -71,7 +63,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io) !void { |
| 71 | defer gpa.free(expected_path); | 63 | defer gpa.free(expected_path); |
| 72 | 64 | ||
| 73 | // change current working directory to new test directory | 65 | // change current working directory to new test directory |
| 74 | try std.posix.chdir(relative_dir_name); | 66 | try std.Io.Threaded.chdir(relative_dir_name); |
| 75 | 67 | ||
| 76 | var new_cwd_buf: [path_max]u8 = undefined; | 68 | var new_cwd_buf: [path_max]u8 = undefined; |
| 77 | const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); | 69 | const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); |
test/standalone/posix/getenv.zig+12-16| ... | @@ -1,32 +1,28 @@ | ... | @@ -1,32 +1,28 @@ |
| 1 | // test getting environment variables | 1 | //! test getting environment variables |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 5 | 5 | ||
| 6 | pub fn main() !void { | 6 | pub fn main(init: std.process.Init.Minimal) !void { |
| 7 | if (builtin.target.os.tag == .windows) { | 7 | if (builtin.target.os.tag == .windows) return; |
| 8 | return; // Windows env strings are WTF-16, so not supported by Zig's std.posix.getenv() | 8 | if (builtin.target.os.tag == .wasi and !builtin.link_libc) return; |
| 9 | } | ||
| 10 | 9 | ||
| 11 | if (builtin.target.os.tag == .wasi and !builtin.link_libc) { | 10 | const environ = init.environ; |
| 12 | return; // std.posix.getenv is not supported on WASI due to the need of allocation | ||
| 13 | } | ||
| 14 | 11 | ||
| 15 | // Test some unset env vars: | 12 | // Test some unset env vars: |
| 16 | 13 | try std.testing.expectEqual(environ.getPosix(""), null); | |
| 17 | try std.testing.expectEqual(std.posix.getenv(""), null); | 14 | try std.testing.expectEqual(environ.getPosix("BOGUSDOESNOTEXISTENVVAR"), null); |
| 18 | try std.testing.expectEqual(std.posix.getenv("BOGUSDOESNOTEXISTENVVAR"), null); | 15 | try std.testing.expectEqual(environ.getPosix("BOGUSDOESNOTEXISTENVVAR"), null); |
| 19 | try std.testing.expectEqual(std.posix.getenvZ("BOGUSDOESNOTEXISTENVVAR"), null); | ||
| 20 | 16 | ||
| 21 | if (builtin.link_libc) { | 17 | if (builtin.link_libc) { |
| 22 | // Test if USER matches what C library sees | 18 | // Test if USER matches what C library sees |
| 23 | const expected = std.mem.span(std.c.getenv("USER") orelse ""); | 19 | const expected = std.mem.span(std.c.getenv("USER") orelse ""); |
| 24 | const actual = std.posix.getenv("USER") orelse ""; | 20 | const actual = environ.getPosix("USER") orelse ""; |
| 25 | try std.testing.expectEqualStrings(expected, actual); | 21 | try std.testing.expectEqualStrings(expected, actual); |
| 26 | } | 22 | } |
| 27 | 23 | ||
| 28 | // env vars set by our build.zig run step: | 24 | // env vars set by our build.zig run step: |
| 29 | try std.testing.expectEqualStrings("", std.posix.getenv("ZIG_TEST_POSIX_EMPTY") orelse "invalid"); | 25 | try std.testing.expectEqualStrings("", environ.getPosix("ZIG_TEST_POSIX_EMPTY") orelse "invalid"); |
| 30 | try std.testing.expectEqualStrings("test=variable", std.posix.getenv("ZIG_TEST_POSIX_1EQ") orelse "invalid"); | 26 | try std.testing.expectEqualStrings("test=variable", environ.getPosix("ZIG_TEST_POSIX_1EQ") orelse "invalid"); |
| 31 | try std.testing.expectEqualStrings("=test=variable=", std.posix.getenv("ZIG_TEST_POSIX_3EQ") orelse "invalid"); | 27 | try std.testing.expectEqualStrings("=test=variable=", environ.getPosix("ZIG_TEST_POSIX_3EQ") orelse "invalid"); |
| 32 | } | 28 | } |
test/standalone/posix/relpaths.zig+2-8| ... | @@ -6,16 +6,10 @@ const builtin = @import("builtin"); | ... | @@ -6,16 +6,10 @@ const builtin = @import("builtin"); |
| 6 | const std = @import("std"); | 6 | const std = @import("std"); |
| 7 | const Io = std.Io; | 7 | const Io = std.Io; |
| 8 | 8 | ||
| 9 | pub fn main() !void { | 9 | pub fn main(init: std.process.Init) !void { |
| 10 | if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir | 10 | if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir |
| 11 | 11 | ||
| 12 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | 12 | const io = init.io; |
| 13 | const gpa = debug_allocator.allocator(); | ||
| 14 | defer std.debug.assert(debug_allocator.deinit() == .ok); | ||
| 15 | |||
| 16 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 17 | defer threaded.deinit(); | ||
| 18 | const io = threaded.io(); | ||
| 19 | 13 | ||
| 20 | var tmp = tmpDir(io, .{}); | 14 | var tmp = tmpDir(io, .{}); |
| 21 | defer tmp.cleanup(io); | 15 | defer tmp.cleanup(io); |
test/standalone/run_cwd/check_file_exists.zig+3-6| ... | @@ -1,9 +1,6 @@ | ... | @@ -1,9 +1,6 @@ |
| 1 | pub fn main() !void { | 1 | pub fn main(init: std.process.Init) !void { |
| 2 | var arena_state: std.heap.ArenaAllocator = .init(std.heap.page_allocator); | 2 | const arena = init.arena.allocator(); |
| 3 | defer arena_state.deinit(); | 3 | const args = try init.minimal.args.toSlice(arena); |
| 4 | const arena = arena_state.allocator(); | ||
| 5 | |||
| 6 | const args = try std.process.argsAlloc(arena); | ||
| 7 | 4 | ||
| 8 | if (args.len != 2) return error.BadUsage; | 5 | if (args.len != 2) return error.BadUsage; |
| 9 | const path = args[1]; | 6 | const path = args[1]; |
test/standalone/run_output_caching/main.zig+3-3| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | 4 | const io = init.io; |
| 5 | var args = try std.process.argsWithAllocator(std.heap.page_allocator); | 5 | var args = try init.minimal.args.iterateAllocator(init.arena.allocator()); |
| 6 | _ = args.skip(); | 6 | _ = args.skip(); |
| 7 | const filename = args.next().?; | 7 | const filename = args.next().?; |
| 8 | const file = try std.Io.Dir.cwd().createFile(io, filename, .{}); | 8 | const file = try std.Io.Dir.cwd().createFile(io, filename, .{}); |
test/standalone/run_output_paths/create_file.zig+3-3| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | 4 | const io = init.io; |
| 5 | var args = try std.process.argsWithAllocator(std.heap.page_allocator); | 5 | var args = try init.minimal.args.iterateAllocator(init.arena.allocator()); |
| 6 | _ = args.skip(); | 6 | _ = args.skip(); |
| 7 | const dir_name = args.next().?; | 7 | const dir_name = args.next().?; |
| 8 | const dir = try std.Io.Dir.cwd().openDir(io, if (std.mem.startsWith(u8, dir_name, "--dir=")) | 8 | const dir = try std.Io.Dir.cwd().openDir(io, if (std.mem.startsWith(u8, dir_name, "--dir=")) |
test/standalone/self_exe_symlink/create-symlink.zig+8-10| ... | @@ -1,21 +1,19 @@ | ... | @@ -1,21 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() anyerror!void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 4 | const io = init.io; |
| 5 | defer if (gpa.deinit() == .leak) @panic("found memory leaks"); | 5 | const gpa = init.gpa; |
| 6 | const allocator = gpa.allocator(); | 6 | var it = try init.minimal.args.iterateAllocator(gpa); |
| 7 | |||
| 8 | var it = try std.process.argsWithAllocator(allocator); | ||
| 9 | defer it.deinit(); | 7 | defer it.deinit(); |
| 10 | _ = it.next() orelse unreachable; // skip binary name | 8 | _ = it.next() orelse unreachable; // skip binary name |
| 11 | const exe_path = it.next() orelse unreachable; | 9 | const exe_path = it.next() orelse unreachable; |
| 12 | const symlink_path = it.next() orelse unreachable; | 10 | const symlink_path = it.next() orelse unreachable; |
| 13 | 11 | ||
| 14 | // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`. | 12 | const cwd = try std.process.getCwdAlloc(init.arena.allocator()); |
| 15 | const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path); | ||
| 16 | defer allocator.free(exe_rel_path); | ||
| 17 | 13 | ||
| 18 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | 14 | // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`. |
| 15 | const exe_rel_path = try std.fs.path.relative(gpa, cwd, init.environ_map, std.fs.path.dirname(symlink_path) orelse ".", exe_path); | ||
| 16 | defer gpa.free(exe_rel_path); | ||
| 19 | 17 | ||
| 20 | try std.Io.Dir.cwd().symLink(io, exe_rel_path, symlink_path, .{}); | 18 | try std.Io.Dir.cwd().symLink(io, exe_rel_path, symlink_path, .{}); |
| 21 | } | 19 | } |
test/standalone/self_exe_symlink/main.zig+3-8| ... | @@ -1,13 +1,8 @@ | ... | @@ -1,13 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | 4 | const gpa = init.gpa; |
| 5 | defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks"); | 5 | const io = init.io; |
| 6 | const gpa = debug_allocator.allocator(); | ||
| 7 | |||
| 8 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 9 | defer threaded.deinit(); | ||
| 10 | const io = threaded.io(); | ||
| 11 | 6 | ||
| 12 | const self_path = try std.process.executablePathAlloc(io, gpa); | 7 | const self_path = try std.process.executablePathAlloc(io, gpa); |
| 13 | defer gpa.free(self_path); | 8 | defer gpa.free(self_path); |
test/standalone/simple/cat/main.zig+4-10| ... | @@ -4,16 +4,10 @@ const mem = std.mem; | ... | @@ -4,16 +4,10 @@ const mem = std.mem; |
| 4 | const warn = std.log.warn; | 4 | const warn = std.log.warn; |
| 5 | const fatal = std.process.fatal; | 5 | const fatal = std.process.fatal; |
| 6 | 6 | ||
| 7 | pub fn main() !void { | 7 | pub fn main(init: std.process.Init) !void { |
| 8 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 8 | const arena = init.arena.allocator(); |
| 9 | defer arena_instance.deinit(); | 9 | const io = init.io; |
| 10 | const arena = arena_instance.allocator(); | 10 | const args = try init.minimal.args.toSlice(arena); |
| 11 | |||
| 12 | var threaded: std.Io.Threaded = .init(arena, .{}); | ||
| 13 | defer threaded.deinit(); | ||
| 14 | const io = threaded.io(); | ||
| 15 | |||
| 16 | const args = try std.process.argsAlloc(arena); | ||
| 17 | 11 | ||
| 18 | const exe = args[0]; | 12 | const exe = args[0]; |
| 19 | var catted_anything = false; | 13 | var catted_anything = false; |
test/standalone/simple/guess_number/main.zig+3-14| ... | @@ -1,22 +1,11 @@ | ... | @@ -1,22 +1,11 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | 1 | const std = @import("std"); |
| 3 | 2 | ||
| 4 | // See https://github.com/ziglang/zig/issues/24510 | 3 | pub fn main(init: std.process.Init) !void { |
| 5 | // for the plan to simplify this code. | 4 | var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{}); |
| 6 | pub fn main() !void { | ||
| 7 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | ||
| 8 | defer _ = debug_allocator.deinit(); | ||
| 9 | const gpa = debug_allocator.allocator(); | ||
| 10 | |||
| 11 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 12 | defer threaded.deinit(); | ||
| 13 | const io = threaded.io(); | ||
| 14 | |||
| 15 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | ||
| 16 | const out = &stdout_writer.interface; | 5 | const out = &stdout_writer.interface; |
| 17 | 6 | ||
| 18 | var line_buffer: [20]u8 = undefined; | 7 | var line_buffer: [20]u8 = undefined; |
| 19 | var stdin_reader: std.Io.File.Reader = .init(.stdin(), io, &line_buffer); | 8 | var stdin_reader: std.Io.File.Reader = .init(.stdin(), init.io, &line_buffer); |
| 20 | const in = &stdin_reader.interface; | 9 | const in = &stdin_reader.interface; |
| 21 | 10 | ||
| 22 | try out.writeAll("Welcome to the Guess Number Game in Zig.\n"); | 11 | try out.writeAll("Welcome to the Guess Number Game in Zig.\n"); |
test/standalone/simple/hello_world/hello.zig+2-12| ... | @@ -1,15 +1,5 @@ | ... | @@ -1,15 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | // See https://github.com/ziglang/zig/issues/24510 | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | // for the plan to simplify this code. | 4 | try std.Io.File.stdout().writeStreamingAll(init.io, "Hello, World!\n"); |
| 5 | pub fn main() !void { | ||
| 6 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | ||
| 7 | defer _ = debug_allocator.deinit(); | ||
| 8 | const gpa = debug_allocator.allocator(); | ||
| 9 | |||
| 10 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 11 | defer threaded.deinit(); | ||
| 12 | const io = threaded.io(); | ||
| 13 | |||
| 14 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | ||
| 15 | } | 5 | } |
test/standalone/windows_argv/build.zig+1-1| ... | @@ -67,7 +67,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -67,7 +67,7 @@ pub fn build(b: *std.Build) !void { |
| 67 | 67 | ||
| 68 | // Only target the MSVC ABI if MSVC/Windows SDK is available | 68 | // Only target the MSVC ABI if MSVC/Windows SDK is available |
| 69 | const has_msvc = has_msvc: { | 69 | const has_msvc = has_msvc: { |
| 70 | const sdk = std.zig.WindowsSdk.find(b.allocator, b.graph.io, builtin.cpu.arch) catch |err| switch (err) { | 70 | const sdk = std.zig.WindowsSdk.find(b.allocator, b.graph.io, builtin.cpu.arch, &b.graph.environ_map) catch |err| switch (err) { |
| 71 | error.OutOfMemory => @panic("oom"), | 71 | error.OutOfMemory => @panic("oom"), |
| 72 | else => break :has_msvc false, | 72 | else => break :has_msvc false, |
| 73 | }; | 73 | }; |
test/standalone/windows_argv/fuzz.zig+10-14| ... | @@ -3,19 +3,15 @@ const builtin = @import("builtin"); | ... | @@ -3,19 +3,15 @@ const builtin = @import("builtin"); |
| 3 | const windows = std.os.windows; | 3 | const windows = std.os.windows; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | ||
| 6 | pub fn main() !void { | 6 | pub fn main(init: std.process.Init) !void { |
| 7 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 7 | const gpa = init.gpa; |
| 8 | defer std.debug.assert(gpa.deinit() == .ok); | 8 | const args = try init.minimal.args.toSlice(init.arena.allocator()); |
| 9 | const allocator = gpa.allocator(); | ||
| 10 | |||
| 11 | const args = try std.process.argsAlloc(allocator); | ||
| 12 | defer std.process.argsFree(allocator, args); | ||
| 13 | 9 | ||
| 14 | if (args.len < 2) return error.MissingArgs; | 10 | if (args.len < 2) return error.MissingArgs; |
| 15 | 11 | ||
| 16 | const verify_path_wtf8 = args[1]; | 12 | const verify_path_wtf8 = args[1]; |
| 17 | const verify_path_w = try std.unicode.wtf8ToWtf16LeAllocZ(allocator, verify_path_wtf8); | 13 | const verify_path_w = try std.unicode.wtf8ToWtf16LeAllocZ(gpa, verify_path_wtf8); |
| 18 | defer allocator.free(verify_path_w); | 14 | defer gpa.free(verify_path_w); |
| 19 | 15 | ||
| 20 | const iterations: u64 = iterations: { | 16 | const iterations: u64 = iterations: { |
| 21 | if (args.len < 3) break :iterations 0; | 17 | if (args.len < 3) break :iterations 0; |
| ... | @@ -41,14 +37,14 @@ pub fn main() !void { | ... | @@ -41,14 +37,14 @@ pub fn main() !void { |
| 41 | std.debug.print("rand seed: {}\n", .{seed}); | 37 | std.debug.print("rand seed: {}\n", .{seed}); |
| 42 | } | 38 | } |
| 43 | 39 | ||
| 44 | var cmd_line_w_buf = std.array_list.Managed(u16).init(allocator); | 40 | var cmd_line_w_buf = std.array_list.Managed(u16).init(gpa); |
| 45 | defer cmd_line_w_buf.deinit(); | 41 | defer cmd_line_w_buf.deinit(); |
| 46 | 42 | ||
| 47 | var i: u64 = 0; | 43 | var i: u64 = 0; |
| 48 | var errors: u64 = 0; | 44 | var errors: u64 = 0; |
| 49 | while (iterations == 0 or i < iterations) { | 45 | while (iterations == 0 or i < iterations) { |
| 50 | const cmd_line_w = try randomCommandLineW(allocator, rand); | 46 | const cmd_line_w = try randomCommandLineW(gpa, rand); |
| 51 | defer allocator.free(cmd_line_w); | 47 | defer gpa.free(cmd_line_w); |
| 52 | 48 | ||
| 53 | // avoid known difference for 0-length command lines | 49 | // avoid known difference for 0-length command lines |
| 54 | if (cmd_line_w.len == 0 or cmd_line_w[0] == '\x00') continue; | 50 | if (cmd_line_w.len == 0 or cmd_line_w[0] == '\x00') continue; |
| ... | @@ -56,8 +52,8 @@ pub fn main() !void { | ... | @@ -56,8 +52,8 @@ pub fn main() !void { |
| 56 | const exit_code = try spawnVerify(verify_path_w, cmd_line_w); | 52 | const exit_code = try spawnVerify(verify_path_w, cmd_line_w); |
| 57 | if (exit_code != 0) { | 53 | if (exit_code != 0) { |
| 58 | std.debug.print(">>> found discrepancy <<<\n", .{}); | 54 | std.debug.print(">>> found discrepancy <<<\n", .{}); |
| 59 | const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, cmd_line_w); | 55 | const cmd_line_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(gpa, cmd_line_w); |
| 60 | defer allocator.free(cmd_line_wtf8); | 56 | defer gpa.free(cmd_line_wtf8); |
| 61 | std.debug.print("\"{f}\"\n\n", .{std.zig.fmtString(cmd_line_wtf8)}); | 57 | std.debug.print("\"{f}\"\n\n", .{std.zig.fmtString(cmd_line_wtf8)}); |
| 62 | 58 | ||
| 63 | errors += 1; | 59 | errors += 1; |
test/standalone/windows_argv/lib.zig+5-2| ... | @@ -5,7 +5,7 @@ export fn verify(argc: c_int, argv: [*]const [*:0]const u16) c_int { | ... | @@ -5,7 +5,7 @@ export fn verify(argc: c_int, argv: [*]const [*:0]const u16) c_int { |
| 5 | const argv_slice = argv[0..@intCast(argc)]; | 5 | const argv_slice = argv[0..@intCast(argc)]; |
| 6 | testArgv(argv_slice) catch |err| switch (err) { | 6 | testArgv(argv_slice) catch |err| switch (err) { |
| 7 | error.OutOfMemory => @panic("oom"), | 7 | error.OutOfMemory => @panic("oom"), |
| 8 | error.Overflow => @panic("bytes needed to contain args would overflow usize"), | 8 | error.Unexpected => @panic("unexpected error"), |
| 9 | error.ArgvMismatch => return 0, | 9 | error.ArgvMismatch => return 0, |
| 10 | }; | 10 | }; |
| 11 | return 1; | 11 | return 1; |
| ... | @@ -16,7 +16,10 @@ fn testArgv(expected_args: []const [*:0]const u16) !void { | ... | @@ -16,7 +16,10 @@ fn testArgv(expected_args: []const [*:0]const u16) !void { |
| 16 | defer arena_state.deinit(); | 16 | defer arena_state.deinit(); |
| 17 | const allocator = arena_state.allocator(); | 17 | const allocator = arena_state.allocator(); |
| 18 | 18 | ||
| 19 | const args = try std.process.argsAlloc(allocator); | 19 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 20 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; | ||
| 21 | const raw_args: std.process.Args = .{ .vector = cmd_line_w }; | ||
| 22 | const args = try raw_args.toSlice(allocator); | ||
| 20 | var wtf8_buf = std.array_list.Managed(u8).init(allocator); | 23 | var wtf8_buf = std.array_list.Managed(u8).init(allocator); |
| 21 | 24 | ||
| 22 | var eql = true; | 25 | var eql = true; |
test/standalone/windows_bat_args/echo-args.zig+4-7| ... | @@ -1,15 +1,12 @@ | ... | @@ -1,15 +1,12 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 4 | const arena = init.arena.allocator(); |
| 5 | defer arena_state.deinit(); | 5 | const io = init.io; |
| 6 | const arena = arena_state.allocator(); | 6 | const args = try init.minimal.args.toSlice(arena); |
| 7 | |||
| 8 | const io = std.Options.debug_io; | ||
| 9 | 7 | ||
| 10 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | 8 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); |
| 11 | const stdout = &stdout_writer.interface; | 9 | const stdout = &stdout_writer.interface; |
| 12 | var args = try std.process.argsAlloc(arena); | ||
| 13 | for (args[1..], 1..) |arg, i| { | 10 | for (args[1..], 1..) |arg, i| { |
| 14 | try stdout.writeAll(arg); | 11 | try stdout.writeAll(arg); |
| 15 | if (i != args.len - 1) try stdout.writeByte('\x00'); | 12 | if (i != args.len - 1) try stdout.writeByte('\x00'); |
test/standalone/windows_bat_args/fuzz.zig+8-13| ... | @@ -4,16 +4,11 @@ const std = @import("std"); | ... | @@ -4,16 +4,11 @@ const std = @import("std"); |
| 4 | const Io = std.Io; | 4 | const Io = std.Io; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | 6 | ||
| 7 | pub fn main() anyerror!void { | 7 | pub fn main(init: std.process.Init) !void { |
| 8 | var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init; | 8 | const gpa = init.gpa; |
| 9 | defer std.debug.assert(debug_alloc_inst.deinit() == .ok); | 9 | const io = init.io; |
| 10 | const gpa = debug_alloc_inst.allocator(); | ||
| 11 | 10 | ||
| 12 | var threaded: Io.Threaded = .init(gpa, .{}); | 11 | var it = try init.minimal.args.iterateAllocator(gpa); |
| 13 | defer threaded.deinit(); | ||
| 14 | const io = threaded.io(); | ||
| 15 | |||
| 16 | var it = try std.process.argsWithAllocator(gpa); | ||
| 17 | defer it.deinit(); | 12 | defer it.deinit(); |
| 18 | _ = it.next() orelse unreachable; // skip binary name | 13 | _ = it.next() orelse unreachable; // skip binary name |
| 19 | const child_exe_path_orig = it.next() orelse unreachable; | 14 | const child_exe_path_orig = it.next() orelse unreachable; |
| ... | @@ -84,13 +79,13 @@ pub fn main() anyerror!void { | ... | @@ -84,13 +79,13 @@ pub fn main() anyerror!void { |
| 84 | } | 79 | } |
| 85 | } | 80 | } |
| 86 | 81 | ||
| 87 | fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.EnvMap) !void { | 82 | fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.Environ.Map) !void { |
| 88 | try testExecBat(gpa, io, "args1.bat", args, env); | 83 | try testExecBat(gpa, io, "args1.bat", args, env); |
| 89 | try testExecBat(gpa, io, "args2.bat", args, env); | 84 | try testExecBat(gpa, io, "args2.bat", args, env); |
| 90 | try testExecBat(gpa, io, "args3.bat", args, env); | 85 | try testExecBat(gpa, io, "args3.bat", args, env); |
| 91 | } | 86 | } |
| 92 | 87 | ||
| 93 | fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void { | 88 | fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.Environ.Map) !void { |
| 94 | const argv = try gpa.alloc([]const u8, 1 + args.len); | 89 | const argv = try gpa.alloc([]const u8, 1 + args.len); |
| 95 | defer gpa.free(argv); | 90 | defer gpa.free(argv); |
| 96 | argv[0] = bat; | 91 | argv[0] = bat; |
| ... | @@ -98,8 +93,8 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8 | ... | @@ -98,8 +93,8 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8 |
| 98 | 93 | ||
| 99 | const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat"); | 94 | const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat"); |
| 100 | 95 | ||
| 101 | const result = try std.process.Child.run(gpa, io, .{ | 96 | const result = try std.process.run(gpa, io, .{ |
| 102 | .env_map = env, | 97 | .environ_map = env, |
| 103 | .argv = argv, | 98 | .argv = argv, |
| 104 | }); | 99 | }); |
| 105 | defer gpa.free(result.stdout); | 100 | defer gpa.free(result.stdout); |
test/standalone/windows_bat_args/test.zig+9-13| ... | @@ -2,15 +2,11 @@ const std = @import("std"); | ... | @@ -2,15 +2,11 @@ const std = @import("std"); |
| 2 | const Io = std.Io; | 2 | const Io = std.Io; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | ||
| 5 | pub fn main() anyerror!void { | 5 | pub fn main(init: std.process.Init) !void { |
| 6 | var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init; | 6 | const gpa = init.gpa; |
| 7 | defer std.debug.assert(debug_alloc_inst.deinit() == .ok); | 7 | const io = init.io; |
| 8 | const gpa = debug_alloc_inst.allocator(); | ||
| 9 | 8 | ||
| 10 | var threaded: Io.Threaded = .init(gpa, .{}); | 9 | var it = try init.minimal.args.iterateAllocator(gpa); |
| 11 | const io = threaded.io(); | ||
| 12 | |||
| 13 | var it = try std.process.argsWithAllocator(gpa); | ||
| 14 | defer it.deinit(); | 10 | defer it.deinit(); |
| 15 | _ = it.next() orelse unreachable; // skip binary name | 11 | _ = it.next() orelse unreachable; // skip binary name |
| 16 | const child_exe_path_orig = it.next() orelse unreachable; | 12 | const child_exe_path_orig = it.next() orelse unreachable; |
| ... | @@ -109,7 +105,7 @@ pub fn main() anyerror!void { | ... | @@ -109,7 +105,7 @@ pub fn main() anyerror!void { |
| 109 | try std.testing.expectError(error.FileNotFound, testExecBat(gpa, io, absolute_with_trailing, &.{"abc"}, null)); | 105 | try std.testing.expectError(error.FileNotFound, testExecBat(gpa, io, absolute_with_trailing, &.{"abc"}, null)); |
| 110 | 106 | ||
| 111 | var env = env: { | 107 | var env = env: { |
| 112 | var env = try std.process.getEnvMap(gpa); | 108 | var env = try init.environ_map.clone(gpa); |
| 113 | errdefer env.deinit(); | 109 | errdefer env.deinit(); |
| 114 | // No escaping | 110 | // No escaping |
| 115 | try env.put("FOO", "123"); | 111 | try env.put("FOO", "123"); |
| ... | @@ -130,13 +126,13 @@ fn testExecError(err: anyerror, gpa: Allocator, io: Io, args: []const []const u8 | ... | @@ -130,13 +126,13 @@ fn testExecError(err: anyerror, gpa: Allocator, io: Io, args: []const []const u8 |
| 130 | return std.testing.expectError(err, testExec(gpa, io, args, null)); | 126 | return std.testing.expectError(err, testExec(gpa, io, args, null)); |
| 131 | } | 127 | } |
| 132 | 128 | ||
| 133 | fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.EnvMap) !void { | 129 | fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.Environ.Map) !void { |
| 134 | try testExecBat(gpa, io, "args1.bat", args, env); | 130 | try testExecBat(gpa, io, "args1.bat", args, env); |
| 135 | try testExecBat(gpa, io, "args2.bat", args, env); | 131 | try testExecBat(gpa, io, "args2.bat", args, env); |
| 136 | try testExecBat(gpa, io, "args3.bat", args, env); | 132 | try testExecBat(gpa, io, "args3.bat", args, env); |
| 137 | } | 133 | } |
| 138 | 134 | ||
| 139 | fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void { | 135 | fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.Environ.Map) !void { |
| 140 | const argv = try gpa.alloc([]const u8, 1 + args.len); | 136 | const argv = try gpa.alloc([]const u8, 1 + args.len); |
| 141 | defer gpa.free(argv); | 137 | defer gpa.free(argv); |
| 142 | argv[0] = bat; | 138 | argv[0] = bat; |
| ... | @@ -144,8 +140,8 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8 | ... | @@ -144,8 +140,8 @@ fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8 |
| 144 | 140 | ||
| 145 | const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat"); | 141 | const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat"); |
| 146 | 142 | ||
| 147 | const result = try std.process.Child.run(gpa, io, .{ | 143 | const result = try std.process.run(gpa, io, .{ |
| 148 | .env_map = env, | 144 | .environ_map = env, |
| 149 | .argv = argv, | 145 | .argv = argv, |
| 150 | }); | 146 | }); |
| 151 | defer gpa.free(result.stdout); | 147 | defer gpa.free(result.stdout); |
test/standalone/windows_paths/relative.zig+6-13| ... | @@ -1,21 +1,14 @@ | ... | @@ -1,21 +1,14 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main(init: std.process.Init) !void { |
| 4 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 4 | const arena = init.arena.allocator(); |
| 5 | defer std.debug.assert(gpa.deinit() == .ok); | 5 | const args = try init.minimal.args.toSlice(arena); |
| 6 | const allocator = gpa.allocator(); | 6 | const io = init.io; |
| 7 | 7 | const cwd_path = try std.process.getCwdAlloc(arena); | |
| 8 | const args = try std.process.argsAlloc(allocator); | ||
| 9 | defer std.process.argsFree(allocator, args); | ||
| 10 | 8 | ||
| 11 | if (args.len < 3) return error.MissingArgs; | 9 | if (args.len < 3) return error.MissingArgs; |
| 12 | 10 | ||
| 13 | var threaded: std.Io.Threaded = .init(allocator, .{}); | 11 | const relative = try std.fs.path.relative(arena, cwd_path, init.environ_map, args[1], args[2]); |
| 14 | defer threaded.deinit(); | ||
| 15 | const io = threaded.io(); | ||
| 16 | |||
| 17 | const relative = try std.fs.path.relative(allocator, args[1], args[2]); | ||
| 18 | defer allocator.free(relative); | ||
| 19 | 12 | ||
| 20 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); | 13 | var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); |
| 21 | const stdout = &stdout_writer.interface; | 14 | const stdout = &stdout_writer.interface; |
test/standalone/windows_paths/test.zig+9-13| ... | @@ -1,17 +1,13 @@ | ... | @@ -1,17 +1,13 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Io = std.Io; | 2 | const Io = std.Io; |
| 3 | 3 | ||
| 4 | pub fn main() anyerror!void { | 4 | pub fn main(init: std.process.Init) !void { |
| 5 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 5 | const arena = init.arena.allocator(); |
| 6 | defer arena_state.deinit(); | 6 | const args = try init.minimal.args.toSlice(arena); |
| 7 | const arena = arena_state.allocator(); | 7 | const io = init.io; |
| 8 | |||
| 9 | const args = try std.process.argsAlloc(arena); | ||
| 10 | 8 | ||
| 11 | if (args.len < 2) return error.MissingArgs; | 9 | if (args.len < 2) return error.MissingArgs; |
| 12 | 10 | ||
| 13 | const io = std.Io.Threaded.global_single_threaded.ioBasic(); | ||
| 14 | |||
| 15 | const exe_path = args[1]; | 11 | const exe_path = args[1]; |
| 16 | 12 | ||
| 17 | const cwd_path = try std.process.getCwdAlloc(arena); | 13 | const cwd_path = try std.process.getCwdAlloc(arena); |
| ... | @@ -25,10 +21,10 @@ pub fn main() anyerror!void { | ... | @@ -25,10 +21,10 @@ pub fn main() anyerror!void { |
| 25 | const alt_drive_letter = try getAltDriveLetter(cwd_path); | 21 | const alt_drive_letter = try getAltDriveLetter(cwd_path); |
| 26 | const alt_drive_cwd_key = try std.fmt.allocPrint(arena, "={c}:", .{alt_drive_letter}); | 22 | const alt_drive_cwd_key = try std.fmt.allocPrint(arena, "={c}:", .{alt_drive_letter}); |
| 27 | const alt_drive_cwd = try std.fmt.allocPrint(arena, "{c}:\\baz", .{alt_drive_letter}); | 23 | const alt_drive_cwd = try std.fmt.allocPrint(arena, "{c}:\\baz", .{alt_drive_letter}); |
| 28 | var alt_drive_env_map = std.process.EnvMap.init(arena); | 24 | var alt_drive_env_map = std.process.Environ.Map.init(arena); |
| 29 | try alt_drive_env_map.put(alt_drive_cwd_key, alt_drive_cwd); | 25 | try alt_drive_env_map.put(alt_drive_cwd_key, alt_drive_cwd); |
| 30 | 26 | ||
| 31 | const empty_env = std.process.EnvMap.init(arena); | 27 | const empty_env = std.process.Environ.Map.init(arena); |
| 32 | 28 | ||
| 33 | { | 29 | { |
| 34 | const drive_rel = try std.fmt.allocPrint(arena, "{c}:foo", .{alt_drive_letter}); | 30 | const drive_rel = try std.fmt.allocPrint(arena, "{c}:foo", .{alt_drive_letter}); |
| ... | @@ -96,12 +92,12 @@ fn checkRelative( | ... | @@ -96,12 +92,12 @@ fn checkRelative( |
| 96 | expected_stdout: []const u8, | 92 | expected_stdout: []const u8, |
| 97 | argv: []const []const u8, | 93 | argv: []const []const u8, |
| 98 | cwd: ?[]const u8, | 94 | cwd: ?[]const u8, |
| 99 | env_map: ?*const std.process.EnvMap, | 95 | environ_map: ?*const std.process.Environ.Map, |
| 100 | ) !void { | 96 | ) !void { |
| 101 | const result = try std.process.Child.run(allocator, io, .{ | 97 | const result = try std.process.run(allocator, io, .{ |
| 102 | .argv = argv, | 98 | .argv = argv, |
| 103 | .cwd = cwd, | 99 | .cwd = cwd, |
| 104 | .env_map = env_map, | 100 | .environ_map = environ_map, |
| 105 | }); | 101 | }); |
| 106 | defer allocator.free(result.stdout); | 102 | defer allocator.free(result.stdout); |
| 107 | defer allocator.free(result.stderr); | 103 | defer allocator.free(result.stderr); |
test/standalone/windows_spawn/main.zig+7-11| ... | @@ -5,16 +5,12 @@ const Allocator = std.mem.Allocator; | ... | @@ -5,16 +5,12 @@ const Allocator = std.mem.Allocator; |
| 5 | const windows = std.os.windows; | 5 | const windows = std.os.windows; |
| 6 | const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral; | 6 | const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral; |
| 7 | 7 | ||
| 8 | pub fn main() anyerror!void { | 8 | pub fn main(init: std.process.Init) !void { |
| 9 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | 9 | const gpa = init.gpa; |
| 10 | defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks"); | 10 | const io = init.io; |
| 11 | const gpa = debug_allocator.allocator(); | 11 | const process_cwd_path = try std.process.getCwdAlloc(init.arena.allocator()); |
| 12 | 12 | ||
| 13 | var threaded: std.Io.Threaded = .init(gpa, .{}); | 13 | var it = try init.minimal.args.iterateAllocator(gpa); |
| 14 | defer threaded.deinit(); | ||
| 15 | const io = threaded.io(); | ||
| 16 | |||
| 17 | var it = try std.process.argsWithAllocator(gpa); | ||
| 18 | defer it.deinit(); | 14 | defer it.deinit(); |
| 19 | _ = it.next() orelse unreachable; // skip binary name | 15 | _ = it.next() orelse unreachable; // skip binary name |
| 20 | const hello_exe_cache_path = it.next() orelse unreachable; | 16 | const hello_exe_cache_path = it.next() orelse unreachable; |
| ... | @@ -28,7 +24,7 @@ pub fn main() anyerror!void { | ... | @@ -28,7 +24,7 @@ pub fn main() anyerror!void { |
| 28 | defer gpa.free(tmp_absolute_path_w); | 24 | defer gpa.free(tmp_absolute_path_w); |
| 29 | const cwd_absolute_path = try Io.Dir.cwd().realPathFileAlloc(io, ".", gpa); | 25 | const cwd_absolute_path = try Io.Dir.cwd().realPathFileAlloc(io, ".", gpa); |
| 30 | defer gpa.free(cwd_absolute_path); | 26 | defer gpa.free(cwd_absolute_path); |
| 31 | const tmp_relative_path = try std.fs.path.relative(gpa, cwd_absolute_path, tmp_absolute_path); | 27 | const tmp_relative_path = try std.fs.path.relative(gpa, process_cwd_path, init.environ_map, cwd_absolute_path, tmp_absolute_path); |
| 32 | defer gpa.free(tmp_relative_path); | 28 | defer gpa.free(tmp_relative_path); |
| 33 | 29 | ||
| 34 | // Clear PATH | 30 | // Clear PATH |
| ... | @@ -212,7 +208,7 @@ fn testExec(gpa: Allocator, io: Io, command: []const u8, expected_stdout: []cons | ... | @@ -212,7 +208,7 @@ fn testExec(gpa: Allocator, io: Io, command: []const u8, expected_stdout: []cons |
| 212 | } | 208 | } |
| 213 | 209 | ||
| 214 | fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void { | 210 | fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void { |
| 215 | const result = try std.process.Child.run(gpa, io, .{ | 211 | const result = try std.process.run(gpa, io, .{ |
| 216 | .argv = &[_][]const u8{command}, | 212 | .argv = &[_][]const u8{command}, |
| 217 | .cwd = cwd, | 213 | .cwd = cwd, |
| 218 | }); | 214 | }); |
test/tests.zig+2-2| ... | @@ -2062,7 +2062,7 @@ pub fn addCliTests(b: *std.Build) *Step { | ... | @@ -2062,7 +2062,7 @@ pub fn addCliTests(b: *std.Build) *Step { |
| 2062 | run_run.setCwd(.{ .cwd_relative = tmp_path }); | 2062 | run_run.setCwd(.{ .cwd_relative = tmp_path }); |
| 2063 | run_run.setName("zig build run"); | 2063 | run_run.setName("zig build run"); |
| 2064 | run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n"); | 2064 | run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n"); |
| 2065 | run_run.expectStdErrEqual("All your codebase are belong to us.\n"); | 2065 | run_run.expectStdErrMatch("All your codebase are belong to us.\n"); |
| 2066 | run_run.step.dependOn(&init_exe.step); | 2066 | run_run.step.dependOn(&init_exe.step); |
| 2067 | 2067 | ||
| 2068 | const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path }); | 2068 | const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path }); |
| ... | @@ -2718,7 +2718,7 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons | ... | @@ -2718,7 +2718,7 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons |
| 2718 | if (b.enable_wasmtime) run.addArg("-fwasmtime"); | 2718 | if (b.enable_wasmtime) run.addArg("-fwasmtime"); |
| 2719 | if (b.enable_darling) run.addArg("-fdarling"); | 2719 | if (b.enable_darling) run.addArg("-fdarling"); |
| 2720 | 2720 | ||
| 2721 | run.addCheck(.{ .expect_term = .{ .Exited = 0 } }); | 2721 | run.addCheck(.{ .expect_term = .{ .exited = 0 } }); |
| 2722 | 2722 | ||
| 2723 | test_step.dependOn(&run.step); | 2723 | test_step.dependOn(&run.step); |
| 2724 | } | 2724 | } |
tools/docgen.zig+4-12| ... | @@ -28,21 +28,13 @@ const usage = | ... | @@ -28,21 +28,13 @@ const usage = |
| 28 | \\ | 28 | \\ |
| 29 | ; | 29 | ; |
| 30 | 30 | ||
| 31 | pub fn main() !void { | 31 | pub fn main(init: std.process.Init) !void { |
| 32 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 32 | const arena = init.arena.allocator(); |
| 33 | defer arena_instance.deinit(); | 33 | const io = init.io; |
| 34 | 34 | ||
| 35 | const arena = arena_instance.allocator(); | 35 | var args_it = try init.minimal.args.iterateAllocator(arena); |
| 36 | |||
| 37 | var args_it = try process.argsWithAllocator(arena); | ||
| 38 | if (!args_it.skip()) @panic("expected self arg"); | 36 | if (!args_it.skip()) @panic("expected self arg"); |
| 39 | 37 | ||
| 40 | const gpa = arena; | ||
| 41 | |||
| 42 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 43 | defer threaded.deinit(); | ||
| 44 | const io = threaded.io(); | ||
| 45 | |||
| 46 | var opt_code_dir: ?[]const u8 = null; | 38 | var opt_code_dir: ?[]const u8 = null; |
| 47 | var opt_input: ?[]const u8 = null; | 39 | var opt_input: ?[]const u8 = null; |
| 48 | var opt_output: ?[]const u8 = null; | 40 | var opt_output: ?[]const u8 = null; |
tools/doctest.zig+43-43| ... | @@ -29,21 +29,17 @@ const usage = | ... | @@ -29,21 +29,17 @@ const usage = |
| 29 | \\ | 29 | \\ |
| 30 | ; | 30 | ; |
| 31 | 31 | ||
| 32 | pub fn main() !void { | 32 | pub fn main(init: std.process.Init) !void { |
| 33 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 33 | const arena = init.arena.allocator(); |
| 34 | defer arena_instance.deinit(); | 34 | const io = init.io; |
| 35 | const environ_map = init.environ_map; | ||
| 36 | const cwd_path = try std.process.getCwdAlloc(arena); | ||
| 35 | 37 | ||
| 36 | const arena = arena_instance.allocator(); | 38 | try environ_map.put("CLICOLOR_FORCE", "1"); |
| 37 | 39 | ||
| 38 | var args_it = try process.argsWithAllocator(arena); | 40 | var args_it = try init.minimal.args.iterateAllocator(arena); |
| 39 | if (!args_it.skip()) fatal("missing argv[0]", .{}); | 41 | if (!args_it.skip()) fatal("missing argv[0]", .{}); |
| 40 | 42 | ||
| 41 | const gpa = arena; | ||
| 42 | |||
| 43 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 44 | defer threaded.deinit(); | ||
| 45 | const io = threaded.io(); | ||
| 46 | |||
| 47 | var opt_input: ?[]const u8 = null; | 43 | var opt_input: ?[]const u8 = null; |
| 48 | var opt_output: ?[]const u8 = null; | 44 | var opt_output: ?[]const u8 = null; |
| 49 | var opt_zig: ?[]const u8 = null; | 45 | var opt_zig: ?[]const u8 = null; |
| ... | @@ -105,12 +101,13 @@ pub fn main() !void { | ... | @@ -105,12 +101,13 @@ pub fn main() !void { |
| 105 | out, | 101 | out, |
| 106 | code, | 102 | code, |
| 107 | tmp_dir_path, | 103 | tmp_dir_path, |
| 108 | try Dir.path.relative(arena, tmp_dir_path, zig_path), | 104 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, zig_path), |
| 109 | try Dir.path.relative(arena, tmp_dir_path, input_path), | 105 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, input_path), |
| 110 | if (opt_zig_lib_dir) |zig_lib_dir| | 106 | if (opt_zig_lib_dir) |zig_lib_dir| |
| 111 | try Dir.path.relative(arena, tmp_dir_path, zig_lib_dir) | 107 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, zig_lib_dir) |
| 112 | else | 108 | else |
| 113 | null, | 109 | null, |
| 110 | environ_map, | ||
| 114 | ); | 111 | ); |
| 115 | 112 | ||
| 116 | try out_file_writer.end(); | 113 | try out_file_writer.end(); |
| ... | @@ -129,10 +126,8 @@ fn printOutput( | ... | @@ -129,10 +126,8 @@ fn printOutput( |
| 129 | input_path: []const u8, | 126 | input_path: []const u8, |
| 130 | /// Relative to `tmp_dir_path`. | 127 | /// Relative to `tmp_dir_path`. |
| 131 | opt_zig_lib_dir: ?[]const u8, | 128 | opt_zig_lib_dir: ?[]const u8, |
| 129 | environ_map: *const process.Environ.Map, | ||
| 132 | ) !void { | 130 | ) !void { |
| 133 | var env_map = try process.getEnvMap(arena); | ||
| 134 | try env_map.put("CLICOLOR_FORCE", "1"); | ||
| 135 | |||
| 136 | const host = try std.zig.system.resolveTargetQuery(io, .{}); | 131 | const host = try std.zig.system.resolveTargetQuery(io, .{}); |
| 137 | const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch); | 132 | const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch); |
| 138 | const print = std.debug.print; | 133 | const print = std.debug.print; |
| ... | @@ -201,14 +196,14 @@ fn printOutput( | ... | @@ -201,14 +196,14 @@ fn printOutput( |
| 201 | try shell_out.print("\n", .{}); | 196 | try shell_out.print("\n", .{}); |
| 202 | 197 | ||
| 203 | if (expected_outcome == .build_fail) { | 198 | if (expected_outcome == .build_fail) { |
| 204 | const result = try process.Child.run(arena, io, .{ | 199 | const result = try process.run(arena, io, .{ |
| 205 | .argv = build_args.items, | 200 | .argv = build_args.items, |
| 206 | .cwd = tmp_dir_path, | 201 | .cwd = tmp_dir_path, |
| 207 | .env_map = &env_map, | 202 | .environ_map = environ_map, |
| 208 | .max_output_bytes = max_doc_file_size, | 203 | .max_output_bytes = max_doc_file_size, |
| 209 | }); | 204 | }); |
| 210 | switch (result.term) { | 205 | switch (result.term) { |
| 211 | .Exited => |exit_code| { | 206 | .exited => |exit_code| { |
| 212 | if (exit_code == 0) { | 207 | if (exit_code == 0) { |
| 213 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); | 208 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); |
| 214 | dumpArgs(build_args.items); | 209 | dumpArgs(build_args.items); |
| ... | @@ -226,7 +221,7 @@ fn printOutput( | ... | @@ -226,7 +221,7 @@ fn printOutput( |
| 226 | try shell_out.writeAll(colored_stderr); | 221 | try shell_out.writeAll(colored_stderr); |
| 227 | break :code_block; | 222 | break :code_block; |
| 228 | } | 223 | } |
| 229 | const exec_result = run(arena, io, &env_map, tmp_dir_path, build_args.items) catch | 224 | const exec_result = run(arena, io, environ_map, tmp_dir_path, build_args.items) catch |
| 230 | fatal("example failed to compile", .{}); | 225 | fatal("example failed to compile", .{}); |
| 231 | 226 | ||
| 232 | if (code.verbose_cimport) { | 227 | if (code.verbose_cimport) { |
| ... | @@ -257,26 +252,26 @@ fn printOutput( | ... | @@ -257,26 +252,26 @@ fn printOutput( |
| 257 | var exited_with_signal = false; | 252 | var exited_with_signal = false; |
| 258 | 253 | ||
| 259 | const result = if (expected_outcome == .fail) blk: { | 254 | const result = if (expected_outcome == .fail) blk: { |
| 260 | const result = try process.Child.run(arena, io, .{ | 255 | const result = try process.run(arena, io, .{ |
| 261 | .argv = run_args, | 256 | .argv = run_args, |
| 262 | .env_map = &env_map, | 257 | .environ_map = environ_map, |
| 263 | .cwd = tmp_dir_path, | 258 | .cwd = tmp_dir_path, |
| 264 | .max_output_bytes = max_doc_file_size, | 259 | .max_output_bytes = max_doc_file_size, |
| 265 | }); | 260 | }); |
| 266 | switch (result.term) { | 261 | switch (result.term) { |
| 267 | .Exited => |exit_code| { | 262 | .exited => |exit_code| { |
| 268 | if (exit_code == 0) { | 263 | if (exit_code == 0) { |
| 269 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); | 264 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); |
| 270 | dumpArgs(run_args); | 265 | dumpArgs(run_args); |
| 271 | fatal("example incorrectly compiled", .{}); | 266 | fatal("example incorrectly compiled", .{}); |
| 272 | } | 267 | } |
| 273 | }, | 268 | }, |
| 274 | .Signal => exited_with_signal = true, | 269 | .signal => exited_with_signal = true, |
| 275 | else => {}, | 270 | else => {}, |
| 276 | } | 271 | } |
| 277 | break :blk result; | 272 | break :blk result; |
| 278 | } else blk: { | 273 | } else blk: { |
| 279 | break :blk run(arena, io, &env_map, tmp_dir_path, run_args) catch | 274 | break :blk run(arena, io, environ_map, tmp_dir_path, run_args) catch |
| 280 | fatal("example crashed", .{}); | 275 | fatal("example crashed", .{}); |
| 281 | }; | 276 | }; |
| 282 | 277 | ||
| ... | @@ -345,7 +340,7 @@ fn printOutput( | ... | @@ -345,7 +340,7 @@ fn printOutput( |
| 345 | } | 340 | } |
| 346 | } | 341 | } |
| 347 | 342 | ||
| 348 | const result = run(arena, io, &env_map, tmp_dir_path, test_args.items) catch | 343 | const result = run(arena, io, environ_map, tmp_dir_path, test_args.items) catch |
| 349 | fatal("test failed", .{}); | 344 | fatal("test failed", .{}); |
| 350 | const escaped_stderr = try escapeHtml(arena, result.stderr); | 345 | const escaped_stderr = try escapeHtml(arena, result.stderr); |
| 351 | const escaped_stdout = try escapeHtml(arena, result.stdout); | 346 | const escaped_stdout = try escapeHtml(arena, result.stdout); |
| ... | @@ -376,14 +371,14 @@ fn printOutput( | ... | @@ -376,14 +371,14 @@ fn printOutput( |
| 376 | try test_args.append("-lc"); | 371 | try test_args.append("-lc"); |
| 377 | try shell_out.print("-lc ", .{}); | 372 | try shell_out.print("-lc ", .{}); |
| 378 | } | 373 | } |
| 379 | const result = try process.Child.run(arena, io, .{ | 374 | const result = try process.run(arena, io, .{ |
| 380 | .argv = test_args.items, | 375 | .argv = test_args.items, |
| 381 | .env_map = &env_map, | 376 | .environ_map = environ_map, |
| 382 | .cwd = tmp_dir_path, | 377 | .cwd = tmp_dir_path, |
| 383 | .max_output_bytes = max_doc_file_size, | 378 | .max_output_bytes = max_doc_file_size, |
| 384 | }); | 379 | }); |
| 385 | switch (result.term) { | 380 | switch (result.term) { |
| 386 | .Exited => |exit_code| { | 381 | .exited => |exit_code| { |
| 387 | if (exit_code == 0) { | 382 | if (exit_code == 0) { |
| 388 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); | 383 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); |
| 389 | dumpArgs(test_args.items); | 384 | dumpArgs(test_args.items); |
| ... | @@ -432,14 +427,14 @@ fn printOutput( | ... | @@ -432,14 +427,14 @@ fn printOutput( |
| 432 | }, | 427 | }, |
| 433 | } | 428 | } |
| 434 | 429 | ||
| 435 | const result = try process.Child.run(arena, io, .{ | 430 | const result = try process.run(arena, io, .{ |
| 436 | .argv = test_args.items, | 431 | .argv = test_args.items, |
| 437 | .env_map = &env_map, | 432 | .environ_map = environ_map, |
| 438 | .cwd = tmp_dir_path, | 433 | .cwd = tmp_dir_path, |
| 439 | .max_output_bytes = max_doc_file_size, | 434 | .max_output_bytes = max_doc_file_size, |
| 440 | }); | 435 | }); |
| 441 | switch (result.term) { | 436 | switch (result.term) { |
| 442 | .Exited => |exit_code| { | 437 | .exited => |exit_code| { |
| 443 | if (exit_code == 0) { | 438 | if (exit_code == 0) { |
| 444 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); | 439 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); |
| 445 | dumpArgs(test_args.items); | 440 | dumpArgs(test_args.items); |
| ... | @@ -508,14 +503,14 @@ fn printOutput( | ... | @@ -508,14 +503,14 @@ fn printOutput( |
| 508 | } | 503 | } |
| 509 | 504 | ||
| 510 | if (maybe_error_match) |error_match| { | 505 | if (maybe_error_match) |error_match| { |
| 511 | const result = try process.Child.run(arena, io, .{ | 506 | const result = try process.run(arena, io, .{ |
| 512 | .argv = build_args.items, | 507 | .argv = build_args.items, |
| 513 | .env_map = &env_map, | 508 | .environ_map = environ_map, |
| 514 | .cwd = tmp_dir_path, | 509 | .cwd = tmp_dir_path, |
| 515 | .max_output_bytes = max_doc_file_size, | 510 | .max_output_bytes = max_doc_file_size, |
| 516 | }); | 511 | }); |
| 517 | switch (result.term) { | 512 | switch (result.term) { |
| 518 | .Exited => |exit_code| { | 513 | .exited => |exit_code| { |
| 519 | if (exit_code == 0) { | 514 | if (exit_code == 0) { |
| 520 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); | 515 | print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); |
| 521 | dumpArgs(build_args.items); | 516 | dumpArgs(build_args.items); |
| ... | @@ -536,7 +531,7 @@ fn printOutput( | ... | @@ -536,7 +531,7 @@ fn printOutput( |
| 536 | const colored_stderr = try termColor(arena, escaped_stderr); | 531 | const colored_stderr = try termColor(arena, escaped_stderr); |
| 537 | try shell_out.print("\n{s} ", .{colored_stderr}); | 532 | try shell_out.print("\n{s} ", .{colored_stderr}); |
| 538 | } else { | 533 | } else { |
| 539 | _ = run(arena, io, &env_map, tmp_dir_path, build_args.items) catch fatal("example failed to compile", .{}); | 534 | _ = run(arena, io, environ_map, tmp_dir_path, build_args.items) catch fatal("example failed to compile", .{}); |
| 540 | } | 535 | } |
| 541 | try shell_out.writeAll("\n"); | 536 | try shell_out.writeAll("\n"); |
| 542 | }, | 537 | }, |
| ... | @@ -595,7 +590,7 @@ fn printOutput( | ... | @@ -595,7 +590,7 @@ fn printOutput( |
| 595 | try test_args.append(option); | 590 | try test_args.append(option); |
| 596 | try shell_out.print("{s} ", .{option}); | 591 | try shell_out.print("{s} ", .{option}); |
| 597 | } | 592 | } |
| 598 | const result = run(arena, io, &env_map, tmp_dir_path, test_args.items) catch fatal("test failed", .{}); | 593 | const result = run(arena, io, environ_map, tmp_dir_path, test_args.items) catch fatal("test failed", .{}); |
| 599 | const escaped_stderr = try escapeHtml(arena, result.stderr); | 594 | const escaped_stderr = try escapeHtml(arena, result.stderr); |
| 600 | const escaped_stdout = try escapeHtml(arena, result.stdout); | 595 | const escaped_stdout = try escapeHtml(arena, result.stdout); |
| 601 | try shell_out.print("\n{s}{s}\n", .{ escaped_stderr, escaped_stdout }); | 596 | try shell_out.print("\n{s}{s}\n", .{ escaped_stderr, escaped_stdout }); |
| ... | @@ -1128,24 +1123,29 @@ fn in(slice: []const u8, number: u8) bool { | ... | @@ -1128,24 +1123,29 @@ fn in(slice: []const u8, number: u8) bool { |
| 1128 | fn run( | 1123 | fn run( |
| 1129 | allocator: Allocator, | 1124 | allocator: Allocator, |
| 1130 | io: Io, | 1125 | io: Io, |
| 1131 | env_map: *process.EnvMap, | 1126 | environ_map: *const process.Environ.Map, |
| 1132 | cwd: []const u8, | 1127 | cwd: []const u8, |
| 1133 | args: []const []const u8, | 1128 | args: []const []const u8, |
| 1134 | ) !process.Child.RunResult { | 1129 | ) !process.RunResult { |
| 1135 | const result = try process.Child.run(allocator, io, .{ | 1130 | const result = try process.run(allocator, io, .{ |
| 1136 | .argv = args, | 1131 | .argv = args, |
| 1137 | .env_map = env_map, | 1132 | .environ_map = environ_map, |
| 1138 | .cwd = cwd, | 1133 | .cwd = cwd, |
| 1139 | .max_output_bytes = max_doc_file_size, | 1134 | .max_output_bytes = max_doc_file_size, |
| 1140 | }); | 1135 | }); |
| 1141 | switch (result.term) { | 1136 | switch (result.term) { |
| 1142 | .Exited => |exit_code| { | 1137 | .exited => |exit_code| { |
| 1143 | if (exit_code != 0) { | 1138 | if (exit_code != 0) { |
| 1144 | std.debug.print("{s}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code }); | 1139 | std.debug.print("{s}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code }); |
| 1145 | dumpArgs(args); | 1140 | dumpArgs(args); |
| 1146 | return error.ChildExitError; | 1141 | return error.ChildExitError; |
| 1147 | } | 1142 | } |
| 1148 | }, | 1143 | }, |
| 1144 | .signal => |sig| { | ||
| 1145 | std.debug.print("{s}\nThe following command terminated with signal {t}:\n", .{ result.stderr, sig }); | ||
| 1146 | dumpArgs(args); | ||
| 1147 | return error.ChildCrashed; | ||
| 1148 | }, | ||
| 1149 | else => { | 1149 | else => { |
| 1150 | std.debug.print("{s}\nThe following command crashed:\n", .{result.stderr}); | 1150 | std.debug.print("{s}\nThe following command crashed:\n", .{result.stderr}); |
| 1151 | dumpArgs(args); | 1151 | dumpArgs(args); |
tools/dump-cov.zig+5-14| ... | @@ -8,20 +8,11 @@ const Path = std.Build.Cache.Path; | ... | @@ -8,20 +8,11 @@ const Path = std.Build.Cache.Path; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader; | 9 | const SeenPcsHeader = std.Build.abi.fuzz.SeenPcsHeader; |
| 10 | 10 | ||
| 11 | pub fn main() !void { | 11 | pub fn main(init: std.process.Init) !void { |
| 12 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | 12 | const gpa = init.gpa; |
| 13 | defer _ = debug_allocator.deinit(); | 13 | const arena = init.arena.allocator(); |
| 14 | const gpa = debug_allocator.allocator(); | 14 | const io = init.io; |
| 15 | 15 | const args = try init.minimal.args.toSlice(arena); | |
| 16 | var arena_instance: std.heap.ArenaAllocator = .init(gpa); | ||
| 17 | defer arena_instance.deinit(); | ||
| 18 | const arena = arena_instance.allocator(); | ||
| 19 | |||
| 20 | var threaded: Io.Threaded = .init(gpa, .{}); | ||
| 21 | defer threaded.deinit(); | ||
| 22 | const io = threaded.io(); | ||
| 23 | |||
| 24 | const args = try std.process.argsAlloc(arena); | ||
| 25 | 16 | ||
| 26 | const target_query_str = switch (args.len) { | 17 | const target_query_str = switch (args.len) { |
| 27 | 3 => "native", | 18 | 3 => "native", |
tools/fetch_them_macos_headers.zig+11-21| ... | @@ -6,13 +6,9 @@ const process = std.process; | ... | @@ -6,13 +6,9 @@ const process = std.process; |
| 6 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 7 | const fatal = std.process.fatal; | 7 | const fatal = std.process.fatal; |
| 8 | const info = std.log.info; | 8 | const info = std.log.info; |
| 9 | 9 | const Allocator = std.mem.Allocator; | |
| 10 | const Allocator = mem.Allocator; | ||
| 11 | const OsTag = std.Target.Os.Tag; | 10 | const OsTag = std.Target.Os.Tag; |
| 12 | 11 | ||
| 13 | var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 14 | const gpa = general_purpose_allocator.allocator(); | ||
| 15 | |||
| 16 | const Arch = enum { | 12 | const Arch = enum { |
| 17 | aarch64, | 13 | aarch64, |
| 18 | x86_64, | 14 | x86_64, |
| ... | @@ -66,14 +62,12 @@ const usage = | ... | @@ -66,14 +62,12 @@ const usage = |
| 66 | \\-h, --help Print this help and exit | 62 | \\-h, --help Print this help and exit |
| 67 | ; | 63 | ; |
| 68 | 64 | ||
| 69 | pub fn main() anyerror!void { | 65 | pub fn main(init: std.process.Init) !void { |
| 70 | var arena = std.heap.ArenaAllocator.init(gpa); | 66 | const io = init.io; |
| 71 | defer arena.deinit(); | 67 | const arena = init.arena.allocator(); |
| 72 | const allocator = arena.allocator(); | 68 | const args = try init.minimal.args.toSlice(arena); |
| 73 | 69 | ||
| 74 | const args = try std.process.argsAlloc(allocator); | 70 | var argv = std.array_list.Managed([]const u8).init(arena); |
| 75 | |||
| 76 | var argv = std.array_list.Managed([]const u8).init(allocator); | ||
| 77 | var sysroot: ?[]const u8 = null; | 71 | var sysroot: ?[]const u8 = null; |
| 78 | 72 | ||
| 79 | var args_iter = ArgsIterator{ .args = args[1..] }; | 73 | var args_iter = ArgsIterator{ .args = args[1..] }; |
| ... | @@ -85,23 +79,19 @@ pub fn main() anyerror!void { | ... | @@ -85,23 +79,19 @@ pub fn main() anyerror!void { |
| 85 | } else try argv.append(arg); | 79 | } else try argv.append(arg); |
| 86 | } | 80 | } |
| 87 | 81 | ||
| 88 | var threaded: Io.Threaded = .init(gpa, .{}); | ||
| 89 | defer threaded.deinit(); | ||
| 90 | const io = threaded.io(); | ||
| 91 | |||
| 92 | const sysroot_path = sysroot orelse blk: { | 82 | const sysroot_path = sysroot orelse blk: { |
| 93 | const target = try std.zig.system.resolveTargetQuery(io, .{}); | 83 | const target = try std.zig.system.resolveTargetQuery(io, .{}); |
| 94 | break :blk std.zig.system.darwin.getSdk(allocator, io, &target) orelse | 84 | break :blk std.zig.system.darwin.getSdk(arena, io, &target) orelse |
| 95 | fatal("no SDK found; you can provide one explicitly with '--sysroot' flag", .{}); | 85 | fatal("no SDK found; you can provide one explicitly with '--sysroot' flag", .{}); |
| 96 | }; | 86 | }; |
| 97 | 87 | ||
| 98 | var sdk_dir = try Dir.cwd().openDir(io, sysroot_path, .{}); | 88 | var sdk_dir = try Dir.cwd().openDir(io, sysroot_path, .{}); |
| 99 | defer sdk_dir.close(io); | 89 | defer sdk_dir.close(io); |
| 100 | const sdk_info = try sdk_dir.readFileAlloc(io, "SDKSettings.json", allocator, .limited(std.math.maxInt(u32))); | 90 | const sdk_info = try sdk_dir.readFileAlloc(io, "SDKSettings.json", arena, .limited(std.math.maxInt(u32))); |
| 101 | 91 | ||
| 102 | const parsed_json = try std.json.parseFromSlice(struct { | 92 | const parsed_json = try std.json.parseFromSlice(struct { |
| 103 | DefaultProperties: struct { MACOSX_DEPLOYMENT_TARGET: []const u8 }, | 93 | DefaultProperties: struct { MACOSX_DEPLOYMENT_TARGET: []const u8 }, |
| 104 | }, allocator, sdk_info, .{ .ignore_unknown_fields = true }); | 94 | }, arena, sdk_info, .{ .ignore_unknown_fields = true }); |
| 105 | 95 | ||
| 106 | const version = Version.parse(parsed_json.value.DefaultProperties.MACOSX_DEPLOYMENT_TARGET) orelse | 96 | const version = Version.parse(parsed_json.value.DefaultProperties.MACOSX_DEPLOYMENT_TARGET) orelse |
| 107 | fatal("don't know how to parse SDK version: {s}", .{ | 97 | fatal("don't know how to parse SDK version: {s}", .{ |
| ... | @@ -117,7 +107,7 @@ pub fn main() anyerror!void { | ... | @@ -117,7 +107,7 @@ pub fn main() anyerror!void { |
| 117 | .arch = arch, | 107 | .arch = arch, |
| 118 | .os_ver = os_ver, | 108 | .os_ver = os_ver, |
| 119 | }; | 109 | }; |
| 120 | try fetchTarget(allocator, io, argv.items, sysroot_path, target, version, tmp_dir); | 110 | try fetchTarget(arena, io, argv.items, sysroot_path, target, version, tmp_dir); |
| 121 | } | 111 | } |
| 122 | } | 112 | } |
| 123 | 113 | ||
| ... | @@ -164,7 +154,7 @@ fn fetchTarget( | ... | @@ -164,7 +154,7 @@ fn fetchTarget( |
| 164 | }); | 154 | }); |
| 165 | try cc_argv.appendSlice(args); | 155 | try cc_argv.appendSlice(args); |
| 166 | 156 | ||
| 167 | const res = try std.process.Child.run(arena, io, .{ .argv = cc_argv.items }); | 157 | const res = try std.process.run(arena, io, .{ .argv = cc_argv.items }); |
| 168 | 158 | ||
| 169 | if (res.stderr.len != 0) { | 159 | if (res.stderr.len != 0) { |
| 170 | std.log.err("{s}", .{res.stderr}); | 160 | std.log.err("{s}", .{res.stderr}); |
tools/gen_macos_headers_c.zig+4-12| ... | @@ -6,9 +6,6 @@ const info = std.log.info; | ... | @@ -6,9 +6,6 @@ const info = std.log.info; |
| 6 | const fatal = std.process.fatal; | 6 | const fatal = std.process.fatal; |
| 7 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 8 | 8 | ||
| 9 | var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 10 | const gpa = general_purpose_allocator.allocator(); | ||
| 11 | |||
| 12 | const usage = | 9 | const usage = |
| 13 | \\gen_macos_headers_c [dir] | 10 | \\gen_macos_headers_c [dir] |
| 14 | \\ | 11 | \\ |
| ... | @@ -16,16 +13,11 @@ const usage = | ... | @@ -16,16 +13,11 @@ const usage = |
| 16 | \\-h, --help Print this help and exit | 13 | \\-h, --help Print this help and exit |
| 17 | ; | 14 | ; |
| 18 | 15 | ||
| 19 | pub fn main() anyerror!void { | 16 | pub fn main(init: std.process.Init) !void { |
| 20 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | 17 | const arena = init.arena.allocator(); |
| 21 | defer arena_allocator.deinit(); | 18 | const io = init.io; |
| 22 | const arena = arena_allocator.allocator(); | 19 | const args = try init.minimal.args.toSlice(arena); |
| 23 | |||
| 24 | var threaded: Io.Threaded = .init(gpa, .{}); | ||
| 25 | defer threaded.deinit(); | ||
| 26 | const io = threaded.io(); | ||
| 27 | 20 | ||
| 28 | const args = try std.process.argsAlloc(arena); | ||
| 29 | if (args.len == 1) fatal("no command or option specified", .{}); | 21 | if (args.len == 1) fatal("no command or option specified", .{}); |
| 30 | 22 | ||
| 31 | var positionals = std.array_list.Managed([]const u8).init(arena); | 23 | var positionals = std.array_list.Managed([]const u8).init(arena); |
tools/gen_outline_atomics.zig+3-8| ... | @@ -11,14 +11,9 @@ const AtomicOp = enum { | ... | @@ -11,14 +11,9 @@ const AtomicOp = enum { |
| 11 | ldset, | 11 | ldset, |
| 12 | }; | 12 | }; |
| 13 | 13 | ||
| 14 | pub fn main() !void { | 14 | pub fn main(init: std.process.Init) !void { |
| 15 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 15 | const arena = init.arena.allocator(); |
| 16 | defer arena_instance.deinit(); | 16 | const io = init.io; |
| 17 | const arena = arena_instance.allocator(); | ||
| 18 | |||
| 19 | var threaded: std.Io.Threaded = .init(arena, .{}); | ||
| 20 | defer threaded.deinit(); | ||
| 21 | const io = threaded.io(); | ||
| 22 | 17 | ||
| 23 | //const args = try std.process.argsAlloc(arena); | 18 | //const args = try std.process.argsAlloc(arena); |
| 24 | 19 |
tools/gen_spirv_spec.zig+49-49| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Io = std.Io; | 2 | const Io = std.Io; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const assert = std.debug.assert; | ||
| 4 | 5 | ||
| 5 | const g = @import("spirv/grammar.zig"); | 6 | const g = @import("spirv/grammar.zig"); |
| 6 | const CoreRegistry = g.CoreRegistry; | 7 | const CoreRegistry = g.CoreRegistry; |
| ... | @@ -54,28 +55,22 @@ const set_names = std.StaticStringMap(struct { []const u8, []const u8 }).initCom | ... | @@ -54,28 +55,22 @@ const set_names = std.StaticStringMap(struct { []const u8, []const u8 }).initCom |
| 54 | .{ "zig", .{ "zig", "Zig" } }, | 55 | .{ "zig", .{ "zig", "Zig" } }, |
| 55 | }); | 56 | }); |
| 56 | 57 | ||
| 57 | var arena = std.heap.ArenaAllocator.init(std.heap.smp_allocator); | 58 | pub fn main(init: std.process.Init) !void { |
| 58 | const allocator = arena.allocator(); | 59 | const arena = init.arena.allocator(); |
| 59 | 60 | const args = try init.minimal.args.toSlice(arena); | |
| 60 | pub fn main() !void { | ||
| 61 | defer arena.deinit(); | ||
| 62 | |||
| 63 | const args = try std.process.argsAlloc(allocator); | ||
| 64 | if (args.len != 3) { | 61 | if (args.len != 3) { |
| 65 | usageAndExit(args[0], 1); | 62 | usageAndExit(args[0], 1); |
| 66 | } | 63 | } |
| 67 | 64 | ||
| 68 | var threaded: std.Io.Threaded = .init(allocator, .{}); | 65 | const io = init.io; |
| 69 | defer threaded.deinit(); | ||
| 70 | const io = threaded.io(); | ||
| 71 | 66 | ||
| 72 | const json_path = try Io.Dir.path.join(allocator, &.{ args[1], "include/spirv/unified1/" }); | 67 | const json_path = try Io.Dir.path.join(arena, &.{ args[1], "include/spirv/unified1/" }); |
| 73 | const dir = try Io.Dir.cwd().openDir(io, json_path, .{ .iterate = true }); | 68 | const dir = try Io.Dir.cwd().openDir(io, json_path, .{ .iterate = true }); |
| 74 | 69 | ||
| 75 | const core_spec = try readRegistry(io, CoreRegistry, dir, "spirv.core.grammar.json"); | 70 | const core_spec = try readRegistry(io, arena, CoreRegistry, dir, "spirv.core.grammar.json"); |
| 76 | std.mem.sortUnstable(Instruction, core_spec.instructions, CmpInst{}, CmpInst.lt); | 71 | std.mem.sortUnstable(Instruction, core_spec.instructions, CmpInst{}, CmpInst.lt); |
| 77 | 72 | ||
| 78 | var exts = std.array_list.Managed(Extension).init(allocator); | 73 | var exts = std.array_list.Managed(Extension).init(arena); |
| 79 | 74 | ||
| 80 | var it = dir.iterate(); | 75 | var it = dir.iterate(); |
| 81 | while (try it.next(io)) |entry| { | 76 | while (try it.next(io)) |entry| { |
| ... | @@ -83,48 +78,48 @@ pub fn main() !void { | ... | @@ -83,48 +78,48 @@ pub fn main() !void { |
| 83 | continue; | 78 | continue; |
| 84 | } | 79 | } |
| 85 | 80 | ||
| 86 | try readExtRegistry(io, &exts, dir, entry.name); | 81 | try readExtRegistry(io, arena, &exts, dir, entry.name); |
| 87 | } | 82 | } |
| 88 | 83 | ||
| 89 | try readExtRegistry(io, &exts, Io.Dir.cwd(), args[2]); | 84 | try readExtRegistry(io, arena, &exts, Io.Dir.cwd(), args[2]); |
| 90 | 85 | ||
| 91 | var allocating: std.Io.Writer.Allocating = .init(allocator); | 86 | var allocating: std.Io.Writer.Allocating = .init(arena); |
| 92 | defer allocating.deinit(); | 87 | defer allocating.deinit(); |
| 93 | try render(&allocating.writer, core_spec, exts.items); | 88 | try render(arena, &allocating.writer, core_spec, exts.items); |
| 94 | try allocating.writer.writeByte(0); | 89 | try allocating.writer.writeByte(0); |
| 95 | const output = allocating.written()[0 .. allocating.written().len - 1 :0]; | 90 | const output = allocating.written()[0 .. allocating.written().len - 1 :0]; |
| 96 | 91 | ||
| 97 | var tree = try std.zig.Ast.parse(allocator, output, .zig); | 92 | var tree = try std.zig.Ast.parse(arena, output, .zig); |
| 98 | 93 | ||
| 99 | if (tree.errors.len != 0) { | 94 | if (tree.errors.len != 0) { |
| 100 | try std.zig.printAstErrorsToStderr(allocator, io, tree, "", .auto); | 95 | try std.zig.printAstErrorsToStderr(arena, io, tree, "", .auto); |
| 101 | return; | 96 | return; |
| 102 | } | 97 | } |
| 103 | 98 | ||
| 104 | var zir = try std.zig.AstGen.generate(allocator, tree); | 99 | var zir = try std.zig.AstGen.generate(arena, tree); |
| 105 | if (zir.hasCompileErrors()) { | 100 | if (zir.hasCompileErrors()) { |
| 106 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; | 101 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 107 | try wip_errors.init(allocator); | 102 | try wip_errors.init(arena); |
| 108 | defer wip_errors.deinit(); | 103 | defer wip_errors.deinit(); |
| 109 | try wip_errors.addZirErrorMessages(zir, tree, output, ""); | 104 | try wip_errors.addZirErrorMessages(zir, tree, output, ""); |
| 110 | var error_bundle = try wip_errors.toOwnedBundle(""); | 105 | var error_bundle = try wip_errors.toOwnedBundle(""); |
| 111 | defer error_bundle.deinit(allocator); | 106 | defer error_bundle.deinit(arena); |
| 112 | try error_bundle.renderToStderr(io, .{}, .auto); | 107 | try error_bundle.renderToStderr(io, .{}, .auto); |
| 113 | } | 108 | } |
| 114 | 109 | ||
| 115 | const formatted_output = try tree.renderAlloc(allocator); | 110 | const formatted_output = try tree.renderAlloc(arena); |
| 116 | try Io.File.stdout().writeStreamingAll(io, formatted_output); | 111 | try Io.File.stdout().writeStreamingAll(io, formatted_output); |
| 117 | } | 112 | } |
| 118 | 113 | ||
| 119 | fn readExtRegistry(io: Io, exts: *std.array_list.Managed(Extension), dir: Io.Dir, sub_path: []const u8) !void { | 114 | fn readExtRegistry(io: Io, arena: Allocator, exts: *std.array_list.Managed(Extension), dir: Io.Dir, sub_path: []const u8) !void { |
| 120 | const filename = Io.Dir.path.basename(sub_path); | 115 | const filename = Io.Dir.path.basename(sub_path); |
| 121 | if (!std.mem.startsWith(u8, filename, "extinst.")) { | 116 | if (!std.mem.startsWith(u8, filename, "extinst.")) { |
| 122 | return; | 117 | return; |
| 123 | } | 118 | } |
| 124 | 119 | ||
| 125 | std.debug.assert(std.mem.endsWith(u8, filename, ".grammar.json")); | 120 | assert(std.mem.endsWith(u8, filename, ".grammar.json")); |
| 126 | const name = filename["extinst.".len .. filename.len - ".grammar.json".len]; | 121 | const name = filename["extinst.".len .. filename.len - ".grammar.json".len]; |
| 127 | const spec = try readRegistry(io, ExtensionRegistry, dir, sub_path); | 122 | const spec = try readRegistry(io, arena, ExtensionRegistry, dir, sub_path); |
| 128 | 123 | ||
| 129 | const set_name = set_names.get(name) orelse { | 124 | const set_name = set_names.get(name) orelse { |
| 130 | std.log.info("ignored instruction set '{s}'", .{name}); | 125 | 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 | ... | @@ -140,16 +135,16 @@ fn readExtRegistry(io: Io, exts: *std.array_list.Managed(Extension), dir: Io.Dir |
| 140 | }); | 135 | }); |
| 141 | } | 136 | } |
| 142 | 137 | ||
| 143 | fn readRegistry(io: Io, comptime RegistryType: type, dir: Io.Dir, path: []const u8) !RegistryType { | 138 | fn readRegistry(io: Io, arena: Allocator, comptime RegistryType: type, dir: Io.Dir, path: []const u8) !RegistryType { |
| 144 | const spec = try dir.readFileAlloc(io, path, allocator, .unlimited); | 139 | const spec = try dir.readFileAlloc(io, path, arena, .unlimited); |
| 145 | // Required for json parsing. | 140 | // Required for json parsing. |
| 146 | // TODO: ALI | 141 | // TODO: ALI |
| 147 | @setEvalBranchQuota(10000); | 142 | @setEvalBranchQuota(10000); |
| 148 | 143 | ||
| 149 | var scanner = std.json.Scanner.initCompleteInput(allocator, spec); | 144 | var scanner = std.json.Scanner.initCompleteInput(arena, spec); |
| 150 | var diagnostics = std.json.Diagnostics{}; | 145 | var diagnostics = std.json.Diagnostics{}; |
| 151 | scanner.enableDiagnostics(&diagnostics); | 146 | scanner.enableDiagnostics(&diagnostics); |
| 152 | const parsed = std.json.parseFromTokenSource(RegistryType, allocator, &scanner, .{}) catch |err| { | 147 | const parsed = std.json.parseFromTokenSource(RegistryType, arena, &scanner, .{}) catch |err| { |
| 153 | std.debug.print("{s}:{}:{}:\n", .{ path, diagnostics.getLine(), diagnostics.getColumn() }); | 148 | std.debug.print("{s}:{}:{}:\n", .{ path, diagnostics.getLine(), diagnostics.getColumn() }); |
| 154 | return err; | 149 | return err; |
| 155 | }; | 150 | }; |
| ... | @@ -158,8 +153,8 @@ fn readRegistry(io: Io, comptime RegistryType: type, dir: Io.Dir, path: []const | ... | @@ -158,8 +153,8 @@ fn readRegistry(io: Io, comptime RegistryType: type, dir: Io.Dir, path: []const |
| 158 | 153 | ||
| 159 | /// Returns a set with types that require an extra struct for the `Instruction` interface | 154 | /// Returns a set with types that require an extra struct for the `Instruction` interface |
| 160 | /// to the spir-v spec, or whether the original type can be used. | 155 | /// to the spir-v spec, or whether the original type can be used. |
| 161 | fn extendedStructs(kinds: []const OperandKind) !ExtendedStructSet { | 156 | fn extendedStructs(arena: Allocator, kinds: []const OperandKind) !ExtendedStructSet { |
| 162 | var map = ExtendedStructSet.init(allocator); | 157 | var map = ExtendedStructSet.init(arena); |
| 163 | try map.ensureTotalCapacity(@as(u32, @intCast(kinds.len))); | 158 | try map.ensureTotalCapacity(@as(u32, @intCast(kinds.len))); |
| 164 | 159 | ||
| 165 | for (kinds) |kind| { | 160 | for (kinds) |kind| { |
| ... | @@ -194,6 +189,7 @@ fn tagPriorityScore(tag: []const u8) usize { | ... | @@ -194,6 +189,7 @@ fn tagPriorityScore(tag: []const u8) usize { |
| 194 | } | 189 | } |
| 195 | 190 | ||
| 196 | fn render( | 191 | fn render( |
| 192 | arena: Allocator, | ||
| 197 | writer: *std.Io.Writer, | 193 | writer: *std.Io.Writer, |
| 198 | registry: CoreRegistry, | 194 | registry: CoreRegistry, |
| 199 | extensions: []const Extension, | 195 | extensions: []const Extension, |
| ... | @@ -299,7 +295,7 @@ fn render( | ... | @@ -299,7 +295,7 @@ fn render( |
| 299 | ); | 295 | ); |
| 300 | 296 | ||
| 301 | // Merge the operand kinds from all extensions together. | 297 | // Merge the operand kinds from all extensions together. |
| 302 | var all_operand_kinds = OperandKindMap.init(allocator); | 298 | var all_operand_kinds = OperandKindMap.init(arena); |
| 303 | for (registry.operand_kinds) |kind| { | 299 | for (registry.operand_kinds) |kind| { |
| 304 | try all_operand_kinds.putNoClobber(.{ "core", kind.kind }, kind); | 300 | try all_operand_kinds.putNoClobber(.{ "core", kind.kind }, kind); |
| 305 | } | 301 | } |
| ... | @@ -312,22 +308,22 @@ fn render( | ... | @@ -312,22 +308,22 @@ fn render( |
| 312 | try all_operand_kinds.ensureUnusedCapacity(ext.spec.operand_kinds.len); | 308 | try all_operand_kinds.ensureUnusedCapacity(ext.spec.operand_kinds.len); |
| 313 | for (ext.spec.operand_kinds) |kind| { | 309 | for (ext.spec.operand_kinds) |kind| { |
| 314 | var new_kind = kind; | 310 | var new_kind = kind; |
| 315 | new_kind.kind = try std.mem.join(allocator, ".", &.{ ext.name, kind.kind }); | 311 | new_kind.kind = try std.mem.join(arena, ".", &.{ ext.name, kind.kind }); |
| 316 | try all_operand_kinds.putNoClobber(.{ ext.name, kind.kind }, new_kind); | 312 | try all_operand_kinds.putNoClobber(.{ ext.name, kind.kind }, new_kind); |
| 317 | } | 313 | } |
| 318 | } | 314 | } |
| 319 | 315 | ||
| 320 | const extended_structs = try extendedStructs(all_operand_kinds.values()); | 316 | const extended_structs = try extendedStructs(arena, all_operand_kinds.values()); |
| 321 | // Note: extensions don't seem to have class. | 317 | // Note: extensions don't seem to have class. |
| 322 | try renderClass(writer, registry.instructions); | 318 | try renderClass(arena, writer, registry.instructions); |
| 323 | try renderOperandKind(writer, all_operand_kinds.values()); | 319 | try renderOperandKind(writer, all_operand_kinds.values()); |
| 324 | 320 | ||
| 325 | try renderOpcodes(writer, "Opcode", true, registry.instructions, extended_structs); | 321 | try renderOpcodes(arena, writer, "Opcode", true, registry.instructions, extended_structs); |
| 326 | for (extensions) |ext| { | 322 | for (extensions) |ext| { |
| 327 | try renderOpcodes(writer, ext.opcode_name, false, ext.spec.instructions, extended_structs); | 323 | try renderOpcodes(arena, writer, ext.opcode_name, false, ext.spec.instructions, extended_structs); |
| 328 | } | 324 | } |
| 329 | 325 | ||
| 330 | try renderOperandKinds(writer, all_operand_kinds.values(), extended_structs); | 326 | try renderOperandKinds(arena, writer, all_operand_kinds.values(), extended_structs); |
| 331 | try renderInstructionSet(writer, registry, extensions, all_operand_kinds); | 327 | try renderInstructionSet(writer, registry, extensions, all_operand_kinds); |
| 332 | } | 328 | } |
| 333 | 329 | ||
| ... | @@ -414,8 +410,8 @@ fn renderInstructionsCase( | ... | @@ -414,8 +410,8 @@ fn renderInstructionsCase( |
| 414 | ); | 410 | ); |
| 415 | } | 411 | } |
| 416 | 412 | ||
| 417 | fn renderClass(writer: *std.Io.Writer, instructions: []const Instruction) !void { | 413 | fn renderClass(arena: Allocator, writer: *std.Io.Writer, instructions: []const Instruction) !void { |
| 418 | var class_map = std.StringArrayHashMap(void).init(allocator); | 414 | var class_map = std.StringArrayHashMap(void).init(arena); |
| 419 | 415 | ||
| 420 | for (instructions) |inst| { | 416 | for (instructions) |inst| { |
| 421 | if (std.mem.eql(u8, inst.class.?, "@exclude")) continue; | 417 | if (std.mem.eql(u8, inst.class.?, "@exclude")) continue; |
| ... | @@ -535,16 +531,17 @@ fn renderEnumerant(writer: *std.Io.Writer, enumerant: Enumerant) !void { | ... | @@ -535,16 +531,17 @@ fn renderEnumerant(writer: *std.Io.Writer, enumerant: Enumerant) !void { |
| 535 | } | 531 | } |
| 536 | 532 | ||
| 537 | fn renderOpcodes( | 533 | fn renderOpcodes( |
| 534 | arena: Allocator, | ||
| 538 | writer: *std.Io.Writer, | 535 | writer: *std.Io.Writer, |
| 539 | opcode_type_name: []const u8, | 536 | opcode_type_name: []const u8, |
| 540 | want_operands: bool, | 537 | want_operands: bool, |
| 541 | instructions: []const Instruction, | 538 | instructions: []const Instruction, |
| 542 | extended_structs: ExtendedStructSet, | 539 | extended_structs: ExtendedStructSet, |
| 543 | ) !void { | 540 | ) !void { |
| 544 | var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator); | 541 | var inst_map = std.AutoArrayHashMap(u32, usize).init(arena); |
| 545 | try inst_map.ensureTotalCapacity(instructions.len); | 542 | try inst_map.ensureTotalCapacity(instructions.len); |
| 546 | 543 | ||
| 547 | var aliases = std.array_list.Managed(struct { inst: usize, alias: usize }).init(allocator); | 544 | var aliases = std.array_list.Managed(struct { inst: usize, alias: usize }).init(arena); |
| 548 | try aliases.ensureTotalCapacity(instructions.len); | 545 | try aliases.ensureTotalCapacity(instructions.len); |
| 549 | 546 | ||
| 550 | for (instructions, 0..) |inst, i| { | 547 | for (instructions, 0..) |inst, i| { |
| ... | @@ -634,30 +631,32 @@ fn renderOpcodes( | ... | @@ -634,30 +631,32 @@ fn renderOpcodes( |
| 634 | } | 631 | } |
| 635 | 632 | ||
| 636 | fn renderOperandKinds( | 633 | fn renderOperandKinds( |
| 634 | arena: Allocator, | ||
| 637 | writer: *std.Io.Writer, | 635 | writer: *std.Io.Writer, |
| 638 | kinds: []const OperandKind, | 636 | kinds: []const OperandKind, |
| 639 | extended_structs: ExtendedStructSet, | 637 | extended_structs: ExtendedStructSet, |
| 640 | ) !void { | 638 | ) !void { |
| 641 | for (kinds) |kind| { | 639 | for (kinds) |kind| { |
| 642 | switch (kind.category) { | 640 | switch (kind.category) { |
| 643 | .ValueEnum => try renderValueEnum(writer, kind, extended_structs), | 641 | .ValueEnum => try renderValueEnum(arena, writer, kind, extended_structs), |
| 644 | .BitEnum => try renderBitEnum(writer, kind, extended_structs), | 642 | .BitEnum => try renderBitEnum(arena, writer, kind, extended_structs), |
| 645 | else => {}, | 643 | else => {}, |
| 646 | } | 644 | } |
| 647 | } | 645 | } |
| 648 | } | 646 | } |
| 649 | 647 | ||
| 650 | fn renderValueEnum( | 648 | fn renderValueEnum( |
| 649 | arena: Allocator, | ||
| 651 | writer: *std.Io.Writer, | 650 | writer: *std.Io.Writer, |
| 652 | enumeration: OperandKind, | 651 | enumeration: OperandKind, |
| 653 | extended_structs: ExtendedStructSet, | 652 | extended_structs: ExtendedStructSet, |
| 654 | ) !void { | 653 | ) !void { |
| 655 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; | 654 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 656 | 655 | ||
| 657 | var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator); | 656 | var enum_map = std.AutoArrayHashMap(u32, usize).init(arena); |
| 658 | try enum_map.ensureTotalCapacity(enumerants.len); | 657 | try enum_map.ensureTotalCapacity(enumerants.len); |
| 659 | 658 | ||
| 660 | var aliases = std.array_list.Managed(struct { enumerant: usize, alias: usize }).init(allocator); | 659 | var aliases = std.array_list.Managed(struct { enumerant: usize, alias: usize }).init(arena); |
| 661 | try aliases.ensureTotalCapacity(enumerants.len); | 660 | try aliases.ensureTotalCapacity(enumerants.len); |
| 662 | 661 | ||
| 663 | for (enumerants, 0..) |enumerant, i| { | 662 | for (enumerants, 0..) |enumerant, i| { |
| ... | @@ -726,6 +725,7 @@ fn renderValueEnum( | ... | @@ -726,6 +725,7 @@ fn renderValueEnum( |
| 726 | } | 725 | } |
| 727 | 726 | ||
| 728 | fn renderBitEnum( | 727 | fn renderBitEnum( |
| 728 | arena: Allocator, | ||
| 729 | writer: *std.Io.Writer, | 729 | writer: *std.Io.Writer, |
| 730 | enumeration: OperandKind, | 730 | enumeration: OperandKind, |
| 731 | extended_structs: ExtendedStructSet, | 731 | extended_structs: ExtendedStructSet, |
| ... | @@ -735,7 +735,7 @@ fn renderBitEnum( | ... | @@ -735,7 +735,7 @@ fn renderBitEnum( |
| 735 | var flags_by_bitpos = [_]?usize{null} ** 32; | 735 | var flags_by_bitpos = [_]?usize{null} ** 32; |
| 736 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; | 736 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 737 | 737 | ||
| 738 | var aliases = std.array_list.Managed(struct { flag: usize, alias: u5 }).init(allocator); | 738 | var aliases = std.array_list.Managed(struct { flag: usize, alias: u5 }).init(arena); |
| 739 | try aliases.ensureTotalCapacity(enumerants.len); | 739 | try aliases.ensureTotalCapacity(enumerants.len); |
| 740 | 740 | ||
| 741 | for (enumerants, 0..) |enumerant, i| { | 741 | for (enumerants, 0..) |enumerant, i| { |
| ... | @@ -749,7 +749,7 @@ fn renderBitEnum( | ... | @@ -749,7 +749,7 @@ fn renderBitEnum( |
| 749 | continue; | 749 | continue; |
| 750 | } | 750 | } |
| 751 | 751 | ||
| 752 | std.debug.assert(@popCount(value) == 1); | 752 | assert(@popCount(value) == 1); |
| 753 | 753 | ||
| 754 | const bitpos = std.math.log2_int(u32, value); | 754 | const bitpos = std.math.log2_int(u32, value); |
| 755 | if (flags_by_bitpos[bitpos]) |*existing| { | 755 | if (flags_by_bitpos[bitpos]) |*existing| { |
tools/gen_stubs.zig+4-10| ... | @@ -281,16 +281,10 @@ const Parse = struct { | ... | @@ -281,16 +281,10 @@ const Parse = struct { |
| 281 | arch: Arch, | 281 | arch: Arch, |
| 282 | }; | 282 | }; |
| 283 | 283 | ||
| 284 | pub fn main() !void { | 284 | pub fn main(init: std.process.Init) !void { |
| 285 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 285 | const arena = init.arena.allocator(); |
| 286 | defer arena_instance.deinit(); | 286 | const io = init.io; |
| 287 | const arena = arena_instance.allocator(); | 287 | const args = try init.minimal.args.toSlice(arena); |
| 288 | |||
| 289 | var threaded: std.Io.Threaded = .init(arena, .{}); | ||
| 290 | defer threaded.deinit(); | ||
| 291 | const io = threaded.io(); | ||
| 292 | |||
| 293 | const args = try std.process.argsAlloc(arena); | ||
| 294 | const build_all_path = args[1]; | 288 | const build_all_path = args[1]; |
| 295 | 289 | ||
| 296 | var build_all_dir = try Io.Dir.cwd().openDir(io, build_all_path, .{}); | 290 | var build_all_dir = try Io.Dir.cwd().openDir(io, build_all_path, .{}); |
tools/generate_JSONTestSuite.zig+3-7| ... | @@ -3,13 +3,9 @@ | ... | @@ -3,13 +3,9 @@ |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const Io = std.Io; | 4 | const Io = std.Io; |
| 5 | 5 | ||
| 6 | pub fn main() !void { | 6 | pub fn main(init: std.process.Init) !void { |
| 7 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 7 | const allocator = init.gpa; |
| 8 | var allocator = gpa.allocator(); | 8 | const io = init.io; |
| 9 | |||
| 10 | var threaded: std.Io.Threaded = .init(allocator, .{}); | ||
| 11 | defer threaded.deinit(); | ||
| 12 | const io = threaded.io(); | ||
| 13 | 9 | ||
| 14 | var stdout_buffer: [2000]u8 = undefined; | 10 | var stdout_buffer: [2000]u8 = undefined; |
| 15 | var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); | 11 | var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); |
tools/generate_c_size_and_align_checks.zig+3-10| ... | @@ -28,22 +28,15 @@ fn cName(ty: std.Target.CType) []const u8 { | ... | @@ -28,22 +28,15 @@ fn cName(ty: std.Target.CType) []const u8 { |
| 28 | 28 | ||
| 29 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; | 29 | var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init; |
| 30 | 30 | ||
| 31 | pub fn main() !void { | 31 | pub fn main(init: std.process.Init) !void { |
| 32 | const gpa = general_purpose_allocator.allocator(); | 32 | const args = try init.minimal.args.toSlice(init.arena.allocator()); |
| 33 | defer std.debug.assert(general_purpose_allocator.deinit() == .ok); | 33 | const io = init.io; |
| 34 | |||
| 35 | const args = try std.process.argsAlloc(gpa); | ||
| 36 | defer std.process.argsFree(gpa, args); | ||
| 37 | 34 | ||
| 38 | if (args.len != 2) { | 35 | if (args.len != 2) { |
| 39 | std.debug.print("Usage: {s} [target_triple]\n", .{args[0]}); | 36 | std.debug.print("Usage: {s} [target_triple]\n", .{args[0]}); |
| 40 | std.process.exit(1); | 37 | std.process.exit(1); |
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 44 | defer threaded.deinit(); | ||
| 45 | const io = threaded.io(); | ||
| 46 | |||
| 47 | const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] }); | 40 | const query = try std.Target.Query.parse(.{ .arch_os_abi = args[1] }); |
| 48 | const target = try std.zig.system.resolveTargetQuery(io, query); | 41 | const target = try std.zig.system.resolveTargetQuery(io, query); |
| 49 | 42 |
tools/generate_linux_syscalls.zig+5-11| ... | @@ -170,16 +170,11 @@ const architectures: []const Arch = &.{ | ... | @@ -170,16 +170,11 @@ const architectures: []const Arch = &.{ |
| 170 | // .{ .@"var" = "Microblaze", .table = .{ .specific = "arch/microblaze/kernel/syscalls/syscall.tbl" } }, | 170 | // .{ .@"var" = "Microblaze", .table = .{ .specific = "arch/microblaze/kernel/syscalls/syscall.tbl" } }, |
| 171 | }; | 171 | }; |
| 172 | 172 | ||
| 173 | pub fn main() !void { | 173 | pub fn main(init: std.process.Init) !void { |
| 174 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 174 | const gpa = init.gpa; |
| 175 | defer arena.deinit(); | 175 | const io = init.io; |
| 176 | const gpa = arena.allocator(); | ||
| 177 | 176 | ||
| 178 | var threaded: Io.Threaded = .init(gpa, .{}); | 177 | const args = try init.minimal.args.toSlice(init.arena.allocator()); |
| 179 | defer threaded.deinit(); | ||
| 180 | const io = threaded.io(); | ||
| 181 | |||
| 182 | const args = try std.process.argsAlloc(gpa); | ||
| 183 | if (args.len < 2 or mem.eql(u8, args[1], "--help")) { | 178 | if (args.len < 2 or mem.eql(u8, args[1], "--help")) { |
| 184 | const stderr = std.debug.lockStderr(&.{}); | 179 | const stderr = std.debug.lockStderr(&.{}); |
| 185 | const w = &stderr.file_writer.interface; | 180 | const w = &stderr.file_writer.interface; |
| ... | @@ -217,8 +212,7 @@ pub fn main() !void { | ... | @@ -217,8 +212,7 @@ pub fn main() !void { |
| 217 | }; | 212 | }; |
| 218 | 213 | ||
| 219 | try Io.Writer.print(stdout, | 214 | try Io.Writer.print(stdout, |
| 220 | \\// This file is automatically generated, DO NOT edit it manually. | 215 | \\// This file is automatically generated by tools/generate_linux_syscalls.zig |
| 221 | \\// See tools/generate_linux_syscalls.zig for more info. | ||
| 222 | \\// This list current as of kernel: {f} | 216 | \\// This list current as of kernel: {f} |
| 223 | \\ | 217 | \\ |
| 224 | \\ | 218 | \\ |
tools/incr-check.zig+34-38| ... | @@ -27,18 +27,12 @@ fn logImpl( | ... | @@ -27,18 +27,12 @@ fn logImpl( |
| 27 | ); | 27 | ); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | pub fn main() !void { | 30 | pub fn main(init: std.process.Init) !void { |
| 31 | const fatal = std.process.fatal; | 31 | const fatal = std.process.fatal; |
| 32 | 32 | const arena = init.arena.allocator(); | |
| 33 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 33 | const io = init.io; |
| 34 | defer arena_instance.deinit(); | 34 | const environ_map = init.environ_map; |
| 35 | const arena = arena_instance.allocator(); | 35 | const cwd_path = try std.process.getCwdAlloc(arena); |
| 36 | |||
| 37 | const gpa = arena; | ||
| 38 | |||
| 39 | var threaded: Io.Threaded = .init(gpa, .{}); | ||
| 40 | defer threaded.deinit(); | ||
| 41 | const io = threaded.io(); | ||
| 42 | 36 | ||
| 43 | var opt_zig_exe: ?[]const u8 = null; | 37 | var opt_zig_exe: ?[]const u8 = null; |
| 44 | var opt_input_file_name: ?[]const u8 = null; | 38 | var opt_input_file_name: ?[]const u8 = null; |
| ... | @@ -52,7 +46,7 @@ pub fn main() !void { | ... | @@ -52,7 +46,7 @@ pub fn main() !void { |
| 52 | 46 | ||
| 53 | var debug_log_args: std.ArrayList([]const u8) = .empty; | 47 | var debug_log_args: std.ArrayList([]const u8) = .empty; |
| 54 | 48 | ||
| 55 | var arg_it = try std.process.argsWithAllocator(arena); | 49 | var arg_it = try init.minimal.args.iterateAllocator(arena); |
| 56 | _ = arg_it.skip(); | 50 | _ = arg_it.skip(); |
| 57 | while (arg_it.next()) |arg| { | 51 | while (arg_it.next()) |arg| { |
| 58 | if (arg.len > 0 and arg[0] == '-') { | 52 | if (arg.len > 0 and arg[0] == '-') { |
| ... | @@ -119,9 +113,9 @@ pub fn main() !void { | ... | @@ -119,9 +113,9 @@ pub fn main() !void { |
| 119 | } | 113 | } |
| 120 | 114 | ||
| 121 | // Convert paths to be relative to the cwd of the subprocess. | 115 | // Convert paths to be relative to the cwd of the subprocess. |
| 122 | const resolved_zig_exe = try Dir.path.relative(arena, tmp_dir_path, zig_exe); | 116 | const resolved_zig_exe = try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, zig_exe); |
| 123 | const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir| | 117 | const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir| |
| 124 | try Dir.path.relative(arena, tmp_dir_path, lib_dir) | 118 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, lib_dir) |
| 125 | else | 119 | else |
| 126 | null; | 120 | null; |
| 127 | 121 | ||
| ... | @@ -179,18 +173,10 @@ pub fn main() !void { | ... | @@ -179,18 +173,10 @@ pub fn main() !void { |
| 179 | const zig_prog_node = target_prog_node.start("zig build-exe", 0); | 173 | const zig_prog_node = target_prog_node.start("zig build-exe", 0); |
| 180 | defer zig_prog_node.end(); | 174 | defer zig_prog_node.end(); |
| 181 | 175 | ||
| 182 | var child = std.process.Child.init(child_args.items, arena); | ||
| 183 | child.stdin_behavior = .Pipe; | ||
| 184 | child.stdout_behavior = .Pipe; | ||
| 185 | child.stderr_behavior = .Pipe; | ||
| 186 | child.progress_node = zig_prog_node; | ||
| 187 | child.cwd_dir = tmp_dir; | ||
| 188 | child.cwd = tmp_dir_path; | ||
| 189 | |||
| 190 | var cc_child_args: std.ArrayList([]const u8) = .empty; | 176 | var cc_child_args: std.ArrayList([]const u8) = .empty; |
| 191 | if (target.backend == .cbe) { | 177 | if (target.backend == .cbe) { |
| 192 | const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe| | 178 | const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe| |
| 193 | try Dir.path.relative(arena, tmp_dir_path, cc_zig_exe) | 179 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, cc_zig_exe) |
| 194 | else | 180 | else |
| 195 | resolved_zig_exe; | 181 | resolved_zig_exe; |
| 196 | 182 | ||
| ... | @@ -209,6 +195,17 @@ pub fn main() !void { | ... | @@ -209,6 +195,17 @@ pub fn main() !void { |
| 209 | try cc_child_args.append(arena, "-o"); | 195 | try cc_child_args.append(arena, "-o"); |
| 210 | } | 196 | } |
| 211 | 197 | ||
| 198 | var child = try std.process.spawn(io, .{ | ||
| 199 | .argv = child_args.items, | ||
| 200 | .stdin = .pipe, | ||
| 201 | .stdout = .pipe, | ||
| 202 | .stderr = .pipe, | ||
| 203 | .progress_node = zig_prog_node, | ||
| 204 | .cwd_dir = tmp_dir, | ||
| 205 | .cwd = tmp_dir_path, | ||
| 206 | }); | ||
| 207 | defer child.kill(io); | ||
| 208 | |||
| 212 | var eval: Eval = .{ | 209 | var eval: Eval = .{ |
| 213 | .arena = arena, | 210 | .arena = arena, |
| 214 | .io = io, | 211 | .io = io, |
| ... | @@ -227,11 +224,6 @@ pub fn main() !void { | ... | @@ -227,11 +224,6 @@ pub fn main() !void { |
| 227 | .enable_darling = enable_darling, | 224 | .enable_darling = enable_darling, |
| 228 | }; | 225 | }; |
| 229 | 226 | ||
| 230 | try child.spawn(io); | ||
| 231 | errdefer { | ||
| 232 | _ = child.kill(io) catch {}; | ||
| 233 | } | ||
| 234 | |||
| 235 | var poller = Io.poll(arena, Eval.StreamEnum, .{ | 227 | var poller = Io.poll(arena, Eval.StreamEnum, .{ |
| 236 | .stdout = child.stdout.?, | 228 | .stdout = child.stdout.?, |
| 237 | .stderr = child.stderr.?, | 229 | .stderr = child.stderr.?, |
| ... | @@ -536,7 +528,7 @@ const Eval = struct { | ... | @@ -536,7 +528,7 @@ const Eval = struct { |
| 536 | const run_prog_node = prog_node.start("run generated executable", 0); | 528 | const run_prog_node = prog_node.start("run generated executable", 0); |
| 537 | defer run_prog_node.end(); | 529 | defer run_prog_node.end(); |
| 538 | 530 | ||
| 539 | const result = std.process.Child.run(eval.arena, io, .{ | 531 | const result = std.process.run(eval.arena, io, .{ |
| 540 | .argv = argv, | 532 | .argv = argv, |
| 541 | .cwd_dir = eval.tmp_dir, | 533 | .cwd_dir = eval.tmp_dir, |
| 542 | .cwd = eval.tmp_dir_path, | 534 | .cwd = eval.tmp_dir_path, |
| ... | @@ -564,7 +556,7 @@ const Eval = struct { | ... | @@ -564,7 +556,7 @@ const Eval = struct { |
| 564 | } | 556 | } |
| 565 | 557 | ||
| 566 | switch (result.term) { | 558 | switch (result.term) { |
| 567 | .Exited => |code| switch (update.outcome) { | 559 | .exited => |code| switch (update.outcome) { |
| 568 | .unknown, .compile_errors => unreachable, | 560 | .unknown, .compile_errors => unreachable, |
| 569 | .stdout => |expected_stdout| { | 561 | .stdout => |expected_stdout| { |
| 570 | if (code != 0) { | 562 | if (code != 0) { |
| ... | @@ -572,9 +564,12 @@ const Eval = struct { | ... | @@ -572,9 +564,12 @@ const Eval = struct { |
| 572 | } | 564 | } |
| 573 | try std.testing.expectEqualStrings(expected_stdout, result.stdout); | 565 | try std.testing.expectEqualStrings(expected_stdout, result.stdout); |
| 574 | }, | 566 | }, |
| 575 | .exit_code => |expected_code| try std.testing.expectEqual(expected_code, result.term.Exited), | 567 | .exit_code => |expected_code| try std.testing.expectEqual(expected_code, code), |
| 568 | }, | ||
| 569 | .signal => |sig| { | ||
| 570 | eval.fatal("generated executable '{s}' terminated with signal {t}", .{ binary_path, sig }); | ||
| 576 | }, | 571 | }, |
| 577 | .Signal, .Stopped, .Unknown => { | 572 | .stopped, .unknown => { |
| 578 | eval.fatal("generated executable '{s}' terminated unexpectedly", .{binary_path}); | 573 | eval.fatal("generated executable '{s}' terminated unexpectedly", .{binary_path}); |
| 579 | }, | 574 | }, |
| 580 | } | 575 | } |
| ... | @@ -622,7 +617,7 @@ const Eval = struct { | ... | @@ -622,7 +617,7 @@ const Eval = struct { |
| 622 | try eval.cc_child_args.appendSlice(eval.arena, &.{ out_path, c_path }); | 617 | try eval.cc_child_args.appendSlice(eval.arena, &.{ out_path, c_path }); |
| 623 | defer eval.cc_child_args.items.len -= 2; | 618 | defer eval.cc_child_args.items.len -= 2; |
| 624 | 619 | ||
| 625 | const result = std.process.Child.run(eval.arena, eval.io, .{ | 620 | const result = std.process.run(eval.arena, eval.io, .{ |
| 626 | .argv = eval.cc_child_args.items, | 621 | .argv = eval.cc_child_args.items, |
| 627 | .cwd_dir = eval.tmp_dir, | 622 | .cwd_dir = eval.tmp_dir, |
| 628 | .cwd = eval.tmp_dir_path, | 623 | .cwd = eval.tmp_dir_path, |
| ... | @@ -631,13 +626,13 @@ const Eval = struct { | ... | @@ -631,13 +626,13 @@ const Eval = struct { |
| 631 | eval.fatal("failed to spawn zig cc for '{s}': {t}", .{ c_path, err }); | 626 | eval.fatal("failed to spawn zig cc for '{s}': {t}", .{ c_path, err }); |
| 632 | }; | 627 | }; |
| 633 | switch (result.term) { | 628 | switch (result.term) { |
| 634 | .Exited => |code| if (code != 0) { | 629 | .exited => |code| if (code != 0) { |
| 635 | if (result.stderr.len != 0) { | 630 | if (result.stderr.len != 0) { |
| 636 | std.log.err("zig cc stderr:\n{s}", .{result.stderr}); | 631 | std.log.err("zig cc stderr:\n{s}", .{result.stderr}); |
| 637 | } | 632 | } |
| 638 | eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code }); | 633 | eval.fatal("zig cc for '{s}' failed with code {d}", .{ c_path, code }); |
| 639 | }, | 634 | }, |
| 640 | .Signal, .Stopped, .Unknown => { | 635 | .signal, .stopped, .unknown => { |
| 641 | if (result.stderr.len != 0) { | 636 | if (result.stderr.len != 0) { |
| 642 | std.log.err("zig cc stderr:\n{s}", .{result.stderr}); | 637 | std.log.err("zig cc stderr:\n{s}", .{result.stderr}); |
| 643 | } | 638 | } |
| ... | @@ -651,7 +646,7 @@ const Eval = struct { | ... | @@ -651,7 +646,7 @@ const Eval = struct { |
| 651 | eval.tmp_dir.close(io); | 646 | eval.tmp_dir.close(io); |
| 652 | if (!eval.preserve_tmp_on_fatal) { | 647 | if (!eval.preserve_tmp_on_fatal) { |
| 653 | // Kill the child since it holds an open handle to its CWD which is the tmp dir path | 648 | // Kill the child since it holds an open handle to its CWD which is the tmp dir path |
| 654 | _ = eval.child.kill(io) catch {}; | 649 | eval.child.kill(io); |
| 655 | Dir.cwd().deleteTree(io, eval.tmp_dir_path) catch |err| { | 650 | Dir.cwd().deleteTree(io, eval.tmp_dir_path) catch |err| { |
| 656 | std.log.warn("failed to delete tree '{s}': {t}", .{ eval.tmp_dir_path, err }); | 651 | std.log.warn("failed to delete tree '{s}': {t}", .{ eval.tmp_dir_path, err }); |
| 657 | }; | 652 | }; |
| ... | @@ -917,8 +912,9 @@ fn waitChild(child: *std.process.Child, eval: *Eval) void { | ... | @@ -917,8 +912,9 @@ fn waitChild(child: *std.process.Child, eval: *Eval) void { |
| 917 | requestExit(child, eval); | 912 | requestExit(child, eval); |
| 918 | const term = child.wait(io) catch |err| eval.fatal("child process failed: {t}", .{err}); | 913 | const term = child.wait(io) catch |err| eval.fatal("child process failed: {t}", .{err}); |
| 919 | switch (term) { | 914 | switch (term) { |
| 920 | .Exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}), | 915 | .exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}), |
| 921 | .Signal, .Stopped, .Unknown => eval.fatal("compiler terminated unexpectedly", .{}), | 916 | .signal => |sig| eval.fatal("compiler terminated with signal {t}", .{sig}), |
| 917 | .stopped, .unknown => eval.fatal("compiler terminated unexpectedly", .{}), | ||
| 922 | } | 918 | } |
| 923 | } | 919 | } |
| 924 | 920 |
tools/migrate_langref.zig+4-11| ... | @@ -11,21 +11,14 @@ const fatal = std.process.fatal; | ... | @@ -11,21 +11,14 @@ const fatal = std.process.fatal; |
| 11 | 11 | ||
| 12 | const max_doc_file_size = 10 * 1024 * 1024; | 12 | const max_doc_file_size = 10 * 1024 * 1024; |
| 13 | 13 | ||
| 14 | pub fn main() !void { | 14 | pub fn main(init: std.process.Init) !void { |
| 15 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 15 | const arena = init.arena.allocator(); |
| 16 | defer arena_instance.deinit(); | 16 | const io = init.io; |
| 17 | const arena = arena_instance.allocator(); | 17 | const args = try init.minimal.args.toSlice(arena); |
| 18 | 18 | ||
| 19 | const gpa = arena; | ||
| 20 | |||
| 21 | const args = try std.process.argsAlloc(arena); | ||
| 22 | const input_file = args[1]; | 19 | const input_file = args[1]; |
| 23 | const output_file = args[2]; | 20 | const output_file = args[2]; |
| 24 | 21 | ||
| 25 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 26 | defer threaded.deinit(); | ||
| 27 | const io = threaded.io(); | ||
| 28 | |||
| 29 | var in_file = try Dir.cwd().openFile(io, input_file, .{ .mode = .read_only }); | 22 | var in_file = try Dir.cwd().openFile(io, input_file, .{ .mode = .read_only }); |
| 30 | defer in_file.close(io); | 23 | defer in_file.close(io); |
| 31 | 24 |
tools/process_headers.zig+24-26| ... | @@ -127,16 +127,14 @@ const LibCVendor = enum { | ... | @@ -127,16 +127,14 @@ const LibCVendor = enum { |
| 127 | netbsd, | 127 | netbsd, |
| 128 | }; | 128 | }; |
| 129 | 129 | ||
| 130 | pub fn main() !void { | 130 | pub fn main(init: std.process.Init) !void { |
| 131 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 131 | const arena = init.arena.allocator(); |
| 132 | const allocator = arena.allocator(); | 132 | const io = init.io; |
| 133 | 133 | const args = try init.minimal.args.toSlice(arena); | |
| 134 | var threaded: Io.Threaded = .init(allocator, .{}); | 134 | const cwd_path = try std.process.getCwdAlloc(arena); |
| 135 | defer threaded.deinit(); | 135 | const environ_map = init.environ_map; |
| 136 | const io = threaded.io(); | 136 | |
| 137 | 137 | var search_paths = std.array_list.Managed([]const u8).init(arena); | |
| 138 | const args = try std.process.argsAlloc(allocator); | ||
| 139 | var search_paths = std.array_list.Managed([]const u8).init(allocator); | ||
| 140 | var opt_out_dir: ?[]const u8 = null; | 138 | var opt_out_dir: ?[]const u8 = null; |
| 141 | var opt_abi: ?[]const u8 = null; | 139 | var opt_abi: ?[]const u8 = null; |
| 142 | 140 | ||
| ... | @@ -172,7 +170,7 @@ pub fn main() !void { | ... | @@ -172,7 +170,7 @@ pub fn main() !void { |
| 172 | usageAndExit(args[0]); | 170 | usageAndExit(args[0]); |
| 173 | }; | 171 | }; |
| 174 | 172 | ||
| 175 | const generic_name = try std.fmt.allocPrint(allocator, "generic-{s}", .{abi_name}); | 173 | const generic_name = try std.fmt.allocPrint(arena, "generic-{s}", .{abi_name}); |
| 176 | const libc_targets = switch (vendor) { | 174 | const libc_targets = switch (vendor) { |
| 177 | .glibc => &glibc_targets, | 175 | .glibc => &glibc_targets, |
| 178 | .musl => &musl_targets, | 176 | .musl => &musl_targets, |
| ... | @@ -180,8 +178,8 @@ pub fn main() !void { | ... | @@ -180,8 +178,8 @@ pub fn main() !void { |
| 180 | .netbsd => &netbsd_targets, | 178 | .netbsd => &netbsd_targets, |
| 181 | }; | 179 | }; |
| 182 | 180 | ||
| 183 | var path_table = PathTable.init(allocator); | 181 | var path_table = PathTable.init(arena); |
| 184 | var hash_to_contents = HashToContents.init(allocator); | 182 | var hash_to_contents = HashToContents.init(arena); |
| 185 | var max_bytes_saved: usize = 0; | 183 | var max_bytes_saved: usize = 0; |
| 186 | var total_bytes: usize = 0; | 184 | var total_bytes: usize = 0; |
| 187 | 185 | ||
| ... | @@ -189,7 +187,7 @@ pub fn main() !void { | ... | @@ -189,7 +187,7 @@ pub fn main() !void { |
| 189 | 187 | ||
| 190 | for (libc_targets) |libc_target| { | 188 | for (libc_targets) |libc_target| { |
| 191 | const libc_dir = switch (vendor) { | 189 | const libc_dir = switch (vendor) { |
| 192 | .glibc => try std.zig.target.glibcRuntimeTriple(allocator, libc_target.arch, .linux, libc_target.abi), | 190 | .glibc => try std.zig.target.glibcRuntimeTriple(arena, libc_target.arch, .linux, libc_target.abi), |
| 193 | .musl => std.zig.target.muslArchName(libc_target.arch, libc_target.abi), | 191 | .musl => std.zig.target.muslArchName(libc_target.arch, libc_target.abi), |
| 194 | .freebsd => switch (libc_target.arch) { | 192 | .freebsd => switch (libc_target.arch) { |
| 195 | .arm => "armv7", | 193 | .arm => "armv7", |
| ... | @@ -221,7 +219,7 @@ pub fn main() !void { | ... | @@ -221,7 +219,7 @@ pub fn main() !void { |
| 221 | }, | 219 | }, |
| 222 | }; | 220 | }; |
| 223 | 221 | ||
| 224 | const dest_target = if (libc_target.dest) |dest| dest else try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ | 222 | const dest_target = if (libc_target.dest) |dest| dest else try std.fmt.allocPrint(arena, "{s}-{s}-{s}", .{ |
| 225 | @tagName(libc_target.arch), | 223 | @tagName(libc_target.arch), |
| 226 | switch (vendor) { | 224 | switch (vendor) { |
| 227 | .musl, .glibc => "linux", | 225 | .musl, .glibc => "linux", |
| ... | @@ -239,8 +237,8 @@ pub fn main() !void { | ... | @@ -239,8 +237,8 @@ pub fn main() !void { |
| 239 | => &[_][]const u8{ search_path, libc_dir, "usr", "include" }, | 237 | => &[_][]const u8{ search_path, libc_dir, "usr", "include" }, |
| 240 | .musl => &[_][]const u8{ search_path, libc_dir, "usr", "local", "musl", "include" }, | 238 | .musl => &[_][]const u8{ search_path, libc_dir, "usr", "local", "musl", "include" }, |
| 241 | }; | 239 | }; |
| 242 | const target_include_dir = try Dir.path.join(allocator, sub_path); | 240 | const target_include_dir = try Dir.path.join(arena, sub_path); |
| 243 | var dir_stack = std.array_list.Managed([]const u8).init(allocator); | 241 | var dir_stack = std.array_list.Managed([]const u8).init(arena); |
| 244 | try dir_stack.append(target_include_dir); | 242 | try dir_stack.append(target_include_dir); |
| 245 | 243 | ||
| 246 | while (dir_stack.pop()) |full_dir_name| { | 244 | while (dir_stack.pop()) |full_dir_name| { |
| ... | @@ -254,16 +252,16 @@ pub fn main() !void { | ... | @@ -254,16 +252,16 @@ pub fn main() !void { |
| 254 | var dir_it = dir.iterate(); | 252 | var dir_it = dir.iterate(); |
| 255 | 253 | ||
| 256 | while (try dir_it.next(io)) |entry| { | 254 | while (try dir_it.next(io)) |entry| { |
| 257 | const full_path = try Dir.path.join(allocator, &[_][]const u8{ full_dir_name, entry.name }); | 255 | const full_path = try Dir.path.join(arena, &[_][]const u8{ full_dir_name, entry.name }); |
| 258 | switch (entry.kind) { | 256 | switch (entry.kind) { |
| 259 | .directory => try dir_stack.append(full_path), | 257 | .directory => try dir_stack.append(full_path), |
| 260 | .file, .sym_link => { | 258 | .file, .sym_link => { |
| 261 | const rel_path = try Dir.path.relative(allocator, target_include_dir, full_path); | 259 | const rel_path = try Dir.path.relative(arena, cwd_path, environ_map, target_include_dir, full_path); |
| 262 | const max_size = 2 * 1024 * 1024 * 1024; | 260 | const max_size = 2 * 1024 * 1024 * 1024; |
| 263 | const raw_bytes = try Dir.cwd().readFileAlloc(io, full_path, allocator, .limited(max_size)); | 261 | const raw_bytes = try Dir.cwd().readFileAlloc(io, full_path, arena, .limited(max_size)); |
| 264 | const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t"); | 262 | const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t"); |
| 265 | total_bytes += raw_bytes.len; | 263 | total_bytes += raw_bytes.len; |
| 266 | const hash = try allocator.alloc(u8, 32); | 264 | const hash = try arena.alloc(u8, 32); |
| 267 | hasher = Blake3.init(.{}); | 265 | hasher = Blake3.init(.{}); |
| 268 | hasher.update(rel_path); | 266 | hasher.update(rel_path); |
| 269 | hasher.update(trimmed); | 267 | hasher.update(trimmed); |
| ... | @@ -285,8 +283,8 @@ pub fn main() !void { | ... | @@ -285,8 +283,8 @@ pub fn main() !void { |
| 285 | } | 283 | } |
| 286 | const path_gop = try path_table.getOrPut(rel_path); | 284 | const path_gop = try path_table.getOrPut(rel_path); |
| 287 | const target_to_hash = if (path_gop.found_existing) path_gop.value_ptr.* else blk: { | 285 | const target_to_hash = if (path_gop.found_existing) path_gop.value_ptr.* else blk: { |
| 288 | const ptr = try allocator.create(TargetToHash); | 286 | const ptr = try arena.create(TargetToHash); |
| 289 | ptr.* = TargetToHash.init(allocator); | 287 | ptr.* = TargetToHash.init(arena); |
| 290 | path_gop.value_ptr.* = ptr; | 288 | path_gop.value_ptr.* = ptr; |
| 291 | break :blk ptr; | 289 | break :blk ptr; |
| 292 | }; | 290 | }; |
| ... | @@ -327,7 +325,7 @@ pub fn main() !void { | ... | @@ -327,7 +325,7 @@ pub fn main() !void { |
| 327 | // gets their header in a separate arch directory. | 325 | // gets their header in a separate arch directory. |
| 328 | var path_it = path_table.iterator(); | 326 | var path_it = path_table.iterator(); |
| 329 | while (path_it.next()) |path_kv| { | 327 | while (path_it.next()) |path_kv| { |
| 330 | var contents_list = std.array_list.Managed(*Contents).init(allocator); | 328 | var contents_list = std.array_list.Managed(*Contents).init(arena); |
| 331 | { | 329 | { |
| 332 | var hash_it = path_kv.value_ptr.*.iterator(); | 330 | var hash_it = path_kv.value_ptr.*.iterator(); |
| 333 | while (hash_it.next()) |hash_kv| { | 331 | while (hash_it.next()) |hash_kv| { |
| ... | @@ -339,7 +337,7 @@ pub fn main() !void { | ... | @@ -339,7 +337,7 @@ pub fn main() !void { |
| 339 | const best_contents = contents_list.pop().?; | 337 | const best_contents = contents_list.pop().?; |
| 340 | if (best_contents.hit_count > 1) { | 338 | if (best_contents.hit_count > 1) { |
| 341 | // worth it to make it generic | 339 | // worth it to make it generic |
| 342 | const full_path = try Dir.path.join(allocator, &[_][]const u8{ out_dir, generic_name, path_kv.key_ptr.* }); | 340 | const full_path = try Dir.path.join(arena, &[_][]const u8{ out_dir, generic_name, path_kv.key_ptr.* }); |
| 343 | try Dir.cwd().createDirPath(io, Dir.path.dirname(full_path).?); | 341 | try Dir.cwd().createDirPath(io, Dir.path.dirname(full_path).?); |
| 344 | try Dir.cwd().writeFile(io, .{ .sub_path = full_path, .data = best_contents.bytes }); | 342 | try Dir.cwd().writeFile(io, .{ .sub_path = full_path, .data = best_contents.bytes }); |
| 345 | best_contents.is_generic = true; | 343 | best_contents.is_generic = true; |
| ... | @@ -360,7 +358,7 @@ pub fn main() !void { | ... | @@ -360,7 +358,7 @@ pub fn main() !void { |
| 360 | if (contents.is_generic) continue; | 358 | if (contents.is_generic) continue; |
| 361 | 359 | ||
| 362 | const dest_target = hash_kv.key_ptr.*; | 360 | const dest_target = hash_kv.key_ptr.*; |
| 363 | const full_path = try Dir.path.join(allocator, &[_][]const u8{ out_dir, dest_target, path_kv.key_ptr.* }); | 361 | const full_path = try Dir.path.join(arena, &[_][]const u8{ out_dir, dest_target, path_kv.key_ptr.* }); |
| 364 | try Dir.cwd().createDirPath(io, Dir.path.dirname(full_path).?); | 362 | try Dir.cwd().createDirPath(io, Dir.path.dirname(full_path).?); |
| 365 | try Dir.cwd().writeFile(io, .{ .sub_path = full_path, .data = contents.bytes }); | 363 | try Dir.cwd().writeFile(io, .{ .sub_path = full_path, .data = contents.bytes }); |
| 366 | } | 364 | } |
tools/update-linux-headers.zig+7-9| ... | @@ -141,15 +141,13 @@ const HashToContents = std.StringHashMap(Contents); | ... | @@ -141,15 +141,13 @@ const HashToContents = std.StringHashMap(Contents); |
| 141 | const TargetToHash = std.ArrayHashMap(DestTarget, []const u8, DestTarget.HashContext, true); | 141 | const TargetToHash = std.ArrayHashMap(DestTarget, []const u8, DestTarget.HashContext, true); |
| 142 | const PathTable = std.StringHashMap(*TargetToHash); | 142 | const PathTable = std.StringHashMap(*TargetToHash); |
| 143 | 143 | ||
| 144 | pub fn main() !void { | 144 | pub fn main(init: std.process.Init) !void { |
| 145 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 145 | const arena = init.arena.allocator(); |
| 146 | const arena = arena_state.allocator(); | 146 | const io = init.io; |
| 147 | const args = try init.minimal.args.toSlice(arena); | ||
| 148 | const environ_map = init.environ_map; | ||
| 149 | const cwd = try std.process.getCwdAlloc(arena); | ||
| 147 | 150 | ||
| 148 | var threaded: Io.Threaded = .init(arena, .{}); | ||
| 149 | defer threaded.deinit(); | ||
| 150 | const io = threaded.io(); | ||
| 151 | |||
| 152 | const args = try std.process.argsAlloc(arena); | ||
| 153 | var search_paths = std.array_list.Managed([]const u8).init(arena); | 151 | var search_paths = std.array_list.Managed([]const u8).init(arena); |
| 154 | var opt_out_dir: ?[]const u8 = null; | 152 | var opt_out_dir: ?[]const u8 = null; |
| 155 | 153 | ||
| ... | @@ -211,7 +209,7 @@ pub fn main() !void { | ... | @@ -211,7 +209,7 @@ pub fn main() !void { |
| 211 | switch (entry.kind) { | 209 | switch (entry.kind) { |
| 212 | .directory => try dir_stack.append(full_path), | 210 | .directory => try dir_stack.append(full_path), |
| 213 | .file => { | 211 | .file => { |
| 214 | const rel_path = try Dir.path.relative(arena, target_include_dir, full_path); | 212 | const rel_path = try Dir.path.relative(arena, cwd, environ_map, target_include_dir, full_path); |
| 215 | const max_size = 2 * 1024 * 1024 * 1024; | 213 | const max_size = 2 * 1024 * 1024 * 1024; |
| 216 | const raw_bytes = try Dir.cwd().readFileAlloc(io, full_path, arena, .limited(max_size)); | 214 | const raw_bytes = try Dir.cwd().readFileAlloc(io, full_path, arena, .limited(max_size)); |
| 217 | const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t"); | 215 | const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t"); |
tools/update_clang_options.zig+12-18| ... | @@ -627,16 +627,10 @@ const cpu_targets = struct { | ... | @@ -627,16 +627,10 @@ const cpu_targets = struct { |
| 627 | pub const xtensa = std.Target.xtensa; | 627 | pub const xtensa = std.Target.xtensa; |
| 628 | }; | 628 | }; |
| 629 | 629 | ||
| 630 | pub fn main() anyerror!void { | 630 | pub fn main(init: std.process.Init) !void { |
| 631 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 631 | const arena = init.arena.allocator(); |
| 632 | defer arena.deinit(); | 632 | const args = try init.minimal.args.toSlice(arena); |
| 633 | 633 | const io = init.io; | |
| 634 | const allocator = arena.allocator(); | ||
| 635 | const args = try std.process.argsAlloc(allocator); | ||
| 636 | |||
| 637 | var threaded: std.Io.Threaded = .init(allocator, .{}); | ||
| 638 | defer threaded.deinit(); | ||
| 639 | const io = threaded.io(); | ||
| 640 | 634 | ||
| 641 | var stdout_buffer: [4000]u8 = undefined; | 635 | var stdout_buffer: [4000]u8 = undefined; |
| 642 | var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); | 636 | var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); |
| ... | @@ -658,7 +652,7 @@ pub fn main() anyerror!void { | ... | @@ -658,7 +652,7 @@ pub fn main() anyerror!void { |
| 658 | const llvm_src_root = args[2]; | 652 | const llvm_src_root = args[2]; |
| 659 | if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]); | 653 | if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]); |
| 660 | 654 | ||
| 661 | var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator); | 655 | var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(arena); |
| 662 | 656 | ||
| 663 | inline for (@typeInfo(cpu_targets).@"struct".decls) |decl| { | 657 | inline for (@typeInfo(cpu_targets).@"struct".decls) |decl| { |
| 664 | const Feature = @field(cpu_targets, decl.name).Feature; | 658 | const Feature = @field(cpu_targets, decl.name).Feature; |
| ... | @@ -675,12 +669,12 @@ pub fn main() anyerror!void { | ... | @@ -675,12 +669,12 @@ pub fn main() anyerror!void { |
| 675 | const child_args = [_][]const u8{ | 669 | const child_args = [_][]const u8{ |
| 676 | llvm_tblgen_exe, | 670 | llvm_tblgen_exe, |
| 677 | "--dump-json", | 671 | "--dump-json", |
| 678 | try std.fmt.allocPrint(allocator, "{s}/clang/include/clang/Driver/Options.td", .{llvm_src_root}), | 672 | try std.fmt.allocPrint(arena, "{s}/clang/include/clang/Driver/Options.td", .{llvm_src_root}), |
| 679 | try std.fmt.allocPrint(allocator, "-I={s}/llvm/include", .{llvm_src_root}), | 673 | try std.fmt.allocPrint(arena, "-I={s}/llvm/include", .{llvm_src_root}), |
| 680 | try std.fmt.allocPrint(allocator, "-I={s}/clang/include/clang/Driver", .{llvm_src_root}), | 674 | try std.fmt.allocPrint(arena, "-I={s}/clang/include/clang/Driver", .{llvm_src_root}), |
| 681 | }; | 675 | }; |
| 682 | 676 | ||
| 683 | const child_result = try std.process.Child.run(allocator, io, .{ | 677 | const child_result = try std.process.run(arena, io, .{ |
| 684 | .argv = &child_args, | 678 | .argv = &child_args, |
| 685 | .max_output_bytes = 100 * 1024 * 1024, | 679 | .max_output_bytes = 100 * 1024 * 1024, |
| 686 | }); | 680 | }); |
| ... | @@ -688,7 +682,7 @@ pub fn main() anyerror!void { | ... | @@ -688,7 +682,7 @@ pub fn main() anyerror!void { |
| 688 | std.debug.print("{s}\n", .{child_result.stderr}); | 682 | std.debug.print("{s}\n", .{child_result.stderr}); |
| 689 | 683 | ||
| 690 | const json_text = switch (child_result.term) { | 684 | const json_text = switch (child_result.term) { |
| 691 | .Exited => |code| if (code == 0) child_result.stdout else { | 685 | .exited => |code| if (code == 0) child_result.stdout else { |
| 692 | std.debug.print("llvm-tblgen exited with code {d}\n", .{code}); | 686 | std.debug.print("llvm-tblgen exited with code {d}\n", .{code}); |
| 693 | std.process.exit(1); | 687 | std.process.exit(1); |
| 694 | }, | 688 | }, |
| ... | @@ -698,11 +692,11 @@ pub fn main() anyerror!void { | ... | @@ -698,11 +692,11 @@ pub fn main() anyerror!void { |
| 698 | }, | 692 | }, |
| 699 | }; | 693 | }; |
| 700 | 694 | ||
| 701 | const parsed = try json.parseFromSlice(json.Value, allocator, json_text, .{}); | 695 | const parsed = try json.parseFromSlice(json.Value, arena, json_text, .{}); |
| 702 | defer parsed.deinit(); | 696 | defer parsed.deinit(); |
| 703 | const root_map = &parsed.value.object; | 697 | const root_map = &parsed.value.object; |
| 704 | 698 | ||
| 705 | var all_objects = std.array_list.Managed(*json.ObjectMap).init(allocator); | 699 | var all_objects = std.array_list.Managed(*json.ObjectMap).init(arena); |
| 706 | { | 700 | { |
| 707 | var it = root_map.iterator(); | 701 | var it = root_map.iterator(); |
| 708 | it_map: while (it.next()) |kv| { | 702 | it_map: while (it.next()) |kv| { |
tools/update_cpu_features.zig+6-15| ... | @@ -1883,20 +1883,11 @@ const targets = [_]ArchTarget{ | ... | @@ -1883,20 +1883,11 @@ const targets = [_]ArchTarget{ |
| 1883 | }, | 1883 | }, |
| 1884 | }; | 1884 | }; |
| 1885 | 1885 | ||
| 1886 | pub fn main() anyerror!void { | 1886 | pub fn main(init: std.process.Init) !void { |
| 1887 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | 1887 | const arena = init.arena.allocator(); |
| 1888 | defer _ = debug_allocator.deinit(); | 1888 | const io = init.io; |
| 1889 | const gpa = debug_allocator.allocator(); | ||
| 1890 | 1889 | ||
| 1891 | var arena_state: std.heap.ArenaAllocator = .init(gpa); | 1890 | var args = try init.minimal.args.iterateAllocator(arena); |
| 1892 | defer arena_state.deinit(); | ||
| 1893 | const arena = arena_state.allocator(); | ||
| 1894 | |||
| 1895 | var threaded: std.Io.Threaded = .init(gpa, .{}); | ||
| 1896 | defer threaded.deinit(); | ||
| 1897 | const io = threaded.io(); | ||
| 1898 | |||
| 1899 | var args = try std.process.argsWithAllocator(arena); | ||
| 1900 | const args0 = args.next().?; | 1891 | const args0 = args.next().?; |
| 1901 | 1892 | ||
| 1902 | const llvm_tblgen_exe = args.next() orelse | 1893 | const llvm_tblgen_exe = args.next() orelse |
| ... | @@ -1994,7 +1985,7 @@ fn processOneTarget(io: Io, job: Job) void { | ... | @@ -1994,7 +1985,7 @@ fn processOneTarget(io: Io, job: Job) void { |
| 1994 | }), | 1985 | }), |
| 1995 | }; | 1986 | }; |
| 1996 | 1987 | ||
| 1997 | const child_result = try std.process.Child.run(arena, io, .{ | 1988 | const child_result = try std.process.run(arena, io, .{ |
| 1998 | .argv = &child_args, | 1989 | .argv = &child_args, |
| 1999 | .max_output_bytes = 500 * 1024 * 1024, | 1990 | .max_output_bytes = 500 * 1024 * 1024, |
| 2000 | }); | 1991 | }); |
| ... | @@ -2004,7 +1995,7 @@ fn processOneTarget(io: Io, job: Job) void { | ... | @@ -2004,7 +1995,7 @@ fn processOneTarget(io: Io, job: Job) void { |
| 2004 | } | 1995 | } |
| 2005 | 1996 | ||
| 2006 | const json_text = switch (child_result.term) { | 1997 | const json_text = switch (child_result.term) { |
| 2007 | .Exited => |code| if (code == 0) child_result.stdout else { | 1998 | .exited => |code| if (code == 0) child_result.stdout else { |
| 2008 | std.debug.print("llvm-tblgen exited with code {d}\n", .{code}); | 1999 | std.debug.print("llvm-tblgen exited with code {d}\n", .{code}); |
| 2009 | std.process.exit(1); | 2000 | std.process.exit(1); |
| 2010 | }, | 2001 | }, |
tools/update_crc_catalog.zig+7-9| ... | @@ -6,16 +6,14 @@ const ascii = std.ascii; | ... | @@ -6,16 +6,14 @@ const ascii = std.ascii; |
| 6 | 6 | ||
| 7 | const catalog_txt = @embedFile("crc/catalog.txt"); | 7 | const catalog_txt = @embedFile("crc/catalog.txt"); |
| 8 | 8 | ||
| 9 | pub fn main() anyerror!void { | 9 | pub fn main(init: std.process.Init) !void { |
| 10 | var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 10 | const arena = init.arena.allocator(); |
| 11 | defer arena_state.deinit(); | 11 | const io = init.io; |
| 12 | const arena = arena_state.allocator(); | 12 | const args = try init.minimal.args.toSlice(arena); |
| 13 | 13 | return @"i like cheese"(arena, io, args); | |
| 14 | var threaded: Io.Threaded = .init(arena, .{}); | 14 | } |
| 15 | defer threaded.deinit(); | ||
| 16 | const io = threaded.io(); | ||
| 17 | 15 | ||
| 18 | const args = try std.process.argsAlloc(arena); | 16 | fn @"i like cheese"(arena: std.mem.Allocator, io: Io, args: []const []const u8) !void { |
| 19 | if (args.len <= 1) printUsageAndExit(args[0]); | 17 | if (args.len <= 1) printUsageAndExit(args[0]); |
| 20 | 18 | ||
| 21 | const zig_src_root = args[1]; | 19 | const zig_src_root = args[1]; |
tools/update_freebsd_libc.zig+4-9| ... | @@ -12,16 +12,11 @@ const exempt_files = [_][]const u8{ | ... | @@ -12,16 +12,11 @@ const exempt_files = [_][]const u8{ |
| 12 | "abilists", | 12 | "abilists", |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| 15 | pub fn main() !void { | 15 | pub fn main(init: std.process.Init) !void { |
| 16 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 16 | const arena = init.arena.allocator(); |
| 17 | defer arena_instance.deinit(); | 17 | const io = init.io; |
| 18 | const arena = arena_instance.allocator(); | 18 | const args = try init.minimal.args.toSlice(arena); |
| 19 | 19 | ||
| 20 | var threaded: Io.Threaded = .init(arena, .{}); | ||
| 21 | defer threaded.deinit(); | ||
| 22 | const io = threaded.io(); | ||
| 23 | |||
| 24 | const args = try std.process.argsAlloc(arena); | ||
| 25 | const freebsd_src_path = args[1]; | 20 | const freebsd_src_path = args[1]; |
| 26 | const zig_src_path = args[2]; | 21 | const zig_src_path = args[2]; |
| 27 | 22 |
tools/update_glibc.zig+4-9| ... | @@ -38,16 +38,11 @@ const exempt_extensions = [_][]const u8{ | ... | @@ -38,16 +38,11 @@ const exempt_extensions = [_][]const u8{ |
| 38 | "-2.33.c", | 38 | "-2.33.c", |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | pub fn main() !void { | 41 | pub fn main(init: std.process.Init) !void { |
| 42 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 42 | const arena = init.arena.allocator(); |
| 43 | defer arena_instance.deinit(); | 43 | const io = init.io; |
| 44 | const arena = arena_instance.allocator(); | 44 | const args = try init.minimal.args.toSlice(arena); |
| 45 | 45 | ||
| 46 | var threaded: Io.Threaded = .init(arena, .{}); | ||
| 47 | defer threaded.deinit(); | ||
| 48 | const io = threaded.io(); | ||
| 49 | |||
| 50 | const args = try std.process.argsAlloc(arena); | ||
| 51 | const glibc_src_path = args[1]; | 46 | const glibc_src_path = args[1]; |
| 52 | const zig_src_path = args[2]; | 47 | const zig_src_path = args[2]; |
| 53 | 48 |
tools/update_mingw.zig+4-9| ... | @@ -2,16 +2,11 @@ const std = @import("std"); | ... | @@ -2,16 +2,11 @@ const std = @import("std"); |
| 2 | const Io = std.Io; | 2 | const Io = std.Io; |
| 3 | const Dir = std.Io.Dir; | 3 | const Dir = std.Io.Dir; |
| 4 | 4 | ||
| 5 | pub fn main() !void { | 5 | pub fn main(init: std.process.Init) !void { |
| 6 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 6 | const arena = init.arena.allocator(); |
| 7 | defer arena_instance.deinit(); | 7 | const io = init.io; |
| 8 | const arena = arena_instance.allocator(); | 8 | const args = try init.minimal.args.toSlice(arena); |
| 9 | 9 | ||
| 10 | var threaded: Io.Threaded = .init(arena, .{}); | ||
| 11 | defer threaded.deinit(); | ||
| 12 | const io = threaded.io(); | ||
| 13 | |||
| 14 | const args = try std.process.argsAlloc(arena); | ||
| 15 | const zig_src_lib_path = args[1]; | 10 | const zig_src_lib_path = args[1]; |
| 16 | const mingw_src_path = args[2]; | 11 | const mingw_src_path = args[2]; |
| 17 | 12 |
tools/update_netbsd_libc.zig+4-9| ... | @@ -12,16 +12,11 @@ const exempt_files = [_][]const u8{ | ... | @@ -12,16 +12,11 @@ const exempt_files = [_][]const u8{ |
| 12 | "abilists", | 12 | "abilists", |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| 15 | pub fn main() !void { | 15 | pub fn main(init: std.process.Init) !void { |
| 16 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 16 | const arena = init.arena.allocator(); |
| 17 | defer arena_instance.deinit(); | 17 | const io = init.io; |
| 18 | const arena = arena_instance.allocator(); | 18 | const args = try init.minimal.args.toSlice(arena); |
| 19 | 19 | ||
| 20 | var threaded: Io.Threaded = .init(arena, .{}); | ||
| 21 | defer threaded.deinit(); | ||
| 22 | const io = threaded.io(); | ||
| 23 | |||
| 24 | const args = try std.process.argsAlloc(arena); | ||
| 25 | const netbsd_src_path = args[1]; | 20 | const netbsd_src_path = args[1]; |
| 26 | const zig_src_path = args[2]; | 21 | const zig_src_path = args[2]; |
| 27 | 22 |