authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-09 18:13:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-15 10:48:14-07:00
log7cc4a6965c28e427cbfba57a985f837734d6257e
tree94c753ad16675b7cf2441b117f02282be20fae34
parent3186658e602b13a98c388872bbdc0b5cc1eb9267

build runner enhancements in preparation for test-cases

* std.zig.ErrorBundle: support rendering options for whether to include the reference trace, whether to include the source line, and TTY configuration. * build runner: don't print progress in dumb terminals * std.Build.CompileStep: - add a way to expect compilation errors via the new `expect_errors` field. This is an advanced setting that can change the intent of the CompileStep. If this slice has nonzero length, it means that the CompileStep exists to check for compile errors and return *success* if they match, and failure otherwise. - remove the object format parameter from `checkObject`. The object format is known based on the CompileStep's target. - Avoid passing -L and -I flags for nonexistent directories within search_prefixes. This prevents a warning, that should probably be upgraded to an error in Zig's CLI parsing code, when the linker sees an -L directory that does not exist. * std.Build.Step: - When spawning the zig compiler process, takes advantage of the new `std.Progress.Node.setName` API to avoid ticking up a meaningless number at every progress update.

6 files changed, 173 insertions(+), 49 deletions(-)

lib/build_runner.zig+21-9
......@@ -84,8 +84,6 @@ pub fn main() !void {
8484 );
8585 defer builder.destroy();
8686
87 const Color = enum { auto, off, on };
88
8987 var targets = ArrayList([]const u8).init(arena);
9088 var debug_log_scopes = ArrayList([]const u8).init(arena);
9189 var thread_pool_options: std.Thread.Pool.Options = .{ .allocator = arena };
......@@ -273,13 +271,9 @@ pub fn main() !void {
273271 }
274272
275273 const stderr = std.io.getStdErr();
276 const ttyconf: std.debug.TTY.Config = switch (color) {
277 .auto => std.debug.detectTTYConfig(stderr),
278 .on => .escape_codes,
279 .off => .no_color,
280 };
274 const ttyconf = get_tty_conf(color, stderr);
281275
282 var progress: std.Progress = .{};
276 var progress: std.Progress = .{ .dont_print_on_dumb = true };
283277 const main_progress_node = progress.start("", 0);
284278
285279 builder.debug_log_scopes = debug_log_scopes.items;
......@@ -498,7 +492,7 @@ fn runStepNames(
498492 if (total_compile_errors > 0) {
499493 for (compile_error_steps.items) |s| {
500494 if (s.result_error_bundle.errorMessageCount() > 0) {
501 s.result_error_bundle.renderToStdErr(ttyconf);
495 s.result_error_bundle.renderToStdErr(renderOptions(ttyconf));
502496 }
503497 }
504498
......@@ -961,3 +955,21 @@ fn cleanExit() void {
961955 // of calling exit.
962956 process.exit(0);
963957}
958
959const Color = enum { auto, off, on };
960
961fn get_tty_conf(color: Color, stderr: std.fs.File) std.debug.TTY.Config {
962 return switch (color) {
963 .auto => std.debug.detectTTYConfig(stderr),
964 .on => .escape_codes,
965 .off => .no_color,
966 };
967}
968
969fn renderOptions(ttyconf: std.debug.TTY.Config) std.zig.ErrorBundle.RenderOptions {
970 return .{
971 .ttyconf = ttyconf,
972 .include_source_line = ttyconf != .no_color,
973 .include_reference_trace = ttyconf != .no_color,
974 };
975}
lib/std/Build/CompileStep.zig+102-11
......@@ -207,6 +207,12 @@ want_lto: ?bool = null,
207207use_llvm: ?bool = null,
208208use_lld: ?bool = null,
209209
210/// This is an advanced setting that can change the intent of this CompileStep.
211/// If this slice has nonzero length, it means that this CompileStep exists to
212/// check for compile errors and return *success* if they match, and failure
213/// otherwise.
214expect_errors: []const []const u8 = &.{},
215
210216output_path_source: GeneratedFile,
211217output_lib_path_source: GeneratedFile,
212218output_h_path_source: GeneratedFile,
......@@ -552,8 +558,8 @@ pub fn run(cs: *CompileStep) *RunStep {
552558 return cs.step.owner.addRunArtifact(cs);
553559}
554560
555pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
556 return CheckObjectStep.create(self.step.owner, self.getOutputSource(), obj_format);
561pub fn checkObject(self: *CompileStep) *CheckObjectStep {
562 return CheckObjectStep.create(self.step.owner, self.getOutputSource(), self.target_info.target.ofmt);
557563}
558564
559565pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
......@@ -1838,14 +1844,38 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
18381844 }
18391845
18401846 for (b.search_prefixes.items) |search_prefix| {
1841 try zig_args.append("-L");
1842 try zig_args.append(b.pathJoin(&.{
1843 search_prefix, "lib",
1844 }));
1845 try zig_args.append("-I");
1846 try zig_args.append(b.pathJoin(&.{
1847 search_prefix, "include",
1848 }));
1847 var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| {
1848 return step.fail("unable to open prefix directory '{s}': {s}", .{
1849 search_prefix, @errorName(err),
1850 });
1851 };
1852 defer prefix_dir.close();
1853
1854 // Avoid passing -L and -I flags for nonexistent directories.
1855 // This prevents a warning, that should probably be upgraded to an error in Zig's
1856 // CLI parsing code, when the linker sees an -L directory that does not exist.
1857
1858 if (prefix_dir.accessZ("lib", .{})) |_| {
1859 try zig_args.appendSlice(&.{
1860 "-L", try fs.path.join(b.allocator, &.{ search_prefix, "lib" }),
1861 });
1862 } else |err| switch (err) {
1863 error.FileNotFound => {},
1864 else => |e| return step.fail("unable to access '{s}/lib' directory: {s}", .{
1865 search_prefix, @errorName(e),
1866 }),
1867 }
1868
1869 if (prefix_dir.accessZ("include", .{})) |_| {
1870 try zig_args.appendSlice(&.{
1871 "-I", try fs.path.join(b.allocator, &.{ search_prefix, "include" }),
1872 });
1873 } else |err| switch (err) {
1874 error.FileNotFound => {},
1875 else => |e| return step.fail("unable to access '{s}/include' directory: {s}", .{
1876 search_prefix, @errorName(e),
1877 }),
1878 }
18491879 }
18501880
18511881 try addFlag(&zig_args, "valgrind", self.valgrind_support);
......@@ -1943,7 +1973,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
19431973 try zig_args.append(resolved_args_file);
19441974 }
19451975
1946 const output_bin_path = try step.evalZigProcess(zig_args.items, prog_node);
1976 const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) {
1977 error.NeedCompileErrorCheck => {
1978 assert(self.expect_errors.len != 0);
1979 try checkCompileErrors(self);
1980 return;
1981 },
1982 else => |e| return e,
1983 };
19471984 const build_output_dir = fs.path.dirname(output_bin_path).?;
19481985
19491986 if (self.output_dir) |output_dir| {
......@@ -2178,3 +2215,57 @@ const TransitiveDeps = struct {
21782215 }
21792216 }
21802217};
2218
2219fn checkCompileErrors(self: *CompileStep) !void {
2220 // Clear this field so that it does not get printed by the build runner.
2221 const actual_eb = self.step.result_error_bundle;
2222 self.step.result_error_bundle = std.zig.ErrorBundle.empty;
2223
2224 const arena = self.step.owner.allocator;
2225
2226 var actual_stderr_list = std.ArrayList(u8).init(arena);
2227 try actual_eb.renderToWriter(.{
2228 .ttyconf = .no_color,
2229 .include_reference_trace = false,
2230 .include_source_line = false,
2231 }, actual_stderr_list.writer());
2232 const actual_stderr = try actual_stderr_list.toOwnedSlice();
2233
2234 // Render the expected lines into a string that we can compare verbatim.
2235 var expected_generated = std.ArrayList(u8).init(arena);
2236
2237 var actual_line_it = mem.split(u8, actual_stderr, "\n");
2238 for (self.expect_errors) |expect_line| {
2239 const actual_line = actual_line_it.next() orelse {
2240 try expected_generated.appendSlice(expect_line);
2241 try expected_generated.append('\n');
2242 continue;
2243 };
2244 if (mem.endsWith(u8, actual_line, expect_line)) {
2245 try expected_generated.appendSlice(actual_line);
2246 try expected_generated.append('\n');
2247 continue;
2248 }
2249 if (mem.startsWith(u8, expect_line, ":?:?: ")) {
2250 if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) {
2251 try expected_generated.appendSlice(actual_line);
2252 try expected_generated.append('\n');
2253 continue;
2254 }
2255 }
2256 try expected_generated.appendSlice(expect_line);
2257 try expected_generated.append('\n');
2258 }
2259
2260 if (mem.eql(u8, expected_generated.items, actual_stderr)) return;
2261
2262 // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
2263 return self.step.fail(
2264 \\
2265 \\========= expected: =====================
2266 \\{s}
2267 \\========= but found: ====================
2268 \\{s}
2269 \\=========================================
2270 , .{ expected_generated.items, actual_stderr });
2271}
lib/std/Build/Step.zig+15-5
......@@ -295,8 +295,8 @@ pub fn evalZigProcess(
295295
296296 var node_name: std.ArrayListUnmanaged(u8) = .{};
297297 defer node_name.deinit(gpa);
298 var sub_prog_node: ?std.Progress.Node = null;
299 defer if (sub_prog_node) |*n| n.end();
298 var sub_prog_node = prog_node.start("", 0);
299 defer sub_prog_node.end();
300300
301301 const stdout = poller.fifo(.stdout);
302302
......@@ -336,11 +336,9 @@ pub fn evalZigProcess(
336336 };
337337 },
338338 .progress => {
339 if (sub_prog_node) |*n| n.end();
340339 node_name.clearRetainingCapacity();
341340 try node_name.appendSlice(gpa, body);
342 sub_prog_node = prog_node.start(node_name.items, 0);
343 sub_prog_node.?.activate();
341 sub_prog_node.setName(node_name.items);
344342 },
345343 .emit_bin_path => {
346344 const EbpHdr = std.zig.Server.Message.EmitBinPath;
......@@ -371,6 +369,18 @@ pub fn evalZigProcess(
371369 s.result_duration_ns = timer.read();
372370 s.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0;
373371
372 // Special handling for CompileStep that is expecting compile errors.
373 if (s.cast(Build.CompileStep)) |compile| switch (term) {
374 .Exited => {
375 // Note that the exit code may be 0 in this case due to the
376 // compiler server protocol.
377 if (compile.expect_errors.len != 0 and s.result_error_bundle.errorMessageCount() > 0) {
378 return error.NeedCompileErrorCheck;
379 }
380 },
381 else => {},
382 };
383
374384 try handleChildProcessTerm(s, term, null, argv);
375385
376386 if (s.result_error_bundle.errorMessageCount() > 0) {
lib/std/zig/ErrorBundle.zig+16-13
......@@ -141,32 +141,35 @@ pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 {
141141 return string_bytes[index..end :0];
142142}
143143
144pub fn renderToStdErr(eb: ErrorBundle, ttyconf: std.debug.TTY.Config) void {
144pub const RenderOptions = struct {
145 ttyconf: std.debug.TTY.Config,
146 include_reference_trace: bool = true,
147 include_source_line: bool = true,
148};
149
150pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
145151 std.debug.getStderrMutex().lock();
146152 defer std.debug.getStderrMutex().unlock();
147153 const stderr = std.io.getStdErr();
148 return renderToWriter(eb, ttyconf, stderr.writer()) catch return;
154 return renderToWriter(eb, options, stderr.writer()) catch return;
149155}
150156
151pub fn renderToWriter(
152 eb: ErrorBundle,
153 ttyconf: std.debug.TTY.Config,
154 writer: anytype,
155) anyerror!void {
157pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, writer: anytype) anyerror!void {
156158 for (eb.getMessages()) |err_msg| {
157 try renderErrorMessageToWriter(eb, err_msg, ttyconf, writer, "error", .Red, 0);
159 try renderErrorMessageToWriter(eb, options, err_msg, writer, "error", .Red, 0);
158160 }
159161}
160162
161163fn renderErrorMessageToWriter(
162164 eb: ErrorBundle,
165 options: RenderOptions,
163166 err_msg_index: MessageIndex,
164 ttyconf: std.debug.TTY.Config,
165167 stderr: anytype,
166168 kind: []const u8,
167169 color: std.debug.TTY.Color,
168170 indent: usize,
169171) anyerror!void {
172 const ttyconf = options.ttyconf;
170173 var counting_writer = std.io.countingWriter(stderr);
171174 const counting_stderr = counting_writer.writer();
172175 const err_msg = eb.getErrorMessage(err_msg_index);
......@@ -196,7 +199,7 @@ fn renderErrorMessageToWriter(
196199 try stderr.print(" ({d} times)\n", .{err_msg.count});
197200 }
198201 try ttyconf.setColor(stderr, .Reset);
199 if (src.data.source_line != 0) {
202 if (src.data.source_line != 0 and options.include_source_line) {
200203 const line = eb.nullTerminatedString(src.data.source_line);
201204 for (line) |b| switch (b) {
202205 '\t' => try stderr.writeByte(' '),
......@@ -216,9 +219,9 @@ fn renderErrorMessageToWriter(
216219 try ttyconf.setColor(stderr, .Reset);
217220 }
218221 for (eb.getNotes(err_msg_index)) |note| {
219 try renderErrorMessageToWriter(eb, note, ttyconf, stderr, "note", .Cyan, indent);
222 try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent);
220223 }
221 if (src.data.reference_trace_len > 0) {
224 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
222225 try ttyconf.setColor(stderr, .Reset);
223226 try ttyconf.setColor(stderr, .Dim);
224227 try stderr.print("referenced by:\n", .{});
......@@ -266,7 +269,7 @@ fn renderErrorMessageToWriter(
266269 }
267270 try ttyconf.setColor(stderr, .Reset);
268271 for (eb.getNotes(err_msg_index)) |note| {
269 try renderErrorMessageToWriter(eb, note, ttyconf, stderr, "note", .Cyan, indent + 4);
272 try renderErrorMessageToWriter(eb, options, note, stderr, "note", .Cyan, indent + 4);
270273 }
271274 }
272275}
src/Sema.zig+1-1
......@@ -2220,7 +2220,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
22202220 Compilation.addModuleErrorMsg(&wip_errors, err_msg.*) catch unreachable;
22212221 std.debug.print("compile error during Sema:\n", .{});
22222222 var error_bundle = wip_errors.toOwnedBundle() catch unreachable;
2223 error_bundle.renderToStdErr(.no_color);
2223 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
22242224 crash_report.compilerPanic("unexpected compile error occurred", null, null);
22252225 }
22262226
src/main.zig+18-10
......@@ -4082,7 +4082,7 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void
40824082 defer errors.deinit(comp.gpa);
40834083
40844084 if (errors.errorMessageCount() > 0) {
4085 errors.renderToStdErr(get_tty_conf(comp.color));
4085 errors.renderToStdErr(renderOptions(comp.color));
40864086 const log_text = comp.getCompileLogOutput();
40874087 if (log_text.len != 0) {
40884088 std.debug.print("\nCompile Log Output:\n{s}", .{log_text});
......@@ -4711,7 +4711,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
47114711 if (wip_errors.root_list.items.len > 0) {
47124712 var errors = try wip_errors.toOwnedBundle();
47134713 defer errors.deinit(gpa);
4714 errors.renderToStdErr(get_tty_conf(color));
4714 errors.renderToStdErr(renderOptions(color));
47154715 process.exit(1);
47164716 }
47174717 try fetch_result;
......@@ -4974,7 +4974,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
49744974 try Compilation.addZirErrorMessages(&wip_errors, &file);
49754975 var error_bundle = try wip_errors.toOwnedBundle();
49764976 defer error_bundle.deinit(gpa);
4977 error_bundle.renderToStdErr(get_tty_conf(color));
4977 error_bundle.renderToStdErr(renderOptions(color));
49784978 process.exit(2);
49794979 }
49804980 } else if (tree.errors.len != 0) {
......@@ -5180,7 +5180,7 @@ fn fmtPathFile(
51805180 try Compilation.addZirErrorMessages(&wip_errors, &file);
51815181 var error_bundle = try wip_errors.toOwnedBundle();
51825182 defer error_bundle.deinit(gpa);
5183 error_bundle.renderToStdErr(get_tty_conf(fmt.color));
5183 error_bundle.renderToStdErr(renderOptions(fmt.color));
51845184 fmt.any_error = true;
51855185 }
51865186 }
......@@ -5217,7 +5217,7 @@ fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Co
52175217
52185218 var error_bundle = try wip_errors.toOwnedBundle();
52195219 defer error_bundle.deinit(gpa);
5220 error_bundle.renderToStdErr(get_tty_conf(color));
5220 error_bundle.renderToStdErr(renderOptions(color));
52215221}
52225222
52235223pub fn putAstErrorsIntoBundle(
......@@ -5834,7 +5834,7 @@ pub fn cmdAstCheck(
58345834 try Compilation.addZirErrorMessages(&wip_errors, &file);
58355835 var error_bundle = try wip_errors.toOwnedBundle();
58365836 defer error_bundle.deinit(gpa);
5837 error_bundle.renderToStdErr(get_tty_conf(color));
5837 error_bundle.renderToStdErr(renderOptions(color));
58385838 process.exit(1);
58395839 }
58405840
......@@ -5892,6 +5892,7 @@ pub fn cmdChangelist(
58925892 arena: Allocator,
58935893 args: []const []const u8,
58945894) !void {
5895 const color: Color = .auto;
58955896 const Zir = @import("Zir.zig");
58965897
58975898 const old_source_file = args[0];
......@@ -5948,10 +5949,9 @@ pub fn cmdChangelist(
59485949 try wip_errors.init(gpa);
59495950 defer wip_errors.deinit();
59505951 try Compilation.addZirErrorMessages(&wip_errors, &file);
5951 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
59525952 var error_bundle = try wip_errors.toOwnedBundle();
59535953 defer error_bundle.deinit(gpa);
5954 error_bundle.renderToStdErr(ttyconf);
5954 error_bundle.renderToStdErr(renderOptions(color));
59555955 process.exit(1);
59565956 }
59575957
......@@ -5984,10 +5984,9 @@ pub fn cmdChangelist(
59845984 try wip_errors.init(gpa);
59855985 defer wip_errors.deinit();
59865986 try Compilation.addZirErrorMessages(&wip_errors, &file);
5987 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
59885987 var error_bundle = try wip_errors.toOwnedBundle();
59895988 defer error_bundle.deinit(gpa);
5990 error_bundle.renderToStdErr(ttyconf);
5989 error_bundle.renderToStdErr(renderOptions(color));
59915990 process.exit(1);
59925991 }
59935992
......@@ -6256,3 +6255,12 @@ fn get_tty_conf(color: Color) std.debug.TTY.Config {
62566255 .off => .no_color,
62576256 };
62586257}
6258
6259fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
6260 const ttyconf = get_tty_conf(color);
6261 return .{
6262 .ttyconf = ttyconf,
6263 .include_source_line = ttyconf != .no_color,
6264 .include_reference_trace = ttyconf != .no_color,
6265 };
6266}