authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-14 02:53:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-15 10:48:12-07:00
log9580fbcf3596a39ba4c7d7af2f3a1df0e0abb746
treecbec640f8c4cc29508b33b01b10cf85851233ba4
parent1fa1484288dc7431f73facb8c423b71670d6914e

build system: capture stderr and report it later

Instead of dumping directly to stderr. This prevents processes running simultaneously from racing their stderr against each other. For now it only reports at the end, but an improvement would be to report as soon as a failed step occurs.

4 files changed, 90 insertions(+), 50 deletions(-)

build.zig+1-4
......@@ -676,10 +676,7 @@ fn addCxxKnownPath(
676676) !void {
677677 if (!std.process.can_spawn)
678678 return error.RequiredLibraryNotFound;
679 const path_padded = try b.exec(&[_][]const u8{
680 ctx.cxx_compiler,
681 b.fmt("-print-file-name={s}", .{objname}),
682 });
679 const path_padded = b.exec(&.{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}) });
683680 var tokenizer = mem.tokenize(u8, path_padded, "\r\n");
684681 const path_unpadded = tokenizer.next().?;
685682 if (mem.eql(u8, path_unpadded, objname)) {
lib/build_runner.zig+3-5
......@@ -318,8 +318,8 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void {
318318 .success => continue,
319319 .failure => {
320320 any_failed = true;
321 std.debug.print("{s}: {s}\n", .{
322 s.name, @errorName(s.result.err_code),
321 std.debug.print("{s}: {s}\n{s}", .{
322 s.name, @errorName(s.result.err_code), s.result.stderr,
323323 });
324324 },
325325 }
......@@ -404,9 +404,7 @@ fn workerMakeOneStep(
404404 // *Build object in install header steps that might be able to be removed
405405 // by passing the *Build object through the make() functions.
406406 s.make() catch |err| {
407 s.result = .{
408 .err_code = err,
409 };
407 s.result.err_code = err;
410408 @atomicStore(Step.State, &s.state, .failure, .SeqCst);
411409 return;
412410 };
lib/std/Build.zig+81-40
......@@ -1133,17 +1133,24 @@ pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
11331133 return self.spawnChildEnvMap(null, self.env_map, argv);
11341134}
11351135
1136fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1137 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1136fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
1137 var buf = ArrayList(u8).init(ally);
1138 if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
11381139 for (argv) |arg| {
1139 std.debug.print("{s} ", .{arg});
1140 try buf.writer().print("{s} ", .{arg});
11401141 }
1141 std.debug.print("\n", .{});
1142 try buf.append('\n');
1143 return buf.toOwnedSlice();
1144}
1145
1146fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
1147 const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM");
1148 std.debug.print("{s}", .{text});
11421149}
11431150
11441151pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
11451152 if (self.verbose) {
1146 printCmd(cwd, argv);
1153 printCmd(self.allocator, cwd, argv);
11471154 }
11481155
11491156 if (!std.process.can_spawn)
......@@ -1162,13 +1169,13 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap,
11621169 .Exited => |code| {
11631170 if (code != 0) {
11641171 log.err("The following command exited with error code {}:", .{code});
1165 printCmd(cwd, argv);
1172 printCmd(self.allocator, cwd, argv);
11661173 return error.UncleanExit;
11671174 }
11681175 },
11691176 else => {
11701177 log.err("The following command terminated unexpectedly:", .{});
1171 printCmd(cwd, argv);
1178 printCmd(self.allocator, cwd, argv);
11721179
11731180 return error.UncleanExit;
11741181 },
......@@ -1381,57 +1388,91 @@ pub fn execAllowFail(
13811388 }
13821389}
13831390
1384pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1391pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step) ![]u8 {
13851392 assert(argv.len != 0);
13861393
1387 if (self.verbose) {
1388 printCmd(null, argv);
1394 if (b.verbose) {
1395 printCmd(b.allocator, null, argv);
13891396 }
13901397
13911398 if (!std.process.can_spawn) {
1392 if (src_step) |s| log.err("{s}...", .{s.name});
1393 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1394 printCmd(null, argv);
1395 std.os.abort();
1399 s.result.stderr = b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{
1400 try allocPrintCmd(b.allocator, null, argv),
1401 });
1402 return error.CannotSpawnProcesses;
13961403 }
13971404
13981405 var code: u8 = undefined;
1399 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1400 error.ExecNotSupported => {
1401 if (src_step) |s| log.err("{s}...", .{s.name});
1402 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1403 printCmd(null, argv);
1404 std.os.abort();
1405 },
1406 const result = unwrapExecResult(&code, std.ChildProcess.exec(.{
1407 .allocator = b.allocator,
1408 .argv = argv,
1409 .env_map = b.env_map,
1410 .max_output_bytes = 10 * 1024 * 1024,
1411 })) catch |err| switch (err) {
14061412 error.FileNotFound => {
1407 if (src_step) |s| log.err("{s}...", .{s.name});
1408 log.err("Unable to spawn the following command: file not found", .{});
1409 printCmd(null, argv);
1410 std.os.exit(@truncate(u8, code));
1413 s.result.stderr = b.fmt("unable to spawn the following command: file not found\n{s}", .{
1414 try allocPrintCmd(b.allocator, null, argv),
1415 });
1416 return error.ExecFailed;
14111417 },
14121418 error.ExitCodeFailure => {
1413 if (src_step) |s| log.err("{s}...", .{s.name});
1414 if (self.prominent_compile_errors) {
1415 log.err("The step exited with error code {d}", .{code});
1416 } else {
1417 log.err("The following command exited with error code {d}:", .{code});
1418 printCmd(null, argv);
1419 }
1420
1421 std.os.exit(@truncate(u8, code));
1419 s.result.stderr = b.fmt("the following command exited with error code {d}:\n{s}", .{
1420 code, try allocPrintCmd(b.allocator, null, argv),
1421 });
1422 return error.ExecFailed;
14221423 },
14231424 error.ProcessTerminated => {
1424 if (src_step) |s| log.err("{s}...", .{s.name});
1425 log.err("The following command terminated unexpectedly:", .{});
1426 printCmd(null, argv);
1427 std.os.exit(@truncate(u8, code));
1425 s.result.stderr = b.fmt("the following command terminated unexpectedly:\n{s}", .{
1426 try allocPrintCmd(b.allocator, null, argv),
1427 });
1428 return error.ExecFailed;
14281429 },
14291430 else => |e| return e,
14301431 };
1432
1433 s.result.stderr = result.stderr;
1434 return result.stdout;
14311435}
14321436
1433pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1434 return self.execFromStep(argv, null);
1437fn unwrapExecResult(
1438 code_ptr: *u8,
1439 wrapped: std.ChildProcess.ExecError!std.ChildProcess.ExecResult,
1440) !std.ChildProcess.ExecResult {
1441 const result = try wrapped;
1442 switch (result.term) {
1443 .Exited => |code| {
1444 code_ptr.* = code;
1445 if (code != 0) {
1446 return error.ExitCodeFailure;
1447 }
1448 return result;
1449 },
1450 .Signal, .Stopped, .Unknown => |code| {
1451 _ = code;
1452 return error.ProcessTerminated;
1453 },
1454 }
1455}
1456
1457/// This is a helper function to be called from build.zig scripts, *not* from
1458/// inside step make() functions. If any errors occur, it fails the build with
1459/// a helpful message.
1460pub fn exec(b: *Build, argv: []const []const u8) []u8 {
1461 if (!std.process.can_spawn) {
1462 std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}", .{
1463 try allocPrintCmd(b.allocator, null, argv),
1464 });
1465 process.exit(1);
1466 }
1467
1468 var code: u8 = undefined;
1469 return b.execAllowFail(argv, &code, .Inherit) catch |err| {
1470 const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM");
1471 std.debug.print("unable to spawn the following command: {s}\n{s}", .{
1472 @errorName(err), printed_cmd,
1473 });
1474 process.exit(1);
1475 };
14351476}
14361477
14371478pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
lib/std/Build/Step.zig+5-1
......@@ -9,6 +9,7 @@ state: State,
99/// Populated only if state is success.
1010result: struct {
1111 err_code: anyerror,
12 stderr: []u8,
1213},
1314
1415pub const State = enum {
......@@ -78,7 +79,10 @@ pub fn init(
7879 .dependencies = std.ArrayList(*Step).init(allocator),
7980 .dependants = .{},
8081 .state = .precheck_unstarted,
81 .result = undefined,
82 .result = .{
83 .err_code = undefined,
84 .stderr = &.{},
85 },
8286 };
8387}
8488