From 299991019dddb2acd076d4b2698a4fd6a7a6ae94 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 Oct 2019 20:20:22 -0400 Subject: rework the progress module and integrate with stage1 --- src/codegen.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index f313c6cd1c..ae02676414 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7610,7 +7610,7 @@ static void zig_llvm_emit_output(CodeGen *g) { if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) { - zig_link_add_compiler_rt(g); + zig_link_add_compiler_rt(g, g->progress_node); } break; @@ -9453,7 +9453,7 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose } // returns true if it was a cache miss -static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { +static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file, Stage2ProgressNode *parent_prog_node) { Error err; Buf *artifact_dir; @@ -9464,6 +9464,10 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { Buf *c_source_file = buf_create_from_str(c_file->source_path); Buf *c_source_basename = buf_alloc(); os_path_split(c_source_file, nullptr, c_source_basename); + + Stage2ProgressNode *child_prog_node = stage2_progress_start(parent_prog_node, buf_ptr(c_source_basename), + buf_len(c_source_basename), 0); + Buf *final_o_basename = buf_alloc(); os_path_extname(c_source_basename, final_o_basename, nullptr); buf_append_str(final_o_basename, target_o_file_ext(g->zig_target)); @@ -9580,6 +9584,8 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { g->link_objects.append(o_final_path); g->caches_to_release.append(cache_hash); + + stage2_progress_end(child_prog_node); } // returns true if we had any cache misses @@ -9596,11 +9602,16 @@ static void gen_c_objects(CodeGen *g) { } codegen_add_time_event(g, "Compile C Code"); + const char *c_prog_name = "compiling C objects"; + Stage2ProgressNode *c_prog_node = stage2_progress_start(g->progress_node, c_prog_name, strlen(c_prog_name), + g->c_source_files.length); for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { CFile *c_file = g->c_source_files.at(c_file_i); - gen_c_object(g, self_exe_path, c_file); + gen_c_object(g, self_exe_path, c_file, c_prog_node); } + + stage2_progress_end(c_prog_node); } void codegen_add_object(CodeGen *g, Buf *object_path) { @@ -10320,6 +10331,10 @@ void codegen_build_and_link(CodeGen *g) { init(g); codegen_add_time_event(g, "Semantic Analysis"); + const char *progress_name = "Semantic Analysis"; + Stage2ProgressNode *child_progress_node = stage2_progress_start(g->progress_node, + progress_name, strlen(progress_name), 0); + (void)child_progress_node; gen_root_source(g); @@ -10343,13 +10358,31 @@ void codegen_build_and_link(CodeGen *g) { if (need_llvm_module(g)) { codegen_add_time_event(g, "Code Generation"); + { + const char *progress_name = "Code Generation"; + Stage2ProgressNode *child_progress_node = stage2_progress_start(g->progress_node, + progress_name, strlen(progress_name), 0); + (void)child_progress_node; + } do_code_gen(g); codegen_add_time_event(g, "LLVM Emit Output"); + { + const char *progress_name = "LLVM Emit Output"; + Stage2ProgressNode *child_progress_node = stage2_progress_start(g->progress_node, + progress_name, strlen(progress_name), 0); + (void)child_progress_node; + } zig_llvm_emit_output(g); if (!g->disable_gen_h && (g->out_type == OutTypeObj || g->out_type == OutTypeLib)) { codegen_add_time_event(g, "Generate .h"); + { + const char *progress_name = "Generate .h"; + Stage2ProgressNode *child_progress_node = stage2_progress_start(g->progress_node, + progress_name, strlen(progress_name), 0); + (void)child_progress_node; + } gen_h_file(g); } } @@ -10446,10 +10479,15 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c } CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - ZigLibCInstallation *libc) + ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *child_progress_node) { + if (!child_progress_node) { + child_progress_node = stage2_progress_start(parent_gen->progress_node, name, strlen(name), 0); + } + CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_stage1_cache_path(), false); + parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_stage1_cache_path(), false, child_progress_node); + child_gen->root_out_name = buf_create_from_str(name); child_gen->disable_gen_h = true; child_gen->want_stack_check = WantStackCheckDisabled; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; @@ -10478,9 +10516,10 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *override_lib_dir, - ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build) + ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node) { CodeGen *g = allocate(1); + g->progress_node = progress_node; codegen_add_time_event(g, "Initialize"); -- cgit v1.2.3 From 2d5b2bf1c986d037ef965bf8c9b4d8dfd5967478 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 17 Oct 2019 21:46:41 -0400 Subject: improve progress reporting * use erase rest of line escape code. * use `stderr.supportsAnsiEscapeCodes` rather than `isTty`. * respect `--color off` * avoid unnecessary recursion * add `Progress.log` * disable the progress std lib test since it's noisy and uses `time.sleep()`. * enable/integrate progress printing with the default test runner --- lib/std/progress.zig | 88 ++++++++++++++++++++++++----------------- lib/std/special/test_runner.zig | 25 +++++++----- src-self-hosted/stage1.zig | 12 +++++- src/codegen.cpp | 8 ++-- src/main.cpp | 7 +++- src/userland.cpp | 1 + src/userland.h | 2 + test/cli.zig | 4 +- 8 files changed, 92 insertions(+), 55 deletions(-) (limited to 'src/codegen.cpp') diff --git a/lib/std/progress.zig b/lib/std/progress.zig index 75103aaf5a..1d29763c10 100644 --- a/lib/std/progress.zig +++ b/lib/std/progress.zig @@ -26,10 +26,6 @@ pub const Progress = struct { /// with each refresh. output_buffer: [100]u8 = undefined, - /// Keeps track of how many columns in the terminal have been output, so that - /// we can move the cursor back later. - columns_written: usize = undefined, - /// How many nanoseconds between writing updates to the terminal. refresh_rate_ns: u64 = 50 * std.time.millisecond, @@ -38,6 +34,10 @@ pub const Progress = struct { done: bool = true, + /// Keeps track of how many columns in the terminal have been output, so that + /// we can move the cursor back later. + columns_written: usize = undefined, + /// Represents one unit of progress. Each node can have children nodes, or /// one can use integers with `update`. pub const Node = struct { @@ -99,8 +99,7 @@ pub const Progress = struct { /// API to return Progress rather than accept it as a parameter. pub fn start(self: *Progress, name: []const u8, estimated_total_items: ?usize) !*Node { if (std.io.getStdErr()) |stderr| { - const is_term = stderr.isTty(); - self.terminal = if (is_term) stderr else null; + self.terminal = if (stderr.supportsAnsiEscapeCodes()) stderr else null; } else |_| { self.terminal = null; } @@ -111,8 +110,8 @@ pub const Progress = struct { .name = name, .estimated_total_items = estimated_total_items, }; - self.prev_refresh_timestamp = 0; self.columns_written = 0; + self.prev_refresh_timestamp = 0; self.timer = try std.time.Timer.start(); self.done = false; return &self.root; @@ -133,20 +132,42 @@ pub const Progress = struct { const prev_columns_written = self.columns_written; var end: usize = 0; if (self.columns_written > 0) { + // restore cursor position end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{}D", self.columns_written) catch unreachable).len; self.columns_written = 0; - } - if (!self.done) { - self.bufWriteNode(self.root, &end); - self.bufWrite(&end, "..."); + // clear rest of line + end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K") catch unreachable).len; } - if (prev_columns_written > self.columns_written) { - const amt = prev_columns_written - self.columns_written; - std.mem.set(u8, self.output_buffer[end .. end + amt], ' '); - end += amt; - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{}D", amt) catch unreachable).len; + if (!self.done) { + var need_ellipse = false; + var maybe_node: ?*Node = &self.root; + while (maybe_node) |node| { + if (need_ellipse) { + self.bufWrite(&end, "..."); + } + need_ellipse = false; + if (node.name.len != 0 or node.estimated_total_items != null) { + if (node.name.len != 0) { + self.bufWrite(&end, "{}", node.name); + need_ellipse = true; + } + if (node.estimated_total_items) |total| { + if (need_ellipse) self.bufWrite(&end, " "); + self.bufWrite(&end, "[{}/{}] ", node.completed_items, total); + need_ellipse = false; + } else if (node.completed_items != 0) { + if (need_ellipse) self.bufWrite(&end, " "); + self.bufWrite(&end, "[{}] ", node.completed_items); + need_ellipse = false; + } + } + maybe_node = node.recently_updated_child; + } + if (need_ellipse) { + self.bufWrite(&end, "..."); + } } _ = file.write(self.output_buffer[0..end]) catch |e| { @@ -156,25 +177,14 @@ pub const Progress = struct { self.prev_refresh_timestamp = self.timer.read(); } - fn bufWriteNode(self: *Progress, node: Node, end: *usize) void { - if (node.name.len != 0 or node.estimated_total_items != null) { - if (node.name.len != 0) { - self.bufWrite(end, "{}", node.name); - if (node.recently_updated_child != null or node.estimated_total_items != null or - node.completed_items != 0) - { - self.bufWrite(end, "..."); - } - } - if (node.estimated_total_items) |total| { - self.bufWrite(end, "[{}/{}] ", node.completed_items, total); - } else if (node.completed_items != 0) { - self.bufWrite(end, "[{}] ", node.completed_items); - } - } - if (node.recently_updated_child) |child| { - self.bufWriteNode(child.*, end); - } + pub fn log(self: *Progress, comptime format: []const u8, args: ...) void { + const file = self.terminal orelse return; + self.refresh(); + file.outStream().stream.print(format, args) catch { + self.terminal = null; + return; + }; + self.columns_written = 0; } fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: ...) void { @@ -200,6 +210,12 @@ pub const Progress = struct { }; test "basic functionality" { + var disable = true; + if (disable) { + // This test is disabled because it uses time.sleep() and is therefore slow. It also + // prints bogus progress data to stderr. + return error.SkipZigTest; + } var progress = Progress{}; const root_node = try progress.start("", 100); defer root_node.end(); @@ -235,7 +251,7 @@ test "basic functionality" { var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", null); node.activate(); std.time.sleep(10 * std.time.millisecond); - progress.maybeRefresh(); + progress.refresh(); std.time.sleep(10 * std.time.millisecond); node.end(); } diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index db01293059..8fb1b5842d 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -2,28 +2,33 @@ const std = @import("std"); const io = std.io; const builtin = @import("builtin"); const test_fn_list = builtin.test_functions; -const warn = std.debug.warn; -pub fn main() !void { +pub fn main() anyerror!void { var ok_count: usize = 0; var skip_count: usize = 0; - for (test_fn_list) |test_fn, i| { - warn("{}/{} {}...", i + 1, test_fn_list.len, test_fn.name); + var progress = std.Progress{}; + const root_node = progress.start("Test", test_fn_list.len) catch |err| switch (err) { + // TODO still run tests in this case + error.TimerUnsupported => @panic("timer unsupported"), + }; + for (test_fn_list) |test_fn, i| { + var test_node = root_node.start(test_fn.name, null); + test_node.activate(); if (test_fn.func()) |_| { ok_count += 1; - warn("OK\n"); + test_node.end(); } else |err| switch (err) { error.SkipZigTest => { skip_count += 1; - warn("SKIP\n"); + test_node.end(); + progress.log("{}...SKIP\n", test_fn.name); }, else => return err, } } - if (ok_count == test_fn_list.len) { - warn("All tests passed.\n"); - } else { - warn("{} passed; {} skipped.\n", ok_count, skip_count); + root_node.end(); + if (ok_count != test_fn_list.len) { + progress.log("{} passed; {} skipped.\n", ok_count, skip_count); } } diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index fd6413070d..48b14ebcac 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -470,13 +470,23 @@ export fn stage2_progress_destroy(progress: *std.Progress) void { } // ABI warning -export fn stage2_progress_start_root(progress: *std.Progress, name_ptr: [*]const u8, name_len: usize, estimated_total_items: usize) *std.Progress.Node { +export fn stage2_progress_start_root( + progress: *std.Progress, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { return progress.start( name_ptr[0..name_len], if (estimated_total_items == 0) null else estimated_total_items, ) catch @panic("timer unsupported"); } +// ABI warning +export fn stage2_progress_disable_tty(progress: *std.Progress) void { + progress.terminal = null; +} + // ABI warning export fn stage2_progress_start( node: *std.Progress.Node, diff --git a/src/codegen.cpp b/src/codegen.cpp index ae02676414..18af74afd7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -10479,11 +10479,11 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c } CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *child_progress_node) + ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node) { - if (!child_progress_node) { - child_progress_node = stage2_progress_start(parent_gen->progress_node, name, strlen(name), 0); - } + Stage2ProgressNode *child_progress_node = stage2_progress_start( + parent_progress_node ? parent_progress_node : parent_gen->progress_node, + name, strlen(name), 0); CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_stage1_cache_path(), false, child_progress_node); diff --git a/src/main.cpp b/src/main.cpp index 13c93b269d..b398eec991 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -506,8 +506,6 @@ int main(int argc, char **argv) { ZigList llvm_argv = {0}; llvm_argv.append("zig (LLVM option parsing)"); - Stage2ProgressNode *root_progress_node = stage2_progress_start_root(stage2_progress_create(), "", 0, 0); - if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; if ((err = os_self_exe_path(&zig_exe_path_buf))) { @@ -589,6 +587,7 @@ int main(int argc, char **argv) { Buf *cache_dir_buf = buf_create_from_str(cache_dir); full_cache_dir = os_path_resolve(&cache_dir_buf, 1); } + Stage2ProgressNode *root_progress_node = stage2_progress_start_root(stage2_progress_create(), "", 0, 0); CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe, BuildModeDebug, override_lib_dir, nullptr, &full_cache_dir, false, root_progress_node); @@ -965,6 +964,10 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } + Stage2Progress *progress = stage2_progress_create(); + Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0); + if (color == ErrColorOff) stage2_progress_disable_tty(progress); + init_all_targets(); ZigTarget target; diff --git a/src/userland.cpp b/src/userland.cpp index 9b5a2e9054..ec7f6b2dee 100644 --- a/src/userland.cpp +++ b/src/userland.cpp @@ -87,3 +87,4 @@ Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, } void stage2_progress_end(Stage2ProgressNode *node) {} void stage2_progress_complete_one(Stage2ProgressNode *node) {} +void stage2_progress_disable_tty(Stage2Progress *progress) {} diff --git a/src/userland.h b/src/userland.h index e58f49200c..eb419c1660 100644 --- a/src/userland.h +++ b/src/userland.h @@ -163,6 +163,8 @@ struct Stage2ProgressNode; // ABI warning ZIG_EXTERN_C Stage2Progress *stage2_progress_create(void); // ABI warning +ZIG_EXTERN_C void stage2_progress_disable_tty(Stage2Progress *progress); +// ABI warning ZIG_EXTERN_C void stage2_progress_destroy(Stage2Progress *progress); // ABI warning ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, diff --git a/test/cli.zig b/test/cli.zig index 8287c41b81..63a116a811 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -87,7 +87,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" }); const test_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "test" }); - testing.expect(std.mem.endsWith(u8, test_result.stderr, "All tests passed.\n")); + testing.expect(std.mem.eql(u8, test_result.stderr, "")); } fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { @@ -136,6 +136,6 @@ fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void { const output_path = try fs.path.join(a, [_][]const u8{ "does", "not", "exist" }); const source_path = try fs.path.join(a, [_][]const u8{ "src", "main.zig" }); _ = try exec(dir_path, [_][]const u8{ - zig_exe, "build-exe", source_path, "--output-dir", output_path + zig_exe, "build-exe", source_path, "--output-dir", output_path, }); } -- cgit v1.2.3