authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-28 19:27:14-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-05-28 19:27:14-04:00
log963ffe9d572e6da4ef22672af9b7c54150f66b27
tree950c39722d71cdd6f2af75c255ee92a318cda516
parent759c2211c2eba44cccf0608267bf1a05934ad8a1
parent3a3d2187f986066859cfb793fb7ee1cae4dfea08
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20059 from ziglang/progress

rework std.Progress

62 files changed, 1758 insertions(+), 819 deletions(-)

lib/compiler/aro/aro/Diagnostics.zig+2-2
...@@ -528,7 +528,7 @@ const MsgWriter = struct {...@@ -528,7 +528,7 @@ const MsgWriter = struct {
528 config: std.io.tty.Config,528 config: std.io.tty.Config,
529529
530 fn init(config: std.io.tty.Config) MsgWriter {530 fn init(config: std.io.tty.Config) MsgWriter {
531 std.debug.getStderrMutex().lock();531 std.debug.lockStdErr();
532 return .{532 return .{
533 .w = std.io.bufferedWriter(std.io.getStdErr().writer()),533 .w = std.io.bufferedWriter(std.io.getStdErr().writer()),
534 .config = config,534 .config = config,
...@@ -537,7 +537,7 @@ const MsgWriter = struct {...@@ -537,7 +537,7 @@ const MsgWriter = struct {
537537
538 pub fn deinit(m: *MsgWriter) void {538 pub fn deinit(m: *MsgWriter) void {
539 m.w.flush() catch {};539 m.w.flush() catch {};
540 std.debug.getStderrMutex().unlock();540 std.debug.unlockStdErr();
541 }541 }
542542
543 pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void {543 pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void {
lib/compiler/build_runner.zig+13-13
...@@ -289,13 +289,14 @@ pub fn main() !void {...@@ -289,13 +289,14 @@ pub fn main() !void {
289 .windows_api => {},289 .windows_api => {},
290 }290 }
291291
292 var progress: std.Progress = .{ .dont_print_on_dumb = true };292 const main_progress_node = std.Progress.start(.{
293 const main_progress_node = progress.start("", 0);293 .disable_printing = (color == .off),
294 });
294295
295 builder.debug_log_scopes = debug_log_scopes.items;296 builder.debug_log_scopes = debug_log_scopes.items;
296 builder.resolveInstallPrefix(install_prefix, dir_list);297 builder.resolveInstallPrefix(install_prefix, dir_list);
297 {298 {
298 var prog_node = main_progress_node.start("user build.zig logic", 0);299 var prog_node = main_progress_node.start("Configure", 0);
299 defer prog_node.end();300 defer prog_node.end();
300 try builder.runBuild(root);301 try builder.runBuild(root);
301 }302 }
...@@ -385,7 +386,7 @@ fn runStepNames(...@@ -385,7 +386,7 @@ fn runStepNames(
385 arena: std.mem.Allocator,386 arena: std.mem.Allocator,
386 b: *std.Build,387 b: *std.Build,
387 step_names: []const []const u8,388 step_names: []const []const u8,
388 parent_prog_node: *std.Progress.Node,389 parent_prog_node: std.Progress.Node,
389 thread_pool_options: std.Thread.Pool.Options,390 thread_pool_options: std.Thread.Pool.Options,
390 run: *Run,391 run: *Run,
391 seed: u32,392 seed: u32,
...@@ -452,7 +453,7 @@ fn runStepNames(...@@ -452,7 +453,7 @@ fn runStepNames(
452 {453 {
453 defer parent_prog_node.end();454 defer parent_prog_node.end();
454455
455 var step_prog = parent_prog_node.start("steps", step_stack.count());456 const step_prog = parent_prog_node.start("steps", step_stack.count());
456 defer step_prog.end();457 defer step_prog.end();
457458
458 var wait_group: std.Thread.WaitGroup = .{};459 var wait_group: std.Thread.WaitGroup = .{};
...@@ -467,7 +468,7 @@ fn runStepNames(...@@ -467,7 +468,7 @@ fn runStepNames(
467 if (step.state == .skipped_oom) continue;468 if (step.state == .skipped_oom) continue;
468469
469 thread_pool.spawnWg(&wait_group, workerMakeOneStep, .{470 thread_pool.spawnWg(&wait_group, workerMakeOneStep, .{
470 &wait_group, &thread_pool, b, step, &step_prog, run,471 &wait_group, &thread_pool, b, step, step_prog, run,
471 });472 });
472 }473 }
473 }474 }
...@@ -891,7 +892,7 @@ fn workerMakeOneStep(...@@ -891,7 +892,7 @@ fn workerMakeOneStep(
891 thread_pool: *std.Thread.Pool,892 thread_pool: *std.Thread.Pool,
892 b: *std.Build,893 b: *std.Build,
893 s: *Step,894 s: *Step,
894 prog_node: *std.Progress.Node,895 prog_node: std.Progress.Node,
895 run: *Run,896 run: *Run,
896) void {897) void {
897 // First, check the conditions for running this step. If they are not met,898 // First, check the conditions for running this step. If they are not met,
...@@ -941,11 +942,10 @@ fn workerMakeOneStep(...@@ -941,11 +942,10 @@ fn workerMakeOneStep(
941 }942 }
942 }943 }
943944
944 var sub_prog_node = prog_node.start(s.name, 0);945 const sub_prog_node = prog_node.start(s.name, 0);
945 sub_prog_node.activate();
946 defer sub_prog_node.end();946 defer sub_prog_node.end();
947947
948 const make_result = s.make(&sub_prog_node);948 const make_result = s.make(sub_prog_node);
949949
950 // No matter the result, we want to display error/warning messages.950 // No matter the result, we want to display error/warning messages.
951 const show_compile_errors = !run.prominent_compile_errors and951 const show_compile_errors = !run.prominent_compile_errors and
...@@ -954,8 +954,8 @@ fn workerMakeOneStep(...@@ -954,8 +954,8 @@ fn workerMakeOneStep(
954 const show_stderr = s.result_stderr.len > 0;954 const show_stderr = s.result_stderr.len > 0;
955955
956 if (show_error_msgs or show_compile_errors or show_stderr) {956 if (show_error_msgs or show_compile_errors or show_stderr) {
957 sub_prog_node.context.lock_stderr();957 std.debug.lockStdErr();
958 defer sub_prog_node.context.unlock_stderr();958 defer std.debug.unlockStdErr();
959959
960 printErrorMessages(b, s, run) catch {};960 printErrorMessages(b, s, run) catch {};
961 }961 }
...@@ -1225,7 +1225,7 @@ fn cleanExit() void {...@@ -1225,7 +1225,7 @@ fn cleanExit() void {
1225 process.exit(0);1225 process.exit(0);
1226}1226}
12271227
1228const Color = enum { auto, off, on };1228const Color = std.zig.Color;
1229const Summary = enum { all, new, failures, none };1229const Summary = enum { all, new, failures, none };
12301230
1231fn get_tty_conf(color: Color, stderr: File) std.io.tty.Config {1231fn get_tty_conf(color: Color, stderr: File) std.io.tty.Config {
lib/compiler/resinator/cli.zig+2-2
...@@ -108,8 +108,8 @@ pub const Diagnostics = struct {...@@ -108,8 +108,8 @@ pub const Diagnostics = struct {
108 }108 }
109109
110 pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void {110 pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void {
111 std.debug.getStderrMutex().lock();111 std.debug.lockStdErr();
112 defer std.debug.getStderrMutex().unlock();112 defer std.debug.unlockStdErr();
113 const stderr = std.io.getStdErr().writer();113 const stderr = std.io.getStdErr().writer();
114 self.renderToWriter(args, stderr, config) catch return;114 self.renderToWriter(args, stderr, config) catch return;
115 }115 }
lib/compiler/resinator/errors.zig+2-2
...@@ -60,8 +60,8 @@ pub const Diagnostics = struct {...@@ -60,8 +60,8 @@ pub const Diagnostics = struct {
60 }60 }
6161
62 pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void {62 pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void {
63 std.debug.getStderrMutex().lock();63 std.debug.lockStdErr();
64 defer std.debug.getStderrMutex().unlock();64 defer std.debug.unlockStdErr();
65 const stderr = std.io.getStdErr().writer();65 const stderr = std.io.getStdErr().writer();
66 for (self.errors.items) |err_details| {66 for (self.errors.items) |err_details| {
67 renderErrorMessage(self.allocator, stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return;67 renderErrorMessage(self.allocator, stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return;
lib/compiler/resinator/main.zig-6
...@@ -50,12 +50,6 @@ pub fn main() !void {...@@ -50,12 +50,6 @@ pub fn main() !void {
50 },50 },
51 };51 };
5252
53 if (zig_integration) {
54 // Send progress with a special string to indicate that the building of the
55 // resinator binary is finished and we've moved on to actually compiling the .rc file
56 try error_handler.server.serveStringMessage(.progress, "<resinator>");
57 }
58
59 var options = options: {53 var options = options: {
60 var cli_diagnostics = cli.Diagnostics.init(allocator);54 var cli_diagnostics = cli.Diagnostics.init(allocator);
61 defer cli_diagnostics.deinit();55 defer cli_diagnostics.deinit();
lib/compiler/test_runner.zig+19-12
...@@ -129,12 +129,11 @@ fn mainTerminal() void {...@@ -129,12 +129,11 @@ fn mainTerminal() void {
129 var ok_count: usize = 0;129 var ok_count: usize = 0;
130 var skip_count: usize = 0;130 var skip_count: usize = 0;
131 var fail_count: usize = 0;131 var fail_count: usize = 0;
132 var progress = std.Progress{132 const root_node = std.Progress.start(.{
133 .dont_print_on_dumb = true,133 .root_name = "Test",
134 };134 .estimated_total_items = test_fn_list.len,
135 const root_node = progress.start("Test", test_fn_list.len);135 });
136 const have_tty = progress.terminal != null and136 const have_tty = std.io.getStdErr().isTty();
137 (progress.supports_ansi_escape_codes or progress.is_windows_terminal);
138137
139 var async_frame_buffer: []align(builtin.target.stackAlignment()) u8 = undefined;138 var async_frame_buffer: []align(builtin.target.stackAlignment()) u8 = undefined;
140 // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly139 // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly
...@@ -151,11 +150,9 @@ fn mainTerminal() void {...@@ -151,11 +150,9 @@ fn mainTerminal() void {
151 }150 }
152 std.testing.log_level = .warn;151 std.testing.log_level = .warn;
153152
154 var test_node = root_node.start(test_fn.name, 0);153 const test_node = root_node.start(test_fn.name, 0);
155 test_node.activate();
156 progress.refresh();
157 if (!have_tty) {154 if (!have_tty) {
158 std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name });155 std.debug.print("{d}/{d} {s}...", .{ i + 1, test_fn_list.len, test_fn.name });
159 }156 }
160 if (test_fn.func()) |_| {157 if (test_fn.func()) |_| {
161 ok_count += 1;158 ok_count += 1;
...@@ -164,12 +161,22 @@ fn mainTerminal() void {...@@ -164,12 +161,22 @@ fn mainTerminal() void {
164 } else |err| switch (err) {161 } else |err| switch (err) {
165 error.SkipZigTest => {162 error.SkipZigTest => {
166 skip_count += 1;163 skip_count += 1;
167 progress.log("SKIP\n", .{});164 if (have_tty) {
165 std.debug.print("{d}/{d} {s}...SKIP\n", .{ i + 1, test_fn_list.len, test_fn.name });
166 } else {
167 std.debug.print("SKIP\n", .{});
168 }
168 test_node.end();169 test_node.end();
169 },170 },
170 else => {171 else => {
171 fail_count += 1;172 fail_count += 1;
172 progress.log("FAIL ({s})\n", .{@errorName(err)});173 if (have_tty) {
174 std.debug.print("{d}/{d} {s}...FAIL ({s})\n", .{
175 i + 1, test_fn_list.len, test_fn.name, @errorName(err),
176 });
177 } else {
178 std.debug.print("FAIL ({s})\n", .{@errorName(err)});
179 }
173 if (@errorReturnTrace()) |trace| {180 if (@errorReturnTrace()) |trace| {
174 std.debug.dumpStackTrace(trace.*);181 std.debug.dumpStackTrace(trace.*);
175 }182 }
lib/std/Build.zig+5-5
...@@ -1059,7 +1059,7 @@ pub fn getUninstallStep(b: *Build) *Step {...@@ -1059,7 +1059,7 @@ pub fn getUninstallStep(b: *Build) *Step {
1059 return &b.uninstall_tls.step;1059 return &b.uninstall_tls.step;
1060}1060}
10611061
1062fn makeUninstall(uninstall_step: *Step, prog_node: *std.Progress.Node) anyerror!void {1062fn makeUninstall(uninstall_step: *Step, prog_node: std.Progress.Node) anyerror!void {
1063 _ = prog_node;1063 _ = prog_node;
1064 const uninstall_tls: *TopLevelStep = @fieldParentPtr("step", uninstall_step);1064 const uninstall_tls: *TopLevelStep = @fieldParentPtr("step", uninstall_step);
1065 const b: *Build = @fieldParentPtr("uninstall_tls", uninstall_tls);1065 const b: *Build = @fieldParentPtr("uninstall_tls", uninstall_tls);
...@@ -2281,10 +2281,10 @@ pub const LazyPath = union(enum) {...@@ -2281,10 +2281,10 @@ pub const LazyPath = union(enum) {
2281 .cwd_relative => |p| return src_builder.pathFromCwd(p),2281 .cwd_relative => |p| return src_builder.pathFromCwd(p),
2282 .generated => |gen| {2282 .generated => |gen| {
2283 var file_path: []const u8 = gen.file.step.owner.pathFromRoot(gen.file.path orelse {2283 var file_path: []const u8 = gen.file.step.owner.pathFromRoot(gen.file.path orelse {
2284 std.debug.getStderrMutex().lock();2284 std.debug.lockStdErr();
2285 const stderr = std.io.getStdErr();2285 const stderr = std.io.getStdErr();
2286 dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};2286 dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};
2287 std.debug.getStderrMutex().unlock();2287 std.debug.unlockStdErr();
2288 @panic("misconfigured build script");2288 @panic("misconfigured build script");
2289 });2289 });
22902290
...@@ -2351,8 +2351,8 @@ fn dumpBadDirnameHelp(...@@ -2351,8 +2351,8 @@ fn dumpBadDirnameHelp(
2351 comptime msg: []const u8,2351 comptime msg: []const u8,
2352 args: anytype,2352 args: anytype,
2353) anyerror!void {2353) anyerror!void {
2354 debug.getStderrMutex().lock();2354 debug.lockStdErr();
2355 defer debug.getStderrMutex().unlock();2355 defer debug.unlockStdErr();
23562356
2357 const stderr = io.getStdErr();2357 const stderr = io.getStdErr();
2358 const w = stderr.writer();2358 const w = stderr.writer();
lib/std/Build/Step.zig+5-14
...@@ -58,7 +58,7 @@ pub const TestResults = struct {...@@ -58,7 +58,7 @@ pub const TestResults = struct {
58 }58 }
59};59};
6060
61pub const MakeFn = *const fn (step: *Step, prog_node: *std.Progress.Node) anyerror!void;61pub const MakeFn = *const fn (step: *Step, prog_node: std.Progress.Node) anyerror!void;
6262
63pub const State = enum {63pub const State = enum {
64 precheck_unstarted,64 precheck_unstarted,
...@@ -176,7 +176,7 @@ pub fn init(options: StepOptions) Step {...@@ -176,7 +176,7 @@ pub fn init(options: StepOptions) Step {
176/// If the Step's `make` function reports `error.MakeFailed`, it indicates they176/// If the Step's `make` function reports `error.MakeFailed`, it indicates they
177/// have already reported the error. Otherwise, we add a simple error report177/// have already reported the error. Otherwise, we add a simple error report
178/// here.178/// here.
179pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void {179pub fn make(s: *Step, prog_node: std.Progress.Node) error{ MakeFailed, MakeSkipped }!void {
180 const arena = s.owner.allocator;180 const arena = s.owner.allocator;
181181
182 s.makeFn(s, prog_node) catch |err| switch (err) {182 s.makeFn(s, prog_node) catch |err| switch (err) {
...@@ -217,7 +217,7 @@ pub fn getStackTrace(s: *Step) ?std.builtin.StackTrace {...@@ -217,7 +217,7 @@ pub fn getStackTrace(s: *Step) ?std.builtin.StackTrace {
217 };217 };
218}218}
219219
220fn makeNoOp(step: *Step, prog_node: *std.Progress.Node) anyerror!void {220fn makeNoOp(step: *Step, prog_node: std.Progress.Node) anyerror!void {
221 _ = prog_node;221 _ = prog_node;
222222
223 var all_cached = true;223 var all_cached = true;
...@@ -303,7 +303,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO...@@ -303,7 +303,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO
303pub fn evalZigProcess(303pub fn evalZigProcess(
304 s: *Step,304 s: *Step,
305 argv: []const []const u8,305 argv: []const []const u8,
306 prog_node: *std.Progress.Node,306 prog_node: std.Progress.Node,
307) !?[]const u8 {307) !?[]const u8 {
308 assert(argv.len != 0);308 assert(argv.len != 0);
309 const b = s.owner;309 const b = s.owner;
...@@ -319,6 +319,7 @@ pub fn evalZigProcess(...@@ -319,6 +319,7 @@ pub fn evalZigProcess(
319 child.stdout_behavior = .Pipe;319 child.stdout_behavior = .Pipe;
320 child.stderr_behavior = .Pipe;320 child.stderr_behavior = .Pipe;
321 child.request_resource_usage_statistics = true;321 child.request_resource_usage_statistics = true;
322 child.progress_node = prog_node;
322323
323 child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{324 child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{
324 argv[0], @errorName(err),325 argv[0], @errorName(err),
...@@ -337,11 +338,6 @@ pub fn evalZigProcess(...@@ -337,11 +338,6 @@ pub fn evalZigProcess(
337 const Header = std.zig.Server.Message.Header;338 const Header = std.zig.Server.Message.Header;
338 var result: ?[]const u8 = null;339 var result: ?[]const u8 = null;
339340
340 var node_name: std.ArrayListUnmanaged(u8) = .{};
341 defer node_name.deinit(gpa);
342 var sub_prog_node = prog_node.start("", 0);
343 defer sub_prog_node.end();
344
345 const stdout = poller.fifo(.stdout);341 const stdout = poller.fifo(.stdout);
346342
347 poll: while (true) {343 poll: while (true) {
...@@ -379,11 +375,6 @@ pub fn evalZigProcess(...@@ -379,11 +375,6 @@ pub fn evalZigProcess(
379 .extra = extra_array,375 .extra = extra_array,
380 };376 };
381 },377 },
382 .progress => {
383 node_name.clearRetainingCapacity();
384 try node_name.appendSlice(gpa, body);
385 sub_prog_node.setName(node_name.items);
386 },
387 .emit_bin_path => {378 .emit_bin_path => {
388 const EbpHdr = std.zig.Server.Message.EmitBinPath;379 const EbpHdr = std.zig.Server.Message.EmitBinPath;
389 const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body));380 const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body));
lib/std/Build/Step/CheckFile.zig+1-1
...@@ -46,7 +46,7 @@ pub fn setName(check_file: *CheckFile, name: []const u8) void {...@@ -46,7 +46,7 @@ pub fn setName(check_file: *CheckFile, name: []const u8) void {
46 check_file.step.name = name;46 check_file.step.name = name;
47}47}
4848
49fn make(step: *Step, prog_node: *std.Progress.Node) !void {49fn make(step: *Step, prog_node: std.Progress.Node) !void {
50 _ = prog_node;50 _ = prog_node;
51 const b = step.owner;51 const b = step.owner;
52 const check_file: *CheckFile = @fieldParentPtr("step", step);52 const check_file: *CheckFile = @fieldParentPtr("step", step);
lib/std/Build/Step/CheckObject.zig+1-1
...@@ -550,7 +550,7 @@ pub fn checkComputeCompare(...@@ -550,7 +550,7 @@ pub fn checkComputeCompare(
550 check_object.checks.append(check) catch @panic("OOM");550 check_object.checks.append(check) catch @panic("OOM");
551}551}
552552
553fn make(step: *Step, prog_node: *std.Progress.Node) !void {553fn make(step: *Step, prog_node: std.Progress.Node) !void {
554 _ = prog_node;554 _ = prog_node;
555 const b = step.owner;555 const b = step.owner;
556 const gpa = b.allocator;556 const gpa = b.allocator;
lib/std/Build/Step/Compile.zig+3-3
...@@ -967,7 +967,7 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking...@@ -967,7 +967,7 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
967 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);967 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);
968968
969 const generated_file = maybe_path orelse {969 const generated_file = maybe_path orelse {
970 std.debug.getStderrMutex().lock();970 std.debug.lockStdErr();
971 const stderr = std.io.getStdErr();971 const stderr = std.io.getStdErr();
972972
973 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};973 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
...@@ -976,7 +976,7 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking...@@ -976,7 +976,7 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
976 };976 };
977977
978 const path = generated_file.path orelse {978 const path = generated_file.path orelse {
979 std.debug.getStderrMutex().lock();979 std.debug.lockStdErr();
980 const stderr = std.io.getStdErr();980 const stderr = std.io.getStdErr();
981981
982 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};982 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
...@@ -987,7 +987,7 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking...@@ -987,7 +987,7 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
987 return path;987 return path;
988}988}
989989
990fn make(step: *Step, prog_node: *std.Progress.Node) !void {990fn make(step: *Step, prog_node: std.Progress.Node) !void {
991 const b = step.owner;991 const b = step.owner;
992 const arena = b.allocator;992 const arena = b.allocator;
993 const compile: *Compile = @fieldParentPtr("step", step);993 const compile: *Compile = @fieldParentPtr("step", step);
lib/std/Build/Step/ConfigHeader.zig+1-1
...@@ -164,7 +164,7 @@ fn putValue(config_header: *ConfigHeader, field_name: []const u8, comptime T: ty...@@ -164,7 +164,7 @@ fn putValue(config_header: *ConfigHeader, field_name: []const u8, comptime T: ty
164 }164 }
165}165}
166166
167fn make(step: *Step, prog_node: *std.Progress.Node) !void {167fn make(step: *Step, prog_node: std.Progress.Node) !void {
168 _ = prog_node;168 _ = prog_node;
169 const b = step.owner;169 const b = step.owner;
170 const config_header: *ConfigHeader = @fieldParentPtr("step", step);170 const config_header: *ConfigHeader = @fieldParentPtr("step", step);
lib/std/Build/Step/Fmt.zig+1-1
...@@ -36,7 +36,7 @@ pub fn create(owner: *std.Build, options: Options) *Fmt {...@@ -36,7 +36,7 @@ pub fn create(owner: *std.Build, options: Options) *Fmt {
36 return fmt;36 return fmt;
37}37}
3838
39fn make(step: *Step, prog_node: *std.Progress.Node) !void {39fn make(step: *Step, prog_node: std.Progress.Node) !void {
40 // zig fmt is fast enough that no progress is needed.40 // zig fmt is fast enough that no progress is needed.
41 _ = prog_node;41 _ = prog_node;
4242
lib/std/Build/Step/InstallArtifact.zig+1-1
...@@ -115,7 +115,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins...@@ -115,7 +115,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
115 return install_artifact;115 return install_artifact;
116}116}
117117
118fn make(step: *Step, prog_node: *std.Progress.Node) !void {118fn make(step: *Step, prog_node: std.Progress.Node) !void {
119 _ = prog_node;119 _ = prog_node;
120 const install_artifact: *InstallArtifact = @fieldParentPtr("step", step);120 const install_artifact: *InstallArtifact = @fieldParentPtr("step", step);
121 const b = step.owner;121 const b = step.owner;
lib/std/Build/Step/InstallDir.zig+1-1
...@@ -56,7 +56,7 @@ pub fn create(owner: *std.Build, options: Options) *InstallDir {...@@ -56,7 +56,7 @@ pub fn create(owner: *std.Build, options: Options) *InstallDir {
56 return install_dir;56 return install_dir;
57}57}
5858
59fn make(step: *Step, prog_node: *std.Progress.Node) !void {59fn make(step: *Step, prog_node: std.Progress.Node) !void {
60 _ = prog_node;60 _ = prog_node;
61 const b = step.owner;61 const b = step.owner;
62 const install_dir: *InstallDir = @fieldParentPtr("step", step);62 const install_dir: *InstallDir = @fieldParentPtr("step", step);
lib/std/Build/Step/InstallFile.zig+1-1
...@@ -36,7 +36,7 @@ pub fn create(...@@ -36,7 +36,7 @@ pub fn create(
36 return install_file;36 return install_file;
37}37}
3838
39fn make(step: *Step, prog_node: *std.Progress.Node) !void {39fn make(step: *Step, prog_node: std.Progress.Node) !void {
40 _ = prog_node;40 _ = prog_node;
41 const b = step.owner;41 const b = step.owner;
42 const install_file: *InstallFile = @fieldParentPtr("step", step);42 const install_file: *InstallFile = @fieldParentPtr("step", step);
lib/std/Build/Step/ObjCopy.zig+1-1
...@@ -90,7 +90,7 @@ pub fn getOutputSeparatedDebug(objcopy: *const ObjCopy) ?std.Build.LazyPath {...@@ -90,7 +90,7 @@ pub fn getOutputSeparatedDebug(objcopy: *const ObjCopy) ?std.Build.LazyPath {
90 return if (objcopy.output_file_debug) |*file| .{ .generated = .{ .file = file } } else null;90 return if (objcopy.output_file_debug) |*file| .{ .generated = .{ .file = file } } else null;
91}91}
9292
93fn make(step: *Step, prog_node: *std.Progress.Node) !void {93fn make(step: *Step, prog_node: std.Progress.Node) !void {
94 const b = step.owner;94 const b = step.owner;
95 const objcopy: *ObjCopy = @fieldParentPtr("step", step);95 const objcopy: *ObjCopy = @fieldParentPtr("step", step);
9696
lib/std/Build/Step/Options.zig+1-1
...@@ -410,7 +410,7 @@ pub fn getOutput(options: *Options) LazyPath {...@@ -410,7 +410,7 @@ pub fn getOutput(options: *Options) LazyPath {
410 return .{ .generated = .{ .file = &options.generated_file } };410 return .{ .generated = .{ .file = &options.generated_file } };
411}411}
412412
413fn make(step: *Step, prog_node: *std.Progress.Node) !void {413fn make(step: *Step, prog_node: std.Progress.Node) !void {
414 // This step completes so quickly that no progress is necessary.414 // This step completes so quickly that no progress is necessary.
415 _ = prog_node;415 _ = prog_node;
416416
lib/std/Build/Step/RemoveDir.zig+1-1
...@@ -22,7 +22,7 @@ pub fn create(owner: *std.Build, dir_path: []const u8) *RemoveDir {...@@ -22,7 +22,7 @@ pub fn create(owner: *std.Build, dir_path: []const u8) *RemoveDir {
22 return remove_dir;22 return remove_dir;
23}23}
2424
25fn make(step: *Step, prog_node: *std.Progress.Node) !void {25fn make(step: *Step, prog_node: std.Progress.Node) !void {
26 // TODO update progress node while walking file system.26 // TODO update progress node while walking file system.
27 // Should the standard library support this use case??27 // Should the standard library support this use case??
28 _ = prog_node;28 _ = prog_node;
lib/std/Build/Step/Run.zig+17-7
...@@ -23,6 +23,11 @@ cwd: ?Build.LazyPath,...@@ -23,6 +23,11 @@ cwd: ?Build.LazyPath,
23/// Override this field to modify the environment, or use setEnvironmentVariable23/// Override this field to modify the environment, or use setEnvironmentVariable
24env_map: ?*EnvMap,24env_map: ?*EnvMap,
2525
26/// When `true` prevents `ZIG_PROGRESS` environment variable from being passed
27/// to the child process, which otherwise would be used for the child to send
28/// progress updates to the parent.
29disable_zig_progress: bool,
30
26/// Configures whether the Run step is considered to have side-effects, and also31/// Configures whether the Run step is considered to have side-effects, and also
27/// whether the Run step will inherit stdio streams, forwarding them to the32/// whether the Run step will inherit stdio streams, forwarding them to the
28/// parent process, in which case will require a global lock to prevent other33/// parent process, in which case will require a global lock to prevent other
...@@ -152,6 +157,7 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {...@@ -152,6 +157,7 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {
152 .argv = .{},157 .argv = .{},
153 .cwd = null,158 .cwd = null,
154 .env_map = null,159 .env_map = null,
160 .disable_zig_progress = false,
155 .stdio = .infer_from_args,161 .stdio = .infer_from_args,
156 .stdin = .none,162 .stdin = .none,
157 .extra_file_dependencies = &.{},163 .extra_file_dependencies = &.{},
...@@ -574,7 +580,7 @@ const IndexedOutput = struct {...@@ -574,7 +580,7 @@ const IndexedOutput = struct {
574 tag: @typeInfo(Arg).Union.tag_type.?,580 tag: @typeInfo(Arg).Union.tag_type.?,
575 output: *Output,581 output: *Output,
576};582};
577fn make(step: *Step, prog_node: *std.Progress.Node) !void {583fn make(step: *Step, prog_node: std.Progress.Node) !void {
578 const b = step.owner;584 const b = step.owner;
579 const arena = b.allocator;585 const arena = b.allocator;
580 const run: *Run = @fieldParentPtr("step", step);586 const run: *Run = @fieldParentPtr("step", step);
...@@ -878,7 +884,7 @@ fn runCommand(...@@ -878,7 +884,7 @@ fn runCommand(
878 argv: []const []const u8,884 argv: []const []const u8,
879 has_side_effects: bool,885 has_side_effects: bool,
880 output_dir_path: []const u8,886 output_dir_path: []const u8,
881 prog_node: *std.Progress.Node,887 prog_node: std.Progress.Node,
882) !void {888) !void {
883 const step = &run.step;889 const step = &run.step;
884 const b = step.owner;890 const b = step.owner;
...@@ -1195,7 +1201,7 @@ fn spawnChildAndCollect(...@@ -1195,7 +1201,7 @@ fn spawnChildAndCollect(
1195 run: *Run,1201 run: *Run,
1196 argv: []const []const u8,1202 argv: []const []const u8,
1197 has_side_effects: bool,1203 has_side_effects: bool,
1198 prog_node: *std.Progress.Node,1204 prog_node: std.Progress.Node,
1199) !ChildProcResult {1205) !ChildProcResult {
1200 const b = run.step.owner;1206 const b = run.step.owner;
1201 const arena = b.allocator;1207 const arena = b.allocator;
...@@ -1235,6 +1241,10 @@ fn spawnChildAndCollect(...@@ -1235,6 +1241,10 @@ fn spawnChildAndCollect(
1235 child.stdin_behavior = .Pipe;1241 child.stdin_behavior = .Pipe;
1236 }1242 }
12371243
1244 if (run.stdio != .zig_test and !run.disable_zig_progress) {
1245 child.progress_node = prog_node;
1246 }
1247
1238 try child.spawn();1248 try child.spawn();
1239 var timer = try std.time.Timer.start();1249 var timer = try std.time.Timer.start();
12401250
...@@ -1264,7 +1274,7 @@ const StdIoResult = struct {...@@ -1264,7 +1274,7 @@ const StdIoResult = struct {
1264fn evalZigTest(1274fn evalZigTest(
1265 run: *Run,1275 run: *Run,
1266 child: *std.process.Child,1276 child: *std.process.Child,
1267 prog_node: *std.Progress.Node,1277 prog_node: std.Progress.Node,
1268) !StdIoResult {1278) !StdIoResult {
1269 const gpa = run.step.owner.allocator;1279 const gpa = run.step.owner.allocator;
1270 const arena = run.step.owner.allocator;1280 const arena = run.step.owner.allocator;
...@@ -1291,7 +1301,7 @@ fn evalZigTest(...@@ -1291,7 +1301,7 @@ fn evalZigTest(
1291 var metadata: ?TestMetadata = null;1301 var metadata: ?TestMetadata = null;
12921302
1293 var sub_prog_node: ?std.Progress.Node = null;1303 var sub_prog_node: ?std.Progress.Node = null;
1294 defer if (sub_prog_node) |*n| n.end();1304 defer if (sub_prog_node) |n| n.end();
12951305
1296 poll: while (true) {1306 poll: while (true) {
1297 while (stdout.readableLength() < @sizeOf(Header)) {1307 while (stdout.readableLength() < @sizeOf(Header)) {
...@@ -1406,7 +1416,7 @@ const TestMetadata = struct {...@@ -1406,7 +1416,7 @@ const TestMetadata = struct {
1406 expected_panic_msgs: []const u32,1416 expected_panic_msgs: []const u32,
1407 string_bytes: []const u8,1417 string_bytes: []const u8,
1408 next_index: u32,1418 next_index: u32,
1409 prog_node: *std.Progress.Node,1419 prog_node: std.Progress.Node,
14101420
1411 fn testName(tm: TestMetadata, index: u32) []const u8 {1421 fn testName(tm: TestMetadata, index: u32) []const u8 {
1412 return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0);1422 return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0);
...@@ -1421,7 +1431,7 @@ fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Pr...@@ -1421,7 +1431,7 @@ fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Pr
1421 if (metadata.expected_panic_msgs[i] != 0) continue;1431 if (metadata.expected_panic_msgs[i] != 0) continue;
14221432
1423 const name = metadata.testName(i);1433 const name = metadata.testName(i);
1424 if (sub_prog_node.*) |*n| n.end();1434 if (sub_prog_node.*) |n| n.end();
1425 sub_prog_node.* = metadata.prog_node.start(name, 0);1435 sub_prog_node.* = metadata.prog_node.start(name, 0);
14261436
1427 try sendRunTestMessage(in, i);1437 try sendRunTestMessage(in, i);
lib/std/Build/Step/TranslateC.zig+1-1
...@@ -116,7 +116,7 @@ pub fn defineCMacroRaw(translate_c: *TranslateC, name_and_value: []const u8) voi...@@ -116,7 +116,7 @@ pub fn defineCMacroRaw(translate_c: *TranslateC, name_and_value: []const u8) voi
116 translate_c.c_macros.append(translate_c.step.owner.dupe(name_and_value)) catch @panic("OOM");116 translate_c.c_macros.append(translate_c.step.owner.dupe(name_and_value)) catch @panic("OOM");
117}117}
118118
119fn make(step: *Step, prog_node: *std.Progress.Node) !void {119fn make(step: *Step, prog_node: std.Progress.Node) !void {
120 const b = step.owner;120 const b = step.owner;
121 const translate_c: *TranslateC = @fieldParentPtr("step", step);121 const translate_c: *TranslateC = @fieldParentPtr("step", step);
122122
lib/std/Build/Step/WriteFile.zig+1-1
...@@ -198,7 +198,7 @@ fn maybeUpdateName(write_file: *WriteFile) void {...@@ -198,7 +198,7 @@ fn maybeUpdateName(write_file: *WriteFile) void {
198 }198 }
199}199}
200200
201fn make(step: *Step, prog_node: *std.Progress.Node) !void {201fn make(step: *Step, prog_node: std.Progress.Node) !void {
202 _ = prog_node;202 _ = prog_node;
203 const b = step.owner;203 const b = step.owner;
204 const write_file: *WriteFile = @fieldParentPtr("step", step);204 const write_file: *WriteFile = @fieldParentPtr("step", step);
lib/std/Progress.zig+1194-343
...@@ -1,10 +1,4 @@...@@ -1,10 +1,4 @@
1//! This API is non-allocating, non-fallible, and thread-safe.1//! This API is non-allocating, non-fallible, thread-safe, and lock-free.
2//! The tradeoff is that users of this API must provide the storage
3//! for each `Progress.Node`.
4//!
5//! Initialize the struct directly, overriding these fields as desired:
6//! * `refresh_rate_ms`
7//! * `initial_delay_ms`
82
9const std = @import("std");3const std = @import("std");
10const builtin = @import("builtin");4const builtin = @import("builtin");
...@@ -12,436 +6,1293 @@ const windows = std.os.windows;...@@ -12,436 +6,1293 @@ const windows = std.os.windows;
12const testing = std.testing;6const testing = std.testing;
13const assert = std.debug.assert;7const assert = std.debug.assert;
14const Progress = @This();8const Progress = @This();
9const posix = std.posix;
10const is_big_endian = builtin.cpu.arch.endian() == .big;
11const is_windows = builtin.os.tag == .windows;
1512
16/// `null` if the current node (and its children) should13/// `null` if the current node (and its children) should
17/// not print on update()14/// not print on update()
18terminal: ?std.fs.File = undefined,15terminal: std.fs.File,
1916
20/// Is this a windows API terminal (note: this is not the same as being run on windows17terminal_mode: TerminalMode,
21/// because other terminals exist like MSYS/git-bash)
22is_windows_terminal: bool = false,
2318
24/// Whether the terminal supports ANSI escape codes.19update_thread: ?std.Thread,
25supports_ansi_escape_codes: bool = false,
2620
27/// If the terminal is "dumb", don't print output.21/// Atomically set by SIGWINCH as well as the root done() function.
28/// This can be useful if you don't want to print all22redraw_event: std.Thread.ResetEvent,
29/// the stages of code generation if there are a lot.23/// Indicates a request to shut down and reset global state.
30/// You should not use it if the user should see output24/// Accessed atomically.
31/// for example showing the user what tests run.25done: bool,
32dont_print_on_dumb: bool = false,
3326
34root: Node = undefined,27refresh_rate_ns: u64,
28initial_delay_ns: u64,
3529
36/// Keeps track of how much time has passed since the beginning.30rows: u16,
37/// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.31cols: u16,
38timer: ?std.time.Timer = null,32/// Tracks the number of newlines that have been actually written to the terminal.
33written_newline_count: u16,
34/// Tracks the number of newlines that will be written to the terminal if the
35/// draw buffer is sent.
36accumulated_newline_count: u16,
3937
40/// When the previous refresh was written to the terminal.38/// Accessed only by the update thread.
41/// Used to compare with `refresh_rate_ms`.39draw_buffer: []u8,
42prev_refresh_timestamp: u64 = undefined,
4340
44/// This buffer represents the maximum number of bytes written to the terminal41/// This is in a separate array from `node_storage` but with the same length so
45/// with each refresh.42/// that it can be iterated over efficiently without trashing too much of the
46output_buffer: [100]u8 = undefined,43/// CPU cache.
44node_parents: []Node.Parent,
45node_storage: []Node.Storage,
46node_freelist: []Node.OptionalIndex,
47node_freelist_first: Node.OptionalIndex,
48node_end_index: u32,
4749
48/// How many nanoseconds between writing updates to the terminal.50pub const TerminalMode = union(enum) {
49refresh_rate_ns: u64 = 50 * std.time.ns_per_ms,51 off,
52 ansi_escape_codes,
53 /// This is not the same as being run on windows because other terminals
54 /// exist like MSYS/git-bash.
55 windows_api: if (is_windows) WindowsApi else void,
5056
51/// How many nanoseconds to keep the output hidden57 pub const WindowsApi = struct {
52initial_delay_ns: u64 = 500 * std.time.ns_per_ms,58 /// The output code page of the console.
5359 code_page: windows.UINT,
54done: bool = true,60 };
5561};
56/// Protects the `refresh` function, as well as `node.recently_updated_child`.
57/// Without this, callsites would call `Node.end` and then free `Node` memory
58/// while it was still being accessed by the `refresh` function.
59update_mutex: std.Thread.Mutex = .{},
6062
61/// Keeps track of how many columns in the terminal have been output, so that63pub const Options = struct {
62/// we can move the cursor back later.64 /// User-provided buffer with static lifetime.
63columns_written: usize = undefined,65 ///
66 /// Used to store the entire write buffer sent to the terminal. Progress output will be truncated if it
67 /// cannot fit into this buffer which will look bad but not cause any malfunctions.
68 ///
69 /// Must be at least 200 bytes.
70 draw_buffer: []u8 = &default_draw_buffer,
71 /// How many nanoseconds between writing updates to the terminal.
72 refresh_rate_ns: u64 = 80 * std.time.ns_per_ms,
73 /// How many nanoseconds to keep the output hidden
74 initial_delay_ns: u64 = 200 * std.time.ns_per_ms,
75 /// If provided, causes the progress item to have a denominator.
76 /// 0 means unknown.
77 estimated_total_items: usize = 0,
78 root_name: []const u8 = "",
79 disable_printing: bool = false,
80};
6481
65/// Represents one unit of progress. Each node can have children nodes, or82/// Represents one unit of progress. Each node can have children nodes, or
66/// one can use integers with `update`.83/// one can use integers with `update`.
67pub const Node = struct {84pub const Node = struct {
68 context: *Progress,85 index: OptionalIndex,
69 parent: ?*Node,86
70 name: []const u8,87 pub const max_name_len = 40;
71 unit: []const u8 = "",88
72 /// Must be handled atomically to be thread-safe.89 const Storage = extern struct {
73 recently_updated_child: ?*Node = null,90 /// Little endian.
74 /// Must be handled atomically to be thread-safe. 0 means null.91 completed_count: u32,
75 unprotected_estimated_total_items: usize,92 /// 0 means unknown.
76 /// Must be handled atomically to be thread-safe.93 /// Little endian.
77 unprotected_completed_items: usize,94 estimated_total_count: u32,
95 name: [max_name_len]u8,
96
97 /// Not thread-safe.
98 fn getIpcFd(s: Storage) ?posix.fd_t {
99 return if (s.estimated_total_count == std.math.maxInt(u32)) switch (@typeInfo(posix.fd_t)) {
100 .Int => @bitCast(s.completed_count),
101 .Pointer => @ptrFromInt(s.completed_count),
102 else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)),
103 } else null;
104 }
105
106 /// Thread-safe.
107 fn setIpcFd(s: *Storage, fd: posix.fd_t) void {
108 const integer: u32 = switch (@typeInfo(posix.fd_t)) {
109 .Int => @bitCast(fd),
110 .Pointer => @intFromPtr(fd),
111 else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)),
112 };
113 // `estimated_total_count` max int indicates the special state that
114 // causes `completed_count` to be treated as a file descriptor, so
115 // the order here matters.
116 @atomicStore(u32, &s.completed_count, integer, .monotonic);
117 @atomicStore(u32, &s.estimated_total_count, std.math.maxInt(u32), .release);
118 }
119
120 /// Not thread-safe.
121 fn byteSwap(s: *Storage) void {
122 s.completed_count = @byteSwap(s.completed_count);
123 s.estimated_total_count = @byteSwap(s.estimated_total_count);
124 }
125
126 comptime {
127 assert((@sizeOf(Storage) % 4) == 0);
128 }
129 };
130
131 const Parent = enum(u8) {
132 /// Unallocated storage.
133 unused = std.math.maxInt(u8) - 1,
134 /// Indicates root node.
135 none = std.math.maxInt(u8),
136 /// Index into `node_storage`.
137 _,
138
139 fn unwrap(i: @This()) ?Index {
140 return switch (i) {
141 .unused, .none => return null,
142 else => @enumFromInt(@intFromEnum(i)),
143 };
144 }
145 };
146
147 pub const OptionalIndex = enum(u8) {
148 none = std.math.maxInt(u8),
149 /// Index into `node_storage`.
150 _,
151
152 pub fn unwrap(i: @This()) ?Index {
153 if (i == .none) return null;
154 return @enumFromInt(@intFromEnum(i));
155 }
156
157 fn toParent(i: @This()) Parent {
158 assert(@intFromEnum(i) != @intFromEnum(Parent.unused));
159 return @enumFromInt(@intFromEnum(i));
160 }
161 };
162
163 /// Index into `node_storage`.
164 pub const Index = enum(u8) {
165 _,
166
167 fn toParent(i: @This()) Parent {
168 assert(@intFromEnum(i) != @intFromEnum(Parent.unused));
169 assert(@intFromEnum(i) != @intFromEnum(Parent.none));
170 return @enumFromInt(@intFromEnum(i));
171 }
172
173 pub fn toOptional(i: @This()) OptionalIndex {
174 return @enumFromInt(@intFromEnum(i));
175 }
176 };
78177
79 /// Create a new child progress node. Thread-safe.178 /// Create a new child progress node. Thread-safe.
80 /// Call `Node.end` when done.179 ///
81 /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
82 /// API to set `self.parent.recently_updated_child` with the return value.
83 /// Until that is fixed you probably want to call `activate` on the return value.
84 /// Passing 0 for `estimated_total_items` means unknown.180 /// Passing 0 for `estimated_total_items` means unknown.
85 pub fn start(self: *Node, name: []const u8, estimated_total_items: usize) Node {181 pub fn start(node: Node, name: []const u8, estimated_total_items: usize) Node {
86 return Node{182 if (noop_impl) {
87 .context = self.context,183 assert(node.index == .none);
88 .parent = self,184 return .{ .index = .none };
89 .name = name,185 }
90 .unprotected_estimated_total_items = estimated_total_items,186 const node_index = node.index.unwrap() orelse return .{ .index = .none };
91 .unprotected_completed_items = 0,187 const parent = node_index.toParent();
92 };188
189 const freelist_head = &global_progress.node_freelist_first;
190 var opt_free_index = @atomicLoad(Node.OptionalIndex, freelist_head, .seq_cst);
191 while (opt_free_index.unwrap()) |free_index| {
192 const freelist_ptr = freelistByIndex(free_index);
193 opt_free_index = @cmpxchgWeak(Node.OptionalIndex, freelist_head, opt_free_index, freelist_ptr.*, .seq_cst, .seq_cst) orelse {
194 // We won the allocation race.
195 return init(free_index, parent, name, estimated_total_items);
196 };
197 }
198
199 const free_index = @atomicRmw(u32, &global_progress.node_end_index, .Add, 1, .monotonic);
200 if (free_index >= global_progress.node_storage.len) {
201 // Ran out of node storage memory. Progress for this node will not be tracked.
202 _ = @atomicRmw(u32, &global_progress.node_end_index, .Sub, 1, .monotonic);
203 return .{ .index = .none };
204 }
205
206 return init(@enumFromInt(free_index), parent, name, estimated_total_items);
93 }207 }
94208
95 /// This is the same as calling `start` and then `end` on the returned `Node`. Thread-safe.209 /// This is the same as calling `start` and then `end` on the returned `Node`. Thread-safe.
96 pub fn completeOne(self: *Node) void {210 pub fn completeOne(n: Node) void {
97 if (self.parent) |parent| {211 const index = n.index.unwrap() orelse return;
98 @atomicStore(?*Node, &parent.recently_updated_child, self, .release);212 const storage = storageByIndex(index);
99 }213 _ = @atomicRmw(u32, &storage.completed_count, .Add, 1, .monotonic);
100 _ = @atomicRmw(usize, &self.unprotected_completed_items, .Add, 1, .monotonic);214 }
101 self.context.maybeRefresh();215
216 /// Thread-safe.
217 pub fn setCompletedItems(n: Node, completed_items: usize) void {
218 const index = n.index.unwrap() orelse return;
219 const storage = storageByIndex(index);
220 @atomicStore(u32, &storage.completed_count, std.math.lossyCast(u32, completed_items), .monotonic);
221 }
222
223 /// Thread-safe. 0 means unknown.
224 pub fn setEstimatedTotalItems(n: Node, count: usize) void {
225 const index = n.index.unwrap() orelse return;
226 const storage = storageByIndex(index);
227 // Avoid u32 max int which is used to indicate a special state.
228 const saturated = @min(std.math.maxInt(u32) - 1, count);
229 @atomicStore(u32, &storage.estimated_total_count, saturated, .monotonic);
230 }
231
232 /// Thread-safe.
233 pub fn increaseEstimatedTotalItems(n: Node, count: usize) void {
234 const index = n.index.unwrap() orelse return;
235 const storage = storageByIndex(index);
236 _ = @atomicRmw(u32, &storage.estimated_total_count, .Add, std.math.lossyCast(u32, count), .monotonic);
102 }237 }
103238
104 /// Finish a started `Node`. Thread-safe.239 /// Finish a started `Node`. Thread-safe.
105 pub fn end(self: *Node) void {240 pub fn end(n: Node) void {
106 self.context.maybeRefresh();241 if (noop_impl) {
107 if (self.parent) |parent| {242 assert(n.index == .none);
108 {243 return;
109 self.context.update_mutex.lock();244 }
110 defer self.context.update_mutex.unlock();245 const index = n.index.unwrap() orelse return;
111 _ = @cmpxchgStrong(?*Node, &parent.recently_updated_child, self, null, .monotonic, .monotonic);246 const parent_ptr = parentByIndex(index);
247 if (parent_ptr.unwrap()) |parent_index| {
248 _ = @atomicRmw(u32, &storageByIndex(parent_index).completed_count, .Add, 1, .monotonic);
249 @atomicStore(Node.Parent, parent_ptr, .unused, .seq_cst);
250
251 const freelist_head = &global_progress.node_freelist_first;
252 var first = @atomicLoad(Node.OptionalIndex, freelist_head, .seq_cst);
253 while (true) {
254 freelistByIndex(index).* = first;
255 first = @cmpxchgWeak(Node.OptionalIndex, freelist_head, first, index.toOptional(), .seq_cst, .seq_cst) orelse break;
112 }256 }
113 parent.completeOne();
114 } else {257 } else {
115 self.context.update_mutex.lock();258 @atomicStore(bool, &global_progress.done, true, .seq_cst);
116 defer self.context.update_mutex.unlock();259 global_progress.redraw_event.set();
117 self.context.done = true;260 if (global_progress.update_thread) |thread| thread.join();
118 self.context.refreshWithHeldLock();
119 }261 }
120 }262 }
121263
122 /// Tell the parent node that this node is actively being worked on. Thread-safe.264 /// Posix-only. Used by `std.process.Child`. Thread-safe.
123 pub fn activate(self: *Node) void {265 pub fn setIpcFd(node: Node, fd: posix.fd_t) void {
124 if (self.parent) |parent| {266 const index = node.index.unwrap() orelse return;
125 @atomicStore(?*Node, &parent.recently_updated_child, self, .release);267 assert(fd >= 0);
126 self.context.maybeRefresh();268 assert(fd != posix.STDOUT_FILENO);
127 }269 assert(fd != posix.STDIN_FILENO);
270 assert(fd != posix.STDERR_FILENO);
271 storageByIndex(index).setIpcFd(fd);
128 }272 }
129273
130 /// Thread-safe.274 fn storageByIndex(index: Node.Index) *Node.Storage {
131 pub fn setName(self: *Node, name: []const u8) void {275 return &global_progress.node_storage[@intFromEnum(index)];
132 const progress = self.context;
133 progress.update_mutex.lock();
134 defer progress.update_mutex.unlock();
135 self.name = name;
136 if (self.parent) |parent| {
137 @atomicStore(?*Node, &parent.recently_updated_child, self, .release);
138 if (parent.parent) |grand_parent| {
139 @atomicStore(?*Node, &grand_parent.recently_updated_child, parent, .release);
140 }
141 if (progress.timer) |*timer| progress.maybeRefreshWithHeldLock(timer);
142 }
143 }276 }
144277
145 /// Thread-safe.278 fn parentByIndex(index: Node.Index) *Node.Parent {
146 pub fn setUnit(self: *Node, unit: []const u8) void {279 return &global_progress.node_parents[@intFromEnum(index)];
147 const progress = self.context;
148 progress.update_mutex.lock();
149 defer progress.update_mutex.unlock();
150 self.unit = unit;
151 if (self.parent) |parent| {
152 @atomicStore(?*Node, &parent.recently_updated_child, self, .release);
153 if (parent.parent) |grand_parent| {
154 @atomicStore(?*Node, &grand_parent.recently_updated_child, parent, .release);
155 }
156 if (progress.timer) |*timer| progress.maybeRefreshWithHeldLock(timer);
157 }
158 }280 }
159281
160 /// Thread-safe. 0 means unknown.282 fn freelistByIndex(index: Node.Index) *Node.OptionalIndex {
161 pub fn setEstimatedTotalItems(self: *Node, count: usize) void {283 return &global_progress.node_freelist[@intFromEnum(index)];
162 @atomicStore(usize, &self.unprotected_estimated_total_items, count, .monotonic);
163 }284 }
164285
165 /// Thread-safe.286 fn init(free_index: Index, parent: Parent, name: []const u8, estimated_total_items: usize) Node {
166 pub fn setCompletedItems(self: *Node, completed_items: usize) void {287 assert(parent != .unused);
167 @atomicStore(usize, &self.unprotected_completed_items, completed_items, .monotonic);288
289 const storage = storageByIndex(free_index);
290 storage.* = .{
291 .completed_count = 0,
292 .estimated_total_count = std.math.lossyCast(u32, estimated_total_items),
293 .name = [1]u8{0} ** max_name_len,
294 };
295 const name_len = @min(max_name_len, name.len);
296 @memcpy(storage.name[0..name_len], name[0..name_len]);
297
298 const parent_ptr = parentByIndex(free_index);
299 assert(parent_ptr.* == .unused);
300 @atomicStore(Node.Parent, parent_ptr, parent, .release);
301
302 return .{ .index = free_index.toOptional() };
168 }303 }
169};304};
170305
171/// Create a new progress node.306var global_progress: Progress = .{
307 .terminal = undefined,
308 .terminal_mode = .off,
309 .update_thread = null,
310 .redraw_event = .{},
311 .refresh_rate_ns = undefined,
312 .initial_delay_ns = undefined,
313 .rows = 0,
314 .cols = 0,
315 .written_newline_count = 0,
316 .accumulated_newline_count = 0,
317 .draw_buffer = undefined,
318 .done = false,
319
320 .node_parents = &node_parents_buffer,
321 .node_storage = &node_storage_buffer,
322 .node_freelist = &node_freelist_buffer,
323 .node_freelist_first = .none,
324 .node_end_index = 0,
325};
326
327const node_storage_buffer_len = 200;
328var node_parents_buffer: [node_storage_buffer_len]Node.Parent = undefined;
329var node_storage_buffer: [node_storage_buffer_len]Node.Storage = undefined;
330var node_freelist_buffer: [node_storage_buffer_len]Node.OptionalIndex = undefined;
331
332var default_draw_buffer: [4096]u8 = undefined;
333
334var debug_start_trace = std.debug.Trace.init;
335
336const noop_impl = builtin.single_threaded or switch (builtin.os.tag) {
337 .wasi, .freestanding => true,
338 else => false,
339};
340
341/// Initializes a global Progress instance.
342///
343/// Asserts there is only one global Progress instance.
344///
172/// Call `Node.end` when done.345/// Call `Node.end` when done.
173/// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this346pub fn start(options: Options) Node {
174/// API to return Progress rather than accept it as a parameter.347 // Ensure there is only 1 global Progress object.
175/// `estimated_total_items` value of 0 means unknown.348 if (global_progress.node_end_index != 0) {
176pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) *Node {349 debug_start_trace.dump();
177 const stderr = std.io.getStdErr();350 unreachable;
178 self.terminal = null;351 }
179 if (stderr.supportsAnsiEscapeCodes()) {352 debug_start_trace.add("first initialized here");
180 self.terminal = stderr;353
181 self.supports_ansi_escape_codes = true;354 @memset(global_progress.node_parents, .unused);
182 } else if (builtin.os.tag == .windows and stderr.isTty()) {355 const root_node = Node.init(@enumFromInt(0), .none, options.root_name, options.estimated_total_items);
183 self.is_windows_terminal = true;356 global_progress.done = false;
184 self.terminal = stderr;357 global_progress.node_end_index = 1;
185 } else if (builtin.os.tag != .windows) {358
186 // we are in a "dumb" terminal like in acme or writing to a file359 assert(options.draw_buffer.len >= 200);
187 self.terminal = stderr;360 global_progress.draw_buffer = options.draw_buffer;
188 }361 global_progress.refresh_rate_ns = options.refresh_rate_ns;
189 self.root = Node{362 global_progress.initial_delay_ns = options.initial_delay_ns;
190 .context = self,363
191 .parent = null,364 if (noop_impl)
192 .name = name,365 return .{ .index = .none };
193 .unprotected_estimated_total_items = estimated_total_items,366
194 .unprotected_completed_items = 0,367 if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| {
368 global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{
369 @as(posix.fd_t, switch (@typeInfo(posix.fd_t)) {
370 .Int => ipc_fd,
371 .Pointer => @ptrFromInt(ipc_fd),
372 else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)),
373 }),
374 }) catch |err| {
375 std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)});
376 return .{ .index = .none };
377 };
378 } else |env_err| switch (env_err) {
379 error.EnvironmentVariableNotFound => {
380 if (options.disable_printing) {
381 return .{ .index = .none };
382 }
383 const stderr = std.io.getStdErr();
384 global_progress.terminal = stderr;
385 if (stderr.supportsAnsiEscapeCodes()) {
386 global_progress.terminal_mode = .ansi_escape_codes;
387 } else if (is_windows and stderr.isTty()) {
388 global_progress.terminal_mode = TerminalMode{ .windows_api = .{
389 .code_page = windows.kernel32.GetConsoleOutputCP(),
390 } };
391 }
392
393 if (global_progress.terminal_mode == .off) {
394 return .{ .index = .none };
395 }
396
397 if (have_sigwinch) {
398 var act: posix.Sigaction = .{
399 .handler = .{ .sigaction = handleSigWinch },
400 .mask = posix.empty_sigset,
401 .flags = (posix.SA.SIGINFO | posix.SA.RESTART),
402 };
403 posix.sigaction(posix.SIG.WINCH, &act, null) catch |err| {
404 std.log.warn("failed to install SIGWINCH signal handler for noticing terminal resizes: {s}", .{@errorName(err)});
405 };
406 }
407
408 if (switch (global_progress.terminal_mode) {
409 .off => unreachable, // handled a few lines above
410 .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{}),
411 .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{}) else unreachable,
412 }) |thread| {
413 global_progress.update_thread = thread;
414 } else |err| {
415 std.log.warn("unable to spawn thread for printing progress to terminal: {s}", .{@errorName(err)});
416 return .{ .index = .none };
417 }
418 },
419 else => |e| {
420 std.log.warn("invalid ZIG_PROGRESS file descriptor integer: {s}", .{@errorName(e)});
421 return .{ .index = .none };
422 },
423 }
424
425 return root_node;
426}
427
428/// Returns whether a resize is needed to learn the terminal size.
429fn wait(timeout_ns: u64) bool {
430 const resize_flag = if (global_progress.redraw_event.timedWait(timeout_ns)) |_|
431 true
432 else |err| switch (err) {
433 error.Timeout => false,
195 };434 };
196 self.columns_written = 0;435 global_progress.redraw_event.reset();
197 self.prev_refresh_timestamp = 0;436 return resize_flag or (global_progress.cols == 0);
198 self.timer = std.time.Timer.start() catch null;
199 self.done = false;
200 return &self.root;
201}437}
202438
203/// Updates the terminal if enough time has passed since last update. Thread-safe.439fn updateThreadRun() void {
204pub fn maybeRefresh(self: *Progress) void {440 // Store this data in the thread so that it does not need to be part of the
205 if (self.timer) |*timer| {441 // linker data of the main executable.
206 if (!self.update_mutex.tryLock()) return;442 var serialized_buffer: Serialized.Buffer = undefined;
207 defer self.update_mutex.unlock();443
208 maybeRefreshWithHeldLock(self, timer);444 {
445 const resize_flag = wait(global_progress.initial_delay_ns);
446 if (@atomicLoad(bool, &global_progress.done, .seq_cst)) return;
447 maybeUpdateSize(resize_flag);
448
449 const buffer = computeRedraw(&serialized_buffer);
450 if (stderr_mutex.tryLock()) {
451 defer stderr_mutex.unlock();
452 write(buffer) catch return;
453 }
454 }
455
456 while (true) {
457 const resize_flag = wait(global_progress.refresh_rate_ns);
458
459 if (@atomicLoad(bool, &global_progress.done, .seq_cst)) {
460 stderr_mutex.lock();
461 defer stderr_mutex.unlock();
462 return clearWrittenWithEscapeCodes() catch {};
463 }
464
465 maybeUpdateSize(resize_flag);
466
467 const buffer = computeRedraw(&serialized_buffer);
468 if (stderr_mutex.tryLock()) {
469 defer stderr_mutex.unlock();
470 write(buffer) catch return;
471 }
472 }
473}
474
475fn windowsApiUpdateThreadRun() void {
476 var serialized_buffer: Serialized.Buffer = undefined;
477
478 {
479 const resize_flag = wait(global_progress.initial_delay_ns);
480 if (@atomicLoad(bool, &global_progress.done, .seq_cst)) return;
481 maybeUpdateSize(resize_flag);
482
483 const buffer = computeRedraw(&serialized_buffer);
484 if (stderr_mutex.tryLock()) {
485 defer stderr_mutex.unlock();
486 write(buffer) catch return;
487 }
488 }
489
490 while (true) {
491 const resize_flag = wait(global_progress.refresh_rate_ns);
492
493 if (@atomicLoad(bool, &global_progress.done, .seq_cst)) {
494 stderr_mutex.lock();
495 defer stderr_mutex.unlock();
496 return clearWrittenWindowsApi() catch {};
497 }
498
499 maybeUpdateSize(resize_flag);
500
501 const buffer = computeRedraw(&serialized_buffer);
502 if (stderr_mutex.tryLock()) {
503 defer stderr_mutex.unlock();
504 clearWrittenWindowsApi() catch return;
505 write(buffer) catch return;
506 }
209 }507 }
210}508}
211509
212fn maybeRefreshWithHeldLock(self: *Progress, timer: *std.time.Timer) void {510/// Allows the caller to freely write to stderr until `unlockStdErr` is called.
213 const now = timer.read();511///
214 if (now < self.initial_delay_ns) return;512/// During the lock, any `std.Progress` information is cleared from the terminal.
215 // TODO I have observed this to happen sometimes. I think we need to follow Rust's513pub fn lockStdErr() void {
216 // lead and guarantee monotonically increasing times in the std lib itself.514 stderr_mutex.lock();
217 if (now < self.prev_refresh_timestamp) return;515 clearWrittenWithEscapeCodes() catch {};
218 if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;516}
219 return self.refreshWithHeldLock();517
518pub fn unlockStdErr() void {
519 stderr_mutex.unlock();
220}520}
221521
222/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.522fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
223pub fn refresh(self: *Progress) void {523 // Store this data in the thread so that it does not need to be part of the
224 if (!self.update_mutex.tryLock()) return;524 // linker data of the main executable.
225 defer self.update_mutex.unlock();525 var serialized_buffer: Serialized.Buffer = undefined;
526
527 {
528 _ = wait(global_progress.initial_delay_ns);
226529
227 return self.refreshWithHeldLock();530 if (@atomicLoad(bool, &global_progress.done, .seq_cst))
531 return;
532
533 const serialized = serialize(&serialized_buffer);
534 writeIpc(fd, serialized) catch |err| switch (err) {
535 error.BrokenPipe => return,
536 };
537 }
538
539 while (true) {
540 _ = wait(global_progress.refresh_rate_ns);
541
542 if (@atomicLoad(bool, &global_progress.done, .seq_cst))
543 return;
544
545 const serialized = serialize(&serialized_buffer);
546 writeIpc(fd, serialized) catch |err| switch (err) {
547 error.BrokenPipe => return,
548 };
549 }
228}550}
229551
230fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void {552const start_sync = "\x1b[?2026h";
231 const file = p.terminal orelse return;553const up_one_line = "\x1bM";
232 var end = end_ptr.*;554const clear = "\x1b[J";
233 if (p.columns_written > 0) {555const save = "\x1b7";
234 // restore the cursor position by moving the cursor556const restore = "\x1b8";
235 // `columns_written` cells to the left, then clear the rest of the557const finish_sync = "\x1b[?2026l";
236 // line
237 if (p.supports_ansi_escape_codes) {
238 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len;
239 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
240 } else if (builtin.os.tag == .windows) winapi: {
241 std.debug.assert(p.is_windows_terminal);
242
243 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
244 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
245 // stop trying to write to this file
246 p.terminal = null;
247 break :winapi;
248 }
249558
250 var cursor_pos = windows.COORD{559const TreeSymbol = enum {
251 .X = info.dwCursorPosition.X - @as(windows.SHORT, @intCast(p.columns_written)),560 /// ├─
252 .Y = info.dwCursorPosition.Y,561 tee,
562 /// │
563 line,
564 /// └─
565 langle,
566
567 const Encoding = enum {
568 ansi_escapes,
569 code_page_437,
570 utf8,
571 ascii,
572 };
573
574 /// The escape sequence representation as a string literal
575 fn escapeSeq(symbol: TreeSymbol) *const [9:0]u8 {
576 return switch (symbol) {
577 .tee => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ",
578 .line => "\x1B\x28\x30\x78\x1B\x28\x42 ",
579 .langle => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ",
580 };
581 }
582
583 fn bytes(symbol: TreeSymbol, encoding: Encoding) []const u8 {
584 return switch (encoding) {
585 .ansi_escapes => escapeSeq(symbol),
586 .code_page_437 => switch (symbol) {
587 .tee => "\xC3\xC4 ",
588 .line => "\xB3 ",
589 .langle => "\xC0\xC4 ",
590 },
591 .utf8 => switch (symbol) {
592 .tee => "├─ ",
593 .line => "│ ",
594 .langle => "└─ ",
595 },
596 .ascii => switch (symbol) {
597 .tee => "|- ",
598 .line => "| ",
599 .langle => "+- ",
600 },
601 };
602 }
603
604 fn maxByteLen(symbol: TreeSymbol) usize {
605 var max: usize = 0;
606 inline for (@typeInfo(Encoding).Enum.fields) |field| {
607 const len = symbol.bytes(@field(Encoding, field.name)).len;
608 max = @max(max, len);
609 }
610 return max;
611 }
612};
613
614fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize {
615 switch (global_progress.terminal_mode) {
616 .off => unreachable,
617 .ansi_escape_codes => {
618 const bytes = symbol.escapeSeq();
619 buf[start_i..][0..bytes.len].* = bytes.*;
620 return start_i + bytes.len;
621 },
622 .windows_api => |windows_api| {
623 const bytes = if (!is_windows) unreachable else switch (windows_api.code_page) {
624 // Code page 437 is the default code page and contains the box drawing symbols
625 437 => symbol.bytes(.code_page_437),
626 // UTF-8
627 65001 => symbol.bytes(.utf8),
628 // Fall back to ASCII approximation
629 else => symbol.bytes(.ascii),
253 };630 };
631 @memcpy(buf[start_i..][0..bytes.len], bytes);
632 return start_i + bytes.len;
633 },
634 }
635}
254636
255 if (cursor_pos.X < 0)637fn clearWrittenWithEscapeCodes() anyerror!void {
256 cursor_pos.X = 0;638 if (global_progress.written_newline_count == 0) return;
257639
258 const fill_chars = @as(windows.DWORD, @intCast(info.dwSize.X - cursor_pos.X));640 var i: usize = 0;
259641 const buf = global_progress.draw_buffer;
260 var written: windows.DWORD = undefined;642
261 if (windows.kernel32.FillConsoleOutputAttribute(643 buf[i..][0..start_sync.len].* = start_sync.*;
262 file.handle,644 i += start_sync.len;
263 info.wAttributes,645
264 fill_chars,646 i = computeClear(buf, i);
265 cursor_pos,647
266 &written,648 buf[i..][0..finish_sync.len].* = finish_sync.*;
267 ) != windows.TRUE) {649 i += finish_sync.len;
268 // stop trying to write to this file650
269 p.terminal = null;651 global_progress.accumulated_newline_count = 0;
270 break :winapi;652 try write(buf[0..i]);
271 }653}
272 if (windows.kernel32.FillConsoleOutputCharacterW(654
273 file.handle,655fn computeClear(buf: []u8, start_i: usize) usize {
274 ' ',656 var i = start_i;
275 fill_chars,657
276 cursor_pos,658 const prev_nl_n = global_progress.written_newline_count;
277 &written,659 if (prev_nl_n > 0) {
278 ) != windows.TRUE) {660 buf[i] = '\r';
279 // stop trying to write to this file661 i += 1;
280 p.terminal = null;662 for (0..prev_nl_n) |_| {
281 break :winapi;663 buf[i..][0..up_one_line.len].* = up_one_line.*;
282 }664 i += up_one_line.len;
283 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {665 }
284 // stop trying to write to this file666 }
285 p.terminal = null;667
286 break :winapi;668 buf[i..][0..clear.len].* = clear.*;
669 i += clear.len;
670
671 return i;
672}
673
674/// U+25BA or â–º
675const windows_api_start_marker = 0x25BA;
676
677fn clearWrittenWindowsApi() error{Unexpected}!void {
678 // This uses a 'marker' strategy. The idea is:
679 // - Always write a marker (in this case U+25BA or â–º) at the beginning of the progress
680 // - Get the current cursor position (at the end of the progress)
681 // - Subtract the number of lines written to get the expected start of the progress
682 // - Check to see if the first character at the start of the progress is the marker
683 // - If it's not the marker, keep checking the line before until we find it
684 // - Clear the screen from that position down, and set the cursor position to the start
685 //
686 // This strategy works even if there is line wrapping, and can handle the window
687 // being resized/scrolled arbitrarily.
688 //
689 // Notes:
690 // - Ideally, the marker would be a zero-width character, but the Windows console
691 // doesn't seem to support rendering zero-width characters (they show up as a space)
692 // - This same marker idea could technically be done with an attribute instead
693 // (https://learn.microsoft.com/en-us/windows/console/console-screen-buffers#character-attributes)
694 // but it must be a valid attribute and it actually needs to apply to the first
695 // character in order to be readable via ReadConsoleOutputAttribute. It doesn't seem
696 // like any of the available attributes are invisible/benign.
697 const prev_nl_n = global_progress.written_newline_count;
698 if (prev_nl_n > 0) {
699 const handle = global_progress.terminal.handle;
700 const screen_area = @as(windows.DWORD, global_progress.cols) * global_progress.rows;
701
702 var console_info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
703 if (windows.kernel32.GetConsoleScreenBufferInfo(handle, &console_info) == 0) {
704 return error.Unexpected;
705 }
706 const cursor_pos = console_info.dwCursorPosition;
707 const expected_y = cursor_pos.Y - @as(i16, @intCast(prev_nl_n));
708 var start_pos = windows.COORD{ .X = 0, .Y = expected_y };
709 while (start_pos.Y >= 0) {
710 var wchar: [1]u16 = undefined;
711 var num_console_chars_read: windows.DWORD = undefined;
712 if (windows.kernel32.ReadConsoleOutputCharacterW(handle, &wchar, wchar.len, start_pos, &num_console_chars_read) == 0) {
713 return error.Unexpected;
287 }714 }
715
716 if (wchar[0] == windows_api_start_marker) break;
717 start_pos.Y -= 1;
288 } else {718 } else {
289 // we are in a "dumb" terminal like in acme or writing to a file719 // If we couldn't find the marker, then just assume that no lines wrapped
290 p.output_buffer[end] = '\n';720 start_pos = .{ .X = 0, .Y = expected_y };
291 end += 1;721 }
722 var num_chars_written: windows.DWORD = undefined;
723 if (windows.kernel32.FillConsoleOutputCharacterW(handle, ' ', screen_area, start_pos, &num_chars_written) == 0) {
724 return error.Unexpected;
725 }
726 if (windows.kernel32.SetConsoleCursorPosition(handle, start_pos) == 0) {
727 return error.Unexpected;
292 }728 }
293
294 p.columns_written = 0;
295 }729 }
296 end_ptr.* = end;
297}730}
298731
299fn refreshWithHeldLock(self: *Progress) void {732const Children = struct {
300 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;733 child: Node.OptionalIndex,
301 if (is_dumb and self.dont_print_on_dumb) return;734 sibling: Node.OptionalIndex,
735};
736
737const Serialized = struct {
738 parents: []Node.Parent,
739 storage: []Node.Storage,
302740
303 const file = self.terminal orelse return;741 const Buffer = struct {
742 parents: [node_storage_buffer_len]Node.Parent,
743 storage: [node_storage_buffer_len]Node.Storage,
744 map: [node_storage_buffer_len]Node.Index,
304745
305 var end: usize = 0;746 parents_copy: [node_storage_buffer_len]Node.Parent,
306 clearWithHeldLock(self, &end);747 storage_copy: [node_storage_buffer_len]Node.Storage,
748 ipc_metadata_copy: [node_storage_buffer_len]SavedMetadata,
307749
308 if (!self.done) {750 ipc_metadata: [node_storage_buffer_len]SavedMetadata,
309 var need_ellipse = false;751 };
310 var maybe_node: ?*Node = &self.root;752};
311 while (maybe_node) |node| {753
312 if (need_ellipse) {754fn serialize(serialized_buffer: *Serialized.Buffer) Serialized {
313 self.bufWrite(&end, "... ", .{});755 var serialized_len: usize = 0;
756 var any_ipc = false;
757
758 // Iterate all of the nodes and construct a serializable copy of the state that can be examined
759 // without atomics.
760 const end_index = @atomicLoad(u32, &global_progress.node_end_index, .monotonic);
761 const node_parents = global_progress.node_parents[0..end_index];
762 const node_storage = global_progress.node_storage[0..end_index];
763 for (node_parents, node_storage, 0..) |*parent_ptr, *storage_ptr, i| {
764 var begin_parent = @atomicLoad(Node.Parent, parent_ptr, .acquire);
765 while (begin_parent != .unused) {
766 const dest_storage = &serialized_buffer.storage[serialized_len];
767 @memcpy(&dest_storage.name, &storage_ptr.name);
768 dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .acquire);
769 dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic);
770 const end_parent = @atomicLoad(Node.Parent, parent_ptr, .acquire);
771 if (begin_parent == end_parent) {
772 any_ipc = any_ipc or (dest_storage.getIpcFd() != null);
773 serialized_buffer.parents[serialized_len] = begin_parent;
774 serialized_buffer.map[i] = @enumFromInt(serialized_len);
775 serialized_len += 1;
776 break;
314 }777 }
315 need_ellipse = false;778
316 const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .monotonic);779 begin_parent = end_parent;
317 const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .monotonic);780 }
318 const current_item = completed_items + 1;781 }
319 if (node.name.len != 0 or eti > 0) {782
320 if (node.name.len != 0) {783 // Remap parents to point inside serialized arrays.
321 self.bufWrite(&end, "{s}", .{node.name});784 for (serialized_buffer.parents[0..serialized_len]) |*parent| {
322 need_ellipse = true;785 parent.* = switch (parent.*) {
323 }786 .unused => unreachable,
324 if (eti > 0) {787 .none => .none,
325 if (need_ellipse) self.bufWrite(&end, " ", .{});788 _ => |p| serialized_buffer.map[@intFromEnum(p)].toParent(),
326 self.bufWrite(&end, "[{d}/{d}{s}] ", .{ current_item, eti, node.unit });789 };
327 need_ellipse = false;790 }
328 } else if (completed_items != 0) {791
329 if (need_ellipse) self.bufWrite(&end, " ", .{});792 // Find nodes which correspond to child processes.
330 self.bufWrite(&end, "[{d}{s}] ", .{ current_item, node.unit });793 if (any_ipc)
331 need_ellipse = false;794 serialized_len = serializeIpc(serialized_len, serialized_buffer);
795
796 return .{
797 .parents = serialized_buffer.parents[0..serialized_len],
798 .storage = serialized_buffer.storage[0..serialized_len],
799 };
800}
801
802const SavedMetadata = struct {
803 ipc_fd: u16,
804 main_index: u8,
805 start_index: u8,
806 nodes_len: u8,
807
808 fn getIpcFd(metadata: SavedMetadata) posix.fd_t {
809 return if (is_windows)
810 @ptrFromInt(@as(usize, metadata.ipc_fd) << 2)
811 else
812 metadata.ipc_fd;
813 }
814
815 fn setIpcFd(fd: posix.fd_t) u16 {
816 return @intCast(if (is_windows)
817 @shrExact(@intFromPtr(fd), 2)
818 else
819 fd);
820 }
821};
822
823var ipc_metadata_len: u8 = 0;
824var remaining_read_trash_bytes: usize = 0;
825
826fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buffer) usize {
827 const ipc_metadata_copy = &serialized_buffer.ipc_metadata_copy;
828 const ipc_metadata = &serialized_buffer.ipc_metadata;
829
830 var serialized_len = start_serialized_len;
831 var pipe_buf: [2 * 4096]u8 align(4) = undefined;
832
833 const old_ipc_metadata = ipc_metadata_copy[0..ipc_metadata_len];
834 ipc_metadata_len = 0;
835
836 main_loop: for (
837 serialized_buffer.parents[0..serialized_len],
838 serialized_buffer.storage[0..serialized_len],
839 0..,
840 ) |main_parent, *main_storage, main_index| {
841 if (main_parent == .unused) continue;
842 const fd = main_storage.getIpcFd() orelse continue;
843 var bytes_read: usize = 0;
844 while (true) {
845 const n = posix.read(fd, pipe_buf[bytes_read..]) catch |err| switch (err) {
846 error.WouldBlock => break,
847 else => |e| {
848 std.log.debug("failed to read child progress data: {s}", .{@errorName(e)});
849 main_storage.completed_count = 0;
850 main_storage.estimated_total_count = 0;
851 continue :main_loop;
852 },
853 };
854 if (n == 0) break;
855 if (remaining_read_trash_bytes > 0) {
856 assert(bytes_read == 0);
857 if (remaining_read_trash_bytes >= n) {
858 remaining_read_trash_bytes -= n;
859 continue;
332 }860 }
861 const src = pipe_buf[remaining_read_trash_bytes..n];
862 std.mem.copyForwards(u8, &pipe_buf, src);
863 remaining_read_trash_bytes = 0;
864 bytes_read = src.len;
865 continue;
333 }866 }
334 maybe_node = @atomicLoad(?*Node, &node.recently_updated_child, .acquire);867 bytes_read += n;
335 }868 }
336 if (need_ellipse) {869 // Ignore all but the last message on the pipe.
337 self.bufWrite(&end, "... ", .{});870 var input: []u8 = pipe_buf[0..bytes_read];
871 if (input.len == 0) {
872 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
873 continue;
874 }
875
876 const storage, const parents = while (true) {
877 const subtree_len: usize = input[0];
878 const expected_bytes = 1 + subtree_len * (@sizeOf(Node.Storage) + @sizeOf(Node.Parent));
879 if (input.len < expected_bytes) {
880 // Ignore short reads. We'll handle the next full message when it comes instead.
881 assert(remaining_read_trash_bytes == 0);
882 remaining_read_trash_bytes = expected_bytes - input.len;
883 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
884 continue :main_loop;
885 }
886 if (input.len > expected_bytes) {
887 input = input[expected_bytes..];
888 continue;
889 }
890 const storage_bytes = input[1..][0 .. subtree_len * @sizeOf(Node.Storage)];
891 const parents_bytes = input[1 + storage_bytes.len ..][0 .. subtree_len * @sizeOf(Node.Parent)];
892 break .{
893 std.mem.bytesAsSlice(Node.Storage, storage_bytes),
894 std.mem.bytesAsSlice(Node.Parent, parents_bytes),
895 };
896 };
897
898 const nodes_len: u8 = @intCast(@min(parents.len - 1, serialized_buffer.storage.len - serialized_len));
899
900 // Remember in case the pipe is empty on next update.
901 ipc_metadata[ipc_metadata_len] = .{
902 .ipc_fd = SavedMetadata.setIpcFd(fd),
903 .start_index = @intCast(serialized_len),
904 .nodes_len = nodes_len,
905 .main_index = @intCast(main_index),
906 };
907 ipc_metadata_len += 1;
908
909 // Mount the root here.
910 copyRoot(main_storage, &storage[0]);
911 if (is_big_endian) main_storage.byteSwap();
912
913 // Copy the rest of the tree to the end.
914 const storage_dest = serialized_buffer.storage[serialized_len..][0..nodes_len];
915 @memcpy(storage_dest, storage[1..][0..nodes_len]);
916
917 // Always little-endian over the pipe.
918 if (is_big_endian) for (storage_dest) |*s| s.byteSwap();
919
920 // Patch up parent pointers taking into account how the subtree is mounted.
921 for (serialized_buffer.parents[serialized_len..][0..nodes_len], parents[1..][0..nodes_len]) |*dest, p| {
922 dest.* = switch (p) {
923 // Fix bad data so the rest of the code does not see `unused`.
924 .none, .unused => .none,
925 // Root node is being mounted here.
926 @as(Node.Parent, @enumFromInt(0)) => @enumFromInt(main_index),
927 // Other nodes mounted at the end.
928 // Don't trust child data; if the data is outside the expected range, ignore the data.
929 // This also handles the case when data was truncated.
930 _ => |off| if (@intFromEnum(off) > nodes_len)
931 .none
932 else
933 @enumFromInt(serialized_len + @intFromEnum(off) - 1),
934 };
338 }935 }
936
937 serialized_len += nodes_len;
339 }938 }
340939
341 _ = file.write(self.output_buffer[0..end]) catch {940 // Save a copy in case any pipes are empty on the next update.
342 // stop trying to write to this file941 @memcpy(serialized_buffer.parents_copy[0..serialized_len], serialized_buffer.parents[0..serialized_len]);
343 self.terminal = null;942 @memcpy(serialized_buffer.storage_copy[0..serialized_len], serialized_buffer.storage[0..serialized_len]);
943 @memcpy(ipc_metadata_copy[0..ipc_metadata_len], ipc_metadata[0..ipc_metadata_len]);
944
945 return serialized_len;
946}
947
948fn copyRoot(dest: *Node.Storage, src: *align(1) Node.Storage) void {
949 dest.* = .{
950 .completed_count = src.completed_count,
951 .estimated_total_count = src.estimated_total_count,
952 .name = if (src.name[0] == 0) dest.name else src.name,
344 };953 };
345 if (self.timer) |*timer| {954}
346 self.prev_refresh_timestamp = timer.read();955
956fn findOld(ipc_fd: posix.fd_t, old_metadata: []const SavedMetadata) ?*const SavedMetadata {
957 for (old_metadata) |*m| {
958 if (m.getIpcFd() == ipc_fd)
959 return m;
347 }960 }
961 return null;
348}962}
349963
350pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {964fn useSavedIpcData(
351 const file = self.terminal orelse {965 start_serialized_len: usize,
352 std.debug.print(format, args);966 serialized_buffer: *Serialized.Buffer,
353 return;967 main_storage: *Node.Storage,
968 main_index: usize,
969 old_metadata: []const SavedMetadata,
970) usize {
971 const parents_copy = &serialized_buffer.parents_copy;
972 const storage_copy = &serialized_buffer.storage_copy;
973 const ipc_metadata = &serialized_buffer.ipc_metadata;
974
975 const ipc_fd = main_storage.getIpcFd().?;
976 const saved_metadata = findOld(ipc_fd, old_metadata) orelse {
977 main_storage.completed_count = 0;
978 main_storage.estimated_total_count = 0;
979 return start_serialized_len;
354 };980 };
355 self.refresh();981
356 file.writer().print(format, args) catch {982 const start_index = saved_metadata.start_index;
357 self.terminal = null;983 const nodes_len = @min(saved_metadata.nodes_len, serialized_buffer.storage.len - start_serialized_len);
358 return;984 const old_main_index = saved_metadata.main_index;
985
986 ipc_metadata[ipc_metadata_len] = .{
987 .ipc_fd = SavedMetadata.setIpcFd(ipc_fd),
988 .start_index = @intCast(start_serialized_len),
989 .nodes_len = nodes_len,
990 .main_index = @intCast(main_index),
359 };991 };
360 self.columns_written = 0;992 ipc_metadata_len += 1;
361}993
994 const parents = parents_copy[start_index..][0..nodes_len];
995 const storage = storage_copy[start_index..][0..nodes_len];
362996
363/// Allows the caller to freely write to stderr until unlock_stderr() is called.997 copyRoot(main_storage, &storage_copy[old_main_index]);
364/// During the lock, the progress information is cleared from the terminal.998
365pub fn lock_stderr(p: *Progress) void {999 @memcpy(serialized_buffer.storage[start_serialized_len..][0..storage.len], storage);
366 p.update_mutex.lock();1000
367 if (p.terminal) |file| {1001 for (serialized_buffer.parents[start_serialized_len..][0..parents.len], parents) |*dest, p| {
368 var end: usize = 0;1002 dest.* = switch (p) {
369 clearWithHeldLock(p, &end);1003 .none, .unused => .none,
370 _ = file.write(p.output_buffer[0..end]) catch {1004 _ => |prev| d: {
371 // stop trying to write to this file1005 if (@intFromEnum(prev) == old_main_index) {
372 p.terminal = null;1006 break :d @enumFromInt(main_index);
1007 } else if (@intFromEnum(prev) > nodes_len) {
1008 break :d .none;
1009 } else {
1010 break :d @enumFromInt(@intFromEnum(prev) - start_index + start_serialized_len);
1011 }
1012 },
373 };1013 };
374 }1014 }
375 std.debug.getStderrMutex().lock();1015
1016 return start_serialized_len + storage.len;
1017}
1018
1019fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 {
1020 const serialized = serialize(serialized_buffer);
1021
1022 // Now we can analyze our copy of the graph without atomics, reconstructing
1023 // children lists which do not exist in the canonical data. These are
1024 // needed for tree traversal below.
1025
1026 var children_buffer: [node_storage_buffer_len]Children = undefined;
1027 const children = children_buffer[0..serialized.parents.len];
1028
1029 @memset(children, .{ .child = .none, .sibling = .none });
1030
1031 for (serialized.parents, 0..) |parent, child_index_usize| {
1032 const child_index: Node.Index = @enumFromInt(child_index_usize);
1033 assert(parent != .unused);
1034 const parent_index = parent.unwrap() orelse continue;
1035 const children_node = &children[@intFromEnum(parent_index)];
1036 if (children_node.child.unwrap()) |existing_child_index| {
1037 const existing_child = &children[@intFromEnum(existing_child_index)];
1038 children[@intFromEnum(child_index)].sibling = existing_child.sibling;
1039 existing_child.sibling = child_index.toOptional();
1040 } else {
1041 children_node.child = child_index.toOptional();
1042 }
1043 }
1044
1045 // The strategy is: keep the cursor at the end, and then with every redraw:
1046 // move cursor to beginning of line, move cursor up N lines, erase to end of screen, write
1047
1048 var i: usize = 0;
1049 const buf = global_progress.draw_buffer;
1050
1051 buf[i..][0..start_sync.len].* = start_sync.*;
1052 i += start_sync.len;
1053
1054 switch (global_progress.terminal_mode) {
1055 .off => unreachable,
1056 .ansi_escape_codes => i = computeClear(buf, i),
1057 .windows_api => if (!is_windows) unreachable,
1058 }
1059
1060 global_progress.accumulated_newline_count = 0;
1061 const root_node_index: Node.Index = @enumFromInt(0);
1062 i = computeNode(buf, i, serialized, children, root_node_index);
1063
1064 buf[i..][0..finish_sync.len].* = finish_sync.*;
1065 i += finish_sync.len;
1066
1067 return buf[0..i];
1068}
1069
1070fn computePrefix(
1071 buf: []u8,
1072 start_i: usize,
1073 serialized: Serialized,
1074 children: []const Children,
1075 node_index: Node.Index,
1076) usize {
1077 var i = start_i;
1078 const parent_index = serialized.parents[@intFromEnum(node_index)].unwrap() orelse return i;
1079 if (serialized.parents[@intFromEnum(parent_index)] == .none) return i;
1080 if (@intFromEnum(serialized.parents[@intFromEnum(parent_index)]) == 0 and
1081 serialized.storage[0].name[0] == 0)
1082 {
1083 return i;
1084 }
1085 i = computePrefix(buf, i, serialized, children, parent_index);
1086 if (children[@intFromEnum(parent_index)].sibling == .none) {
1087 const prefix = " ";
1088 const upper_bound_len = prefix.len + line_upper_bound_len;
1089 if (i + upper_bound_len > buf.len) return buf.len;
1090 buf[i..][0..prefix.len].* = prefix.*;
1091 i += prefix.len;
1092 } else {
1093 const upper_bound_len = comptime (TreeSymbol.line.maxByteLen() + line_upper_bound_len);
1094 if (i + upper_bound_len > buf.len) return buf.len;
1095 i = appendTreeSymbol(.line, buf, i);
1096 }
1097 return i;
1098}
1099
1100const line_upper_bound_len = @max(TreeSymbol.tee.maxByteLen(), TreeSymbol.langle.maxByteLen()) +
1101 "[4294967296/4294967296] ".len + Node.max_name_len + finish_sync.len;
1102
1103fn computeNode(
1104 buf: []u8,
1105 start_i: usize,
1106 serialized: Serialized,
1107 children: []const Children,
1108 node_index: Node.Index,
1109) usize {
1110 var i = start_i;
1111 i = computePrefix(buf, i, serialized, children, node_index);
1112
1113 if (i + line_upper_bound_len > buf.len)
1114 return start_i;
1115
1116 const storage = &serialized.storage[@intFromEnum(node_index)];
1117 const estimated_total = storage.estimated_total_count;
1118 const completed_items = storage.completed_count;
1119 const name = if (std.mem.indexOfScalar(u8, &storage.name, 0)) |end| storage.name[0..end] else &storage.name;
1120 const parent = serialized.parents[@intFromEnum(node_index)];
1121
1122 if (parent != .none) p: {
1123 if (@intFromEnum(parent) == 0 and serialized.storage[0].name[0] == 0) {
1124 break :p;
1125 }
1126 if (children[@intFromEnum(node_index)].sibling == .none) {
1127 i = appendTreeSymbol(.langle, buf, i);
1128 } else {
1129 i = appendTreeSymbol(.tee, buf, i);
1130 }
1131 }
1132
1133 const is_empty_root = @intFromEnum(node_index) == 0 and serialized.storage[0].name[0] == 0;
1134 if (!is_empty_root) {
1135 if (name.len != 0 or estimated_total > 0) {
1136 if (estimated_total > 0) {
1137 i += (std.fmt.bufPrint(buf[i..], "[{d}/{d}] ", .{ completed_items, estimated_total }) catch &.{}).len;
1138 } else if (completed_items != 0) {
1139 i += (std.fmt.bufPrint(buf[i..], "[{d}] ", .{completed_items}) catch &.{}).len;
1140 }
1141 if (name.len != 0) {
1142 i += (std.fmt.bufPrint(buf[i..], "{s}", .{name}) catch &.{}).len;
1143 }
1144 }
1145
1146 i = @min(global_progress.cols + start_i, i);
1147 buf[i] = '\n';
1148 i += 1;
1149 global_progress.accumulated_newline_count += 1;
1150 }
1151
1152 if (global_progress.withinRowLimit()) {
1153 if (children[@intFromEnum(node_index)].child.unwrap()) |child| {
1154 i = computeNode(buf, i, serialized, children, child);
1155 }
1156 }
1157
1158 if (global_progress.withinRowLimit()) {
1159 if (children[@intFromEnum(node_index)].sibling.unwrap()) |sibling| {
1160 i = computeNode(buf, i, serialized, children, sibling);
1161 }
1162 }
1163
1164 return i;
376}1165}
3771166
378pub fn unlock_stderr(p: *Progress) void {1167fn withinRowLimit(p: *Progress) bool {
379 std.debug.getStderrMutex().unlock();1168 // The +2 here is so that the PS1 is not scrolled off the top of the terminal.
380 p.update_mutex.unlock();1169 // one because we keep the cursor on the next line
1170 // one more to account for the PS1
1171 return p.accumulated_newline_count + 2 < p.rows;
381}1172}
3821173
383fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {1174fn write(buf: []const u8) anyerror!void {
384 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {1175 try global_progress.terminal.writeAll(buf);
385 const amt = written.len;1176 global_progress.written_newline_count = global_progress.accumulated_newline_count;
386 end.* += amt;1177}
387 self.columns_written += amt;1178
1179var remaining_write_trash_bytes: usize = 0;
1180
1181fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {
1182 // Byteswap if necessary to ensure little endian over the pipe. This is
1183 // needed because the parent or child process might be running in qemu.
1184 if (is_big_endian) for (serialized.storage) |*s| s.byteSwap();
1185
1186 assert(serialized.parents.len == serialized.storage.len);
1187 const serialized_len: u8 = @intCast(serialized.parents.len);
1188 const header = std.mem.asBytes(&serialized_len);
1189 const storage = std.mem.sliceAsBytes(serialized.storage);
1190 const parents = std.mem.sliceAsBytes(serialized.parents);
1191
1192 var vecs: [3]posix.iovec_const = .{
1193 .{ .base = header.ptr, .len = header.len },
1194 .{ .base = storage.ptr, .len = storage.len },
1195 .{ .base = parents.ptr, .len = parents.len },
1196 };
1197
1198 while (remaining_write_trash_bytes > 0) {
1199 // We do this in a separate write call to give a better chance for the
1200 // writev below to be in a single packet.
1201 const n = @min(parents.len, remaining_write_trash_bytes);
1202 if (posix.write(fd, parents[0..n])) |written| {
1203 remaining_write_trash_bytes -= written;
1204 continue;
1205 } else |err| switch (err) {
1206 error.WouldBlock => return,
1207 error.BrokenPipe => return error.BrokenPipe,
1208 else => |e| {
1209 std.log.debug("failed to send progress to parent process: {s}", .{@errorName(e)});
1210 return error.BrokenPipe;
1211 },
1212 }
1213 }
1214
1215 // If this write would block we do not want to keep trying, but we need to
1216 // know if a partial message was written.
1217 if (posix.writev(fd, &vecs)) |written| {
1218 const total = header.len + storage.len + parents.len;
1219 if (written < total) {
1220 remaining_write_trash_bytes = total - written;
1221 }
388 } else |err| switch (err) {1222 } else |err| switch (err) {
389 error.NoSpaceLeft => {1223 error.WouldBlock => {},
390 self.columns_written += self.output_buffer.len - end.*;1224 error.BrokenPipe => return error.BrokenPipe,
391 end.* = self.output_buffer.len;1225 else => |e| {
392 const suffix = "... ";1226 std.log.debug("failed to send progress to parent process: {s}", .{@errorName(e)});
393 @memcpy(self.output_buffer[self.output_buffer.len - suffix.len ..], suffix);1227 return error.BrokenPipe;
394 },1228 },
395 }1229 }
396}1230}
3971231
398test "basic functionality" {1232fn maybeUpdateSize(resize_flag: bool) void {
399 var disable = true;1233 if (!resize_flag) return;
400 _ = &disable;
401 if (disable) {
402 // This test is disabled because it uses time.sleep() and is therefore slow. It also
403 // prints bogus progress data to stderr.
404 return error.SkipZigTest;
405 }
406 var progress = Progress{};
407 const root_node = progress.start("", 100);
408 defer root_node.end();
409
410 const speed_factor = std.time.ns_per_ms;
411
412 const sub_task_names = [_][]const u8{
413 "reticulating splines",
414 "adjusting shoes",
415 "climbing towers",
416 "pouring juice",
417 };
418 var next_sub_task: usize = 0;
4191234
420 var i: usize = 0;1235 const fd = global_progress.terminal.handle;
421 while (i < 100) : (i += 1) {
422 var node = root_node.start(sub_task_names[next_sub_task], 5);
423 node.activate();
424 next_sub_task = (next_sub_task + 1) % sub_task_names.len;
4251236
426 node.completeOne();1237 if (is_windows) {
427 std.time.sleep(5 * speed_factor);1238 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
428 node.completeOne();
429 node.completeOne();
430 std.time.sleep(5 * speed_factor);
431 node.completeOne();
432 node.completeOne();
433 std.time.sleep(5 * speed_factor);
4341239
435 node.end();1240 if (windows.kernel32.GetConsoleScreenBufferInfo(fd, &info) != windows.FALSE) {
1241 // In the old Windows console, dwSize.Y is the line count of the
1242 // entire scrollback buffer, so we use this instead so that we
1243 // always get the size of the screen.
1244 const screen_height = info.srWindow.Bottom - info.srWindow.Top;
1245 global_progress.rows = @intCast(screen_height);
1246 global_progress.cols = @intCast(info.dwSize.X);
1247 } else {
1248 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
1249 global_progress.rows = 25;
1250 global_progress.cols = 80;
1251 }
1252 } else {
1253 var winsize: posix.winsize = .{
1254 .ws_row = 0,
1255 .ws_col = 0,
1256 .ws_xpixel = 0,
1257 .ws_ypixel = 0,
1258 };
4361259
437 std.time.sleep(5 * speed_factor);1260 const err = posix.system.ioctl(fd, posix.T.IOCGWINSZ, @intFromPtr(&winsize));
438 }1261 if (posix.errno(err) == .SUCCESS) {
439 {1262 global_progress.rows = winsize.ws_row;
440 var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", 0);1263 global_progress.cols = winsize.ws_col;
441 node.activate();1264 } else {
442 std.time.sleep(10 * speed_factor);1265 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
443 progress.refresh();1266 global_progress.rows = 25;
444 std.time.sleep(10 * speed_factor);1267 global_progress.cols = 80;
445 node.end();1268 }
446 }1269 }
447}1270}
1271
1272fn handleSigWinch(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.C) void {
1273 _ = info;
1274 _ = ctx_ptr;
1275 assert(sig == posix.SIG.WINCH);
1276 global_progress.redraw_event.set();
1277}
1278
1279const have_sigwinch = switch (builtin.os.tag) {
1280 .linux,
1281 .plan9,
1282 .solaris,
1283 .netbsd,
1284 .openbsd,
1285 .haiku,
1286 .macos,
1287 .ios,
1288 .watchos,
1289 .tvos,
1290 .visionos,
1291 .dragonfly,
1292 .freebsd,
1293 => true,
1294
1295 else => false,
1296};
1297
1298var stderr_mutex: std.Thread.Mutex = .{};
lib/std/debug.zig+24-9
...@@ -77,19 +77,28 @@ const PdbOrDwarf = union(enum) {...@@ -77,19 +77,28 @@ const PdbOrDwarf = union(enum) {
77 }77 }
78};78};
7979
80var stderr_mutex = std.Thread.Mutex{};80/// Allows the caller to freely write to stderr until `unlockStdErr` is called.
81///
82/// During the lock, any `std.Progress` information is cleared from the terminal.
83pub fn lockStdErr() void {
84 std.Progress.lockStdErr();
85}
86
87pub fn unlockStdErr() void {
88 std.Progress.unlockStdErr();
89}
8190
82/// Print to stderr, unbuffered, and silently returning on failure. Intended91/// Print to stderr, unbuffered, and silently returning on failure. Intended
83/// for use in "printf debugging." Use `std.log` functions for proper logging.92/// for use in "printf debugging." Use `std.log` functions for proper logging.
84pub fn print(comptime fmt: []const u8, args: anytype) void {93pub fn print(comptime fmt: []const u8, args: anytype) void {
85 stderr_mutex.lock();94 lockStdErr();
86 defer stderr_mutex.unlock();95 defer unlockStdErr();
87 const stderr = io.getStdErr().writer();96 const stderr = io.getStdErr().writer();
88 nosuspend stderr.print(fmt, args) catch return;97 nosuspend stderr.print(fmt, args) catch return;
89}98}
9099
91pub fn getStderrMutex() *std.Thread.Mutex {100pub fn getStderrMutex() *std.Thread.Mutex {
92 return &stderr_mutex;101 @compileError("deprecated. call std.debug.lockStdErr() and std.debug.unlockStdErr() instead which will integrate properly with std.Progress");
93}102}
94103
95/// TODO multithreaded awareness104/// TODO multithreaded awareness
...@@ -107,8 +116,8 @@ pub fn getSelfDebugInfo() !*DebugInfo {...@@ -107,8 +116,8 @@ pub fn getSelfDebugInfo() !*DebugInfo {
107/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.116/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.
108/// Obtains the stderr mutex while dumping.117/// Obtains the stderr mutex while dumping.
109pub fn dump_hex(bytes: []const u8) void {118pub fn dump_hex(bytes: []const u8) void {
110 stderr_mutex.lock();119 lockStdErr();
111 defer stderr_mutex.unlock();120 defer unlockStdErr();
112 dump_hex_fallible(bytes) catch {};121 dump_hex_fallible(bytes) catch {};
113}122}
114123
...@@ -2750,13 +2759,19 @@ pub const Trace = ConfigurableTrace(2, 4, builtin.mode == .Debug);...@@ -2750,13 +2759,19 @@ pub const Trace = ConfigurableTrace(2, 4, builtin.mode == .Debug);
27502759
2751pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize, comptime is_enabled: bool) type {2760pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize, comptime is_enabled: bool) type {
2752 return struct {2761 return struct {
2753 addrs: [actual_size][stack_frame_count]usize = undefined,2762 addrs: [actual_size][stack_frame_count]usize,
2754 notes: [actual_size][]const u8 = undefined,2763 notes: [actual_size][]const u8,
2755 index: Index = 0,2764 index: Index,
27562765
2757 const actual_size = if (enabled) size else 0;2766 const actual_size = if (enabled) size else 0;
2758 const Index = if (enabled) usize else u0;2767 const Index = if (enabled) usize else u0;
27592768
2769 pub const init: @This() = .{
2770 .addrs = undefined,
2771 .notes = undefined,
2772 .index = 0,
2773 };
2774
2760 pub const enabled = is_enabled;2775 pub const enabled = is_enabled;
27612776
2762 pub const add = if (enabled) addNoInline else addNoOp;2777 pub const add = if (enabled) addNoInline else addNoOp;
lib/std/fmt.zig+35-24
...@@ -9,7 +9,7 @@ const assert = std.debug.assert;...@@ -9,7 +9,7 @@ const assert = std.debug.assert;
9const mem = std.mem;9const mem = std.mem;
10const unicode = std.unicode;10const unicode = std.unicode;
11const meta = std.meta;11const meta = std.meta;
12const lossyCast = std.math.lossyCast;12const lossyCast = math.lossyCast;
13const expectFmt = std.testing.expectFmt;13const expectFmt = std.testing.expectFmt;
1414
15pub const default_max_depth = 3;15pub const default_max_depth = 3;
...@@ -1494,10 +1494,20 @@ pub fn Formatter(comptime format_fn: anytype) type {...@@ -1494,10 +1494,20 @@ pub fn Formatter(comptime format_fn: anytype) type {
1494/// Ignores '_' character in `buf`.1494/// Ignores '_' character in `buf`.
1495/// See also `parseUnsigned`.1495/// See also `parseUnsigned`.
1496pub fn parseInt(comptime T: type, buf: []const u8, base: u8) ParseIntError!T {1496pub fn parseInt(comptime T: type, buf: []const u8, base: u8) ParseIntError!T {
1497 return parseIntWithGenericCharacter(T, u8, buf, base);
1498}
1499
1500/// Like `parseInt`, but with a generic `Character` type.
1501pub fn parseIntWithGenericCharacter(
1502 comptime Result: type,
1503 comptime Character: type,
1504 buf: []const Character,
1505 base: u8,
1506) ParseIntError!Result {
1497 if (buf.len == 0) return error.InvalidCharacter;1507 if (buf.len == 0) return error.InvalidCharacter;
1498 if (buf[0] == '+') return parseWithSign(T, buf[1..], base, .pos);1508 if (buf[0] == '+') return parseIntWithSign(Result, Character, buf[1..], base, .pos);
1499 if (buf[0] == '-') return parseWithSign(T, buf[1..], base, .neg);1509 if (buf[0] == '-') return parseIntWithSign(Result, Character, buf[1..], base, .neg);
1500 return parseWithSign(T, buf, base, .pos);1510 return parseIntWithSign(Result, Character, buf, base, .pos);
1501}1511}
15021512
1503test parseInt {1513test parseInt {
...@@ -1560,12 +1570,13 @@ test parseInt {...@@ -1560,12 +1570,13 @@ test parseInt {
1560 try std.testing.expectEqual(@as(i5, -16), try std.fmt.parseInt(i5, "-10", 16));1570 try std.testing.expectEqual(@as(i5, -16), try std.fmt.parseInt(i5, "-10", 16));
1561}1571}
15621572
1563fn parseWithSign(1573fn parseIntWithSign(
1564 comptime T: type,1574 comptime Result: type,
1565 buf: []const u8,1575 comptime Character: type,
1576 buf: []const Character,
1566 base: u8,1577 base: u8,
1567 comptime sign: enum { pos, neg },1578 comptime sign: enum { pos, neg },
1568) ParseIntError!T {1579) ParseIntError!Result {
1569 if (buf.len == 0) return error.InvalidCharacter;1580 if (buf.len == 0) return error.InvalidCharacter;
15701581
1571 var buf_base = base;1582 var buf_base = base;
...@@ -1575,7 +1586,7 @@ fn parseWithSign(...@@ -1575,7 +1586,7 @@ fn parseWithSign(
1575 buf_base = 10;1586 buf_base = 10;
1576 // Detect the base by looking at buf prefix.1587 // Detect the base by looking at buf prefix.
1577 if (buf.len > 2 and buf[0] == '0') {1588 if (buf.len > 2 and buf[0] == '0') {
1578 switch (std.ascii.toLower(buf[1])) {1589 if (math.cast(u8, buf[1])) |c| switch (std.ascii.toLower(c)) {
1579 'b' => {1590 'b' => {
1580 buf_base = 2;1591 buf_base = 2;
1581 buf_start = buf[2..];1592 buf_start = buf[2..];
...@@ -1589,7 +1600,7 @@ fn parseWithSign(...@@ -1589,7 +1600,7 @@ fn parseWithSign(
1589 buf_start = buf[2..];1600 buf_start = buf[2..];
1590 },1601 },
1591 else => {},1602 else => {},
1592 }1603 };
1593 }1604 }
1594 }1605 }
15951606
...@@ -1598,33 +1609,33 @@ fn parseWithSign(...@@ -1598,33 +1609,33 @@ fn parseWithSign(
1598 .neg => math.sub,1609 .neg => math.sub,
1599 };1610 };
16001611
1601 // accumulate into U which is always 8 bits or larger. this prevents1612 // accumulate into Accumulate which is always 8 bits or larger. this prevents
1602 // `buf_base` from overflowing T.1613 // `buf_base` from overflowing Result.
1603 const info = @typeInfo(T);1614 const info = @typeInfo(Result);
1604 const U = std.meta.Int(info.Int.signedness, @max(8, info.Int.bits));1615 const Accumulate = std.meta.Int(info.Int.signedness, @max(8, info.Int.bits));
1605 var x: U = 0;1616 var accumulate: Accumulate = 0;
16061617
1607 if (buf_start[0] == '_' or buf_start[buf_start.len - 1] == '_') return error.InvalidCharacter;1618 if (buf_start[0] == '_' or buf_start[buf_start.len - 1] == '_') return error.InvalidCharacter;
16081619
1609 for (buf_start) |c| {1620 for (buf_start) |c| {
1610 if (c == '_') continue;1621 if (c == '_') continue;
1611 const digit = try charToDigit(c, buf_base);1622 const digit = try charToDigit(math.cast(u8, c) orelse return error.InvalidCharacter, buf_base);
1612 if (x != 0) {1623 if (accumulate != 0) {
1613 x = try math.mul(U, x, math.cast(U, buf_base) orelse return error.Overflow);1624 accumulate = try math.mul(Accumulate, accumulate, math.cast(Accumulate, buf_base) orelse return error.Overflow);
1614 } else if (sign == .neg) {1625 } else if (sign == .neg) {
1615 // The first digit of a negative number.1626 // The first digit of a negative number.
1616 // Consider parsing "-4" as an i3.1627 // Consider parsing "-4" as an i3.
1617 // This should work, but positive 4 overflows i3, so we can't cast the digit to T and subtract.1628 // This should work, but positive 4 overflows i3, so we can't cast the digit to T and subtract.
1618 x = math.cast(U, -@as(i8, @intCast(digit))) orelse return error.Overflow;1629 accumulate = math.cast(Accumulate, -@as(i8, @intCast(digit))) orelse return error.Overflow;
1619 continue;1630 continue;
1620 }1631 }
1621 x = try add(U, x, math.cast(U, digit) orelse return error.Overflow);1632 accumulate = try add(Accumulate, accumulate, math.cast(Accumulate, digit) orelse return error.Overflow);
1622 }1633 }
16231634
1624 return if (T == U)1635 return if (Result == Accumulate)
1625 x1636 accumulate
1626 else1637 else
1627 math.cast(T, x) orelse return error.Overflow;1638 math.cast(Result, accumulate) orelse return error.Overflow;
1628}1639}
16291640
1630/// Parses the string `buf` as unsigned representation in the specified base1641/// Parses the string `buf` as unsigned representation in the specified base
...@@ -1639,7 +1650,7 @@ fn parseWithSign(...@@ -1639,7 +1650,7 @@ fn parseWithSign(
1639/// Ignores '_' character in `buf`.1650/// Ignores '_' character in `buf`.
1640/// See also `parseInt`.1651/// See also `parseInt`.
1641pub fn parseUnsigned(comptime T: type, buf: []const u8, base: u8) ParseIntError!T {1652pub fn parseUnsigned(comptime T: type, buf: []const u8, base: u8) ParseIntError!T {
1642 return parseWithSign(T, buf, base, .pos);1653 return parseIntWithSign(T, u8, buf, base, .pos);
1643}1654}
16441655
1645test parseUnsigned {1656test parseUnsigned {
lib/std/io/tty.zig+1-1
...@@ -24,7 +24,7 @@ pub fn detectConfig(file: File) Config {...@@ -24,7 +24,7 @@ pub fn detectConfig(file: File) Config {
2424
25 if (native_os == .windows and file.isTty()) {25 if (native_os == .windows and file.isTty()) {
26 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;26 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
27 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {27 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
28 return if (force_color == true) .escape_codes else .no_color;28 return if (force_color == true) .escape_codes else .no_color;
29 }29 }
30 return .{ .windows_api = .{30 return .{ .windows_api = .{
lib/std/json/dynamic.zig+2-2
...@@ -52,8 +52,8 @@ pub const Value = union(enum) {...@@ -52,8 +52,8 @@ pub const Value = union(enum) {
52 }52 }
5353
54 pub fn dump(self: Value) void {54 pub fn dump(self: Value) void {
55 std.debug.getStderrMutex().lock();55 std.debug.lockStdErr();
56 defer std.debug.getStderrMutex().unlock();56 defer std.debug.unlockStdErr();
5757
58 const stderr = std.io.getStdErr().writer();58 const stderr = std.io.getStdErr().writer();
59 stringify(self, .{}, stderr) catch return;59 stringify(self, .{}, stderr) catch return;
lib/std/log.zig+4-4
...@@ -45,8 +45,8 @@...@@ -45,8 +45,8 @@
45//! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix;45//! const prefix = "[" ++ comptime level.asText() ++ "] " ++ scope_prefix;
46//!46//!
47//! // Print the message to stderr, silently ignoring any errors47//! // Print the message to stderr, silently ignoring any errors
48//! std.debug.getStderrMutex().lock();48//! std.debug.lockStdErr();
49//! defer std.debug.getStderrMutex().unlock();49//! defer std.debug.unlockStdErr();
50//! const stderr = std.io.getStdErr().writer();50//! const stderr = std.io.getStdErr().writer();
51//! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;51//! nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
52//! }52//! }
...@@ -152,8 +152,8 @@ pub fn defaultLog(...@@ -152,8 +152,8 @@ pub fn defaultLog(
152 var bw = std.io.bufferedWriter(stderr);152 var bw = std.io.bufferedWriter(stderr);
153 const writer = bw.writer();153 const writer = bw.writer();
154154
155 std.debug.getStderrMutex().lock();155 std.debug.lockStdErr();
156 defer std.debug.getStderrMutex().unlock();156 defer std.debug.unlockStdErr();
157 nosuspend {157 nosuspend {
158 writer.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;158 writer.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
159 bw.flush() catch return;159 bw.flush() catch return;
lib/std/os/windows/kernel32.zig+9
...@@ -175,6 +175,15 @@ pub extern "kernel32" fn FillConsoleOutputCharacterW(hConsoleOutput: HANDLE, cCh...@@ -175,6 +175,15 @@ pub extern "kernel32" fn FillConsoleOutputCharacterW(hConsoleOutput: HANDLE, cCh
175pub extern "kernel32" fn FillConsoleOutputAttribute(hConsoleOutput: HANDLE, wAttribute: WORD, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfAttrsWritten: *DWORD) callconv(WINAPI) BOOL;175pub extern "kernel32" fn FillConsoleOutputAttribute(hConsoleOutput: HANDLE, wAttribute: WORD, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfAttrsWritten: *DWORD) callconv(WINAPI) BOOL;
176pub extern "kernel32" fn SetConsoleCursorPosition(hConsoleOutput: HANDLE, dwCursorPosition: COORD) callconv(WINAPI) BOOL;176pub extern "kernel32" fn SetConsoleCursorPosition(hConsoleOutput: HANDLE, dwCursorPosition: COORD) callconv(WINAPI) BOOL;
177177
178pub extern "kernel32" fn WriteConsoleW(hConsoleOutput: HANDLE, lpBuffer: [*]const u16, nNumberOfCharsToWrite: DWORD, lpNumberOfCharsWritten: ?*DWORD, lpReserved: ?LPVOID) callconv(WINAPI) BOOL;
179pub extern "kernel32" fn ReadConsoleOutputCharacterW(
180 hConsoleOutput: windows.HANDLE,
181 lpCharacter: [*]u16,
182 nLength: windows.DWORD,
183 dwReadCoord: windows.COORD,
184 lpNumberOfCharsRead: *windows.DWORD,
185) callconv(windows.WINAPI) windows.BOOL;
186
178pub extern "kernel32" fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) callconv(WINAPI) DWORD;187pub extern "kernel32" fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) callconv(WINAPI) DWORD;
179188
180pub extern "kernel32" fn GetCurrentThread() callconv(WINAPI) HANDLE;189pub extern "kernel32" fn GetCurrentThread() callconv(WINAPI) HANDLE;
lib/std/process.zig+151-11
...@@ -431,6 +431,26 @@ pub fn hasEnvVarConstant(comptime key: []const u8) bool {...@@ -431,6 +431,26 @@ pub fn hasEnvVarConstant(comptime key: []const u8) bool {
431 }431 }
432}432}
433433
434pub const ParseEnvVarIntError = std.fmt.ParseIntError || error{EnvironmentVariableNotFound};
435
436/// Parses an environment variable as an integer.
437///
438/// Since the key is comptime-known, no allocation is needed.
439///
440/// On Windows, `key` must be valid UTF-8.
441pub fn parseEnvVarInt(comptime key: []const u8, comptime I: type, base: u8) ParseEnvVarIntError!I {
442 if (native_os == .windows) {
443 const key_w = comptime std.unicode.utf8ToUtf16LeStringLiteral(key);
444 const text = getenvW(key_w) orelse return error.EnvironmentVariableNotFound;
445 return std.fmt.parseIntWithGenericCharacter(I, u16, text, base);
446 } else if (native_os == .wasi and !builtin.link_libc) {
447 @compileError("parseEnvVarInt is not supported for WASI without libc");
448 } else {
449 const text = posix.getenv(key) orelse return error.EnvironmentVariableNotFound;
450 return std.fmt.parseInt(I, text, base);
451 }
452}
453
434pub const HasEnvVarError = error{454pub const HasEnvVarError = error{
435 OutOfMemory,455 OutOfMemory,
436456
...@@ -1740,6 +1760,7 @@ pub fn cleanExit() void {...@@ -1740,6 +1760,7 @@ pub fn cleanExit() void {
1740 if (builtin.mode == .Debug) {1760 if (builtin.mode == .Debug) {
1741 return;1761 return;
1742 } else {1762 } else {
1763 std.debug.lockStdErr();
1743 exit(0);1764 exit(0);
1744 }1765 }
1745}1766}
...@@ -1790,24 +1811,143 @@ test raiseFileDescriptorLimit {...@@ -1790,24 +1811,143 @@ test raiseFileDescriptorLimit {
1790 raiseFileDescriptorLimit();1811 raiseFileDescriptorLimit();
1791}1812}
17921813
1793pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 {1814pub const CreateEnvironOptions = struct {
1794 const envp_count = env_map.count();1815 /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified.
1816 /// If non-null, negative means to remove the environment variable, and >= 0
1817 /// means to provide it with the given integer.
1818 zig_progress_fd: ?i32 = null,
1819};
1820
1821/// Creates a null-deliminated environment variable block in the format
1822/// expected by POSIX, from a hash map plus options.
1823pub fn createEnvironFromMap(
1824 arena: Allocator,
1825 map: *const EnvMap,
1826 options: CreateEnvironOptions,
1827) Allocator.Error![:null]?[*:0]u8 {
1828 const ZigProgressAction = enum { nothing, edit, delete, add };
1829 const zig_progress_action: ZigProgressAction = a: {
1830 const fd = options.zig_progress_fd orelse break :a .nothing;
1831 const contains = map.get("ZIG_PROGRESS") != null;
1832 if (fd >= 0) {
1833 break :a if (contains) .edit else .add;
1834 } else {
1835 if (contains) break :a .delete;
1836 }
1837 break :a .nothing;
1838 };
1839
1840 const envp_count: usize = c: {
1841 var count: usize = map.count();
1842 switch (zig_progress_action) {
1843 .add => count += 1,
1844 .delete => count -= 1,
1845 .nothing, .edit => {},
1846 }
1847 break :c count;
1848 };
1849
1795 const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);1850 const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);
1851 var i: usize = 0;
1852
1853 if (zig_progress_action == .add) {
1854 envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?});
1855 i += 1;
1856 }
1857
1796 {1858 {
1797 var it = env_map.iterator();1859 var it = map.iterator();
1798 var i: usize = 0;1860 while (it.next()) |pair| {
1799 while (it.next()) |pair| : (i += 1) {1861 if (mem.eql(u8, pair.key_ptr.*, "ZIG_PROGRESS")) switch (zig_progress_action) {
1800 const env_buf = try arena.allocSentinel(u8, pair.key_ptr.len + pair.value_ptr.len + 1, 0);1862 .add => unreachable,
1801 @memcpy(env_buf[0..pair.key_ptr.len], pair.key_ptr.*);1863 .delete => continue,
1802 env_buf[pair.key_ptr.len] = '=';1864 .edit => {
1803 @memcpy(env_buf[pair.key_ptr.len + 1 ..][0..pair.value_ptr.len], pair.value_ptr.*);1865 envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={d}", .{
1804 envp_buf[i] = env_buf.ptr;1866 pair.key_ptr.*, options.zig_progress_fd.?,
1867 });
1868 i += 1;
1869 continue;
1870 },
1871 .nothing => {},
1872 };
1873
1874 envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* });
1875 i += 1;
1876 }
1877 }
1878
1879 assert(i == envp_count);
1880 return envp_buf;
1881}
1882
1883/// Creates a null-deliminated environment variable block in the format
1884/// expected by POSIX, from a hash map plus options.
1885pub fn createEnvironFromExisting(
1886 arena: Allocator,
1887 existing: [*:null]const ?[*:0]const u8,
1888 options: CreateEnvironOptions,
1889) Allocator.Error![:null]?[*:0]u8 {
1890 const existing_count, const contains_zig_progress = c: {
1891 var count: usize = 0;
1892 var contains = false;
1893 while (existing[count]) |line| : (count += 1) {
1894 contains = contains or mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS");
1895 }
1896 break :c .{ count, contains };
1897 };
1898 const ZigProgressAction = enum { nothing, edit, delete, add };
1899 const zig_progress_action: ZigProgressAction = a: {
1900 const fd = options.zig_progress_fd orelse break :a .nothing;
1901 if (fd >= 0) {
1902 break :a if (contains_zig_progress) .edit else .add;
1903 } else {
1904 if (contains_zig_progress) break :a .delete;
1905 }
1906 break :a .nothing;
1907 };
1908
1909 const envp_count: usize = c: {
1910 var count: usize = existing_count;
1911 switch (zig_progress_action) {
1912 .add => count += 1,
1913 .delete => count -= 1,
1914 .nothing, .edit => {},
1805 }1915 }
1806 assert(i == envp_count);1916 break :c count;
1917 };
1918
1919 const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null);
1920 var i: usize = 0;
1921 var existing_index: usize = 0;
1922
1923 if (zig_progress_action == .add) {
1924 envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?});
1925 i += 1;
1926 }
1927
1928 while (existing[existing_index]) |line| : (existing_index += 1) {
1929 if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) {
1930 .add => unreachable,
1931 .delete => continue,
1932 .edit => {
1933 envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?});
1934 i += 1;
1935 continue;
1936 },
1937 .nothing => {},
1938 };
1939 envp_buf[i] = try arena.dupeZ(u8, mem.span(line));
1940 i += 1;
1807 }1941 }
1942
1943 assert(i == envp_count);
1808 return envp_buf;1944 return envp_buf;
1809}1945}
18101946
1947pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) Allocator.Error![:null]?[*:0]u8 {
1948 return createEnvironFromMap(arena, env_map, .{});
1949}
1950
1811test createNullDelimitedEnvMap {1951test createNullDelimitedEnvMap {
1812 const allocator = testing.allocator;1952 const allocator = testing.allocator;
1813 var envmap = EnvMap.init(allocator);1953 var envmap = EnvMap.init(allocator);
lib/std/process/Child.zig+64-25
...@@ -12,6 +12,7 @@ const EnvMap = std.process.EnvMap;...@@ -12,6 +12,7 @@ const EnvMap = std.process.EnvMap;
12const maxInt = std.math.maxInt;12const maxInt = std.math.maxInt;
13const assert = std.debug.assert;13const assert = std.debug.assert;
14const native_os = builtin.os.tag;14const native_os = builtin.os.tag;
15const Allocator = std.mem.Allocator;
15const ChildProcess = @This();16const ChildProcess = @This();
1617
17pub const Id = switch (native_os) {18pub const Id = switch (native_os) {
...@@ -92,6 +93,16 @@ request_resource_usage_statistics: bool = false,...@@ -92,6 +93,16 @@ request_resource_usage_statistics: bool = false,
92/// `spawn`.93/// `spawn`.
93resource_usage_statistics: ResourceUsageStatistics = .{},94resource_usage_statistics: ResourceUsageStatistics = .{},
9495
96/// When populated, a pipe will be created for the child process to
97/// communicate progress back to the parent. The file descriptor of the
98/// write end of the pipe will be specified in the `ZIG_PROGRESS`
99/// environment variable inside the child process. The progress reported by
100/// the child will be attached to this progress node in the parent process.
101///
102/// The child's progress tree will be grafted into the parent's progress tree,
103/// by substituting this node with the child's root node.
104progress_node: std.Progress.Node = .{ .index = .none },
105
95pub const ResourceUsageStatistics = struct {106pub const ResourceUsageStatistics = struct {
96 rusage: @TypeOf(rusage_init) = rusage_init,107 rusage: @TypeOf(rusage_init) = rusage_init,
97108
...@@ -205,9 +216,9 @@ pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess {...@@ -205,9 +216,9 @@ pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess {
205 .stdin = null,216 .stdin = null,
206 .stdout = null,217 .stdout = null,
207 .stderr = null,218 .stderr = null,
208 .stdin_behavior = StdIo.Inherit,219 .stdin_behavior = .Inherit,
209 .stdout_behavior = StdIo.Inherit,220 .stdout_behavior = .Inherit,
210 .stderr_behavior = StdIo.Inherit,221 .stderr_behavior = .Inherit,
211 .expand_arg0 = .no_expand,222 .expand_arg0 = .no_expand,
212 };223 };
213}224}
...@@ -538,22 +549,22 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {...@@ -538,22 +549,22 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
538 // turns out, we `dup2` everything anyway, so there's no need!549 // turns out, we `dup2` everything anyway, so there's no need!
539 const pipe_flags: posix.O = .{ .CLOEXEC = true };550 const pipe_flags: posix.O = .{ .CLOEXEC = true };
540551
541 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;552 const stdin_pipe = if (self.stdin_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined;
542 errdefer if (self.stdin_behavior == StdIo.Pipe) {553 errdefer if (self.stdin_behavior == .Pipe) {
543 destroyPipe(stdin_pipe);554 destroyPipe(stdin_pipe);
544 };555 };
545556
546 const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;557 const stdout_pipe = if (self.stdout_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined;
547 errdefer if (self.stdout_behavior == StdIo.Pipe) {558 errdefer if (self.stdout_behavior == .Pipe) {
548 destroyPipe(stdout_pipe);559 destroyPipe(stdout_pipe);
549 };560 };
550561
551 const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;562 const stderr_pipe = if (self.stderr_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined;
552 errdefer if (self.stderr_behavior == StdIo.Pipe) {563 errdefer if (self.stderr_behavior == .Pipe) {
553 destroyPipe(stderr_pipe);564 destroyPipe(stderr_pipe);
554 };565 };
555566
556 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);567 const any_ignore = (self.stdin_behavior == .Ignore or self.stdout_behavior == .Ignore or self.stderr_behavior == .Ignore);
557 const dev_null_fd = if (any_ignore)568 const dev_null_fd = if (any_ignore)
558 posix.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) {569 posix.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) {
559 error.PathAlreadyExists => unreachable,570 error.PathAlreadyExists => unreachable,
...@@ -572,6 +583,16 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {...@@ -572,6 +583,16 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
572 if (any_ignore) posix.close(dev_null_fd);583 if (any_ignore) posix.close(dev_null_fd);
573 }584 }
574585
586 const prog_pipe: [2]posix.fd_t = p: {
587 if (self.progress_node.index == .none) {
588 break :p .{ -1, -1 };
589 } else {
590 // We use CLOEXEC for the same reason as in `pipe_flags`.
591 break :p try posix.pipe2(.{ .NONBLOCK = true, .CLOEXEC = true });
592 }
593 };
594 errdefer destroyPipe(prog_pipe);
595
575 var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);596 var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
576 defer arena_allocator.deinit();597 defer arena_allocator.deinit();
577 const arena = arena_allocator.allocator();598 const arena = arena_allocator.allocator();
...@@ -588,16 +609,25 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {...@@ -588,16 +609,25 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
588 const argv_buf = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null);609 const argv_buf = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null);
589 for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;610 for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
590611
591 const envp = m: {612 const prog_fileno = 3;
613 comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno);
614
615 const envp: [*:null]const ?[*:0]const u8 = m: {
616 const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno;
592 if (self.env_map) |env_map| {617 if (self.env_map) |env_map| {
593 const envp_buf = try process.createNullDelimitedEnvMap(arena, env_map);618 break :m (try process.createEnvironFromMap(arena, env_map, .{
594 break :m envp_buf.ptr;619 .zig_progress_fd = prog_fd,
620 })).ptr;
595 } else if (builtin.link_libc) {621 } else if (builtin.link_libc) {
596 break :m std.c.environ;622 break :m (try process.createEnvironFromExisting(arena, std.c.environ, .{
623 .zig_progress_fd = prog_fd,
624 })).ptr;
597 } else if (builtin.output_mode == .Exe) {625 } else if (builtin.output_mode == .Exe) {
598 // Then we have Zig start code and this works.626 // Then we have Zig start code and this works.
599 // TODO type-safety for null-termination of `os.environ`.627 // TODO type-safety for null-termination of `os.environ`.
600 break :m @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr));628 break :m (try process.createEnvironFromExisting(arena, @ptrCast(std.os.environ.ptr), .{
629 .zig_progress_fd = prog_fd,
630 })).ptr;
601 } else {631 } else {
602 // TODO come up with a solution for this.632 // TODO come up with a solution for this.
603 @compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process");633 @compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process");
...@@ -631,6 +661,10 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {...@@ -631,6 +661,10 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
631 posix.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);661 posix.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
632 }662 }
633663
664 // Must happen after fchdir above, the cwd file descriptor might be
665 // equal to prog_fileno and be clobbered by this dup2 call.
666 if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkChildErrReport(err_pipe[1], err);
667
634 if (self.gid) |gid| {668 if (self.gid) |gid| {
635 posix.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err);669 posix.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err);
636 }670 }
...@@ -648,18 +682,18 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {...@@ -648,18 +682,18 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
648682
649 // we are the parent683 // we are the parent
650 const pid: i32 = @intCast(pid_result);684 const pid: i32 = @intCast(pid_result);
651 if (self.stdin_behavior == StdIo.Pipe) {685 if (self.stdin_behavior == .Pipe) {
652 self.stdin = File{ .handle = stdin_pipe[1] };686 self.stdin = .{ .handle = stdin_pipe[1] };
653 } else {687 } else {
654 self.stdin = null;688 self.stdin = null;
655 }689 }
656 if (self.stdout_behavior == StdIo.Pipe) {690 if (self.stdout_behavior == .Pipe) {
657 self.stdout = File{ .handle = stdout_pipe[0] };691 self.stdout = .{ .handle = stdout_pipe[0] };
658 } else {692 } else {
659 self.stdout = null;693 self.stdout = null;
660 }694 }
661 if (self.stderr_behavior == StdIo.Pipe) {695 if (self.stderr_behavior == .Pipe) {
662 self.stderr = File{ .handle = stderr_pipe[0] };696 self.stderr = .{ .handle = stderr_pipe[0] };
663 } else {697 } else {
664 self.stderr = null;698 self.stderr = null;
665 }699 }
...@@ -668,15 +702,20 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {...@@ -668,15 +702,20 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
668 self.err_pipe = err_pipe;702 self.err_pipe = err_pipe;
669 self.term = null;703 self.term = null;
670704
671 if (self.stdin_behavior == StdIo.Pipe) {705 if (self.stdin_behavior == .Pipe) {
672 posix.close(stdin_pipe[0]);706 posix.close(stdin_pipe[0]);
673 }707 }
674 if (self.stdout_behavior == StdIo.Pipe) {708 if (self.stdout_behavior == .Pipe) {
675 posix.close(stdout_pipe[1]);709 posix.close(stdout_pipe[1]);
676 }710 }
677 if (self.stderr_behavior == StdIo.Pipe) {711 if (self.stderr_behavior == .Pipe) {
678 posix.close(stderr_pipe[1]);712 posix.close(stderr_pipe[1]);
679 }713 }
714
715 if (prog_pipe[1] != -1) {
716 posix.close(prog_pipe[1]);
717 }
718 self.progress_node.setIpcFd(prog_pipe[0]);
680}719}
681720
682fn spawnWindows(self: *ChildProcess) SpawnError!void {721fn spawnWindows(self: *ChildProcess) SpawnError!void {
...@@ -962,7 +1001,7 @@ fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !...@@ -962,7 +1001,7 @@ fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !
962}1001}
9631002
964fn destroyPipe(pipe: [2]posix.fd_t) void {1003fn destroyPipe(pipe: [2]posix.fd_t) void {
965 posix.close(pipe[0]);1004 if (pipe[0] != -1) posix.close(pipe[0]);
966 if (pipe[0] != pipe[1]) posix.close(pipe[1]);1005 if (pipe[0] != pipe[1]) posix.close(pipe[1]);
967}1006}
9681007
lib/std/zig.zig+1-1
...@@ -718,7 +718,7 @@ pub const LazySrcLoc = union(enum) {...@@ -718,7 +718,7 @@ pub const LazySrcLoc = union(enum) {
718 /// where in semantic analysis the value got set.718 /// where in semantic analysis the value got set.
719 pub const TracedOffset = struct {719 pub const TracedOffset = struct {
720 x: i32,720 x: i32,
721 trace: std.debug.Trace = .{},721 trace: std.debug.Trace = std.debug.Trace.init,
722722
723 const want_tracing = false;723 const want_tracing = false;
724 };724 };
lib/std/zig/ErrorBundle.zig+2-2
...@@ -155,8 +155,8 @@ pub const RenderOptions = struct {...@@ -155,8 +155,8 @@ pub const RenderOptions = struct {
155};155};
156156
157pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {157pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
158 std.debug.getStderrMutex().lock();158 std.debug.lockStdErr();
159 defer std.debug.getStderrMutex().unlock();159 defer std.debug.unlockStdErr();
160 const stderr = std.io.getStdErr();160 const stderr = std.io.getStdErr();
161 return renderToWriter(eb, options, stderr.writer()) catch return;161 return renderToWriter(eb, options, stderr.writer()) catch return;
162}162}
lib/std/zig/Server.zig-2
...@@ -14,8 +14,6 @@ pub const Message = struct {...@@ -14,8 +14,6 @@ pub const Message = struct {
14 zig_version,14 zig_version,
15 /// Body is an ErrorBundle.15 /// Body is an ErrorBundle.
16 error_bundle,16 error_bundle,
17 /// Body is a UTF-8 string.
18 progress,
19 /// Body is a EmitBinPath.17 /// Body is a EmitBinPath.
20 emit_bin_path,18 emit_bin_path,
21 /// Body is a TestMetadata19 /// Body is a TestMetadata
src/Compilation.zig+44-72
...@@ -1273,8 +1273,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil...@@ -1273,8 +1273,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1273 if (options.verbose_llvm_cpu_features) {1273 if (options.verbose_llvm_cpu_features) {
1274 if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: {1274 if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: {
1275 const target = options.root_mod.resolved_target.result;1275 const target = options.root_mod.resolved_target.result;
1276 std.debug.getStderrMutex().lock();1276 std.debug.lockStdErr();
1277 defer std.debug.getStderrMutex().unlock();1277 defer std.debug.unlockStdErr();
1278 const stderr = std.io.getStdErr().writer();1278 const stderr = std.io.getStdErr().writer();
1279 nosuspend {1279 nosuspend {
1280 stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print;1280 stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print;
...@@ -1934,7 +1934,7 @@ pub fn getTarget(self: Compilation) Target {...@@ -1934,7 +1934,7 @@ pub fn getTarget(self: Compilation) Target {
1934/// Only legal to call when cache mode is incremental and a link file is present.1934/// Only legal to call when cache mode is incremental and a link file is present.
1935pub fn hotCodeSwap(1935pub fn hotCodeSwap(
1936 comp: *Compilation,1936 comp: *Compilation,
1937 prog_node: *std.Progress.Node,1937 prog_node: std.Progress.Node,
1938 pid: std.process.Child.Id,1938 pid: std.process.Child.Id,
1939) !void {1939) !void {
1940 const lf = comp.bin_file.?;1940 const lf = comp.bin_file.?;
...@@ -1966,7 +1966,7 @@ fn cleanupAfterUpdate(comp: *Compilation) void {...@@ -1966,7 +1966,7 @@ fn cleanupAfterUpdate(comp: *Compilation) void {
1966}1966}
19671967
1968/// Detect changes to source files, perform semantic analysis, and update the output files.1968/// Detect changes to source files, perform semantic analysis, and update the output files.
1969pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void {1969pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
1970 const tracy_trace = trace(@src());1970 const tracy_trace = trace(@src());
1971 defer tracy_trace.end();1971 defer tracy_trace.end();
19721972
...@@ -2256,7 +2256,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void...@@ -2256,7 +2256,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
2256 }2256 }
2257}2257}
22582258
2259fn flush(comp: *Compilation, arena: Allocator, prog_node: *std.Progress.Node) !void {2259fn flush(comp: *Compilation, arena: Allocator, prog_node: std.Progress.Node) !void {
2260 if (comp.bin_file) |lf| {2260 if (comp.bin_file) |lf| {
2261 // This is needed before reading the error flags.2261 // This is needed before reading the error flags.
2262 lf.flush(arena, prog_node) catch |err| switch (err) {2262 lf.flush(arena, prog_node) catch |err| switch (err) {
...@@ -2566,13 +2566,11 @@ pub fn emitLlvmObject(...@@ -2566,13 +2566,11 @@ pub fn emitLlvmObject(
2566 default_emit: Emit,2566 default_emit: Emit,
2567 bin_emit_loc: ?EmitLoc,2567 bin_emit_loc: ?EmitLoc,
2568 llvm_object: *LlvmObject,2568 llvm_object: *LlvmObject,
2569 prog_node: *std.Progress.Node,2569 prog_node: std.Progress.Node,
2570) !void {2570) !void {
2571 if (build_options.only_c) @compileError("unreachable");2571 if (build_options.only_c) @compileError("unreachable");
25722572
2573 var sub_prog_node = prog_node.start("LLVM Emit Object", 0);2573 const sub_prog_node = prog_node.start("LLVM Emit Object", 0);
2574 sub_prog_node.activate();
2575 sub_prog_node.context.refresh();
2576 defer sub_prog_node.end();2574 defer sub_prog_node.end();
25772575
2578 try llvm_object.emit(.{2576 try llvm_object.emit(.{
...@@ -3249,32 +3247,20 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void {...@@ -3249,32 +3247,20 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void {
32493247
3250pub fn performAllTheWork(3248pub fn performAllTheWork(
3251 comp: *Compilation,3249 comp: *Compilation,
3252 main_progress_node: *std.Progress.Node,3250 main_progress_node: std.Progress.Node,
3253) error{ TimerUnsupported, OutOfMemory }!void {3251) error{ TimerUnsupported, OutOfMemory }!void {
3254 // Here we queue up all the AstGen tasks first, followed by C object compilation.3252 // Here we queue up all the AstGen tasks first, followed by C object compilation.
3255 // We wait until the AstGen tasks are all completed before proceeding to the3253 // We wait until the AstGen tasks are all completed before proceeding to the
3256 // (at least for now) single-threaded main work queue. However, C object compilation3254 // (at least for now) single-threaded main work queue. However, C object compilation
3257 // only needs to be finished by the end of this function.3255 // only needs to be finished by the end of this function.
32583256
3259 var zir_prog_node = main_progress_node.start("AST Lowering", 0);
3260 defer zir_prog_node.end();
3261
3262 var wasm_prog_node = main_progress_node.start("Compile Autodocs", 0);
3263 defer wasm_prog_node.end();
3264
3265 var c_obj_prog_node = main_progress_node.start("Compile C Objects", comp.c_source_files.len);
3266 defer c_obj_prog_node.end();
3267
3268 var win32_resource_prog_node = main_progress_node.start("Compile Win32 Resources", comp.rc_source_files.len);
3269 defer win32_resource_prog_node.end();
3270
3271 comp.work_queue_wait_group.reset();3257 comp.work_queue_wait_group.reset();
3272 defer comp.work_queue_wait_group.wait();3258 defer comp.work_queue_wait_group.wait();
32733259
3274 if (!build_options.only_c and !build_options.only_core_functionality) {3260 if (!build_options.only_c and !build_options.only_core_functionality) {
3275 if (comp.docs_emit != null) {3261 if (comp.docs_emit != null) {
3276 comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerDocsCopy, .{comp});3262 comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerDocsCopy, .{comp});
3277 comp.work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, &wasm_prog_node });3263 comp.work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
3278 }3264 }
3279 }3265 }
32803266
...@@ -3282,6 +3268,9 @@ pub fn performAllTheWork(...@@ -3282,6 +3268,9 @@ pub fn performAllTheWork(
3282 const astgen_frame = tracy.namedFrame("astgen");3268 const astgen_frame = tracy.namedFrame("astgen");
3283 defer astgen_frame.end();3269 defer astgen_frame.end();
32843270
3271 const zir_prog_node = main_progress_node.start("AST Lowering", 0);
3272 defer zir_prog_node.end();
3273
3285 comp.astgen_wait_group.reset();3274 comp.astgen_wait_group.reset();
3286 defer comp.astgen_wait_group.wait();3275 defer comp.astgen_wait_group.wait();
32873276
...@@ -3313,7 +3302,7 @@ pub fn performAllTheWork(...@@ -3313,7 +3302,7 @@ pub fn performAllTheWork(
33133302
3314 while (comp.astgen_work_queue.readItem()) |file| {3303 while (comp.astgen_work_queue.readItem()) |file| {
3315 comp.thread_pool.spawnWg(&comp.astgen_wait_group, workerAstGenFile, .{3304 comp.thread_pool.spawnWg(&comp.astgen_wait_group, workerAstGenFile, .{
3316 comp, file, &zir_prog_node, &comp.astgen_wait_group, .root,3305 comp, file, zir_prog_node, &comp.astgen_wait_group, .root,
3317 });3306 });
3318 }3307 }
33193308
...@@ -3325,14 +3314,14 @@ pub fn performAllTheWork(...@@ -3325,14 +3314,14 @@ pub fn performAllTheWork(
33253314
3326 while (comp.c_object_work_queue.readItem()) |c_object| {3315 while (comp.c_object_work_queue.readItem()) |c_object| {
3327 comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateCObject, .{3316 comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateCObject, .{
3328 comp, c_object, &c_obj_prog_node,3317 comp, c_object, main_progress_node,
3329 });3318 });
3330 }3319 }
33313320
3332 if (!build_options.only_core_functionality) {3321 if (!build_options.only_core_functionality) {
3333 while (comp.win32_resource_work_queue.readItem()) |win32_resource| {3322 while (comp.win32_resource_work_queue.readItem()) |win32_resource| {
3334 comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateWin32Resource, .{3323 comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateWin32Resource, .{
3335 comp, win32_resource, &win32_resource_prog_node,3324 comp, win32_resource, main_progress_node,
3336 });3325 });
3337 }3326 }
3338 }3327 }
...@@ -3342,11 +3331,13 @@ pub fn performAllTheWork(...@@ -3342,11 +3331,13 @@ pub fn performAllTheWork(
3342 try reportMultiModuleErrors(mod);3331 try reportMultiModuleErrors(mod);
3343 try mod.flushRetryableFailures();3332 try mod.flushRetryableFailures();
3344 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);3333 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
3345 mod.sema_prog_node.activate();3334 mod.codegen_prog_node = main_progress_node.start("Code Generation", 0);
3346 }3335 }
3347 defer if (comp.module) |mod| {3336 defer if (comp.module) |mod| {
3348 mod.sema_prog_node.end();3337 mod.sema_prog_node.end();
3349 mod.sema_prog_node = undefined;3338 mod.sema_prog_node = undefined;
3339 mod.codegen_prog_node.end();
3340 mod.codegen_prog_node = undefined;
3350 };3341 };
33513342
3352 while (true) {3343 while (true) {
...@@ -3379,7 +3370,7 @@ pub fn performAllTheWork(...@@ -3379,7 +3370,7 @@ pub fn performAllTheWork(
3379 }3370 }
3380}3371}
33813372
3382fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void {3373fn processOneJob(comp: *Compilation, job: Job, prog_node: std.Progress.Node) !void {
3383 switch (job) {3374 switch (job) {
3384 .codegen_decl => |decl_index| {3375 .codegen_decl => |decl_index| {
3385 const module = comp.module.?;3376 const module = comp.module.?;
...@@ -3803,7 +3794,10 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,...@@ -3803,7 +3794,10 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8,
3803 }3794 }
3804}3795}
38053796
3806fn workerDocsWasm(comp: *Compilation, prog_node: *std.Progress.Node) void {3797fn workerDocsWasm(comp: *Compilation, parent_prog_node: std.Progress.Node) void {
3798 const prog_node = parent_prog_node.start("Compile Autodocs", 0);
3799 defer prog_node.end();
3800
3807 workerDocsWasmFallible(comp, prog_node) catch |err| {3801 workerDocsWasmFallible(comp, prog_node) catch |err| {
3808 comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {s}", .{3802 comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {s}", .{
3809 @errorName(err),3803 @errorName(err),
...@@ -3811,7 +3805,7 @@ fn workerDocsWasm(comp: *Compilation, prog_node: *std.Progress.Node) void {...@@ -3811,7 +3805,7 @@ fn workerDocsWasm(comp: *Compilation, prog_node: *std.Progress.Node) void {
3811 };3805 };
3812}3806}
38133807
3814fn workerDocsWasmFallible(comp: *Compilation, prog_node: *std.Progress.Node) anyerror!void {3808fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
3815 const gpa = comp.gpa;3809 const gpa = comp.gpa;
38163810
3817 var arena_allocator = std.heap.ArenaAllocator.init(gpa);3811 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
...@@ -3952,12 +3946,11 @@ const AstGenSrc = union(enum) {...@@ -3952,12 +3946,11 @@ const AstGenSrc = union(enum) {
3952fn workerAstGenFile(3946fn workerAstGenFile(
3953 comp: *Compilation,3947 comp: *Compilation,
3954 file: *Module.File,3948 file: *Module.File,
3955 prog_node: *std.Progress.Node,3949 prog_node: std.Progress.Node,
3956 wg: *WaitGroup,3950 wg: *WaitGroup,
3957 src: AstGenSrc,3951 src: AstGenSrc,
3958) void {3952) void {
3959 var child_prog_node = prog_node.start(file.sub_file_path, 0);3953 const child_prog_node = prog_node.start(file.sub_file_path, 0);
3960 child_prog_node.activate();
3961 defer child_prog_node.end();3954 defer child_prog_node.end();
39623955
3963 const mod = comp.module.?;3956 const mod = comp.module.?;
...@@ -4265,7 +4258,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module...@@ -4265,7 +4258,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module
4265fn workerUpdateCObject(4258fn workerUpdateCObject(
4266 comp: *Compilation,4259 comp: *Compilation,
4267 c_object: *CObject,4260 c_object: *CObject,
4268 progress_node: *std.Progress.Node,4261 progress_node: std.Progress.Node,
4269) void {4262) void {
4270 comp.updateCObject(c_object, progress_node) catch |err| switch (err) {4263 comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
4271 error.AnalysisFail => return,4264 error.AnalysisFail => return,
...@@ -4282,7 +4275,7 @@ fn workerUpdateCObject(...@@ -4282,7 +4275,7 @@ fn workerUpdateCObject(
4282fn workerUpdateWin32Resource(4275fn workerUpdateWin32Resource(
4283 comp: *Compilation,4276 comp: *Compilation,
4284 win32_resource: *Win32Resource,4277 win32_resource: *Win32Resource,
4285 progress_node: *std.Progress.Node,4278 progress_node: std.Progress.Node,
4286) void {4279) void {
4287 comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) {4280 comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) {
4288 error.AnalysisFail => return,4281 error.AnalysisFail => return,
...@@ -4300,7 +4293,7 @@ fn buildCompilerRtOneShot(...@@ -4300,7 +4293,7 @@ fn buildCompilerRtOneShot(
4300 comp: *Compilation,4293 comp: *Compilation,
4301 output_mode: std.builtin.OutputMode,4294 output_mode: std.builtin.OutputMode,
4302 out: *?CRTFile,4295 out: *?CRTFile,
4303 prog_node: *std.Progress.Node,4296 prog_node: std.Progress.Node,
4304) void {4297) void {
4305 comp.buildOutputFromZig(4298 comp.buildOutputFromZig(
4306 "compiler_rt.zig",4299 "compiler_rt.zig",
...@@ -4427,7 +4420,7 @@ fn reportRetryableEmbedFileError(...@@ -4427,7 +4420,7 @@ fn reportRetryableEmbedFileError(
4427 }4420 }
4428}4421}
44294422
4430fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void {4423fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Progress.Node) !void {
4431 if (comp.config.c_frontend == .aro) {4424 if (comp.config.c_frontend == .aro) {
4432 return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{});4425 return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{});
4433 }4426 }
...@@ -4467,9 +4460,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -4467,9 +4460,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
44674460
4468 const c_source_basename = std.fs.path.basename(c_object.src.src_path);4461 const c_source_basename = std.fs.path.basename(c_object.src.src_path);
44694462
4470 c_obj_prog_node.activate();4463 const child_progress_node = c_obj_prog_node.start(c_source_basename, 0);
4471 var child_progress_node = c_obj_prog_node.start(c_source_basename, 0);
4472 child_progress_node.activate();
4473 defer child_progress_node.end();4464 defer child_progress_node.end();
44744465
4475 // Special case when doing build-obj for just one C file. When there are more than one object4466 // Special case when doing build-obj for just one C file. When there are more than one object
...@@ -4731,7 +4722,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -4731,7 +4722,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
4731 };4722 };
4732}4723}
47334724
4734fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: *std.Progress.Node) !void {4725fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
4735 if (!std.process.can_spawn) {4726 if (!std.process.can_spawn) {
4736 return comp.failWin32Resource(win32_resource, "{s} does not support spawning a child process", .{@tagName(builtin.os.tag)});4727 return comp.failWin32Resource(win32_resource, "{s} does not support spawning a child process", .{@tagName(builtin.os.tag)});
4737 }4728 }
...@@ -4763,9 +4754,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4763,9 +4754,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4763 _ = comp.failed_win32_resources.swapRemove(win32_resource);4754 _ = comp.failed_win32_resources.swapRemove(win32_resource);
4764 }4755 }
47654756
4766 win32_resource_prog_node.activate();4757 const child_progress_node = win32_resource_prog_node.start(src_basename, 0);
4767 var child_progress_node = win32_resource_prog_node.start(src_basename, 0);
4768 child_progress_node.activate();
4769 defer child_progress_node.end();4758 defer child_progress_node.end();
47704759
4771 var man = comp.obtainWin32ResourceCacheManifest();4760 var man = comp.obtainWin32ResourceCacheManifest();
...@@ -4833,7 +4822,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4833,7 +4822,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4833 });4822 });
4834 try argv.appendSlice(&.{ "--", in_rc_path, out_res_path });4823 try argv.appendSlice(&.{ "--", in_rc_path, out_res_path });
48354824
4836 try spawnZigRc(comp, win32_resource, src_basename, arena, argv.items, &child_progress_node);4825 try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
48374826
4838 break :blk digest;4827 break :blk digest;
4839 };4828 };
...@@ -4901,7 +4890,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4901,7 +4890,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4901 try argv.appendSlice(rc_src.extra_flags);4890 try argv.appendSlice(rc_src.extra_flags);
4902 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });4891 try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
49034892
4904 try spawnZigRc(comp, win32_resource, src_basename, arena, argv.items, &child_progress_node);4893 try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
49054894
4906 // Read depfile and update cache manifest4895 // Read depfile and update cache manifest
4907 {4896 {
...@@ -4966,10 +4955,9 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4966,10 +4955,9 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4966fn spawnZigRc(4955fn spawnZigRc(
4967 comp: *Compilation,4956 comp: *Compilation,
4968 win32_resource: *Win32Resource,4957 win32_resource: *Win32Resource,
4969 src_basename: []const u8,
4970 arena: Allocator,4958 arena: Allocator,
4971 argv: []const []const u8,4959 argv: []const []const u8,
4972 child_progress_node: *std.Progress.Node,4960 child_progress_node: std.Progress.Node,
4973) !void {4961) !void {
4974 var node_name: std.ArrayListUnmanaged(u8) = .{};4962 var node_name: std.ArrayListUnmanaged(u8) = .{};
4975 defer node_name.deinit(arena);4963 defer node_name.deinit(arena);
...@@ -4978,6 +4966,7 @@ fn spawnZigRc(...@@ -4978,6 +4966,7 @@ fn spawnZigRc(
4978 child.stdin_behavior = .Ignore;4966 child.stdin_behavior = .Ignore;
4979 child.stdout_behavior = .Pipe;4967 child.stdout_behavior = .Pipe;
4980 child.stderr_behavior = .Pipe;4968 child.stderr_behavior = .Pipe;
4969 child.progress_node = child_progress_node;
49814970
4982 child.spawn() catch |err| {4971 child.spawn() catch |err| {
4983 return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) });4972 return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) });
...@@ -5019,22 +5008,6 @@ fn spawnZigRc(...@@ -5019,22 +5008,6 @@ fn spawnZigRc(
5019 };5008 };
5020 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);5009 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
5021 },5010 },
5022 .progress => {
5023 node_name.clearRetainingCapacity();
5024 // <resinator> is a special string that indicates that the child
5025 // process has reached resinator's main function
5026 if (std.mem.eql(u8, body, "<resinator>")) {
5027 child_progress_node.setName(src_basename);
5028 }
5029 // Ignore 0-length strings since if multiple zig rc commands
5030 // are executed at the same time, only one will send progress strings
5031 // while the other(s) will send empty strings.
5032 else if (body.len > 0) {
5033 try node_name.appendSlice(arena, "build 'zig rc'... ");
5034 try node_name.appendSlice(arena, body);
5035 child_progress_node.setName(node_name.items);
5036 }
5037 },
5038 else => {}, // ignore other messages5011 else => {}, // ignore other messages
5039 }5012 }
50405013
...@@ -5937,8 +5910,8 @@ pub fn lockAndParseLldStderr(comp: *Compilation, prefix: []const u8, stderr: []c...@@ -5937,8 +5910,8 @@ pub fn lockAndParseLldStderr(comp: *Compilation, prefix: []const u8, stderr: []c
5937}5910}
59385911
5939pub fn dump_argv(argv: []const []const u8) void {5912pub fn dump_argv(argv: []const []const u8) void {
5940 std.debug.getStderrMutex().lock();5913 std.debug.lockStdErr();
5941 defer std.debug.getStderrMutex().unlock();5914 defer std.debug.unlockStdErr();
5942 const stderr = std.io.getStdErr().writer();5915 const stderr = std.io.getStdErr().writer();
5943 for (argv[0 .. argv.len - 1]) |arg| {5916 for (argv[0 .. argv.len - 1]) |arg| {
5944 nosuspend stderr.print("{s} ", .{arg}) catch return;5917 nosuspend stderr.print("{s} ", .{arg}) catch return;
...@@ -5989,14 +5962,13 @@ pub fn updateSubCompilation(...@@ -5989,14 +5962,13 @@ pub fn updateSubCompilation(
5989 parent_comp: *Compilation,5962 parent_comp: *Compilation,
5990 sub_comp: *Compilation,5963 sub_comp: *Compilation,
5991 misc_task: MiscTask,5964 misc_task: MiscTask,
5992 prog_node: *std.Progress.Node,5965 prog_node: std.Progress.Node,
5993) !void {5966) !void {
5994 {5967 {
5995 var sub_node = prog_node.start(@tagName(misc_task), 0);5968 const sub_node = prog_node.start(@tagName(misc_task), 0);
5996 sub_node.activate();
5997 defer sub_node.end();5969 defer sub_node.end();
59985970
5999 try sub_comp.update(prog_node);5971 try sub_comp.update(sub_node);
6000 }5972 }
60015973
6002 // Look for compilation errors in this sub compilation5974 // Look for compilation errors in this sub compilation
...@@ -6024,7 +5996,7 @@ fn buildOutputFromZig(...@@ -6024,7 +5996,7 @@ fn buildOutputFromZig(
6024 output_mode: std.builtin.OutputMode,5996 output_mode: std.builtin.OutputMode,
6025 out: *?CRTFile,5997 out: *?CRTFile,
6026 misc_task_tag: MiscTask,5998 misc_task_tag: MiscTask,
6027 prog_node: *std.Progress.Node,5999 prog_node: std.Progress.Node,
6028) !void {6000) !void {
6029 const tracy_trace = trace(@src());6001 const tracy_trace = trace(@src());
6030 defer tracy_trace.end();6002 defer tracy_trace.end();
...@@ -6131,7 +6103,7 @@ pub fn build_crt_file(...@@ -6131,7 +6103,7 @@ pub fn build_crt_file(
6131 root_name: []const u8,6103 root_name: []const u8,
6132 output_mode: std.builtin.OutputMode,6104 output_mode: std.builtin.OutputMode,
6133 misc_task_tag: MiscTask,6105 misc_task_tag: MiscTask,
6134 prog_node: *std.Progress.Node,6106 prog_node: std.Progress.Node,
6135 /// These elements have to get mutated to add the owner module after it is6107 /// These elements have to get mutated to add the owner module after it is
6136 /// created within this function.6108 /// created within this function.
6137 c_source_files: []CSourceFile,6109 c_source_files: []CSourceFile,
src/Module.zig+28-11
...@@ -66,6 +66,7 @@ root_mod: *Package.Module,...@@ -66,6 +66,7 @@ root_mod: *Package.Module,
66main_mod: *Package.Module,66main_mod: *Package.Module,
67std_mod: *Package.Module,67std_mod: *Package.Module,
68sema_prog_node: std.Progress.Node = undefined,68sema_prog_node: std.Progress.Node = undefined,
69codegen_prog_node: std.Progress.Node = undefined,
6970
70/// Used by AstGen worker to load and store ZIR cache.71/// Used by AstGen worker to load and store ZIR cache.
71global_zir_cache: Compilation.Directory,72global_zir_cache: Compilation.Directory,
...@@ -2942,11 +2943,12 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {...@@ -2942,11 +2943,12 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
2942 const tracy = trace(@src());2943 const tracy = trace(@src());
2943 defer tracy.end();2944 defer tracy.end();
29442945
2946 const ip = &mod.intern_pool;
2945 const decl = mod.declPtr(decl_index);2947 const decl = mod.declPtr(decl_index);
29462948
2947 log.debug("ensureDeclAnalyzed '{d}' (name '{}')", .{2949 log.debug("ensureDeclAnalyzed '{d}' (name '{}')", .{
2948 @intFromEnum(decl_index),2950 @intFromEnum(decl_index),
2949 decl.name.fmt(&mod.intern_pool),2951 decl.name.fmt(ip),
2950 });2952 });
29512953
2952 // Determine whether or not this Decl is outdated, i.e. requires re-analysis2954 // Determine whether or not this Decl is outdated, i.e. requires re-analysis
...@@ -2991,10 +2993,6 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {...@@ -2991,10 +2993,6 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
2991 try mod.deleteDeclExports(decl_index);2993 try mod.deleteDeclExports(decl_index);
2992 }2994 }
29932995
2994 var decl_prog_node = mod.sema_prog_node.start("", 0);
2995 decl_prog_node.activate();
2996 defer decl_prog_node.end();
2997
2998 const sema_result: SemaDeclResult = blk: {2996 const sema_result: SemaDeclResult = blk: {
2999 if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) {2997 if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) {
3000 // Anonymous decl. We don't semantically analyze these.2998 // Anonymous decl. We don't semantically analyze these.
...@@ -3012,6 +3010,9 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {...@@ -3012,6 +3010,9 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
3012 };3010 };
3013 }3011 }
30143012
3013 const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
3014 defer decl_prog_node.end();
3015
3015 break :blk mod.semaDecl(decl_index) catch |err| switch (err) {3016 break :blk mod.semaDecl(decl_index) catch |err| switch (err) {
3016 error.AnalysisFail => {3017 error.AnalysisFail => {
3017 if (decl.analysis == .in_progress) {3018 if (decl.analysis == .in_progress) {
...@@ -3215,6 +3216,9 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.In...@@ -3215,6 +3216,9 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.In
3215 };3216 };
3216 }3217 }
32173218
3219 const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(ip), 0);
3220 defer codegen_prog_node.end();
3221
3218 if (comp.bin_file) |lf| {3222 if (comp.bin_file) |lf| {
3219 lf.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {3223 lf.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {
3220 error.OutOfMemory => return error.OutOfMemory,3224 error.OutOfMemory => return error.OutOfMemory,
...@@ -4500,6 +4504,9 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato...@@ -4500,6 +4504,9 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
4500 log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)});4504 log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)});
4501 }4505 }
45024506
4507 const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
4508 defer decl_prog_node.end();
4509
4503 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));4510 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));
45044511
4505 var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa);4512 var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa);
...@@ -5316,7 +5323,7 @@ fn handleUpdateExports(...@@ -5316,7 +5323,7 @@ fn handleUpdateExports(
53165323
5317pub fn populateTestFunctions(5324pub fn populateTestFunctions(
5318 mod: *Module,5325 mod: *Module,
5319 main_progress_node: *std.Progress.Node,5326 main_progress_node: std.Progress.Node,
5320) !void {5327) !void {
5321 const gpa = mod.gpa;5328 const gpa = mod.gpa;
5322 const ip = &mod.intern_pool;5329 const ip = &mod.intern_pool;
...@@ -5333,13 +5340,13 @@ pub fn populateTestFunctions(...@@ -5333,13 +5340,13 @@ pub fn populateTestFunctions(
5333 // We have to call `ensureDeclAnalyzed` here in case `builtin.test_functions`5340 // We have to call `ensureDeclAnalyzed` here in case `builtin.test_functions`
5334 // was not referenced by start code.5341 // was not referenced by start code.
5335 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);5342 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
5336 mod.sema_prog_node.activate();
5337 defer {5343 defer {
5338 mod.sema_prog_node.end();5344 mod.sema_prog_node.end();
5339 mod.sema_prog_node = undefined;5345 mod.sema_prog_node = undefined;
5340 }5346 }
5341 try mod.ensureDeclAnalyzed(decl_index);5347 try mod.ensureDeclAnalyzed(decl_index);
5342 }5348 }
5349
5343 const decl = mod.declPtr(decl_index);5350 const decl = mod.declPtr(decl_index);
5344 const test_fn_ty = decl.typeOf(mod).slicePtrFieldType(mod).childType(mod);5351 const test_fn_ty = decl.typeOf(mod).slicePtrFieldType(mod).childType(mod);
53455352
...@@ -5440,21 +5447,32 @@ pub fn populateTestFunctions(...@@ -5440,21 +5447,32 @@ pub fn populateTestFunctions(
5440 decl.val = new_val;5447 decl.val = new_val;
5441 decl.has_tv = true;5448 decl.has_tv = true;
5442 }5449 }
5443 try mod.linkerUpdateDecl(decl_index);5450 {
5451 mod.codegen_prog_node = main_progress_node.start("Code Generation", 0);
5452 defer {
5453 mod.codegen_prog_node.end();
5454 mod.codegen_prog_node = undefined;
5455 }
5456
5457 try mod.linkerUpdateDecl(decl_index);
5458 }
5444}5459}
54455460
5446pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void {5461pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void {
5447 const comp = zcu.comp;5462 const comp = zcu.comp;
54485463
5464 const decl = zcu.declPtr(decl_index);
5465
5466 const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(&zcu.intern_pool), 0);
5467 defer codegen_prog_node.end();
5468
5449 if (comp.bin_file) |lf| {5469 if (comp.bin_file) |lf| {
5450 lf.updateDecl(zcu, decl_index) catch |err| switch (err) {5470 lf.updateDecl(zcu, decl_index) catch |err| switch (err) {
5451 error.OutOfMemory => return error.OutOfMemory,5471 error.OutOfMemory => return error.OutOfMemory,
5452 error.AnalysisFail => {5472 error.AnalysisFail => {
5453 const decl = zcu.declPtr(decl_index);
5454 decl.analysis = .codegen_failure;5473 decl.analysis = .codegen_failure;
5455 },5474 },
5456 else => {5475 else => {
5457 const decl = zcu.declPtr(decl_index);
5458 const gpa = zcu.gpa;5476 const gpa = zcu.gpa;
5459 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);5477 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);
5460 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(5478 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
...@@ -5472,7 +5490,6 @@ pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void {...@@ -5472,7 +5490,6 @@ pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void {
5472 llvm_object.updateDecl(zcu, decl_index) catch |err| switch (err) {5490 llvm_object.updateDecl(zcu, decl_index) catch |err| switch (err) {
5473 error.OutOfMemory => return error.OutOfMemory,5491 error.OutOfMemory => return error.OutOfMemory,
5474 error.AnalysisFail => {5492 error.AnalysisFail => {
5475 const decl = zcu.declPtr(decl_index);
5476 decl.analysis = .codegen_failure;5493 decl.analysis = .codegen_failure;
5477 },5494 },
5478 };5495 };
src/Package/Fetch.zig+5-9
...@@ -35,7 +35,7 @@ name_tok: std.zig.Ast.TokenIndex,...@@ -35,7 +35,7 @@ name_tok: std.zig.Ast.TokenIndex,
35lazy_status: LazyStatus,35lazy_status: LazyStatus,
36parent_package_root: Cache.Path,36parent_package_root: Cache.Path,
37parent_manifest_ast: ?*const std.zig.Ast,37parent_manifest_ast: ?*const std.zig.Ast,
38prog_node: *std.Progress.Node,38prog_node: std.Progress.Node,
39job_queue: *JobQueue,39job_queue: *JobQueue,
40/// If true, don't add an error for a missing hash. This flag is not passed40/// If true, don't add an error for a missing hash. This flag is not passed
41/// down to recursive dependencies. It's intended to be used only be the CLI.41/// down to recursive dependencies. It's intended to be used only be the CLI.
...@@ -720,8 +720,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {...@@ -720,8 +720,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
720 };720 };
721 }721 }
722722
723 // job_queue mutex is locked so this is OK.723 f.prog_node.increaseEstimatedTotalItems(new_fetch_index);
724 f.prog_node.unprotected_estimated_total_items += new_fetch_index;
725724
726 break :nf .{ new_fetches[0..new_fetch_index], prog_names[0..new_fetch_index] };725 break :nf .{ new_fetches[0..new_fetch_index], prog_names[0..new_fetch_index] };
727 };726 };
...@@ -751,9 +750,8 @@ pub fn relativePathDigest(...@@ -751,9 +750,8 @@ pub fn relativePathDigest(
751}750}
752751
753pub fn workerRun(f: *Fetch, prog_name: []const u8) void {752pub fn workerRun(f: *Fetch, prog_name: []const u8) void {
754 var prog_node = f.prog_node.start(prog_name, 0);753 const prog_node = f.prog_node.start(prog_name, 0);
755 defer prog_node.end();754 defer prog_node.end();
756 prog_node.activate();
757755
758 run(f) catch |err| switch (err) {756 run(f) catch |err| switch (err) {
759 error.OutOfMemory => f.oom_flag = true,757 error.OutOfMemory => f.oom_flag = true,
...@@ -1311,9 +1309,8 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac...@@ -1311,9 +1309,8 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac
1311 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });1309 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });
1312 defer index_file.close();1310 defer index_file.close();
1313 {1311 {
1314 var index_prog_node = f.prog_node.start("Index pack", 0);1312 const index_prog_node = f.prog_node.start("Index pack", 0);
1315 defer index_prog_node.end();1313 defer index_prog_node.end();
1316 index_prog_node.activate();
1317 var index_buffered_writer = std.io.bufferedWriter(index_file.writer());1314 var index_buffered_writer = std.io.bufferedWriter(index_file.writer());
1318 try git.indexPack(gpa, pack_file, index_buffered_writer.writer());1315 try git.indexPack(gpa, pack_file, index_buffered_writer.writer());
1319 try index_buffered_writer.flush();1316 try index_buffered_writer.flush();
...@@ -1321,9 +1318,8 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac...@@ -1321,9 +1318,8 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!Unpac
1321 }1318 }
13221319
1323 {1320 {
1324 var checkout_prog_node = f.prog_node.start("Checkout", 0);1321 const checkout_prog_node = f.prog_node.start("Checkout", 0);
1325 defer checkout_prog_node.end();1322 defer checkout_prog_node.end();
1326 checkout_prog_node.activate();
1327 var repository = try git.Repository.init(gpa, pack_file, index_file);1323 var repository = try git.Repository.init(gpa, pack_file, index_file);
1328 defer repository.deinit();1324 defer repository.deinit();
1329 var diagnostics: git.Diagnostics = .{ .allocator = arena };1325 var diagnostics: git.Diagnostics = .{ .allocator = arena };
src/glibc.zig+3-3
...@@ -160,7 +160,7 @@ pub const CRTFile = enum {...@@ -160,7 +160,7 @@ pub const CRTFile = enum {
160 libc_nonshared_a,160 libc_nonshared_a,
161};161};
162162
163pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {163pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
164 if (!build_options.have_llvm) {164 if (!build_options.have_llvm) {
165 return error.ZigCompilerNotBuiltWithLLVMExtensions;165 return error.ZigCompilerNotBuiltWithLLVMExtensions;
166 }166 }
...@@ -658,7 +658,7 @@ pub const BuiltSharedObjects = struct {...@@ -658,7 +658,7 @@ pub const BuiltSharedObjects = struct {
658658
659const all_map_basename = "all.map";659const all_map_basename = "all.map";
660660
661pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !void {661pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !void {
662 const tracy = trace(@src());662 const tracy = trace(@src());
663 defer tracy.end();663 defer tracy.end();
664664
...@@ -1065,7 +1065,7 @@ fn buildSharedLib(...@@ -1065,7 +1065,7 @@ fn buildSharedLib(
1065 bin_directory: Compilation.Directory,1065 bin_directory: Compilation.Directory,
1066 asm_file_basename: []const u8,1066 asm_file_basename: []const u8,
1067 lib: Lib,1067 lib: Lib,
1068 prog_node: *std.Progress.Node,1068 prog_node: std.Progress.Node,
1069) !void {1069) !void {
1070 const tracy = trace(@src());1070 const tracy = trace(@src());
1071 defer tracy.end();1071 defer tracy.end();
src/libcxx.zig+2-2
...@@ -113,7 +113,7 @@ pub const BuildError = error{...@@ -113,7 +113,7 @@ pub const BuildError = error{
113 ZigCompilerNotBuiltWithLLVMExtensions,113 ZigCompilerNotBuiltWithLLVMExtensions,
114};114};
115115
116pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) BuildError!void {116pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
117 if (!build_options.have_llvm) {117 if (!build_options.have_llvm) {
118 return error.ZigCompilerNotBuiltWithLLVMExtensions;118 return error.ZigCompilerNotBuiltWithLLVMExtensions;
119 }119 }
...@@ -357,7 +357,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) BuildError...@@ -357,7 +357,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) BuildError
357 comp.libcxx_static_lib = try sub_compilation.toCrtFile();357 comp.libcxx_static_lib = try sub_compilation.toCrtFile();
358}358}
359359
360pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) BuildError!void {360pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
361 if (!build_options.have_llvm) {361 if (!build_options.have_llvm) {
362 return error.ZigCompilerNotBuiltWithLLVMExtensions;362 return error.ZigCompilerNotBuiltWithLLVMExtensions;
363 }363 }
src/libtsan.zig+1-1
...@@ -13,7 +13,7 @@ pub const BuildError = error{...@@ -13,7 +13,7 @@ pub const BuildError = error{
13 TSANUnsupportedCPUArchitecture,13 TSANUnsupportedCPUArchitecture,
14};14};
1515
16pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) BuildError!void {16pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
17 if (!build_options.have_llvm) {17 if (!build_options.have_llvm) {
18 return error.ZigCompilerNotBuiltWithLLVMExtensions;18 return error.ZigCompilerNotBuiltWithLLVMExtensions;
19 }19 }
src/libunwind.zig+1-1
...@@ -14,7 +14,7 @@ pub const BuildError = error{...@@ -14,7 +14,7 @@ pub const BuildError = error{
14 ZigCompilerNotBuiltWithLLVMExtensions,14 ZigCompilerNotBuiltWithLLVMExtensions,
15};15};
1616
17pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) BuildError!void {17pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
18 if (!build_options.have_llvm) {18 if (!build_options.have_llvm) {
19 return error.ZigCompilerNotBuiltWithLLVMExtensions;19 return error.ZigCompilerNotBuiltWithLLVMExtensions;
20 }20 }
src/link.zig+4-4
...@@ -535,7 +535,7 @@ pub const File = struct {...@@ -535,7 +535,7 @@ pub const File = struct {
535 /// Commit pending changes and write headers. Takes into account final output mode535 /// Commit pending changes and write headers. Takes into account final output mode
536 /// and `use_lld`, not only `effectiveOutputMode`.536 /// and `use_lld`, not only `effectiveOutputMode`.
537 /// `arena` has the lifetime of the call to `Compilation.update`.537 /// `arena` has the lifetime of the call to `Compilation.update`.
538 pub fn flush(base: *File, arena: Allocator, prog_node: *std.Progress.Node) FlushError!void {538 pub fn flush(base: *File, arena: Allocator, prog_node: std.Progress.Node) FlushError!void {
539 if (build_options.only_c) {539 if (build_options.only_c) {
540 assert(base.tag == .c);540 assert(base.tag == .c);
541 return @as(*C, @fieldParentPtr("base", base)).flush(arena, prog_node);541 return @as(*C, @fieldParentPtr("base", base)).flush(arena, prog_node);
...@@ -572,7 +572,7 @@ pub const File = struct {...@@ -572,7 +572,7 @@ pub const File = struct {
572572
573 /// Commit pending changes and write headers. Works based on `effectiveOutputMode`573 /// Commit pending changes and write headers. Works based on `effectiveOutputMode`
574 /// rather than final output mode.574 /// rather than final output mode.
575 pub fn flushModule(base: *File, arena: Allocator, prog_node: *std.Progress.Node) FlushError!void {575 pub fn flushModule(base: *File, arena: Allocator, prog_node: std.Progress.Node) FlushError!void {
576 switch (base.tag) {576 switch (base.tag) {
577 inline else => |tag| {577 inline else => |tag| {
578 if (tag != .c and build_options.only_c) unreachable;578 if (tag != .c and build_options.only_c) unreachable;
...@@ -688,7 +688,7 @@ pub const File = struct {...@@ -688,7 +688,7 @@ pub const File = struct {
688 }688 }
689 }689 }
690690
691 pub fn linkAsArchive(base: *File, arena: Allocator, prog_node: *std.Progress.Node) FlushError!void {691 pub fn linkAsArchive(base: *File, arena: Allocator, prog_node: std.Progress.Node) FlushError!void {
692 const tracy = trace(@src());692 const tracy = trace(@src());
693 defer tracy.end();693 defer tracy.end();
694694
...@@ -966,7 +966,7 @@ pub const File = struct {...@@ -966,7 +966,7 @@ pub const File = struct {
966 base: File,966 base: File,
967 arena: Allocator,967 arena: Allocator,
968 llvm_object: *LlvmObject,968 llvm_object: *LlvmObject,
969 prog_node: *std.Progress.Node,969 prog_node: std.Progress.Node,
970 ) !void {970 ) !void {
971 return base.comp.emitLlvmObject(arena, base.emit, .{971 return base.comp.emitLlvmObject(arena, base.emit, .{
972 .directory = null,972 .directory = null,
src/link/C.zig+3-4
...@@ -370,7 +370,7 @@ pub fn updateDeclLineNumber(self: *C, zcu: *Zcu, decl_index: InternPool.DeclInde...@@ -370,7 +370,7 @@ pub fn updateDeclLineNumber(self: *C, zcu: *Zcu, decl_index: InternPool.DeclInde
370 _ = decl_index;370 _ = decl_index;
371}371}
372372
373pub fn flush(self: *C, arena: Allocator, prog_node: *std.Progress.Node) !void {373pub fn flush(self: *C, arena: Allocator, prog_node: std.Progress.Node) !void {
374 return self.flushModule(arena, prog_node);374 return self.flushModule(arena, prog_node);
375}375}
376376
...@@ -389,14 +389,13 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {...@@ -389,14 +389,13 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) {
389 return defines;389 return defines;
390}390}
391391
392pub fn flushModule(self: *C, arena: Allocator, prog_node: *std.Progress.Node) !void {392pub fn flushModule(self: *C, arena: Allocator, prog_node: std.Progress.Node) !void {
393 _ = arena; // Has the same lifetime as the call to Compilation.update.393 _ = arena; // Has the same lifetime as the call to Compilation.update.
394394
395 const tracy = trace(@src());395 const tracy = trace(@src());
396 defer tracy.end();396 defer tracy.end();
397397
398 var sub_prog_node = prog_node.start("Flush Module", 0);398 const sub_prog_node = prog_node.start("Flush Module", 0);
399 sub_prog_node.activate();
400 defer sub_prog_node.end();399 defer sub_prog_node.end();
401400
402 const comp = self.base.comp;401 const comp = self.base.comp;
src/link/Coff.zig+3-4
...@@ -1702,7 +1702,7 @@ fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {...@@ -1702,7 +1702,7 @@ fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
1702 gop.value_ptr.* = current;1702 gop.value_ptr.* = current;
1703}1703}
17041704
1705pub fn flush(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {1705pub fn flush(self: *Coff, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
1706 const comp = self.base.comp;1706 const comp = self.base.comp;
1707 const use_lld = build_options.have_llvm and comp.config.use_lld;1707 const use_lld = build_options.have_llvm and comp.config.use_lld;
1708 if (use_lld) {1708 if (use_lld) {
...@@ -1714,7 +1714,7 @@ pub fn flush(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) link....@@ -1714,7 +1714,7 @@ pub fn flush(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) link.
1714 }1714 }
1715}1715}
17161716
1717pub fn flushModule(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {1717pub fn flushModule(self: *Coff, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
1718 const tracy = trace(@src());1718 const tracy = trace(@src());
1719 defer tracy.end();1719 defer tracy.end();
17201720
...@@ -1726,8 +1726,7 @@ pub fn flushModule(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1726,8 +1726,7 @@ pub fn flushModule(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)
1726 return;1726 return;
1727 }1727 }
17281728
1729 var sub_prog_node = prog_node.start("COFF Flush", 0);1729 const sub_prog_node = prog_node.start("COFF Flush", 0);
1730 sub_prog_node.activate();
1731 defer sub_prog_node.end();1730 defer sub_prog_node.end();
17321731
1733 const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented;1732 const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented;
src/link/Coff/lld.zig+2-4
...@@ -16,7 +16,7 @@ const Allocator = mem.Allocator;...@@ -16,7 +16,7 @@ const Allocator = mem.Allocator;
16const Coff = @import("../Coff.zig");16const Coff = @import("../Coff.zig");
17const Compilation = @import("../../Compilation.zig");17const Compilation = @import("../../Compilation.zig");
1818
19pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) !void {19pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: std.Progress.Node) !void {
20 const tracy = trace(@src());20 const tracy = trace(@src());
21 defer tracy.end();21 defer tracy.end();
2222
...@@ -38,9 +38,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)...@@ -38,9 +38,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node)
38 }38 }
39 } else null;39 } else null;
4040
41 var sub_prog_node = prog_node.start("LLD Link", 0);41 const sub_prog_node = prog_node.start("LLD Link", 0);
42 sub_prog_node.activate();
43 sub_prog_node.context.refresh();
44 defer sub_prog_node.end();42 defer sub_prog_node.end();
4543
46 const is_lib = comp.config.output_mode == .Lib;44 const is_lib = comp.config.output_mode == .Lib;
src/link/Elf.zig+5-8
...@@ -1064,7 +1064,7 @@ pub fn markDirty(self: *Elf, shdr_index: u32) void {...@@ -1064,7 +1064,7 @@ pub fn markDirty(self: *Elf, shdr_index: u32) void {
1064 }1064 }
1065}1065}
10661066
1067pub fn flush(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {1067pub fn flush(self: *Elf, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
1068 const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;1068 const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;
1069 if (use_lld) {1069 if (use_lld) {
1070 return self.linkWithLLD(arena, prog_node);1070 return self.linkWithLLD(arena, prog_node);
...@@ -1072,7 +1072,7 @@ pub fn flush(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) link.F...@@ -1072,7 +1072,7 @@ pub fn flush(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) link.F
1072 try self.flushModule(arena, prog_node);1072 try self.flushModule(arena, prog_node);
1073}1073}
10741074
1075pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {1075pub fn flushModule(self: *Elf, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
1076 const tracy = trace(@src());1076 const tracy = trace(@src());
1077 defer tracy.end();1077 defer tracy.end();
10781078
...@@ -1085,8 +1085,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1085,8 +1085,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1085 if (use_lld) return;1085 if (use_lld) return;
1086 }1086 }
10871087
1088 var sub_prog_node = prog_node.start("ELF Flush", 0);1088 const sub_prog_node = prog_node.start("ELF Flush", 0);
1089 sub_prog_node.activate();
1090 defer sub_prog_node.end();1089 defer sub_prog_node.end();
10911090
1092 const target = comp.root_mod.resolved_target.result;1091 const target = comp.root_mod.resolved_target.result;
...@@ -2147,7 +2146,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -2147,7 +2146,7 @@ fn scanRelocs(self: *Elf) !void {
2147 }2146 }
2148}2147}
21492148
2150fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !void {2149fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: std.Progress.Node) !void {
2151 const tracy = trace(@src());2150 const tracy = trace(@src());
2152 defer tracy.end();2151 defer tracy.end();
21532152
...@@ -2169,9 +2168,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi...@@ -2169,9 +2168,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi
2169 }2168 }
2170 } else null;2169 } else null;
21712170
2172 var sub_prog_node = prog_node.start("LLD Link", 0);2171 const sub_prog_node = prog_node.start("LLD Link", 0);
2173 sub_prog_node.activate();
2174 sub_prog_node.context.refresh();
2175 defer sub_prog_node.end();2172 defer sub_prog_node.end();
21762173
2177 const output_mode = comp.config.output_mode;2174 const output_mode = comp.config.output_mode;
src/link/MachO.zig+3-4
...@@ -360,11 +360,11 @@ pub fn deinit(self: *MachO) void {...@@ -360,11 +360,11 @@ pub fn deinit(self: *MachO) void {
360 self.unwind_records.deinit(gpa);360 self.unwind_records.deinit(gpa);
361}361}
362362
363pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {363pub fn flush(self: *MachO, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
364 try self.flushModule(arena, prog_node);364 try self.flushModule(arena, prog_node);
365}365}
366366
367pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {367pub fn flushModule(self: *MachO, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
368 const tracy = trace(@src());368 const tracy = trace(@src());
369 defer tracy.end();369 defer tracy.end();
370370
...@@ -375,8 +375,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -375,8 +375,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
375 try self.base.emitLlvmObject(arena, llvm_object, prog_node);375 try self.base.emitLlvmObject(arena, llvm_object, prog_node);
376 }376 }
377377
378 var sub_prog_node = prog_node.start("MachO Flush", 0);378 const sub_prog_node = prog_node.start("MachO Flush", 0);
379 sub_prog_node.activate();
380 defer sub_prog_node.end();379 defer sub_prog_node.end();
381380
382 const directory = self.base.emit.directory;381 const directory = self.base.emit.directory;
src/link/NvPtx.zig+2-2
...@@ -106,11 +106,11 @@ pub fn freeDecl(self: *NvPtx, decl_index: InternPool.DeclIndex) void {...@@ -106,11 +106,11 @@ pub fn freeDecl(self: *NvPtx, decl_index: InternPool.DeclIndex) void {
106 return self.llvm_object.freeDecl(decl_index);106 return self.llvm_object.freeDecl(decl_index);
107}107}
108108
109pub fn flush(self: *NvPtx, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {109pub fn flush(self: *NvPtx, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
110 return self.flushModule(arena, prog_node);110 return self.flushModule(arena, prog_node);
111}111}
112112
113pub fn flushModule(self: *NvPtx, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {113pub fn flushModule(self: *NvPtx, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
114 if (build_options.skip_non_native)114 if (build_options.skip_non_native)
115 @panic("Attempted to compile for architecture that was disabled by build configuration");115 @panic("Attempted to compile for architecture that was disabled by build configuration");
116116
src/link/Plan9.zig+3-4
...@@ -604,7 +604,7 @@ fn allocateGotIndex(self: *Plan9) usize {...@@ -604,7 +604,7 @@ fn allocateGotIndex(self: *Plan9) usize {
604 }604 }
605}605}
606606
607pub fn flush(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {607pub fn flush(self: *Plan9, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
608 const comp = self.base.comp;608 const comp = self.base.comp;
609 const use_lld = build_options.have_llvm and comp.config.use_lld;609 const use_lld = build_options.have_llvm and comp.config.use_lld;
610 assert(!use_lld);610 assert(!use_lld);
...@@ -663,7 +663,7 @@ fn atomCount(self: *Plan9) usize {...@@ -663,7 +663,7 @@ fn atomCount(self: *Plan9) usize {
663 return data_decl_count + fn_decl_count + unnamed_const_count + lazy_atom_count + extern_atom_count + anon_atom_count;663 return data_decl_count + fn_decl_count + unnamed_const_count + lazy_atom_count + extern_atom_count + anon_atom_count;
664}664}
665665
666pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {666pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
667 if (build_options.skip_non_native and builtin.object_format != .plan9) {667 if (build_options.skip_non_native and builtin.object_format != .plan9) {
668 @panic("Attempted to compile for object format that was disabled by build configuration");668 @panic("Attempted to compile for object format that was disabled by build configuration");
669 }669 }
...@@ -677,8 +677,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node...@@ -677,8 +677,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node
677 const tracy = trace(@src());677 const tracy = trace(@src());
678 defer tracy.end();678 defer tracy.end();
679679
680 var sub_prog_node = prog_node.start("Flush Module", 0);680 const sub_prog_node = prog_node.start("Flush Module", 0);
681 sub_prog_node.activate();
682 defer sub_prog_node.end();681 defer sub_prog_node.end();
683682
684 log.debug("flushModule", .{});683 log.debug("flushModule", .{});
src/link/SpirV.zig+5-6
...@@ -193,11 +193,11 @@ pub fn freeDecl(self: *SpirV, decl_index: InternPool.DeclIndex) void {...@@ -193,11 +193,11 @@ pub fn freeDecl(self: *SpirV, decl_index: InternPool.DeclIndex) void {
193 _ = decl_index;193 _ = decl_index;
194}194}
195195
196pub fn flush(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {196pub fn flush(self: *SpirV, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
197 return self.flushModule(arena, prog_node);197 return self.flushModule(arena, prog_node);
198}198}
199199
200pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {200pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
201 if (build_options.skip_non_native) {201 if (build_options.skip_non_native) {
202 @panic("Attempted to compile for architecture that was disabled by build configuration");202 @panic("Attempted to compile for architecture that was disabled by build configuration");
203 }203 }
...@@ -205,8 +205,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node...@@ -205,8 +205,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node
205 const tracy = trace(@src());205 const tracy = trace(@src());
206 defer tracy.end();206 defer tracy.end();
207207
208 var sub_prog_node = prog_node.start("Flush Module", 0);208 const sub_prog_node = prog_node.start("Flush Module", 0);
209 sub_prog_node.activate();
210 defer sub_prog_node.end();209 defer sub_prog_node.end();
211210
212 const spv = &self.object.spv;211 const spv = &self.object.spv;
...@@ -253,7 +252,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node...@@ -253,7 +252,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node
253 const module = try spv.finalize(arena, target);252 const module = try spv.finalize(arena, target);
254 errdefer arena.free(module);253 errdefer arena.free(module);
255254
256 const linked_module = self.linkModule(arena, module, &sub_prog_node) catch |err| switch (err) {255 const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
257 error.OutOfMemory => return error.OutOfMemory,256 error.OutOfMemory => return error.OutOfMemory,
258 else => |other| {257 else => |other| {
259 log.err("error while linking: {s}\n", .{@errorName(other)});258 log.err("error while linking: {s}\n", .{@errorName(other)});
...@@ -264,7 +263,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node...@@ -264,7 +263,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node
264 try self.base.file.?.writeAll(std.mem.sliceAsBytes(linked_module));263 try self.base.file.?.writeAll(std.mem.sliceAsBytes(linked_module));
265}264}
266265
267fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: *std.Progress.Node) ![]Word {266fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress.Node) ![]Word {
268 _ = self;267 _ = self;
269268
270 const lower_invocation_globals = @import("SpirV/lower_invocation_globals.zig");269 const lower_invocation_globals = @import("SpirV/lower_invocation_globals.zig");
src/link/SpirV/deduplicate.zig+2-3
...@@ -418,9 +418,8 @@ const EntityHashContext = struct {...@@ -418,9 +418,8 @@ const EntityHashContext = struct {
418 }418 }
419};419};
420420
421pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: *std.Progress.Node) !void {421pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Progress.Node) !void {
422 var sub_node = progress.start("deduplicate", 0);422 const sub_node = progress.start("deduplicate", 0);
423 sub_node.activate();
424 defer sub_node.end();423 defer sub_node.end();
425424
426 var arena = std.heap.ArenaAllocator.init(parser.a);425 var arena = std.heap.ArenaAllocator.init(parser.a);
src/link/SpirV/lower_invocation_globals.zig+2-3
...@@ -682,9 +682,8 @@ const ModuleBuilder = struct {...@@ -682,9 +682,8 @@ const ModuleBuilder = struct {
682 }682 }
683};683};
684684
685pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: *std.Progress.Node) !void {685pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Progress.Node) !void {
686 var sub_node = progress.start("Lower invocation globals", 6);686 const sub_node = progress.start("Lower invocation globals", 6);
687 sub_node.activate();
688 defer sub_node.end();687 defer sub_node.end();
689688
690 var arena = std.heap.ArenaAllocator.init(parser.a);689 var arena = std.heap.ArenaAllocator.init(parser.a);
src/link/SpirV/prune_unused.zig+2-3
...@@ -255,9 +255,8 @@ fn removeIdsFromMap(a: Allocator, map: anytype, info: ModuleInfo, alive_marker:...@@ -255,9 +255,8 @@ fn removeIdsFromMap(a: Allocator, map: anytype, info: ModuleInfo, alive_marker:
255 }255 }
256}256}
257257
258pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: *std.Progress.Node) !void {258pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Progress.Node) !void {
259 var sub_node = progress.start("Prune unused IDs", 0);259 const sub_node = progress.start("Prune unused IDs", 0);
260 sub_node.activate();
261 defer sub_node.end();260 defer sub_node.end();
262261
263 var arena = std.heap.ArenaAllocator.init(parser.a);262 var arena = std.heap.ArenaAllocator.init(parser.a);
src/link/Wasm.zig+5-8
...@@ -2464,7 +2464,7 @@ fn appendDummySegment(wasm: *Wasm) !void {...@@ -2464,7 +2464,7 @@ fn appendDummySegment(wasm: *Wasm) !void {
2464 });2464 });
2465}2465}
24662466
2467pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {2467pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
2468 const comp = wasm.base.comp;2468 const comp = wasm.base.comp;
2469 const use_lld = build_options.have_llvm and comp.config.use_lld;2469 const use_lld = build_options.have_llvm and comp.config.use_lld;
24702470
...@@ -2475,7 +2475,7 @@ pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link....@@ -2475,7 +2475,7 @@ pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.
2475}2475}
24762476
2477/// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary.2477/// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary.
2478pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {2478pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: std.Progress.Node) link.File.FlushError!void {
2479 const tracy = trace(@src());2479 const tracy = trace(@src());
2480 defer tracy.end();2480 defer tracy.end();
24812481
...@@ -2486,8 +2486,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)...@@ -2486,8 +2486,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
2486 if (use_lld) return;2486 if (use_lld) return;
2487 }2487 }
24882488
2489 var sub_prog_node = prog_node.start("Wasm Flush", 0);2489 const sub_prog_node = prog_node.start("Wasm Flush", 0);
2490 sub_prog_node.activate();
2491 defer sub_prog_node.end();2490 defer sub_prog_node.end();
24922491
2493 const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.2492 const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type.
...@@ -3323,7 +3322,7 @@ fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void {...@@ -3323,7 +3322,7 @@ fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void {
3323 }3322 }
3324}3323}
33253324
3326fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !void {3325fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: std.Progress.Node) !void {
3327 const tracy = trace(@src());3326 const tracy = trace(@src());
3328 defer tracy.end();3327 defer tracy.end();
33293328
...@@ -3350,9 +3349,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo...@@ -3350,9 +3349,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo
3350 }3349 }
3351 } else null;3350 } else null;
33523351
3353 var sub_prog_node = prog_node.start("LLD Link", 0);3352 const sub_prog_node = prog_node.start("LLD Link", 0);
3354 sub_prog_node.activate();
3355 sub_prog_node.context.refresh();
3356 defer sub_prog_node.end();3353 defer sub_prog_node.end();
33573354
3358 const is_obj = comp.config.output_mode == .Obj;3355 const is_obj = comp.config.output_mode == .Obj;
src/main.zig+57-154
...@@ -3404,11 +3404,16 @@ fn buildOutputType(...@@ -3404,11 +3404,16 @@ fn buildOutputType(
3404 },3404 },
3405 }3405 }
34063406
3407 const root_prog_node = std.Progress.start(.{
3408 .disable_printing = (color == .off),
3409 });
3410 defer root_prog_node.end();
3411
3407 if (arg_mode == .translate_c) {3412 if (arg_mode == .translate_c) {
3408 return cmdTranslateC(comp, arena, null);3413 return cmdTranslateC(comp, arena, null, root_prog_node);
3409 }3414 }
34103415
3411 updateModule(comp, color) catch |err| switch (err) {3416 updateModule(comp, color, root_prog_node) catch |err| switch (err) {
3412 error.SemanticAnalyzeFail => {3417 error.SemanticAnalyzeFail => {
3413 assert(listen == .none);3418 assert(listen == .none);
3414 saveState(comp, debug_incremental);3419 saveState(comp, debug_incremental);
...@@ -4028,22 +4033,7 @@ fn serve(...@@ -4028,22 +4033,7 @@ fn serve(
40284033
4029 var child_pid: ?std.process.Child.Id = null;4034 var child_pid: ?std.process.Child.Id = null;
40304035
4031 var progress: std.Progress = .{4036 const main_progress_node = std.Progress.start(.{});
4032 .terminal = null,
4033 .root = .{
4034 .context = undefined,
4035 .parent = null,
4036 .name = "",
4037 .unprotected_estimated_total_items = 0,
4038 .unprotected_completed_items = 0,
4039 },
4040 .columns_written = 0,
4041 .prev_refresh_timestamp = 0,
4042 .timer = null,
4043 .done = false,
4044 };
4045 const main_progress_node = &progress.root;
4046 main_progress_node.context = &progress;
40474037
4048 while (true) {4038 while (true) {
4049 const hdr = try server.receiveMessage();4039 const hdr = try server.receiveMessage();
...@@ -4051,7 +4041,6 @@ fn serve(...@@ -4051,7 +4041,6 @@ fn serve(
4051 switch (hdr.tag) {4041 switch (hdr.tag) {
4052 .exit => return cleanExit(),4042 .exit => return cleanExit(),
4053 .update => {4043 .update => {
4054 assert(main_progress_node.recently_updated_child == null);
4055 tracy.frameMark();4044 tracy.frameMark();
40564045
4057 if (arg_mode == .translate_c) {4046 if (arg_mode == .translate_c) {
...@@ -4059,7 +4048,7 @@ fn serve(...@@ -4059,7 +4048,7 @@ fn serve(
4059 defer arena_instance.deinit();4048 defer arena_instance.deinit();
4060 const arena = arena_instance.allocator();4049 const arena = arena_instance.allocator();
4061 var output: Compilation.CImportResult = undefined;4050 var output: Compilation.CImportResult = undefined;
4062 try cmdTranslateC(comp, arena, &output);4051 try cmdTranslateC(comp, arena, &output, main_progress_node);
4063 defer output.deinit(gpa);4052 defer output.deinit(gpa);
4064 if (output.errors.errorMessageCount() != 0) {4053 if (output.errors.errorMessageCount() != 0) {
4065 try server.serveErrorBundle(output.errors);4054 try server.serveErrorBundle(output.errors);
...@@ -4075,21 +4064,7 @@ fn serve(...@@ -4075,21 +4064,7 @@ fn serve(
4075 try comp.makeBinFileWritable();4064 try comp.makeBinFileWritable();
4076 }4065 }
40774066
4078 if (builtin.single_threaded) {4067 try comp.update(main_progress_node);
4079 try comp.update(main_progress_node);
4080 } else {
4081 var reset: std.Thread.ResetEvent = .{};
4082
4083 var progress_thread = try std.Thread.spawn(.{}, progressThread, .{
4084 &progress, &server, &reset,
4085 });
4086 defer {
4087 reset.set();
4088 progress_thread.join();
4089 }
4090
4091 try comp.update(main_progress_node);
4092 }
40934068
4094 try comp.makeBinFileExecutable();4069 try comp.makeBinFileExecutable();
4095 try serveUpdateResults(&server, comp);4070 try serveUpdateResults(&server, comp);
...@@ -4116,7 +4091,6 @@ fn serve(...@@ -4116,7 +4091,6 @@ fn serve(
4116 },4091 },
4117 .hot_update => {4092 .hot_update => {
4118 tracy.frameMark();4093 tracy.frameMark();
4119 assert(main_progress_node.recently_updated_child == null);
4120 if (child_pid) |pid| {4094 if (child_pid) |pid| {
4121 try comp.hotCodeSwap(main_progress_node, pid);4095 try comp.hotCodeSwap(main_progress_node, pid);
4122 try serveUpdateResults(&server, comp);4096 try serveUpdateResults(&server, comp);
...@@ -4146,63 +4120,6 @@ fn serve(...@@ -4146,63 +4120,6 @@ fn serve(
4146 }4120 }
4147}4121}
41484122
4149fn progressThread(progress: *std.Progress, server: *const Server, reset: *std.Thread.ResetEvent) void {
4150 while (true) {
4151 if (reset.timedWait(500 * std.time.ns_per_ms)) |_| {
4152 // The Compilation update has completed.
4153 return;
4154 } else |err| switch (err) {
4155 error.Timeout => {},
4156 }
4157
4158 var buf: std.BoundedArray(u8, 160) = .{};
4159
4160 {
4161 progress.update_mutex.lock();
4162 defer progress.update_mutex.unlock();
4163
4164 var need_ellipse = false;
4165 var maybe_node: ?*std.Progress.Node = &progress.root;
4166 while (maybe_node) |node| {
4167 if (need_ellipse) {
4168 buf.appendSlice("... ") catch {};
4169 }
4170 need_ellipse = false;
4171 const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .monotonic);
4172 const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .monotonic);
4173 const current_item = completed_items + 1;
4174 if (node.name.len != 0 or eti > 0) {
4175 if (node.name.len != 0) {
4176 buf.appendSlice(node.name) catch {};
4177 need_ellipse = true;
4178 }
4179 if (eti > 0) {
4180 if (need_ellipse) buf.appendSlice(" ") catch {};
4181 buf.writer().print("[{d}/{d}] ", .{ current_item, eti }) catch {};
4182 need_ellipse = false;
4183 } else if (completed_items != 0) {
4184 if (need_ellipse) buf.appendSlice(" ") catch {};
4185 buf.writer().print("[{d}] ", .{current_item}) catch {};
4186 need_ellipse = false;
4187 }
4188 }
4189 maybe_node = @atomicLoad(?*std.Progress.Node, &node.recently_updated_child, .acquire);
4190 }
4191 }
4192
4193 const progress_string = buf.slice();
4194
4195 server.serveMessage(.{
4196 .tag = .progress,
4197 .bytes_len = @as(u32, @intCast(progress_string.len)),
4198 }, &.{
4199 progress_string,
4200 }) catch |err| {
4201 fatal("unable to write to client: {s}", .{@errorName(err)});
4202 };
4203 }
4204}
4205
4206fn serveUpdateResults(s: *Server, comp: *Compilation) !void {4123fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
4207 const gpa = comp.gpa;4124 const gpa = comp.gpa;
4208 var error_bundle = try comp.getAllErrorsAlloc();4125 var error_bundle = try comp.getAllErrorsAlloc();
...@@ -4469,25 +4386,8 @@ fn runOrTestHotSwap(...@@ -4469,25 +4386,8 @@ fn runOrTestHotSwap(
4469 }4386 }
4470}4387}
44714388
4472fn updateModule(comp: *Compilation, color: Color) !void {4389fn updateModule(comp: *Compilation, color: Color, prog_node: std.Progress.Node) !void {
4473 {4390 try comp.update(prog_node);
4474 // If the terminal is dumb, we dont want to show the user all the output.
4475 var progress: std.Progress = .{ .dont_print_on_dumb = true };
4476 const main_progress_node = progress.start("", 0);
4477 defer main_progress_node.end();
4478 switch (color) {
4479 .off => {
4480 progress.terminal = null;
4481 },
4482 .on => {
4483 progress.terminal = std.io.getStdErr();
4484 progress.supports_ansi_escape_codes = true;
4485 },
4486 .auto => {},
4487 }
4488
4489 try comp.update(main_progress_node);
4490 }
44914391
4492 var errors = try comp.getAllErrorsAlloc();4392 var errors = try comp.getAllErrorsAlloc();
4493 defer errors.deinit(comp.gpa);4393 defer errors.deinit(comp.gpa);
...@@ -4498,7 +4398,12 @@ fn updateModule(comp: *Compilation, color: Color) !void {...@@ -4498,7 +4398,12 @@ fn updateModule(comp: *Compilation, color: Color) !void {
4498 }4398 }
4499}4399}
45004400
4501fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilation.CImportResult) !void {4401fn cmdTranslateC(
4402 comp: *Compilation,
4403 arena: Allocator,
4404 fancy_output: ?*Compilation.CImportResult,
4405 prog_node: std.Progress.Node,
4406) !void {
4502 if (build_options.only_core_functionality) @panic("@translate-c is not available in a zig2.c build");4407 if (build_options.only_core_functionality) @panic("@translate-c is not available in a zig2.c build");
4503 const color: Color = .auto;4408 const color: Color = .auto;
4504 assert(comp.c_source_files.len == 1);4409 assert(comp.c_source_files.len == 1);
...@@ -4559,6 +4464,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati...@@ -4559,6 +4464,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
4559 .root_src_path = "aro_translate_c.zig",4464 .root_src_path = "aro_translate_c.zig",
4560 .depend_on_aro = true,4465 .depend_on_aro = true,
4561 .capture = &stdout,4466 .capture = &stdout,
4467 .progress_node = prog_node,
4562 });4468 });
4563 break :f stdout;4469 break :f stdout;
4564 },4470 },
...@@ -4736,8 +4642,6 @@ const usage_build =...@@ -4736,8 +4642,6 @@ const usage_build =
4736;4642;
47374643
4738fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {4644fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4739 var progress: std.Progress = .{ .dont_print_on_dumb = true };
4740
4741 var build_file: ?[]const u8 = null;4645 var build_file: ?[]const u8 = null;
4742 var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);4646 var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
4743 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);4647 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
...@@ -4798,6 +4702,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4798,6 +4702,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4798 const results_tmp_file_nonce = Package.Manifest.hex64(std.crypto.random.int(u64));4702 const results_tmp_file_nonce = Package.Manifest.hex64(std.crypto.random.int(u64));
4799 try child_argv.append("-Z" ++ results_tmp_file_nonce);4703 try child_argv.append("-Z" ++ results_tmp_file_nonce);
48004704
4705 var color: Color = .auto;
4706
4801 {4707 {
4802 var i: usize = 0;4708 var i: usize = 0;
4803 while (i < args.len) : (i += 1) {4709 while (i < args.len) : (i += 1) {
...@@ -4882,6 +4788,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4882,6 +4788,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4882 verbose_cimport = true;4788 verbose_cimport = true;
4883 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {4789 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
4884 verbose_llvm_cpu_features = true;4790 verbose_llvm_cpu_features = true;
4791 } else if (mem.eql(u8, arg, "--color")) {
4792 if (i + 1 >= args.len) fatal("expected [auto|on|off] after {s}", .{arg});
4793 i += 1;
4794 color = std.meta.stringToEnum(Color, args[i]) orelse {
4795 fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] });
4796 };
4797 try child_argv.appendSlice(&.{ arg, args[i] });
4798 continue;
4885 } else if (mem.eql(u8, arg, "--seed")) {4799 } else if (mem.eql(u8, arg, "--seed")) {
4886 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});4800 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4887 i += 1;4801 i += 1;
...@@ -4895,7 +4809,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4895,7 +4809,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
48954809
4896 const work_around_btrfs_bug = native_os == .linux and4810 const work_around_btrfs_bug = native_os == .linux and
4897 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();4811 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
4898 const color: Color = .auto;4812 const root_prog_node = std.Progress.start(.{
4813 .disable_printing = (color == .off),
4814 .root_name = "Compile Build Script",
4815 });
4816 defer root_prog_node.end();
48994817
4900 const target_query: std.Target.Query = .{};4818 const target_query: std.Target.Query = .{};
4901 const resolved_target: Package.Module.ResolvedTarget = .{4819 const resolved_target: Package.Module.ResolvedTarget = .{
...@@ -5051,8 +4969,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5051,8 +4969,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5051 config,4969 config,
5052 );4970 );
5053 } else {4971 } else {
5054 const root_prog_node = progress.start("Fetch Packages", 0);4972 const fetch_prog_node = root_prog_node.start("Fetch Packages", 0);
5055 defer root_prog_node.end();4973 defer fetch_prog_node.end();
50564974
5057 var job_queue: Package.Fetch.JobQueue = .{4975 var job_queue: Package.Fetch.JobQueue = .{
5058 .http_client = &http_client,4976 .http_client = &http_client,
...@@ -5093,7 +5011,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5093,7 +5011,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5093 .lazy_status = .eager,5011 .lazy_status = .eager,
5094 .parent_package_root = build_mod.root,5012 .parent_package_root = build_mod.root,
5095 .parent_manifest_ast = null,5013 .parent_manifest_ast = null,
5096 .prog_node = root_prog_node,5014 .prog_node = fetch_prog_node,
5097 .job_queue = &job_queue,5015 .job_queue = &job_queue,
5098 .omit_missing_hash_error = true,5016 .omit_missing_hash_error = true,
5099 .allow_missing_paths_field = false,5017 .allow_missing_paths_field = false,
...@@ -5232,7 +5150,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5232,7 +5150,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5232 };5150 };
5233 defer comp.destroy();5151 defer comp.destroy();
52345152
5235 updateModule(comp, color) catch |err| switch (err) {5153 updateModule(comp, color, root_prog_node) catch |err| switch (err) {
5236 error.SemanticAnalyzeFail => process.exit(2),5154 error.SemanticAnalyzeFail => process.exit(2),
5237 else => |e| return e,5155 else => |e| return e,
5238 };5156 };
...@@ -5250,7 +5168,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5250,7 +5168,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5250 child.stdout_behavior = .Inherit;5168 child.stdout_behavior = .Inherit;
5251 child.stderr_behavior = .Inherit;5169 child.stderr_behavior = .Inherit;
52525170
5253 const term = try child.spawnAndWait();5171 const term = t: {
5172 std.debug.lockStdErr();
5173 defer std.debug.unlockStdErr();
5174 break :t try child.spawnAndWait();
5175 };
5176
5254 switch (term) {5177 switch (term) {
5255 .Exited => |code| {5178 .Exited => |code| {
5256 if (code == 0) return cleanExit();5179 if (code == 0) return cleanExit();
...@@ -5326,8 +5249,9 @@ const JitCmdOptions = struct {...@@ -5326,8 +5249,9 @@ const JitCmdOptions = struct {
5326 prepend_zig_exe_path: bool = false,5249 prepend_zig_exe_path: bool = false,
5327 depend_on_aro: bool = false,5250 depend_on_aro: bool = false,
5328 capture: ?*[]u8 = null,5251 capture: ?*[]u8 = null,
5329 /// Send progress and error bundles via std.zig.Server over stdout5252 /// Send error bundles via std.zig.Server over stdout
5330 server: bool = false,5253 server: bool = false,
5254 progress_node: ?std.Progress.Node = null,
5331};5255};
53325256
5333fn jitCmd(5257fn jitCmd(
...@@ -5337,6 +5261,9 @@ fn jitCmd(...@@ -5337,6 +5261,9 @@ fn jitCmd(
5337 options: JitCmdOptions,5261 options: JitCmdOptions,
5338) !void {5262) !void {
5339 const color: Color = .auto;5263 const color: Color = .auto;
5264 const root_prog_node = if (options.progress_node) |node| node else std.Progress.start(.{
5265 .disable_printing = (color == .off),
5266 });
53405267
5341 const target_query: std.Target.Query = .{};5268 const target_query: std.Target.Query = .{};
5342 const resolved_target: Package.Module.ResolvedTarget = .{5269 const resolved_target: Package.Module.ResolvedTarget = .{
...@@ -5473,39 +5400,14 @@ fn jitCmd(...@@ -5473,39 +5400,14 @@ fn jitCmd(
5473 };5400 };
5474 defer comp.destroy();5401 defer comp.destroy();
54755402
5476 if (options.server and !builtin.single_threaded) {5403 if (options.server) {
5477 var reset: std.Thread.ResetEvent = .{};
5478 var progress: std.Progress = .{
5479 .terminal = null,
5480 .root = .{
5481 .context = undefined,
5482 .parent = null,
5483 .name = "",
5484 .unprotected_estimated_total_items = 0,
5485 .unprotected_completed_items = 0,
5486 },
5487 .columns_written = 0,
5488 .prev_refresh_timestamp = 0,
5489 .timer = null,
5490 .done = false,
5491 };
5492 const main_progress_node = &progress.root;
5493 main_progress_node.context = &progress;
5494 var server = std.zig.Server{5404 var server = std.zig.Server{
5495 .out = std.io.getStdOut(),5405 .out = std.io.getStdOut(),
5496 .in = undefined, // won't be receiving messages5406 .in = undefined, // won't be receiving messages
5497 .receive_fifo = undefined, // won't be receiving messages5407 .receive_fifo = undefined, // won't be receiving messages
5498 };5408 };
54995409
5500 var progress_thread = try std.Thread.spawn(.{}, progressThread, .{5410 try comp.update(root_prog_node);
5501 &progress, &server, &reset,
5502 });
5503 defer {
5504 reset.set();
5505 progress_thread.join();
5506 }
5507
5508 try comp.update(main_progress_node);
55095411
5510 var error_bundle = try comp.getAllErrorsAlloc();5412 var error_bundle = try comp.getAllErrorsAlloc();
5511 defer error_bundle.deinit(comp.gpa);5413 defer error_bundle.deinit(comp.gpa);
...@@ -5514,7 +5416,7 @@ fn jitCmd(...@@ -5514,7 +5416,7 @@ fn jitCmd(
5514 process.exit(2);5416 process.exit(2);
5515 }5417 }
5516 } else {5418 } else {
5517 updateModule(comp, color) catch |err| switch (err) {5419 updateModule(comp, color, root_prog_node) catch |err| switch (err) {
5518 error.SemanticAnalyzeFail => process.exit(2),5420 error.SemanticAnalyzeFail => process.exit(2),
5519 else => |e| return e,5421 else => |e| return e,
5520 };5422 };
...@@ -6963,8 +6865,9 @@ fn cmdFetch(...@@ -6963,8 +6865,9 @@ fn cmdFetch(
69636865
6964 try http_client.initDefaultProxies(arena);6866 try http_client.initDefaultProxies(arena);
69656867
6966 var progress: std.Progress = .{ .dont_print_on_dumb = true };6868 var root_prog_node = std.Progress.start(.{
6967 const root_prog_node = progress.start("Fetch", 0);6869 .root_name = "Fetch",
6870 });
6968 defer root_prog_node.end();6871 defer root_prog_node.end();
69696872
6970 var global_cache_directory: Compilation.Directory = l: {6873 var global_cache_directory: Compilation.Directory = l: {
...@@ -7028,8 +6931,8 @@ fn cmdFetch(...@@ -7028,8 +6931,8 @@ fn cmdFetch(
70286931
7029 const hex_digest = Package.Manifest.hexDigest(fetch.actual_hash);6932 const hex_digest = Package.Manifest.hexDigest(fetch.actual_hash);
70306933
7031 progress.done = true;6934 root_prog_node.end();
7032 progress.refresh();6935 root_prog_node = .{ .index = .none };
70336936
7034 const name = switch (save) {6937 const name = switch (save) {
7035 .no => {6938 .no => {
src/mingw.zig+3-3
...@@ -16,7 +16,7 @@ pub const CRTFile = enum {...@@ -16,7 +16,7 @@ pub const CRTFile = enum {
16 mingw32_lib,16 mingw32_lib,
17};17};
1818
19pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {19pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
20 if (!build_options.have_llvm) {20 if (!build_options.have_llvm) {
21 return error.ZigCompilerNotBuiltWithLLVMExtensions;21 return error.ZigCompilerNotBuiltWithLLVMExtensions;
22 }22 }
...@@ -234,8 +234,8 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -234,8 +234,8 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
234 const include_dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" });234 const include_dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" });
235235
236 if (comp.verbose_cc) print: {236 if (comp.verbose_cc) print: {
237 std.debug.getStderrMutex().lock();237 std.debug.lockStdErr();
238 defer std.debug.getStderrMutex().unlock();238 defer std.debug.unlockStdErr();
239 const stderr = std.io.getStdErr().writer();239 const stderr = std.io.getStdErr().writer();
240 nosuspend stderr.print("def file: {s}\n", .{def_file_path}) catch break :print;240 nosuspend stderr.print("def file: {s}\n", .{def_file_path}) catch break :print;
241 nosuspend stderr.print("include dir: {s}\n", .{include_dir}) catch break :print;241 nosuspend stderr.print("include dir: {s}\n", .{include_dir}) catch break :print;
src/musl.zig+1-1
...@@ -19,7 +19,7 @@ pub const CRTFile = enum {...@@ -19,7 +19,7 @@ pub const CRTFile = enum {
19 libc_so,19 libc_so,
20};20};
2121
22pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {22pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
23 if (!build_options.have_llvm) {23 if (!build_options.have_llvm) {
24 return error.ZigCompilerNotBuiltWithLLVMExtensions;24 return error.ZigCompilerNotBuiltWithLLVMExtensions;
25 }25 }
src/wasi_libc.zig+1-1
...@@ -57,7 +57,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co...@@ -57,7 +57,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co
57 };57 };
58}58}
5959
60pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progress.Node) !void {60pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
61 if (!build_options.have_llvm) {61 if (!build_options.have_llvm) {
62 return error.ZigCompilerNotBuiltWithLLVMExtensions;62 return error.ZigCompilerNotBuiltWithLLVMExtensions;
63 }63 }
test/src/Cases.zig+1-1
...@@ -561,7 +561,7 @@ pub fn lowerToTranslateCSteps(...@@ -561,7 +561,7 @@ pub fn lowerToTranslateCSteps(
561 for (self.translate.items) |case| switch (case.kind) {561 for (self.translate.items) |case| switch (case.kind) {
562 .run => |output| {562 .run => |output| {
563 if (translate_c_options.skip_run_translated_c) continue;563 if (translate_c_options.skip_run_translated_c) continue;
564 const annotated_case_name = b.fmt("run-translated-c {s}", .{case.name});564 const annotated_case_name = b.fmt("run-translated-c {s}", .{case.name});
565 for (test_filters) |test_filter| {565 for (test_filters) |test_filter| {
566 if (std.mem.indexOf(u8, annotated_case_name, test_filter)) |_| break;566 if (std.mem.indexOf(u8, annotated_case_name, test_filter)) |_| break;
567 } else if (test_filters.len > 0) continue;567 } else if (test_filters.len > 0) continue;
test/src/RunTranslatedC.zig+1
...@@ -91,6 +91,7 @@ pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void {...@@ -91,6 +91,7 @@ pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void {
91 run.expectStdErrEqual("");91 run.expectStdErrEqual("");
92 }92 }
93 run.expectStdOutEqual(case.expected_stdout);93 run.expectStdOutEqual(case.expected_stdout);
94 run.skip_foreign_checks = true;
9495
95 self.step.dependOn(&run.step);96 self.step.dependOn(&run.step);
96}97}
test/standalone/cmakedefine/build.zig+1-1
...@@ -80,7 +80,7 @@ pub fn build(b: *std.Build) void {...@@ -80,7 +80,7 @@ pub fn build(b: *std.Build) void {
80 test_step.dependOn(&wrapper_header.step);80 test_step.dependOn(&wrapper_header.step);
81}81}
8282
83fn compare_headers(step: *std.Build.Step, prog_node: *std.Progress.Node) !void {83fn compare_headers(step: *std.Build.Step, prog_node: std.Progress.Node) !void {
84 _ = prog_node;84 _ = prog_node;
85 const allocator = step.owner.allocator;85 const allocator = step.owner.allocator;
86 const expected_fmt = "expected_{s}";86 const expected_fmt = "expected_{s}";
test/standalone/empty_env/build.zig+1
...@@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void {...@@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void {
2121
22 const run = b.addRunArtifact(main);22 const run = b.addRunArtifact(main);
23 run.clearEnvironment();23 run.clearEnvironment();
24 run.disable_zig_progress = true;
2425
25 test_step.dependOn(&run.step);26 test_step.dependOn(&run.step);
26}27}