aboutsummaryrefslogtreecommitdiff
path: root/lib/build_runner.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-02-28 21:13:23 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-03-15 10:48:13 -0700
commit7da34bd9e86fe32dec56e6e89a6d4615e2b50794 (patch)
tree43b0aecad53157479a4a90bff07da907a225281f /lib/build_runner.zig
parent27f136e8282e4d5a64420d633e96f2a3ee6da08e (diff)
downloadzig-7da34bd9e86fe32dec56e6e89a6d4615e2b50794.tar.gz
zig-7da34bd9e86fe32dec56e6e89a6d4615e2b50794.zip
build runner: print a fancy tree with build results on failure
Diffstat (limited to 'lib/build_runner.zig')
-rw-r--r--lib/build_runner.zig141
1 files changed, 116 insertions, 25 deletions
diff --git a/lib/build_runner.zig b/lib/build_runner.zig
index 603ff770cc..d70dbf2919 100644
--- a/lib/build_runner.zig
+++ b/lib/build_runner.zig
@@ -261,7 +261,6 @@ pub fn main() !void {
var progress: std.Progress = .{};
const main_progress_node = progress.start("", 0);
- defer main_progress_node.end();
builder.debug_log_scopes = debug_log_scopes.items;
builder.resolveInstallPrefix(install_prefix, dir_list);
@@ -323,6 +322,8 @@ fn runStepNames(
defer thread_pool.deinit();
{
+ defer parent_prog_node.end();
+
var step_prog = parent_prog_node.start("run steps", step_stack.items.len);
defer step_prog.end();
@@ -347,20 +348,28 @@ fn runStepNames(
var success_count: usize = 0;
var failure_count: usize = 0;
var pending_count: usize = 0;
+ var total_compile_errors: usize = 0;
for (step_stack.items) |s| {
switch (s.state) {
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
.running => unreachable,
- // precheck_done is equivalent to dependency_failure in the case of
- // transitive dependencies. For example:
- // A -> B -> C (failure)
- // B will be marked as dependency_failure, while A may never be queued, and thus
- // remain in the initial state of precheck_done.
- .dependency_failure, .precheck_done => pending_count += 1,
+ .precheck_done => {
+ // precheck_done is equivalent to dependency_failure in the case of
+ // transitive dependencies. For example:
+ // A -> B -> C (failure)
+ // B will be marked as dependency_failure, while A may never be queued, and thus
+ // remain in the initial state of precheck_done.
+ s.state = .dependency_failure;
+ pending_count += 1;
+ },
+ .dependency_failure => pending_count += 1,
.success => success_count += 1,
- .failure => failure_count += 1,
+ .failure => {
+ failure_count += 1;
+ total_compile_errors += s.result_error_bundle.errorMessageCount();
+ },
}
}
@@ -371,34 +380,116 @@ fn runStepNames(
const stderr = std.io.getStdErr();
const total_count = success_count + failure_count + pending_count;
- stderr.writer().print("build summary: {d}/{d} steps succeeded; {d} failed\n", .{
- success_count, total_count, failure_count,
+ ttyconf.setColor(stderr, .Cyan) catch {};
+ stderr.writeAll("Build Summary: ") catch {};
+ ttyconf.setColor(stderr, .Reset) catch {};
+ stderr.writer().print("{d}/{d} steps succeeded; {d} failed; {d} total compile errors\n", .{
+ success_count, total_count, failure_count, total_compile_errors,
}) catch {};
+ // Print a fancy tree with build results.
+ var print_node: PrintNode = .{ .parent = null };
+ if (step_names.len == 0) {
+ print_node.last = true;
+ printTreeStep(b, b.default_step, stderr, ttyconf, &print_node) catch {};
+ } else {
+ for (step_names, 0..) |step_name, i| {
+ const tls = b.top_level_steps.get(step_name).?;
+ print_node.last = i + 1 == b.top_level_steps.count();
+ printTreeStep(b, &tls.step, stderr, ttyconf, &print_node) catch {};
+ }
+ }
+
if (failure_count == 0) return cleanExit();
- for (step_stack.items) |s| switch (s.state) {
- .failure => {
- // TODO print the dep prefix too
- ttyconf.setColor(stderr, .Bold) catch break;
- stderr.writeAll(s.name) catch break;
- ttyconf.setColor(stderr, .Reset) catch break;
+ // Finally, render compile errors at the bottom of the terminal.
+ if (total_compile_errors > 0) {
+ for (step_stack.items) |s| {
+ if (s.result_error_bundle.errorMessageCount() > 0) {
+ s.result_error_bundle.renderToStdErr(ttyconf);
+ }
+ }
+
+ // Signal to parent process that we have printed compile errors. The
+ // parent process may choose to omit the "following command failed"
+ // line in this case.
+ process.exit(2);
+ }
+ process.exit(1);
+}
+
+const PrintNode = struct {
+ parent: ?*PrintNode,
+ last: bool = false,
+};
+
+fn printTreeStep(
+ b: *std.Build,
+ s: *Step,
+ stderr: std.fs.File,
+ ttyconf: std.debug.TTY.Config,
+ parent_node: *PrintNode,
+) !void {
+ var opt_node: ?*PrintNode = parent_node.parent;
+ while (opt_node) |n| : (opt_node = n.parent) {
+ if (n.parent == null) break;
+ if (n.last) {
+ try stderr.writeAll(" ");
+ } else {
+ try stderr.writeAll("│ ");
+ }
+ }
+
+ if (parent_node.parent != null) {
+ if (parent_node.last) {
+ try stderr.writeAll("└─ ");
+ } else {
+ try stderr.writeAll("├─ ");
+ }
+ }
+
+ // TODO print the dep prefix too?
+ try stderr.writeAll(s.name);
+
+ switch (s.state) {
+ .precheck_unstarted => unreachable,
+ .precheck_started => unreachable,
+ .precheck_done => unreachable,
+ .running => unreachable,
+
+ .dependency_failure => {
+ try ttyconf.setColor(stderr, .Dim);
+ try stderr.writeAll(" transitive failure\n");
+ try ttyconf.setColor(stderr, .Reset);
+ },
+
+ .success => {
+ try ttyconf.setColor(stderr, .Green);
+ try stderr.writeAll(" success\n");
+ try ttyconf.setColor(stderr, .Reset);
+ },
+
+ .failure => {
+ try ttyconf.setColor(stderr, .Red);
if (s.result_error_bundle.errorMessageCount() > 0) {
- stderr.writer().print(": {d} compilation errors:\n", .{
+ try stderr.writer().print(" {d} errors\n", .{
s.result_error_bundle.errorMessageCount(),
- }) catch break;
- s.result_error_bundle.renderToStdErr(ttyconf);
+ });
} else {
- stderr.writer().print(": {d} error messages (printed above)\n", .{
- s.result_error_msgs.items.len,
- }) catch break;
+ try stderr.writeAll(" failure\n");
}
+ try ttyconf.setColor(stderr, .Reset);
},
- else => continue,
- };
+ }
- process.exit(1);
+ for (s.dependencies.items, 0..) |dep, i| {
+ var print_node: PrintNode = .{
+ .parent = parent_node,
+ .last = i == s.dependencies.items.len - 1,
+ };
+ try printTreeStep(b, dep, stderr, ttyconf, &print_node);
+ }
}
fn checkForDependencyLoop(