authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-07 00:31:17-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-07 00:31:17-05:00
log21135387fb7c2dbaf70a72f2c97341e9c1307045
tree5926ea2d182a76f80429776a5473202738c9b656
parent069dd01ce4ced3cb9664e4f1e09be753cb3ed476
parent33fa29601921d88097a1ee3c0d92b93047a5186d
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10782 from topolarity/gate-child-processes

Avoid depending on child process execution when not supported by host OS

15 files changed, 453 insertions(+), 294 deletions(-)

build.zig+7
......@@ -229,6 +229,10 @@ pub fn build(b: *Builder) !void {
229229 const version = if (opt_version_string) |version| version else v: {
230230 const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch });
231231
232 if (!std.process.can_spawn) {
233 std.debug.print("error: version info cannot be retrieved from git. Zig version must be provided using -Dversion-string\n", .{});
234 std.process.exit(1);
235 }
232236 var code: u8 = undefined;
233237 const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{
234238 "git", "-C", b.build_root, "describe", "--match", "*.*.*", "--tags",
......@@ -542,6 +546,9 @@ fn addCxxKnownPath(
542546 errtxt: ?[]const u8,
543547 need_cpp_includes: bool,
544548) !void {
549 if (!std.process.can_spawn)
550 return error.RequiredLibraryNotFound;
551
545552 const path_padded = try b.exec(&[_][]const u8{
546553 ctx.cxx_compiler,
547554 b.fmt("-print-file-name={s}", .{objname}),
lib/std/build.zig+34-4
......@@ -88,7 +88,14 @@ pub const Builder = struct {
8888 /// Information about the native target. Computed before build() is invoked.
8989 host: NativeTargetInfo,
9090
91 const PkgConfigError = error{
91 pub const ExecError = error{
92 ReadFailure,
93 ExitCodeFailure,
94 ProcessTerminated,
95 ExecNotSupported,
96 } || std.ChildProcess.SpawnError;
97
98 pub const PkgConfigError = error{
9299 PkgConfigCrashed,
93100 PkgConfigFailed,
94101 PkgConfigNotInstalled,
......@@ -959,6 +966,9 @@ pub const Builder = struct {
959966 printCmd(cwd, argv);
960967 }
961968
969 if (!std.process.can_spawn)
970 return error.ExecNotSupported;
971
962972 const child = std.ChildProcess.init(argv, self.allocator) catch unreachable;
963973 defer child.deinit();
964974
......@@ -1168,9 +1178,12 @@ pub const Builder = struct {
11681178 argv: []const []const u8,
11691179 out_code: *u8,
11701180 stderr_behavior: std.ChildProcess.StdIo,
1171 ) ![]u8 {
1181 ) ExecError![]u8 {
11721182 assert(argv.len != 0);
11731183
1184 if (!std.process.can_spawn)
1185 return error.ExecNotSupported;
1186
11741187 const max_output_size = 400 * 1024;
11751188 const child = try std.ChildProcess.init(argv, self.allocator);
11761189 defer child.deinit();
......@@ -1182,7 +1195,9 @@ pub const Builder = struct {
11821195
11831196 try child.spawn();
11841197
1185 const stdout = try child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size);
1198 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1199 return error.ReadFailure;
1200 };
11861201 errdefer self.allocator.free(stdout);
11871202
11881203 const term = try child.wait();
......@@ -1208,8 +1223,21 @@ pub const Builder = struct {
12081223 printCmd(null, argv);
12091224 }
12101225
1226 if (!std.process.can_spawn) {
1227 if (src_step) |s| warn("{s}...", .{s.name});
1228 warn("Unable to spawn the following command: cannot spawn child process\n", .{});
1229 printCmd(null, argv);
1230 std.os.abort();
1231 }
1232
12111233 var code: u8 = undefined;
12121234 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1235 error.ExecNotSupported => {
1236 if (src_step) |s| warn("{s}...", .{s.name});
1237 warn("Unable to spawn the following command: cannot spawn child process\n", .{});
1238 printCmd(null, argv);
1239 std.os.abort();
1240 },
12131241 error.FileNotFound => {
12141242 if (src_step) |s| warn("{s}...", .{s.name});
12151243 warn("Unable to spawn the following command: file not found\n", .{});
......@@ -1260,7 +1288,7 @@ pub const Builder = struct {
12601288 ) catch unreachable;
12611289 }
12621290
1263 fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg {
1291 fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
12641292 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
12651293 var list = ArrayList(PkgConfigPkg).init(self.allocator);
12661294 errdefer list.deinit();
......@@ -1287,6 +1315,7 @@ pub const Builder = struct {
12871315 } else |err| {
12881316 const result = switch (err) {
12891317 error.ProcessTerminated => error.PkgConfigCrashed,
1318 error.ExecNotSupported => error.PkgConfigFailed,
12901319 error.ExitCodeFailure => error.PkgConfigFailed,
12911320 error.FileNotFound => error.PkgConfigNotInstalled,
12921321 error.InvalidName => error.PkgConfigNotInstalled,
......@@ -1929,6 +1958,7 @@ pub const LibExeObjStep = struct {
19291958 "--libs",
19301959 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
19311960 error.ProcessTerminated => return error.PkgConfigCrashed,
1961 error.ExecNotSupported => return error.PkgConfigFailed,
19321962 error.ExitCodeFailure => return error.PkgConfigFailed,
19331963 error.FileNotFound => return error.PkgConfigNotInstalled,
19341964 else => return err,
lib/std/build/RunStep.zig+9
......@@ -10,6 +10,8 @@ const mem = std.mem;
1010const process = std.process;
1111const ArrayList = std.ArrayList;
1212const BufMap = std.BufMap;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
1315
1416const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
1517
......@@ -175,6 +177,13 @@ fn make(step: *Step) !void {
175177
176178 const argv = argv_list.items;
177179
180 if (!std.process.can_spawn) {
181 const cmd = try std.mem.join(self.builder.allocator, " ", argv);
182 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
183 self.builder.allocator.free(cmd);
184 return ExecError.ExecNotSupported;
185 }
186
178187 const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable;
179188 defer child.deinit();
180189
lib/std/child_process.zig+4
......@@ -124,6 +124,10 @@ pub const ChildProcess = struct {
124124
125125 /// On success must call `kill` or `wait`.
126126 pub fn spawn(self: *ChildProcess) SpawnError!void {
127 if (!std.process.can_spawn) {
128 @compileError("the target operating system cannot spawn processes");
129 }
130
127131 if (builtin.os.tag == .windows) {
128132 return self.spawnWindows();
129133 } else {
lib/std/process.zig+7-1
......@@ -950,7 +950,13 @@ pub fn getSelfExeSharedLibPaths(allocator: Allocator) error{OutOfMemory}![][:0]u
950950
951951/// Tells whether calling the `execv` or `execve` functions will be a compile error.
952952pub const can_execv = switch (builtin.os.tag) {
953 .windows, .haiku => false,
953 .windows, .haiku, .wasi => false,
954 else => true,
955};
956
957/// Tells whether spawning child processes is supported (e.g. via ChildProcess)
958pub const can_spawn = switch (builtin.os.tag) {
959 .wasi => false,
954960 else => true,
955961};
956962
src/Compilation.zig+59-42
......@@ -25,6 +25,7 @@ const libunwind = @import("libunwind.zig");
2525const libcxx = @import("libcxx.zig");
2626const wasi_libc = @import("wasi_libc.zig");
2727const fatal = @import("main.zig").fatal;
28const clangMain = @import("main.zig").clangMain;
2829const Module = @import("Module.zig");
2930const Cache = @import("Cache.zig");
3031const stage1 = @import("stage1.zig");
......@@ -3667,55 +3668,71 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
36673668 dump_argv(argv.items);
36683669 }
36693670
3670 const child = try std.ChildProcess.init(argv.items, arena);
3671 defer child.deinit();
3671 if (std.process.can_spawn) {
3672 const child = try std.ChildProcess.init(argv.items, arena);
3673 defer child.deinit();
36723674
3673 if (comp.clang_passthrough_mode) {
3674 child.stdin_behavior = .Inherit;
3675 child.stdout_behavior = .Inherit;
3676 child.stderr_behavior = .Inherit;
3675 if (comp.clang_passthrough_mode) {
3676 child.stdin_behavior = .Inherit;
3677 child.stdout_behavior = .Inherit;
3678 child.stderr_behavior = .Inherit;
36773679
3678 const term = child.spawnAndWait() catch |err| {
3679 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
3680 };
3681 switch (term) {
3682 .Exited => |code| {
3683 if (code != 0) {
3684 std.process.exit(code);
3685 }
3686 if (comp.clang_preprocessor_mode == .stdout)
3687 std.process.exit(0);
3688 },
3689 else => std.process.abort(),
3690 }
3691 } else {
3692 child.stdin_behavior = .Ignore;
3693 child.stdout_behavior = .Ignore;
3694 child.stderr_behavior = .Pipe;
3680 const term = child.spawnAndWait() catch |err| {
3681 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
3682 };
3683 switch (term) {
3684 .Exited => |code| {
3685 if (code != 0) {
3686 std.process.exit(code);
3687 }
3688 if (comp.clang_preprocessor_mode == .stdout)
3689 std.process.exit(0);
3690 },
3691 else => std.process.abort(),
3692 }
3693 } else {
3694 child.stdin_behavior = .Ignore;
3695 child.stdout_behavior = .Ignore;
3696 child.stderr_behavior = .Pipe;
36953697
3696 try child.spawn();
3698 try child.spawn();
36973699
3698 const stderr_reader = child.stderr.?.reader();
3700 const stderr_reader = child.stderr.?.reader();
36993701
3700 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
3702 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
37013703
3702 const term = child.wait() catch |err| {
3703 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
3704 };
3704 const term = child.wait() catch |err| {
3705 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
3706 };
37053707
3706 switch (term) {
3707 .Exited => |code| {
3708 if (code != 0) {
3709 // TODO parse clang stderr and turn it into an error message
3710 // and then call failCObjWithOwnedErrorMsg
3711 log.err("clang failed with stderr: {s}", .{stderr});
3712 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
3713 }
3714 },
3715 else => {
3716 log.err("clang terminated with stderr: {s}", .{stderr});
3717 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
3718 },
3708 switch (term) {
3709 .Exited => |code| {
3710 if (code != 0) {
3711 // TODO parse clang stderr and turn it into an error message
3712 // and then call failCObjWithOwnedErrorMsg
3713 log.err("clang failed with stderr: {s}", .{stderr});
3714 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
3715 }
3716 },
3717 else => {
3718 log.err("clang terminated with stderr: {s}", .{stderr});
3719 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
3720 },
3721 }
3722 }
3723 } else {
3724 const exit_code = try clangMain(arena, argv.items);
3725 if (exit_code != 0) {
3726 if (comp.clang_passthrough_mode) {
3727 std.process.exit(exit_code);
3728 } else {
3729 return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code});
3730 }
3731 }
3732 if (comp.clang_passthrough_mode and
3733 comp.clang_preprocessor_mode == .stdout)
3734 {
3735 std.process.exit(0);
37193736 }
37203737 }
37213738
src/ThreadPool.zig+3
......@@ -82,6 +82,9 @@ pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void {
8282}
8383
8484fn destroyWorkers(self: *ThreadPool, spawned: usize) void {
85 if (builtin.single_threaded)
86 return;
87
8588 for (self.workers[0..spawned]) |*worker| {
8689 worker.thread.join();
8790 worker.idle_node.data.deinit();
src/libc_installation.zig+3-1
......@@ -216,7 +216,7 @@ pub const LibCInstallation = struct {
216216 self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
217217 break :blk batch.wait();
218218 };
219 } else {
219 } else if (std.process.can_spawn) {
220220 try blk: {
221221 var batch = Batch(FindError!void, 2, .auto_async).init();
222222 errdefer batch.wait() catch {};
......@@ -229,6 +229,8 @@ pub const LibCInstallation = struct {
229229 }
230230 break :blk batch.wait();
231231 };
232 } else {
233 return error.LibCRuntimeNotFound;
232234 }
233235 return self;
234236 }
src/link/Coff.zig+64-52
......@@ -9,6 +9,7 @@ const fs = std.fs;
99const allocPrint = std.fmt.allocPrint;
1010const mem = std.mem;
1111
12const lldMain = @import("../main.zig").lldMain;
1213const trace = @import("../tracy.zig").trace;
1314const Module = @import("../Module.zig");
1415const Compilation = @import("../Compilation.zig");
......@@ -1358,60 +1359,71 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
13581359 Compilation.dump_argv(argv.items[1..]);
13591360 }
13601361
1361 // Sadly, we must run LLD as a child process because it does not behave
1362 // properly as a library.
1363 const child = try std.ChildProcess.init(argv.items, arena);
1364 defer child.deinit();
1365
1366 if (comp.clang_passthrough_mode) {
1367 child.stdin_behavior = .Inherit;
1368 child.stdout_behavior = .Inherit;
1369 child.stderr_behavior = .Inherit;
1370
1371 const term = child.spawnAndWait() catch |err| {
1372 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1373 return error.UnableToSpawnSelf;
1374 };
1375 switch (term) {
1376 .Exited => |code| {
1377 if (code != 0) {
1378 // TODO https://github.com/ziglang/zig/issues/6342
1379 std.process.exit(1);
1380 }
1381 },
1382 else => std.process.abort(),
1362 if (std.process.can_spawn) {
1363 // If possible, we run LLD as a child process because it does not always
1364 // behave properly as a library, unfortunately.
1365 // https://github.com/ziglang/zig/issues/3825
1366 const child = try std.ChildProcess.init(argv.items, arena);
1367 defer child.deinit();
1368
1369 if (comp.clang_passthrough_mode) {
1370 child.stdin_behavior = .Inherit;
1371 child.stdout_behavior = .Inherit;
1372 child.stderr_behavior = .Inherit;
1373
1374 const term = child.spawnAndWait() catch |err| {
1375 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1376 return error.UnableToSpawnSelf;
1377 };
1378 switch (term) {
1379 .Exited => |code| {
1380 if (code != 0) {
1381 std.process.exit(code);
1382 }
1383 },
1384 else => std.process.abort(),
1385 }
1386 } else {
1387 child.stdin_behavior = .Ignore;
1388 child.stdout_behavior = .Ignore;
1389 child.stderr_behavior = .Pipe;
1390
1391 try child.spawn();
1392
1393 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1394
1395 const term = child.wait() catch |err| {
1396 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1397 return error.UnableToSpawnSelf;
1398 };
1399
1400 switch (term) {
1401 .Exited => |code| {
1402 if (code != 0) {
1403 // TODO parse this output and surface with the Compilation API rather than
1404 // directly outputting to stderr here.
1405 std.debug.print("{s}", .{stderr});
1406 return error.LLDReportedFailure;
1407 }
1408 },
1409 else => {
1410 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1411 return error.LLDCrashed;
1412 },
1413 }
1414
1415 if (stderr.len != 0) {
1416 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1417 }
13831418 }
13841419 } else {
1385 child.stdin_behavior = .Ignore;
1386 child.stdout_behavior = .Ignore;
1387 child.stderr_behavior = .Pipe;
1388
1389 try child.spawn();
1390
1391 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1392
1393 const term = child.wait() catch |err| {
1394 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1395 return error.UnableToSpawnSelf;
1396 };
1397
1398 switch (term) {
1399 .Exited => |code| {
1400 if (code != 0) {
1401 // TODO parse this output and surface with the Compilation API rather than
1402 // directly outputting to stderr here.
1403 std.debug.print("{s}", .{stderr});
1404 return error.LLDReportedFailure;
1405 }
1406 },
1407 else => {
1408 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1409 return error.LLDCrashed;
1410 },
1411 }
1412
1413 if (stderr.len != 0) {
1414 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1420 const exit_code = try lldMain(arena, argv.items, false);
1421 if (exit_code != 0) {
1422 if (comp.clang_passthrough_mode) {
1423 std.process.exit(exit_code);
1424 } else {
1425 return error.LLDReportedFailure;
1426 }
14151427 }
14161428 }
14171429 }
src/link/Elf.zig+59-47
......@@ -14,6 +14,7 @@ const leb128 = std.leb;
1414const Module = @import("../Module.zig");
1515const Compilation = @import("../Compilation.zig");
1616const codegen = @import("../codegen.zig");
17const lldMain = @import("../main.zig").lldMain;
1718const trace = @import("../tracy.zig").trace;
1819const Package = @import("../Package.zig");
1920const Value = @import("../value.zig").Value;
......@@ -1950,60 +1951,71 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
19501951 Compilation.dump_argv(argv.items[1..]);
19511952 }
19521953
1953 // Sadly, we must run LLD as a child process because it does not behave
1954 // properly as a library.
1955 const child = try std.ChildProcess.init(argv.items, arena);
1956 defer child.deinit();
1954 if (std.process.can_spawn) {
1955 // If possible, we run LLD as a child process because it does not always
1956 // behave properly as a library, unfortunately.
1957 // https://github.com/ziglang/zig/issues/3825
1958 const child = try std.ChildProcess.init(argv.items, arena);
1959 defer child.deinit();
19571960
1958 if (comp.clang_passthrough_mode) {
1959 child.stdin_behavior = .Inherit;
1960 child.stdout_behavior = .Inherit;
1961 child.stderr_behavior = .Inherit;
1961 if (comp.clang_passthrough_mode) {
1962 child.stdin_behavior = .Inherit;
1963 child.stdout_behavior = .Inherit;
1964 child.stderr_behavior = .Inherit;
19621965
1963 const term = child.spawnAndWait() catch |err| {
1964 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1965 return error.UnableToSpawnSelf;
1966 };
1967 switch (term) {
1968 .Exited => |code| {
1969 if (code != 0) {
1970 // TODO https://github.com/ziglang/zig/issues/6342
1971 std.process.exit(1);
1972 }
1973 },
1974 else => std.process.abort(),
1975 }
1976 } else {
1977 child.stdin_behavior = .Ignore;
1978 child.stdout_behavior = .Ignore;
1979 child.stderr_behavior = .Pipe;
1966 const term = child.spawnAndWait() catch |err| {
1967 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1968 return error.UnableToSpawnSelf;
1969 };
1970 switch (term) {
1971 .Exited => |code| {
1972 if (code != 0) {
1973 std.process.exit(code);
1974 }
1975 },
1976 else => std.process.abort(),
1977 }
1978 } else {
1979 child.stdin_behavior = .Ignore;
1980 child.stdout_behavior = .Ignore;
1981 child.stderr_behavior = .Pipe;
19801982
1981 try child.spawn();
1983 try child.spawn();
19821984
1983 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1985 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
19841986
1985 const term = child.wait() catch |err| {
1986 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1987 return error.UnableToSpawnSelf;
1988 };
1987 const term = child.wait() catch |err| {
1988 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1989 return error.UnableToSpawnSelf;
1990 };
19891991
1990 switch (term) {
1991 .Exited => |code| {
1992 if (code != 0) {
1993 // TODO parse this output and surface with the Compilation API rather than
1994 // directly outputting to stderr here.
1995 std.debug.print("{s}", .{stderr});
1996 return error.LLDReportedFailure;
1997 }
1998 },
1999 else => {
2000 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
2001 return error.LLDCrashed;
2002 },
2003 }
1992 switch (term) {
1993 .Exited => |code| {
1994 if (code != 0) {
1995 // TODO parse this output and surface with the Compilation API rather than
1996 // directly outputting to stderr here.
1997 std.debug.print("{s}", .{stderr});
1998 return error.LLDReportedFailure;
1999 }
2000 },
2001 else => {
2002 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
2003 return error.LLDCrashed;
2004 },
2005 }
20042006
2005 if (stderr.len != 0) {
2006 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
2007 if (stderr.len != 0) {
2008 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
2009 }
2010 }
2011 } else {
2012 const exit_code = try lldMain(arena, argv.items, false);
2013 if (exit_code != 0) {
2014 if (comp.clang_passthrough_mode) {
2015 std.process.exit(exit_code);
2016 } else {
2017 return error.LLDReportedFailure;
2018 }
20072019 }
20082020 }
20092021 }
src/link/Wasm.zig+60-48
......@@ -15,6 +15,7 @@ const Module = @import("../Module.zig");
1515const Compilation = @import("../Compilation.zig");
1616const CodeGen = @import("../arch/wasm/CodeGen.zig");
1717const link = @import("../link.zig");
18const lldMain = @import("../main.zig").lldMain;
1819const trace = @import("../tracy.zig").trace;
1920const build_options = @import("build_options");
2021const wasi_libc = @import("../wasi_libc.zig");
......@@ -1486,60 +1487,71 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
14861487 Compilation.dump_argv(argv.items[1..]);
14871488 }
14881489
1489 // Sadly, we must run LLD as a child process because it does not behave
1490 // properly as a library.
1491 const child = try std.ChildProcess.init(argv.items, arena);
1492 defer child.deinit();
1493
1494 if (comp.clang_passthrough_mode) {
1495 child.stdin_behavior = .Inherit;
1496 child.stdout_behavior = .Inherit;
1497 child.stderr_behavior = .Inherit;
1490 if (std.process.can_spawn) {
1491 // If possible, we run LLD as a child process because it does not always
1492 // behave properly as a library, unfortunately.
1493 // https://github.com/ziglang/zig/issues/3825
1494 const child = try std.ChildProcess.init(argv.items, arena);
1495 defer child.deinit();
1496
1497 if (comp.clang_passthrough_mode) {
1498 child.stdin_behavior = .Inherit;
1499 child.stdout_behavior = .Inherit;
1500 child.stderr_behavior = .Inherit;
1501
1502 const term = child.spawnAndWait() catch |err| {
1503 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1504 return error.UnableToSpawnSelf;
1505 };
1506 switch (term) {
1507 .Exited => |code| {
1508 if (code != 0) {
1509 std.process.exit(code);
1510 }
1511 },
1512 else => std.process.abort(),
1513 }
1514 } else {
1515 child.stdin_behavior = .Ignore;
1516 child.stdout_behavior = .Ignore;
1517 child.stderr_behavior = .Pipe;
14981518
1499 const term = child.spawnAndWait() catch |err| {
1500 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1501 return error.UnableToSpawnSelf;
1502 };
1503 switch (term) {
1504 .Exited => |code| {
1505 if (code != 0) {
1506 // TODO https://github.com/ziglang/zig/issues/6342
1507 std.process.exit(1);
1508 }
1509 },
1510 else => std.process.abort(),
1511 }
1512 } else {
1513 child.stdin_behavior = .Ignore;
1514 child.stdout_behavior = .Ignore;
1515 child.stderr_behavior = .Pipe;
1519 try child.spawn();
15161520
1517 try child.spawn();
1521 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
15181522
1519 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1523 const term = child.wait() catch |err| {
1524 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1525 return error.UnableToSpawnSelf;
1526 };
15201527
1521 const term = child.wait() catch |err| {
1522 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1523 return error.UnableToSpawnSelf;
1524 };
1528 switch (term) {
1529 .Exited => |code| {
1530 if (code != 0) {
1531 // TODO parse this output and surface with the Compilation API rather than
1532 // directly outputting to stderr here.
1533 std.debug.print("{s}", .{stderr});
1534 return error.LLDReportedFailure;
1535 }
1536 },
1537 else => {
1538 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1539 return error.LLDCrashed;
1540 },
1541 }
15251542
1526 switch (term) {
1527 .Exited => |code| {
1528 if (code != 0) {
1529 // TODO parse this output and surface with the Compilation API rather than
1530 // directly outputting to stderr here.
1531 std.debug.print("{s}", .{stderr});
1532 return error.LLDReportedFailure;
1533 }
1534 },
1535 else => {
1536 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1537 return error.LLDCrashed;
1538 },
1543 if (stderr.len != 0) {
1544 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1545 }
15391546 }
1540
1541 if (stderr.len != 0) {
1542 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1547 } else {
1548 const exit_code = try lldMain(arena, argv.items, false);
1549 if (exit_code != 0) {
1550 if (comp.clang_passthrough_mode) {
1551 std.process.exit(exit_code);
1552 } else {
1553 return error.LLDReportedFailure;
1554 }
15431555 }
15441556 }
15451557 }
src/main.zig+93-73
......@@ -221,7 +221,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
221221 mem.eql(u8, cmd, "lib") or
222222 mem.eql(u8, cmd, "ar"))
223223 {
224 return punt_to_llvm_ar(arena, args);
224 return process.exit(try llvmArMain(arena, args));
225225 } else if (mem.eql(u8, cmd, "cc")) {
226226 return buildOutputType(gpa, arena, args, .cc);
227227 } else if (mem.eql(u8, cmd, "c++")) {
......@@ -231,12 +231,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
231231 } else if (mem.eql(u8, cmd, "clang") or
232232 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
233233 {
234 return punt_to_clang(arena, args);
234 return process.exit(try clangMain(arena, args));
235235 } else if (mem.eql(u8, cmd, "ld.lld") or
236236 mem.eql(u8, cmd, "lld-link") or
237237 mem.eql(u8, cmd, "wasm-ld"))
238238 {
239 return punt_to_lld(arena, args);
239 return process.exit(try lldMain(arena, args, true));
240240 } else if (mem.eql(u8, cmd, "build")) {
241241 return cmdBuild(gpa, arena, cmd_args);
242242 } else if (mem.eql(u8, cmd, "fmt")) {
......@@ -1347,7 +1347,7 @@ fn buildOutputType(
13471347 .ignore => {},
13481348 .driver_punt => {
13491349 // Never mind what we're doing, just pass the args directly. For example --help.
1350 return punt_to_clang(arena, all_args);
1350 return process.exit(try clangMain(arena, all_args));
13511351 },
13521352 .pic => want_pic = true,
13531353 .no_pic => want_pic = false,
......@@ -1866,7 +1866,7 @@ fn buildOutputType(
18661866 // An error message is generated when there is more than 1 C source file.
18671867 if (c_source_files.items.len != 1) {
18681868 // For example `zig cc` and no args should print the "no input files" message.
1869 return punt_to_clang(arena, all_args);
1869 return process.exit(try clangMain(arena, all_args));
18701870 }
18711871 if (out_path) |p| {
18721872 emit_bin = .{ .yes = p };
......@@ -1882,7 +1882,7 @@ fn buildOutputType(
18821882 {
18831883 // For example `zig cc` and no args should print the "no input files" message.
18841884 // There could be other reasons to punt to clang, for example, --help.
1885 return punt_to_clang(arena, all_args);
1885 return process.exit(try clangMain(arena, all_args));
18861886 }
18871887 },
18881888 }
......@@ -2881,9 +2881,9 @@ fn runOrTest(
28812881 // execv releases the locks; no need to destroy the Compilation here.
28822882 const err = std.process.execv(gpa, argv.items);
28832883 try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);
2884 const cmd = try argvCmd(arena, argv.items);
2884 const cmd = try std.mem.join(arena, " ", argv.items);
28852885 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
2886 } else {
2886 } else if (std.process.can_spawn) {
28872887 const child = try std.ChildProcess.init(argv.items, gpa);
28882888 defer child.deinit();
28892889
......@@ -2900,7 +2900,7 @@ fn runOrTest(
29002900
29012901 const term = child.spawnAndWait() catch |err| {
29022902 try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);
2903 const cmd = try argvCmd(arena, argv.items);
2903 const cmd = try std.mem.join(arena, " ", argv.items);
29042904 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
29052905 };
29062906 switch (arg_mode) {
......@@ -2931,18 +2931,21 @@ fn runOrTest(
29312931 if (code == 0) {
29322932 if (!watch) return cleanExit();
29332933 } else {
2934 const cmd = try argvCmd(arena, argv.items);
2934 const cmd = try std.mem.join(arena, " ", argv.items);
29352935 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
29362936 }
29372937 },
29382938 else => {
2939 const cmd = try argvCmd(arena, argv.items);
2939 const cmd = try std.mem.join(arena, " ", argv.items);
29402940 fatal("the following test command crashed:\n{s}", .{cmd});
29412941 },
29422942 }
29432943 },
29442944 else => unreachable,
29452945 }
2946 } else {
2947 const cmd = try std.mem.join(arena, " ", argv.items);
2948 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
29462949 }
29472950}
29482951
......@@ -3553,41 +3556,36 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
35533556
35543557 break :argv child_argv.items;
35553558 };
3556 const child = try std.ChildProcess.init(child_argv, gpa);
3557 defer child.deinit();
35583559
3559 child.stdin_behavior = .Inherit;
3560 child.stdout_behavior = .Inherit;
3561 child.stderr_behavior = .Inherit;
3560 if (std.process.can_spawn) {
3561 const child = try std.ChildProcess.init(child_argv, gpa);
3562 defer child.deinit();
35623563
3563 const term = try child.spawnAndWait();
3564 switch (term) {
3565 .Exited => |code| {
3566 if (code == 0) return cleanExit();
3564 child.stdin_behavior = .Inherit;
3565 child.stdout_behavior = .Inherit;
3566 child.stderr_behavior = .Inherit;
35673567
3568 if (prominent_compile_errors) {
3569 fatal("the build command failed with exit code {d}", .{code});
3570 } else {
3571 const cmd = try argvCmd(arena, child_argv);
3572 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
3573 }
3574 },
3575 else => {
3576 const cmd = try argvCmd(arena, child_argv);
3577 fatal("the following build command crashed:\n{s}", .{cmd});
3578 },
3579 }
3580}
3568 const term = try child.spawnAndWait();
3569 switch (term) {
3570 .Exited => |code| {
3571 if (code == 0) return cleanExit();
35813572
3582fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 {
3583 var cmd = std.ArrayList(u8).init(allocator);
3584 defer cmd.deinit();
3585 for (argv[0 .. argv.len - 1]) |arg| {
3586 try cmd.appendSlice(arg);
3587 try cmd.append(' ');
3573 if (prominent_compile_errors) {
3574 fatal("the build command failed with exit code {d}", .{code});
3575 } else {
3576 const cmd = try std.mem.join(arena, " ", child_argv);
3577 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
3578 }
3579 },
3580 else => {
3581 const cmd = try std.mem.join(arena, " ", child_argv);
3582 fatal("the following build command crashed:\n{s}", .{cmd});
3583 },
3584 }
3585 } else {
3586 const cmd = try std.mem.join(arena, " ", child_argv);
3587 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
35883588 }
3589 try cmd.appendSlice(argv[argv.len - 1]);
3590 return cmd.toOwnedSlice();
35913589}
35923590
35933591fn readSourceFileToEndAlloc(
......@@ -4080,65 +4078,87 @@ pub const info_zen =
40804078extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
40814079extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
40824080
4083/// TODO https://github.com/ziglang/zig/issues/3257
4084fn punt_to_clang(arena: Allocator, args: []const []const u8) error{OutOfMemory} {
4085 if (!build_options.have_llvm)
4086 fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{});
4087 // Convert the args to the format Clang expects.
4088 const argv = try arena.alloc(?[*:0]u8, args.len + 1);
4081fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 {
4082 var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null);
40894083 for (args) |arg, i| {
4090 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
4084 argv[i] = try alloc.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
40914085 }
4092 argv[args.len] = null;
4093 const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr);
4094 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
4086 return argv;
40954087}
40964088
4097/// TODO https://github.com/ziglang/zig/issues/3257
4098fn punt_to_llvm_ar(arena: Allocator, args: []const []const u8) error{OutOfMemory} {
4089pub fn clangMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 {
4090 if (!build_options.have_llvm)
4091 fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{});
4092
4093 var arena_instance = std.heap.ArenaAllocator.init(alloc);
4094 defer arena_instance.deinit();
4095 const arena = arena_instance.allocator();
4096
4097 // Convert the args to the null-terminated format Clang expects.
4098 const argv = try argsCopyZ(arena, args);
4099 const exit_code = ZigClang_main(@intCast(c_int, argv.len), argv.ptr);
4100 return @bitCast(u8, @truncate(i8, exit_code));
4101}
4102
4103pub fn llvmArMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 {
40994104 if (!build_options.have_llvm)
41004105 fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{});
41014106
4107 var arena_instance = std.heap.ArenaAllocator.init(alloc);
4108 defer arena_instance.deinit();
4109 const arena = arena_instance.allocator();
4110
41024111 // Convert the args to the format llvm-ar expects.
4103 // We subtract 1 to shave off the zig binary from args[0].
4104 const argv = try arena.allocSentinel(?[*:0]u8, args.len - 1, null);
4105 for (args[1..]) |arg, i| {
4106 // TODO If there was an argsAllocZ we could avoid this allocation.
4107 argv[i] = try arena.dupeZ(u8, arg);
4108 }
4109 const argc = @intCast(c_int, argv.len);
4110 const exit_code = ZigLlvmAr_main(argc, argv.ptr);
4111 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
4112 // We intentionally shave off the zig binary at args[0].
4113 const argv = try argsCopyZ(arena, args[1..]);
4114 const exit_code = ZigLlvmAr_main(@intCast(c_int, argv.len), argv.ptr);
4115 return @bitCast(u8, @truncate(i8, exit_code));
41124116}
41134117
41144118/// The first argument determines which backend is invoked. The options are:
41154119/// * `ld.lld` - ELF
41164120/// * `lld-link` - COFF
41174121/// * `wasm-ld` - WebAssembly
4118/// TODO https://github.com/ziglang/zig/issues/3257
4119pub fn punt_to_lld(arena: Allocator, args: []const []const u8) error{OutOfMemory} {
4122pub fn lldMain(
4123 alloc: Allocator,
4124 args: []const []const u8,
4125 can_exit_early: bool,
4126) error{OutOfMemory}!u8 {
41204127 if (!build_options.have_llvm)
41214128 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
4122 // Convert the args to the format LLD expects.
4123 // We subtract 1 to shave off the zig binary from args[0].
4124 const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null);
4125 for (args[1..]) |arg, i| {
4126 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
4129
4130 // Print a warning if lld is called multiple times in the same process,
4131 // since it may misbehave
4132 // https://github.com/ziglang/zig/issues/3825
4133 const CallCounter = struct {
4134 var count: usize = 0;
4135 };
4136 if (CallCounter.count == 1) { // Issue the warning on the first repeat call
4137 warn("invoking LLD for the second time within the same process because the host OS ({s}) does not support spawning child processes. This sometimes activates LLD bugs", .{@tagName(builtin.os.tag)});
41274138 }
4139 CallCounter.count += 1;
4140
4141 var arena_instance = std.heap.ArenaAllocator.init(alloc);
4142 defer arena_instance.deinit();
4143 const arena = arena_instance.allocator();
4144
4145 // Convert the args to the format llvm-ar expects.
4146 // We intentionally shave off the zig binary at args[0].
4147 const argv = try argsCopyZ(arena, args[1..]);
41284148 const exit_code = rc: {
41294149 const llvm = @import("codegen/llvm/bindings.zig");
41304150 const argc = @intCast(c_int, argv.len);
41314151 if (mem.eql(u8, args[1], "ld.lld")) {
4132 break :rc llvm.LinkELF(argc, argv.ptr, true);
4152 break :rc llvm.LinkELF(argc, argv.ptr, can_exit_early);
41334153 } else if (mem.eql(u8, args[1], "lld-link")) {
4134 break :rc llvm.LinkCOFF(argc, argv.ptr, true);
4154 break :rc llvm.LinkCOFF(argc, argv.ptr, can_exit_early);
41354155 } else if (mem.eql(u8, args[1], "wasm-ld")) {
4136 break :rc llvm.LinkWasm(argc, argv.ptr, true);
4156 break :rc llvm.LinkWasm(argc, argv.ptr, can_exit_early);
41374157 } else {
41384158 unreachable;
41394159 }
41404160 };
4141 process.exit(@bitCast(u8, @truncate(i8, exit_code)));
4161 return @bitCast(u8, @truncate(i8, exit_code));
41424162}
41434163
41444164const clang_args = @import("clang_options.zig").list;
src/mingw.zig+31-26
......@@ -5,6 +5,7 @@ const path = std.fs.path;
55const assert = std.debug.assert;
66const log = std.log.scoped(.mingw);
77
8const builtin = @import("builtin");
89const target_util = @import("target.zig");
910const Compilation = @import("Compilation.zig");
1011const build_options = @import("build_options");
......@@ -367,39 +368,43 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
367368 Compilation.dump_argv(&args);
368369 }
369370
370 const child = try std.ChildProcess.init(&args, arena);
371 defer child.deinit();
371 if (std.process.can_spawn) {
372 const child = try std.ChildProcess.init(&args, arena);
373 defer child.deinit();
372374
373 child.stdin_behavior = .Ignore;
374 child.stdout_behavior = .Pipe;
375 child.stderr_behavior = .Pipe;
375 child.stdin_behavior = .Ignore;
376 child.stdout_behavior = .Pipe;
377 child.stderr_behavior = .Pipe;
376378
377 try child.spawn();
379 try child.spawn();
378380
379 const stderr_reader = child.stderr.?.reader();
381 const stderr_reader = child.stderr.?.reader();
380382
381 // TODO https://github.com/ziglang/zig/issues/6343
382 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
383 // TODO https://github.com/ziglang/zig/issues/6343
384 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
383385
384 const term = child.wait() catch |err| {
385 // TODO surface a proper error here
386 log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) });
387 return error.ClangPreprocessorFailed;
388 };
389
390 switch (term) {
391 .Exited => |code| {
392 if (code != 0) {
393 // TODO surface a proper error here
394 log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr });
395 return error.ClangPreprocessorFailed;
396 }
397 },
398 else => {
386 const term = child.wait() catch |err| {
399387 // TODO surface a proper error here
400 log.err("clang terminated unexpectedly with stderr: {s}", .{stderr});
388 log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) });
401389 return error.ClangPreprocessorFailed;
402 },
390 };
391 switch (term) {
392 .Exited => |code| {
393 if (code != 0) {
394 // TODO surface a proper error here
395 log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr });
396 return error.ClangPreprocessorFailed;
397 }
398 },
399 else => {
400 // TODO surface a proper error here
401 log.err("clang terminated unexpectedly with stderr: {s}", .{stderr});
402 return error.ClangPreprocessorFailed;
403 },
404 }
405 } else {
406 log.err("unable to spawn {s}: spawning child process not supported on {s}", .{ args[0], @tagName(builtin.os.tag) });
407 return error.ClangPreprocessorFailed;
403408 }
404409
405410 const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
src/test.zig+11
......@@ -730,6 +730,12 @@ pub const TestContext = struct {
730730 // * cannot handle updates
731731 // because of this we must spawn a child process rather than
732732 // using Compilation directly.
733
734 if (!std.process.can_spawn) {
735 print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)});
736 return; // Pass test.
737 }
738
733739 assert(case.updates.items.len == 1);
734740 const update = case.updates.items[0];
735741 try tmp.dir.writeFile(tmp_src_path, update.src);
......@@ -1104,6 +1110,11 @@ pub const TestContext = struct {
11041110 }
11051111 },
11061112 .Execution => |expected_stdout| {
1113 if (!std.process.can_spawn) {
1114 print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)});
1115 return; // Pass test.
1116 }
1117
11071118 update_node.setEstimatedTotalItems(4);
11081119
11091120 var argv = std.ArrayList([]const u8).init(allocator);
test/tests.zig+9
......@@ -10,6 +10,8 @@ const fmt = std.fmt;
1010const ArrayList = std.ArrayList;
1111const Mode = std.builtin.Mode;
1212const LibExeObjStep = build.LibExeObjStep;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
1315
1416// Cases
1517const compare_output = @import("compare_output.zig");
......@@ -722,6 +724,13 @@ pub const StackTracesContext = struct {
722724
723725 std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name });
724726
727 if (!std.process.can_spawn) {
728 const cmd = try std.mem.join(b.allocator, " ", args.items);
729 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
730 b.allocator.free(cmd);
731 return ExecError.ExecNotSupported;
732 }
733
725734 const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable;
726735 defer child.deinit();
727736