diff --git a/build.zig b/build.zig index 12e5d014e2..797c980739 100644 --- a/build.zig +++ b/build.zig @@ -676,10 +676,7 @@ fn addCxxKnownPath( ) !void { if (!std.process.can_spawn) return error.RequiredLibraryNotFound; - const path_padded = try b.exec(&[_][]const u8{ - ctx.cxx_compiler, - b.fmt("-print-file-name={s}", .{objname}), - }); + const path_padded = b.exec(&.{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}) }); var tokenizer = mem.tokenize(u8, path_padded, "\r\n"); const path_unpadded = tokenizer.next().?; if (mem.eql(u8, path_unpadded, objname)) { diff --git a/lib/build_runner.zig b/lib/build_runner.zig index 87a9226bf1..6ba583c59d 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -318,8 +318,8 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { .success => continue, .failure => { any_failed = true; - std.debug.print("{s}: {s}\n", .{ - s.name, @errorName(s.result.err_code), + std.debug.print("{s}: {s}\n{s}", .{ + s.name, @errorName(s.result.err_code), s.result.stderr, }); }, } @@ -404,9 +404,7 @@ fn workerMakeOneStep( // *Build object in install header steps that might be able to be removed // by passing the *Build object through the make() functions. s.make() catch |err| { - s.result = .{ - .err_code = err, - }; + s.result.err_code = err; @atomicStore(Step.State, &s.state, .failure, .SeqCst); return; }; diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 168180e383..225e4a58aa 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1133,17 +1133,24 @@ pub fn spawnChild(self: *Build, argv: []const []const u8) !void { return self.spawnChildEnvMap(null, self.env_map, argv); } -fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); +fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 { + var buf = ArrayList(u8).init(ally); + if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd}); for (argv) |arg| { - std.debug.print("{s} ", .{arg}); + try buf.writer().print("{s} ", .{arg}); } - std.debug.print("\n", .{}); + try buf.append('\n'); + return buf.toOwnedSlice(); +} + +fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void { + const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM"); + std.debug.print("{s}", .{text}); } pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { if (self.verbose) { - printCmd(cwd, argv); + printCmd(self.allocator, cwd, argv); } if (!std.process.can_spawn) @@ -1162,13 +1169,13 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, .Exited => |code| { if (code != 0) { log.err("The following command exited with error code {}:", .{code}); - printCmd(cwd, argv); + printCmd(self.allocator, cwd, argv); return error.UncleanExit; } }, else => { log.err("The following command terminated unexpectedly:", .{}); - printCmd(cwd, argv); + printCmd(self.allocator, cwd, argv); return error.UncleanExit; }, @@ -1381,57 +1388,91 @@ pub fn execAllowFail( } } -pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 { +pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]u8 { assert(argv.len != 0); - if (self.verbose) { - printCmd(null, argv); + if (b.verbose) { + printCmd(b.allocator, null, argv); } if (!std.process.can_spawn) { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: cannot spawn child process", .{}); - printCmd(null, argv); - std.os.abort(); + s.result.stderr = b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + return error.CannotSpawnProcesses; } var code: u8 = undefined; - return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { - error.ExecNotSupported => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: cannot spawn child process", .{}); - printCmd(null, argv); - std.os.abort(); - }, + const result = unwrapExecResult(&code, std.ChildProcess.exec(.{ + .allocator = b.allocator, + .argv = argv, + .env_map = b.env_map, + .max_output_bytes = 10 * 1024 * 1024, + })) catch |err| switch (err) { error.FileNotFound => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("Unable to spawn the following command: file not found", .{}); - printCmd(null, argv); - std.os.exit(@truncate(u8, code)); + s.result.stderr = b.fmt("unable to spawn the following command: file not found\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + return error.ExecFailed; }, error.ExitCodeFailure => { - if (src_step) |s| log.err("{s}...", .{s.name}); - if (self.prominent_compile_errors) { - log.err("The step exited with error code {d}", .{code}); - } else { - log.err("The following command exited with error code {d}:", .{code}); - printCmd(null, argv); - } - - std.os.exit(@truncate(u8, code)); + s.result.stderr = b.fmt("the following command exited with error code {d}:\n{s}", .{ + code, try allocPrintCmd(b.allocator, null, argv), + }); + return error.ExecFailed; }, error.ProcessTerminated => { - if (src_step) |s| log.err("{s}...", .{s.name}); - log.err("The following command terminated unexpectedly:", .{}); - printCmd(null, argv); - std.os.exit(@truncate(u8, code)); + s.result.stderr = b.fmt("the following command terminated unexpectedly:\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + return error.ExecFailed; }, else => |e| return e, }; + + s.result.stderr = result.stderr; + return result.stdout; } -pub fn exec(self: *Build, argv: []const []const u8) ![]u8 { - return self.execFromStep(argv, null); +fn unwrapExecResult( + code_ptr: *u8, + wrapped: std.ChildProcess.ExecError!std.ChildProcess.ExecResult, +) !std.ChildProcess.ExecResult { + const result = try wrapped; + switch (result.term) { + .Exited => |code| { + code_ptr.* = code; + if (code != 0) { + return error.ExitCodeFailure; + } + return result; + }, + .Signal, .Stopped, .Unknown => |code| { + _ = code; + return error.ProcessTerminated; + }, + } +} + +/// This is a helper function to be called from build.zig scripts, *not* from +/// inside step make() functions. If any errors occur, it fails the build with +/// a helpful message. +pub fn exec(b: *Build, argv: []const []const u8) []u8 { + if (!std.process.can_spawn) { + std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}", .{ + try allocPrintCmd(b.allocator, null, argv), + }); + process.exit(1); + } + + var code: u8 = undefined; + return b.execAllowFail(argv, &code, .Inherit) catch |err| { + const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM"); + std.debug.print("unable to spawn the following command: {s}\n{s}", .{ + @errorName(err), printed_cmd, + }); + process.exit(1); + }; } pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 1226c78c73..44998f4a44 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -9,6 +9,7 @@ state: State, /// Populated only if state is success. result: struct { err_code: anyerror, + stderr: []u8, }, pub const State = enum { @@ -78,7 +79,10 @@ pub fn init( .dependencies = std.ArrayList(*Step).init(allocator), .dependants = .{}, .state = .precheck_unstarted, - .result = undefined, + .result = .{ + .err_code = undefined, + .stderr = &.{}, + }, }; }