authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-28 21:13:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-15 10:48:13-07:00
log7da34bd9e86fe32dec56e6e89a6d4615e2b50794
tree43b0aecad53157479a4a90bff07da907a225281f
parent27f136e8282e4d5a64420d633e96f2a3ee6da08e

build runner: print a fancy tree with build results on failure


1 files changed, 116 insertions(+), 25 deletions(-)

lib/build_runner.zig+116-25
...@@ -261,7 +261,6 @@ pub fn main() !void {...@@ -261,7 +261,6 @@ pub fn main() !void {
261261
262 var progress: std.Progress = .{};262 var progress: std.Progress = .{};
263 const main_progress_node = progress.start("", 0);263 const main_progress_node = progress.start("", 0);
264 defer main_progress_node.end();
265264
266 builder.debug_log_scopes = debug_log_scopes.items;265 builder.debug_log_scopes = debug_log_scopes.items;
267 builder.resolveInstallPrefix(install_prefix, dir_list);266 builder.resolveInstallPrefix(install_prefix, dir_list);
...@@ -323,6 +322,8 @@ fn runStepNames(...@@ -323,6 +322,8 @@ fn runStepNames(
323 defer thread_pool.deinit();322 defer thread_pool.deinit();
324323
325 {324 {
325 defer parent_prog_node.end();
326
326 var step_prog = parent_prog_node.start("run steps", step_stack.items.len);327 var step_prog = parent_prog_node.start("run steps", step_stack.items.len);
327 defer step_prog.end();328 defer step_prog.end();
328329
...@@ -347,20 +348,28 @@ fn runStepNames(...@@ -347,20 +348,28 @@ fn runStepNames(
347 var success_count: usize = 0;348 var success_count: usize = 0;
348 var failure_count: usize = 0;349 var failure_count: usize = 0;
349 var pending_count: usize = 0;350 var pending_count: usize = 0;
351 var total_compile_errors: usize = 0;
350352
351 for (step_stack.items) |s| {353 for (step_stack.items) |s| {
352 switch (s.state) {354 switch (s.state) {
353 .precheck_unstarted => unreachable,355 .precheck_unstarted => unreachable,
354 .precheck_started => unreachable,356 .precheck_started => unreachable,
355 .running => unreachable,357 .running => unreachable,
356 // precheck_done is equivalent to dependency_failure in the case of358 .precheck_done => {
357 // transitive dependencies. For example:359 // precheck_done is equivalent to dependency_failure in the case of
358 // A -> B -> C (failure)360 // transitive dependencies. For example:
359 // B will be marked as dependency_failure, while A may never be queued, and thus361 // A -> B -> C (failure)
360 // remain in the initial state of precheck_done.362 // B will be marked as dependency_failure, while A may never be queued, and thus
361 .dependency_failure, .precheck_done => pending_count += 1,363 // remain in the initial state of precheck_done.
364 s.state = .dependency_failure;
365 pending_count += 1;
366 },
367 .dependency_failure => pending_count += 1,
362 .success => success_count += 1,368 .success => success_count += 1,
363 .failure => failure_count += 1,369 .failure => {
370 failure_count += 1;
371 total_compile_errors += s.result_error_bundle.errorMessageCount();
372 },
364 }373 }
365 }374 }
366375
...@@ -371,34 +380,116 @@ fn runStepNames(...@@ -371,34 +380,116 @@ fn runStepNames(
371 const stderr = std.io.getStdErr();380 const stderr = std.io.getStdErr();
372381
373 const total_count = success_count + failure_count + pending_count;382 const total_count = success_count + failure_count + pending_count;
374 stderr.writer().print("build summary: {d}/{d} steps succeeded; {d} failed\n", .{383 ttyconf.setColor(stderr, .Cyan) catch {};
375 success_count, total_count, failure_count,384 stderr.writeAll("Build Summary: ") catch {};
385 ttyconf.setColor(stderr, .Reset) catch {};
386 stderr.writer().print("{d}/{d} steps succeeded; {d} failed; {d} total compile errors\n", .{
387 success_count, total_count, failure_count, total_compile_errors,
376 }) catch {};388 }) catch {};
377389
390 // Print a fancy tree with build results.
391 var print_node: PrintNode = .{ .parent = null };
392 if (step_names.len == 0) {
393 print_node.last = true;
394 printTreeStep(b, b.default_step, stderr, ttyconf, &print_node) catch {};
395 } else {
396 for (step_names, 0..) |step_name, i| {
397 const tls = b.top_level_steps.get(step_name).?;
398 print_node.last = i + 1 == b.top_level_steps.count();
399 printTreeStep(b, &tls.step, stderr, ttyconf, &print_node) catch {};
400 }
401 }
402
378 if (failure_count == 0) return cleanExit();403 if (failure_count == 0) return cleanExit();
379404
380 for (step_stack.items) |s| switch (s.state) {405 // Finally, render compile errors at the bottom of the terminal.
381 .failure => {406 if (total_compile_errors > 0) {
382 // TODO print the dep prefix too407 for (step_stack.items) |s| {
383 ttyconf.setColor(stderr, .Bold) catch break;408 if (s.result_error_bundle.errorMessageCount() > 0) {
384 stderr.writeAll(s.name) catch break;409 s.result_error_bundle.renderToStdErr(ttyconf);
385 ttyconf.setColor(stderr, .Reset) catch break;410 }
411 }
412
413 // Signal to parent process that we have printed compile errors. The
414 // parent process may choose to omit the "following command failed"
415 // line in this case.
416 process.exit(2);
417 }
386418
419 process.exit(1);
420}
421
422const PrintNode = struct {
423 parent: ?*PrintNode,
424 last: bool = false,
425};
426
427fn printTreeStep(
428 b: *std.Build,
429 s: *Step,
430 stderr: std.fs.File,
431 ttyconf: std.debug.TTY.Config,
432 parent_node: *PrintNode,
433) !void {
434 var opt_node: ?*PrintNode = parent_node.parent;
435 while (opt_node) |n| : (opt_node = n.parent) {
436 if (n.parent == null) break;
437 if (n.last) {
438 try stderr.writeAll(" ");
439 } else {
440 try stderr.writeAll("│ ");
441 }
442 }
443
444 if (parent_node.parent != null) {
445 if (parent_node.last) {
446 try stderr.writeAll("└─ ");
447 } else {
448 try stderr.writeAll("├─ ");
449 }
450 }
451
452 // TODO print the dep prefix too?
453 try stderr.writeAll(s.name);
454
455 switch (s.state) {
456 .precheck_unstarted => unreachable,
457 .precheck_started => unreachable,
458 .precheck_done => unreachable,
459 .running => unreachable,
460
461 .dependency_failure => {
462 try ttyconf.setColor(stderr, .Dim);
463 try stderr.writeAll(" transitive failure\n");
464 try ttyconf.setColor(stderr, .Reset);
465 },
466
467 .success => {
468 try ttyconf.setColor(stderr, .Green);
469 try stderr.writeAll(" success\n");
470 try ttyconf.setColor(stderr, .Reset);
471 },
472
473 .failure => {
474 try ttyconf.setColor(stderr, .Red);
387 if (s.result_error_bundle.errorMessageCount() > 0) {475 if (s.result_error_bundle.errorMessageCount() > 0) {
388 stderr.writer().print(": {d} compilation errors:\n", .{476 try stderr.writer().print(" {d} errors\n", .{
389 s.result_error_bundle.errorMessageCount(),477 s.result_error_bundle.errorMessageCount(),
390 }) catch break;478 });
391 s.result_error_bundle.renderToStdErr(ttyconf);
392 } else {479 } else {
393 stderr.writer().print(": {d} error messages (printed above)\n", .{480 try stderr.writeAll(" failure\n");
394 s.result_error_msgs.items.len,
395 }) catch break;
396 }481 }
482 try ttyconf.setColor(stderr, .Reset);
397 },483 },
398 else => continue,484 }
399 };
400485
401 process.exit(1);486 for (s.dependencies.items, 0..) |dep, i| {
487 var print_node: PrintNode = .{
488 .parent = parent_node,
489 .last = i == s.dependencies.items.len - 1,
490 };
491 try printTreeStep(b, dep, stderr, ttyconf, &print_node);
492 }
402}493}
403494
404fn checkForDependencyLoop(495fn checkForDependencyLoop(