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
signaturebadge-question-mark 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 {...@@ -229,6 +229,10 @@ pub fn build(b: *Builder) !void {
229 const version = if (opt_version_string) |version| version else v: {229 const version = if (opt_version_string) |version| version else v: {
230 const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch });230 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 }
232 var code: u8 = undefined;236 var code: u8 = undefined;
233 const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{237 const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{
234 "git", "-C", b.build_root, "describe", "--match", "*.*.*", "--tags",238 "git", "-C", b.build_root, "describe", "--match", "*.*.*", "--tags",
...@@ -542,6 +546,9 @@ fn addCxxKnownPath(...@@ -542,6 +546,9 @@ fn addCxxKnownPath(
542 errtxt: ?[]const u8,546 errtxt: ?[]const u8,
543 need_cpp_includes: bool,547 need_cpp_includes: bool,
544) !void {548) !void {
549 if (!std.process.can_spawn)
550 return error.RequiredLibraryNotFound;
551
545 const path_padded = try b.exec(&[_][]const u8{552 const path_padded = try b.exec(&[_][]const u8{
546 ctx.cxx_compiler,553 ctx.cxx_compiler,
547 b.fmt("-print-file-name={s}", .{objname}),554 b.fmt("-print-file-name={s}", .{objname}),
lib/std/build.zig+34-4
...@@ -88,7 +88,14 @@ pub const Builder = struct {...@@ -88,7 +88,14 @@ pub const Builder = struct {
88 /// Information about the native target. Computed before build() is invoked.88 /// Information about the native target. Computed before build() is invoked.
89 host: NativeTargetInfo,89 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{
92 PkgConfigCrashed,99 PkgConfigCrashed,
93 PkgConfigFailed,100 PkgConfigFailed,
94 PkgConfigNotInstalled,101 PkgConfigNotInstalled,
...@@ -959,6 +966,9 @@ pub const Builder = struct {...@@ -959,6 +966,9 @@ pub const Builder = struct {
959 printCmd(cwd, argv);966 printCmd(cwd, argv);
960 }967 }
961968
969 if (!std.process.can_spawn)
970 return error.ExecNotSupported;
971
962 const child = std.ChildProcess.init(argv, self.allocator) catch unreachable;972 const child = std.ChildProcess.init(argv, self.allocator) catch unreachable;
963 defer child.deinit();973 defer child.deinit();
964974
...@@ -1168,9 +1178,12 @@ pub const Builder = struct {...@@ -1168,9 +1178,12 @@ pub const Builder = struct {
1168 argv: []const []const u8,1178 argv: []const []const u8,
1169 out_code: *u8,1179 out_code: *u8,
1170 stderr_behavior: std.ChildProcess.StdIo,1180 stderr_behavior: std.ChildProcess.StdIo,
1171 ) ![]u8 {1181 ) ExecError![]u8 {
1172 assert(argv.len != 0);1182 assert(argv.len != 0);
11731183
1184 if (!std.process.can_spawn)
1185 return error.ExecNotSupported;
1186
1174 const max_output_size = 400 * 1024;1187 const max_output_size = 400 * 1024;
1175 const child = try std.ChildProcess.init(argv, self.allocator);1188 const child = try std.ChildProcess.init(argv, self.allocator);
1176 defer child.deinit();1189 defer child.deinit();
...@@ -1182,7 +1195,9 @@ pub const Builder = struct {...@@ -1182,7 +1195,9 @@ pub const Builder = struct {
11821195
1183 try child.spawn();1196 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 };
1186 errdefer self.allocator.free(stdout);1201 errdefer self.allocator.free(stdout);
11871202
1188 const term = try child.wait();1203 const term = try child.wait();
...@@ -1208,8 +1223,21 @@ pub const Builder = struct {...@@ -1208,8 +1223,21 @@ pub const Builder = struct {
1208 printCmd(null, argv);1223 printCmd(null, argv);
1209 }1224 }
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
1211 var code: u8 = undefined;1233 var code: u8 = undefined;
1212 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {1234 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 },
1213 error.FileNotFound => {1241 error.FileNotFound => {
1214 if (src_step) |s| warn("{s}...", .{s.name});1242 if (src_step) |s| warn("{s}...", .{s.name});
1215 warn("Unable to spawn the following command: file not found\n", .{});1243 warn("Unable to spawn the following command: file not found\n", .{});
...@@ -1260,7 +1288,7 @@ pub const Builder = struct {...@@ -1260,7 +1288,7 @@ pub const Builder = struct {
1260 ) catch unreachable;1288 ) catch unreachable;
1261 }1289 }
12621290
1263 fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg {1291 fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1264 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);1292 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1265 var list = ArrayList(PkgConfigPkg).init(self.allocator);1293 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1266 errdefer list.deinit();1294 errdefer list.deinit();
...@@ -1287,6 +1315,7 @@ pub const Builder = struct {...@@ -1287,6 +1315,7 @@ pub const Builder = struct {
1287 } else |err| {1315 } else |err| {
1288 const result = switch (err) {1316 const result = switch (err) {
1289 error.ProcessTerminated => error.PkgConfigCrashed,1317 error.ProcessTerminated => error.PkgConfigCrashed,
1318 error.ExecNotSupported => error.PkgConfigFailed,
1290 error.ExitCodeFailure => error.PkgConfigFailed,1319 error.ExitCodeFailure => error.PkgConfigFailed,
1291 error.FileNotFound => error.PkgConfigNotInstalled,1320 error.FileNotFound => error.PkgConfigNotInstalled,
1292 error.InvalidName => error.PkgConfigNotInstalled,1321 error.InvalidName => error.PkgConfigNotInstalled,
...@@ -1929,6 +1958,7 @@ pub const LibExeObjStep = struct {...@@ -1929,6 +1958,7 @@ pub const LibExeObjStep = struct {
1929 "--libs",1958 "--libs",
1930 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {1959 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
1931 error.ProcessTerminated => return error.PkgConfigCrashed,1960 error.ProcessTerminated => return error.PkgConfigCrashed,
1961 error.ExecNotSupported => return error.PkgConfigFailed,
1932 error.ExitCodeFailure => return error.PkgConfigFailed,1962 error.ExitCodeFailure => return error.PkgConfigFailed,
1933 error.FileNotFound => return error.PkgConfigNotInstalled,1963 error.FileNotFound => return error.PkgConfigNotInstalled,
1934 else => return err,1964 else => return err,
lib/std/build/RunStep.zig+9
...@@ -10,6 +10,8 @@ const mem = std.mem;...@@ -10,6 +10,8 @@ const mem = std.mem;
10const process = std.process;10const process = std.process;
11const ArrayList = std.ArrayList;11const ArrayList = std.ArrayList;
12const BufMap = std.BufMap;12const BufMap = std.BufMap;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
1315
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB16const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
1517
...@@ -175,6 +177,13 @@ fn make(step: *Step) !void {...@@ -175,6 +177,13 @@ fn make(step: *Step) !void {
175177
176 const argv = argv_list.items;178 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
178 const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable;187 const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable;
179 defer child.deinit();188 defer child.deinit();
180189
lib/std/child_process.zig+4
...@@ -124,6 +124,10 @@ pub const ChildProcess = struct {...@@ -124,6 +124,10 @@ pub const ChildProcess = struct {
124124
125 /// On success must call `kill` or `wait`.125 /// On success must call `kill` or `wait`.
126 pub fn spawn(self: *ChildProcess) SpawnError!void {126 pub fn spawn(self: *ChildProcess) SpawnError!void {
127 if (!std.process.can_spawn) {
128 @compileError("the target operating system cannot spawn processes");
129 }
130
127 if (builtin.os.tag == .windows) {131 if (builtin.os.tag == .windows) {
128 return self.spawnWindows();132 return self.spawnWindows();
129 } else {133 } else {
lib/std/process.zig+7-1
...@@ -950,7 +950,13 @@ pub fn getSelfExeSharedLibPaths(allocator: Allocator) error{OutOfMemory}![][:0]u...@@ -950,7 +950,13 @@ pub fn getSelfExeSharedLibPaths(allocator: Allocator) error{OutOfMemory}![][:0]u
950950
951/// Tells whether calling the `execv` or `execve` functions will be a compile error.951/// Tells whether calling the `execv` or `execve` functions will be a compile error.
952pub const can_execv = switch (builtin.os.tag) {952pub 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,
954 else => true,960 else => true,
955};961};
956962
src/Compilation.zig+59-42
...@@ -25,6 +25,7 @@ const libunwind = @import("libunwind.zig");...@@ -25,6 +25,7 @@ const libunwind = @import("libunwind.zig");
25const libcxx = @import("libcxx.zig");25const libcxx = @import("libcxx.zig");
26const wasi_libc = @import("wasi_libc.zig");26const wasi_libc = @import("wasi_libc.zig");
27const fatal = @import("main.zig").fatal;27const fatal = @import("main.zig").fatal;
28const clangMain = @import("main.zig").clangMain;
28const Module = @import("Module.zig");29const Module = @import("Module.zig");
29const Cache = @import("Cache.zig");30const Cache = @import("Cache.zig");
30const stage1 = @import("stage1.zig");31const stage1 = @import("stage1.zig");
...@@ -3667,55 +3668,71 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -3667,55 +3668,71 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
3667 dump_argv(argv.items);3668 dump_argv(argv.items);
3668 }3669 }
36693670
3670 const child = try std.ChildProcess.init(argv.items, arena);3671 if (std.process.can_spawn) {
3671 defer child.deinit();3672 const child = try std.ChildProcess.init(argv.items, arena);
3673 defer child.deinit();
36723674
3673 if (comp.clang_passthrough_mode) {3675 if (comp.clang_passthrough_mode) {
3674 child.stdin_behavior = .Inherit;3676 child.stdin_behavior = .Inherit;
3675 child.stdout_behavior = .Inherit;3677 child.stdout_behavior = .Inherit;
3676 child.stderr_behavior = .Inherit;3678 child.stderr_behavior = .Inherit;
36773679
3678 const term = child.spawnAndWait() catch |err| {3680 const term = child.spawnAndWait() catch |err| {
3679 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });3681 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
3680 };3682 };
3681 switch (term) {3683 switch (term) {
3682 .Exited => |code| {3684 .Exited => |code| {
3683 if (code != 0) {3685 if (code != 0) {
3684 std.process.exit(code);3686 std.process.exit(code);
3685 }3687 }
3686 if (comp.clang_preprocessor_mode == .stdout)3688 if (comp.clang_preprocessor_mode == .stdout)
3687 std.process.exit(0);3689 std.process.exit(0);
3688 },3690 },
3689 else => std.process.abort(),3691 else => std.process.abort(),
3690 }3692 }
3691 } else {3693 } else {
3692 child.stdin_behavior = .Ignore;3694 child.stdin_behavior = .Ignore;
3693 child.stdout_behavior = .Ignore;3695 child.stdout_behavior = .Ignore;
3694 child.stderr_behavior = .Pipe;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| {3704 const term = child.wait() catch |err| {
3703 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });3705 return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
3704 };3706 };
37053707
3706 switch (term) {3708 switch (term) {
3707 .Exited => |code| {3709 .Exited => |code| {
3708 if (code != 0) {3710 if (code != 0) {
3709 // TODO parse clang stderr and turn it into an error message3711 // TODO parse clang stderr and turn it into an error message
3710 // and then call failCObjWithOwnedErrorMsg3712 // and then call failCObjWithOwnedErrorMsg
3711 log.err("clang failed with stderr: {s}", .{stderr});3713 log.err("clang failed with stderr: {s}", .{stderr});
3712 return comp.failCObj(c_object, "clang exited with code {d}", .{code});3714 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
3713 }3715 }
3714 },3716 },
3715 else => {3717 else => {
3716 log.err("clang terminated with stderr: {s}", .{stderr});3718 log.err("clang terminated with stderr: {s}", .{stderr});
3717 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});3719 return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
3718 },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);
3719 }3736 }
3720 }3737 }
37213738
src/ThreadPool.zig+3
...@@ -82,6 +82,9 @@ pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void {...@@ -82,6 +82,9 @@ pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void {
82}82}
8383
84fn destroyWorkers(self: *ThreadPool, spawned: usize) void {84fn destroyWorkers(self: *ThreadPool, spawned: usize) void {
85 if (builtin.single_threaded)
86 return;
87
85 for (self.workers[0..spawned]) |*worker| {88 for (self.workers[0..spawned]) |*worker| {
86 worker.thread.join();89 worker.thread.join();
87 worker.idle_node.data.deinit();90 worker.idle_node.data.deinit();
src/libc_installation.zig+3-1
...@@ -216,7 +216,7 @@ pub const LibCInstallation = struct {...@@ -216,7 +216,7 @@ pub const LibCInstallation = struct {
216 self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");216 self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
217 break :blk batch.wait();217 break :blk batch.wait();
218 };218 };
219 } else {219 } else if (std.process.can_spawn) {
220 try blk: {220 try blk: {
221 var batch = Batch(FindError!void, 2, .auto_async).init();221 var batch = Batch(FindError!void, 2, .auto_async).init();
222 errdefer batch.wait() catch {};222 errdefer batch.wait() catch {};
...@@ -229,6 +229,8 @@ pub const LibCInstallation = struct {...@@ -229,6 +229,8 @@ pub const LibCInstallation = struct {
229 }229 }
230 break :blk batch.wait();230 break :blk batch.wait();
231 };231 };
232 } else {
233 return error.LibCRuntimeNotFound;
232 }234 }
233 return self;235 return self;
234 }236 }
src/link/Coff.zig+64-52
...@@ -9,6 +9,7 @@ const fs = std.fs;...@@ -9,6 +9,7 @@ const fs = std.fs;
9const allocPrint = std.fmt.allocPrint;9const allocPrint = std.fmt.allocPrint;
10const mem = std.mem;10const mem = std.mem;
1111
12const lldMain = @import("../main.zig").lldMain;
12const trace = @import("../tracy.zig").trace;13const trace = @import("../tracy.zig").trace;
13const Module = @import("../Module.zig");14const Module = @import("../Module.zig");
14const Compilation = @import("../Compilation.zig");15const Compilation = @import("../Compilation.zig");
...@@ -1358,60 +1359,71 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1358,60 +1359,71 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1358 Compilation.dump_argv(argv.items[1..]);1359 Compilation.dump_argv(argv.items[1..]);
1359 }1360 }
13601361
1361 // Sadly, we must run LLD as a child process because it does not behave1362 if (std.process.can_spawn) {
1362 // properly as a library.1363 // If possible, we run LLD as a child process because it does not always
1363 const child = try std.ChildProcess.init(argv.items, arena);1364 // behave properly as a library, unfortunately.
1364 defer child.deinit();1365 // https://github.com/ziglang/zig/issues/3825
13651366 const child = try std.ChildProcess.init(argv.items, arena);
1366 if (comp.clang_passthrough_mode) {1367 defer child.deinit();
1367 child.stdin_behavior = .Inherit;1368
1368 child.stdout_behavior = .Inherit;1369 if (comp.clang_passthrough_mode) {
1369 child.stderr_behavior = .Inherit;1370 child.stdin_behavior = .Inherit;
13701371 child.stdout_behavior = .Inherit;
1371 const term = child.spawnAndWait() catch |err| {1372 child.stderr_behavior = .Inherit;
1372 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });1373
1373 return error.UnableToSpawnSelf;1374 const term = child.spawnAndWait() catch |err| {
1374 };1375 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1375 switch (term) {1376 return error.UnableToSpawnSelf;
1376 .Exited => |code| {1377 };
1377 if (code != 0) {1378 switch (term) {
1378 // TODO https://github.com/ziglang/zig/issues/63421379 .Exited => |code| {
1379 std.process.exit(1);1380 if (code != 0) {
1380 }1381 std.process.exit(code);
1381 },1382 }
1382 else => std.process.abort(),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 }
1383 }1418 }
1384 } else {1419 } else {
1385 child.stdin_behavior = .Ignore;1420 const exit_code = try lldMain(arena, argv.items, false);
1386 child.stdout_behavior = .Ignore;1421 if (exit_code != 0) {
1387 child.stderr_behavior = .Pipe;1422 if (comp.clang_passthrough_mode) {
13881423 std.process.exit(exit_code);
1389 try child.spawn();1424 } else {
13901425 return error.LLDReportedFailure;
1391 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);1426 }
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});
1415 }1427 }
1416 }1428 }
1417 }1429 }
src/link/Elf.zig+59-47
...@@ -14,6 +14,7 @@ const leb128 = std.leb;...@@ -14,6 +14,7 @@ const leb128 = std.leb;
14const Module = @import("../Module.zig");14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");15const Compilation = @import("../Compilation.zig");
16const codegen = @import("../codegen.zig");16const codegen = @import("../codegen.zig");
17const lldMain = @import("../main.zig").lldMain;
17const trace = @import("../tracy.zig").trace;18const trace = @import("../tracy.zig").trace;
18const Package = @import("../Package.zig");19const Package = @import("../Package.zig");
19const Value = @import("../value.zig").Value;20const Value = @import("../value.zig").Value;
...@@ -1950,60 +1951,71 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1950,60 +1951,71 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1950 Compilation.dump_argv(argv.items[1..]);1951 Compilation.dump_argv(argv.items[1..]);
1951 }1952 }
19521953
1953 // Sadly, we must run LLD as a child process because it does not behave1954 if (std.process.can_spawn) {
1954 // properly as a library.1955 // If possible, we run LLD as a child process because it does not always
1955 const child = try std.ChildProcess.init(argv.items, arena);1956 // behave properly as a library, unfortunately.
1956 defer child.deinit();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) {1961 if (comp.clang_passthrough_mode) {
1959 child.stdin_behavior = .Inherit;1962 child.stdin_behavior = .Inherit;
1960 child.stdout_behavior = .Inherit;1963 child.stdout_behavior = .Inherit;
1961 child.stderr_behavior = .Inherit;1964 child.stderr_behavior = .Inherit;
19621965
1963 const term = child.spawnAndWait() catch |err| {1966 const term = child.spawnAndWait() catch |err| {
1964 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });1967 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1965 return error.UnableToSpawnSelf;1968 return error.UnableToSpawnSelf;
1966 };1969 };
1967 switch (term) {1970 switch (term) {
1968 .Exited => |code| {1971 .Exited => |code| {
1969 if (code != 0) {1972 if (code != 0) {
1970 // TODO https://github.com/ziglang/zig/issues/63421973 std.process.exit(code);
1971 std.process.exit(1);1974 }
1972 }1975 },
1973 },1976 else => std.process.abort(),
1974 else => std.process.abort(),1977 }
1975 }1978 } else {
1976 } else {1979 child.stdin_behavior = .Ignore;
1977 child.stdin_behavior = .Ignore;1980 child.stdout_behavior = .Ignore;
1978 child.stdout_behavior = .Ignore;1981 child.stderr_behavior = .Pipe;
1979 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| {1987 const term = child.wait() catch |err| {
1986 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });1988 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1987 return error.UnableToSpawnSelf;1989 return error.UnableToSpawnSelf;
1988 };1990 };
19891991
1990 switch (term) {1992 switch (term) {
1991 .Exited => |code| {1993 .Exited => |code| {
1992 if (code != 0) {1994 if (code != 0) {
1993 // TODO parse this output and surface with the Compilation API rather than1995 // TODO parse this output and surface with the Compilation API rather than
1994 // directly outputting to stderr here.1996 // directly outputting to stderr here.
1995 std.debug.print("{s}", .{stderr});1997 std.debug.print("{s}", .{stderr});
1996 return error.LLDReportedFailure;1998 return error.LLDReportedFailure;
1997 }1999 }
1998 },2000 },
1999 else => {2001 else => {
2000 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });2002 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
2001 return error.LLDCrashed;2003 return error.LLDCrashed;
2002 },2004 },
2003 }2005 }
20042006
2005 if (stderr.len != 0) {2007 if (stderr.len != 0) {
2006 log.warn("unexpected LLD stderr:\n{s}", .{stderr});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 }
2007 }2019 }
2008 }2020 }
2009 }2021 }
src/link/Wasm.zig+60-48
...@@ -15,6 +15,7 @@ const Module = @import("../Module.zig");...@@ -15,6 +15,7 @@ const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");15const Compilation = @import("../Compilation.zig");
16const CodeGen = @import("../arch/wasm/CodeGen.zig");16const CodeGen = @import("../arch/wasm/CodeGen.zig");
17const link = @import("../link.zig");17const link = @import("../link.zig");
18const lldMain = @import("../main.zig").lldMain;
18const trace = @import("../tracy.zig").trace;19const trace = @import("../tracy.zig").trace;
19const build_options = @import("build_options");20const build_options = @import("build_options");
20const wasi_libc = @import("../wasi_libc.zig");21const wasi_libc = @import("../wasi_libc.zig");
...@@ -1486,60 +1487,71 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -1486,60 +1487,71 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
1486 Compilation.dump_argv(argv.items[1..]);1487 Compilation.dump_argv(argv.items[1..]);
1487 }1488 }
14881489
1489 // Sadly, we must run LLD as a child process because it does not behave1490 if (std.process.can_spawn) {
1490 // properly as a library.1491 // If possible, we run LLD as a child process because it does not always
1491 const child = try std.ChildProcess.init(argv.items, arena);1492 // behave properly as a library, unfortunately.
1492 defer child.deinit();1493 // https://github.com/ziglang/zig/issues/3825
14931494 const child = try std.ChildProcess.init(argv.items, arena);
1494 if (comp.clang_passthrough_mode) {1495 defer child.deinit();
1495 child.stdin_behavior = .Inherit;1496
1496 child.stdout_behavior = .Inherit;1497 if (comp.clang_passthrough_mode) {
1497 child.stderr_behavior = .Inherit;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| {1519 try child.spawn();
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;
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| {1528 switch (term) {
1522 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });1529 .Exited => |code| {
1523 return error.UnableToSpawnSelf;1530 if (code != 0) {
1524 };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) {1543 if (stderr.len != 0) {
1527 .Exited => |code| {1544 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1528 if (code != 0) {1545 }
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 },
1539 }1546 }
15401547 } else {
1541 if (stderr.len != 0) {1548 const exit_code = try lldMain(arena, argv.items, false);
1542 log.warn("unexpected LLD stderr:\n{s}", .{stderr});1549 if (exit_code != 0) {
1550 if (comp.clang_passthrough_mode) {
1551 std.process.exit(exit_code);
1552 } else {
1553 return error.LLDReportedFailure;
1554 }
1543 }1555 }
1544 }1556 }
1545 }1557 }
src/main.zig+93-73
...@@ -221,7 +221,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -221,7 +221,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
221 mem.eql(u8, cmd, "lib") or221 mem.eql(u8, cmd, "lib") or
222 mem.eql(u8, cmd, "ar"))222 mem.eql(u8, cmd, "ar"))
223 {223 {
224 return punt_to_llvm_ar(arena, args);224 return process.exit(try llvmArMain(arena, args));
225 } else if (mem.eql(u8, cmd, "cc")) {225 } else if (mem.eql(u8, cmd, "cc")) {
226 return buildOutputType(gpa, arena, args, .cc);226 return buildOutputType(gpa, arena, args, .cc);
227 } else if (mem.eql(u8, cmd, "c++")) {227 } else if (mem.eql(u8, cmd, "c++")) {
...@@ -231,12 +231,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -231,12 +231,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
231 } else if (mem.eql(u8, cmd, "clang") or231 } else if (mem.eql(u8, cmd, "clang") or
232 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))232 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
233 {233 {
234 return punt_to_clang(arena, args);234 return process.exit(try clangMain(arena, args));
235 } else if (mem.eql(u8, cmd, "ld.lld") or235 } else if (mem.eql(u8, cmd, "ld.lld") or
236 mem.eql(u8, cmd, "lld-link") or236 mem.eql(u8, cmd, "lld-link") or
237 mem.eql(u8, cmd, "wasm-ld"))237 mem.eql(u8, cmd, "wasm-ld"))
238 {238 {
239 return punt_to_lld(arena, args);239 return process.exit(try lldMain(arena, args, true));
240 } else if (mem.eql(u8, cmd, "build")) {240 } else if (mem.eql(u8, cmd, "build")) {
241 return cmdBuild(gpa, arena, cmd_args);241 return cmdBuild(gpa, arena, cmd_args);
242 } else if (mem.eql(u8, cmd, "fmt")) {242 } else if (mem.eql(u8, cmd, "fmt")) {
...@@ -1347,7 +1347,7 @@ fn buildOutputType(...@@ -1347,7 +1347,7 @@ fn buildOutputType(
1347 .ignore => {},1347 .ignore => {},
1348 .driver_punt => {1348 .driver_punt => {
1349 // Never mind what we're doing, just pass the args directly. For example --help.1349 // 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));
1351 },1351 },
1352 .pic => want_pic = true,1352 .pic => want_pic = true,
1353 .no_pic => want_pic = false,1353 .no_pic => want_pic = false,
...@@ -1866,7 +1866,7 @@ fn buildOutputType(...@@ -1866,7 +1866,7 @@ fn buildOutputType(
1866 // An error message is generated when there is more than 1 C source file.1866 // An error message is generated when there is more than 1 C source file.
1867 if (c_source_files.items.len != 1) {1867 if (c_source_files.items.len != 1) {
1868 // For example `zig cc` and no args should print the "no input files" message.1868 // 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));
1870 }1870 }
1871 if (out_path) |p| {1871 if (out_path) |p| {
1872 emit_bin = .{ .yes = p };1872 emit_bin = .{ .yes = p };
...@@ -1882,7 +1882,7 @@ fn buildOutputType(...@@ -1882,7 +1882,7 @@ fn buildOutputType(
1882 {1882 {
1883 // For example `zig cc` and no args should print the "no input files" message.1883 // For example `zig cc` and no args should print the "no input files" message.
1884 // There could be other reasons to punt to clang, for example, --help.1884 // 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));
1886 }1886 }
1887 },1887 },
1888 }1888 }
...@@ -2881,9 +2881,9 @@ fn runOrTest(...@@ -2881,9 +2881,9 @@ fn runOrTest(
2881 // execv releases the locks; no need to destroy the Compilation here.2881 // execv releases the locks; no need to destroy the Compilation here.
2882 const err = std.process.execv(gpa, argv.items);2882 const err = std.process.execv(gpa, argv.items);
2883 try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);2883 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);
2885 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });2885 fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
2886 } else {2886 } else if (std.process.can_spawn) {
2887 const child = try std.ChildProcess.init(argv.items, gpa);2887 const child = try std.ChildProcess.init(argv.items, gpa);
2888 defer child.deinit();2888 defer child.deinit();
28892889
...@@ -2900,7 +2900,7 @@ fn runOrTest(...@@ -2900,7 +2900,7 @@ fn runOrTest(
29002900
2901 const term = child.spawnAndWait() catch |err| {2901 const term = child.spawnAndWait() catch |err| {
2902 try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);2902 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);
2904 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });2904 fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
2905 };2905 };
2906 switch (arg_mode) {2906 switch (arg_mode) {
...@@ -2931,18 +2931,21 @@ fn runOrTest(...@@ -2931,18 +2931,21 @@ fn runOrTest(
2931 if (code == 0) {2931 if (code == 0) {
2932 if (!watch) return cleanExit();2932 if (!watch) return cleanExit();
2933 } else {2933 } else {
2934 const cmd = try argvCmd(arena, argv.items);2934 const cmd = try std.mem.join(arena, " ", argv.items);
2935 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });2935 fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
2936 }2936 }
2937 },2937 },
2938 else => {2938 else => {
2939 const cmd = try argvCmd(arena, argv.items);2939 const cmd = try std.mem.join(arena, " ", argv.items);
2940 fatal("the following test command crashed:\n{s}", .{cmd});2940 fatal("the following test command crashed:\n{s}", .{cmd});
2941 },2941 },
2942 }2942 }
2943 },2943 },
2944 else => unreachable,2944 else => unreachable,
2945 }2945 }
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 });
2946 }2949 }
2947}2950}
29482951
...@@ -3553,41 +3556,36 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -3553,41 +3556,36 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
35533556
3554 break :argv child_argv.items;3557 break :argv child_argv.items;
3555 };3558 };
3556 const child = try std.ChildProcess.init(child_argv, gpa);
3557 defer child.deinit();
35583559
3559 child.stdin_behavior = .Inherit;3560 if (std.process.can_spawn) {
3560 child.stdout_behavior = .Inherit;3561 const child = try std.ChildProcess.init(child_argv, gpa);
3561 child.stderr_behavior = .Inherit;3562 defer child.deinit();
35623563
3563 const term = try child.spawnAndWait();3564 child.stdin_behavior = .Inherit;
3564 switch (term) {3565 child.stdout_behavior = .Inherit;
3565 .Exited => |code| {3566 child.stderr_behavior = .Inherit;
3566 if (code == 0) return cleanExit();
35673567
3568 if (prominent_compile_errors) {3568 const term = try child.spawnAndWait();
3569 fatal("the build command failed with exit code {d}", .{code});3569 switch (term) {
3570 } else {3570 .Exited => |code| {
3571 const cmd = try argvCmd(arena, child_argv);3571 if (code == 0) return cleanExit();
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}
35813572
3582fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 {3573 if (prominent_compile_errors) {
3583 var cmd = std.ArrayList(u8).init(allocator);3574 fatal("the build command failed with exit code {d}", .{code});
3584 defer cmd.deinit();3575 } else {
3585 for (argv[0 .. argv.len - 1]) |arg| {3576 const cmd = try std.mem.join(arena, " ", child_argv);
3586 try cmd.appendSlice(arg);3577 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
3587 try cmd.append(' ');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 });
3588 }3588 }
3589 try cmd.appendSlice(argv[argv.len - 1]);
3590 return cmd.toOwnedSlice();
3591}3589}
35923590
3593fn readSourceFileToEndAlloc(3591fn readSourceFileToEndAlloc(
...@@ -4080,65 +4078,87 @@ pub const info_zen =...@@ -4080,65 +4078,87 @@ pub const info_zen =
4080extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;4078extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
4081extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;4079extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
40824080
4083/// TODO https://github.com/ziglang/zig/issues/32574081fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 {
4084fn punt_to_clang(arena: Allocator, args: []const []const u8) error{OutOfMemory} {4082 var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null);
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);
4089 for (args) |arg, i| {4083 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.
4091 }4085 }
4092 argv[args.len] = null;4086 return argv;
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)));
4095}4087}
40964088
4097/// TODO https://github.com/ziglang/zig/issues/32574089pub fn clangMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 {
4098fn punt_to_llvm_ar(arena: Allocator, args: []const []const u8) error{OutOfMemory} {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 {
4099 if (!build_options.have_llvm)4104 if (!build_options.have_llvm)
4100 fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{});4105 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
4102 // Convert the args to the format llvm-ar expects.4111 // Convert the args to the format llvm-ar expects.
4103 // We subtract 1 to shave off the zig binary from args[0].4112 // We intentionally shave off the zig binary at args[0].
4104 const argv = try arena.allocSentinel(?[*:0]u8, args.len - 1, null);4113 const argv = try argsCopyZ(arena, args[1..]);
4105 for (args[1..]) |arg, i| {4114 const exit_code = ZigLlvmAr_main(@intCast(c_int, argv.len), argv.ptr);
4106 // TODO If there was an argsAllocZ we could avoid this allocation.4115 return @bitCast(u8, @truncate(i8, exit_code));
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}4116}
41134117
4114/// The first argument determines which backend is invoked. The options are:4118/// The first argument determines which backend is invoked. The options are:
4115/// * `ld.lld` - ELF4119/// * `ld.lld` - ELF
4116/// * `lld-link` - COFF4120/// * `lld-link` - COFF
4117/// * `wasm-ld` - WebAssembly4121/// * `wasm-ld` - WebAssembly
4118/// TODO https://github.com/ziglang/zig/issues/32574122pub fn lldMain(
4119pub fn punt_to_lld(arena: Allocator, args: []const []const u8) error{OutOfMemory} {4123 alloc: Allocator,
4124 args: []const []const u8,
4125 can_exit_early: bool,
4126) error{OutOfMemory}!u8 {
4120 if (!build_options.have_llvm)4127 if (!build_options.have_llvm)
4121 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});4128 fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
4122 // Convert the args to the format LLD expects.4129
4123 // We subtract 1 to shave off the zig binary from args[0].4130 // Print a warning if lld is called multiple times in the same process,
4124 const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null);4131 // since it may misbehave
4125 for (args[1..]) |arg, i| {4132 // https://github.com/ziglang/zig/issues/3825
4126 argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.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)});
4127 }4138 }
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..]);
4128 const exit_code = rc: {4148 const exit_code = rc: {
4129 const llvm = @import("codegen/llvm/bindings.zig");4149 const llvm = @import("codegen/llvm/bindings.zig");
4130 const argc = @intCast(c_int, argv.len);4150 const argc = @intCast(c_int, argv.len);
4131 if (mem.eql(u8, args[1], "ld.lld")) {4151 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);
4133 } else if (mem.eql(u8, args[1], "lld-link")) {4153 } 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);
4135 } else if (mem.eql(u8, args[1], "wasm-ld")) {4155 } 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);
4137 } else {4157 } else {
4138 unreachable;4158 unreachable;
4139 }4159 }
4140 };4160 };
4141 process.exit(@bitCast(u8, @truncate(i8, exit_code)));4161 return @bitCast(u8, @truncate(i8, exit_code));
4142}4162}
41434163
4144const clang_args = @import("clang_options.zig").list;4164const clang_args = @import("clang_options.zig").list;
src/mingw.zig+31-26
...@@ -5,6 +5,7 @@ const path = std.fs.path;...@@ -5,6 +5,7 @@ const path = std.fs.path;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const log = std.log.scoped(.mingw);6const log = std.log.scoped(.mingw);
77
8const builtin = @import("builtin");
8const target_util = @import("target.zig");9const target_util = @import("target.zig");
9const Compilation = @import("Compilation.zig");10const Compilation = @import("Compilation.zig");
10const build_options = @import("build_options");11const build_options = @import("build_options");
...@@ -367,39 +368,43 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -367,39 +368,43 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
367 Compilation.dump_argv(&args);368 Compilation.dump_argv(&args);
368 }369 }
369370
370 const child = try std.ChildProcess.init(&args, arena);371 if (std.process.can_spawn) {
371 defer child.deinit();372 const child = try std.ChildProcess.init(&args, arena);
373 defer child.deinit();
372374
373 child.stdin_behavior = .Ignore;375 child.stdin_behavior = .Ignore;
374 child.stdout_behavior = .Pipe;376 child.stdout_behavior = .Pipe;
375 child.stderr_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/6343383 // TODO https://github.com/ziglang/zig/issues/6343
382 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);384 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
383385
384 const term = child.wait() catch |err| {386 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 => {
399 // TODO surface a proper error here387 // 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) });
401 return error.ClangPreprocessorFailed;389 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;
403 }408 }
404409
405 const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{410 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 {...@@ -730,6 +730,12 @@ pub const TestContext = struct {
730 // * cannot handle updates730 // * cannot handle updates
731 // because of this we must spawn a child process rather than731 // because of this we must spawn a child process rather than
732 // using Compilation directly.732 // 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
733 assert(case.updates.items.len == 1);739 assert(case.updates.items.len == 1);
734 const update = case.updates.items[0];740 const update = case.updates.items[0];
735 try tmp.dir.writeFile(tmp_src_path, update.src);741 try tmp.dir.writeFile(tmp_src_path, update.src);
...@@ -1104,6 +1110,11 @@ pub const TestContext = struct {...@@ -1104,6 +1110,11 @@ pub const TestContext = struct {
1104 }1110 }
1105 },1111 },
1106 .Execution => |expected_stdout| {1112 .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
1107 update_node.setEstimatedTotalItems(4);1118 update_node.setEstimatedTotalItems(4);
11081119
1109 var argv = std.ArrayList([]const u8).init(allocator);1120 var argv = std.ArrayList([]const u8).init(allocator);
test/tests.zig+9
...@@ -10,6 +10,8 @@ const fmt = std.fmt;...@@ -10,6 +10,8 @@ const fmt = std.fmt;
10const ArrayList = std.ArrayList;10const ArrayList = std.ArrayList;
11const Mode = std.builtin.Mode;11const Mode = std.builtin.Mode;
12const LibExeObjStep = build.LibExeObjStep;12const LibExeObjStep = build.LibExeObjStep;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
1315
14// Cases16// Cases
15const compare_output = @import("compare_output.zig");17const compare_output = @import("compare_output.zig");
...@@ -722,6 +724,13 @@ pub const StackTracesContext = struct {...@@ -722,6 +724,13 @@ pub const StackTracesContext = struct {
722724
723 std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name });725 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
725 const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable;734 const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable;
726 defer child.deinit();735 defer child.deinit();
727736