authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-16 12:46:24+01:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-08-17 18:50:10-04:00
log65cbdefe4d923efd8fd2cfb555cc02a52c5635fc
tree4d317a1c0c0916e52eeee3e7daee5e844020ba0b
parent5a8780838fb3fc18f7c7ebfbf164b37032c2f829

tools: add CBE option to incr-check


1 files changed, 120 insertions(+), 31 deletions(-)

tools/incr-check.zig+120-31
...@@ -2,7 +2,13 @@ const std = @import("std");...@@ -2,7 +2,13 @@ const std = @import("std");
2const fatal = std.process.fatal;2const fatal = std.process.fatal;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
44
5const usage = "usage: incr-check <zig binary path> <input file> [-fno-emit-bin] [--zig-lib-dir lib] [--debug-zcu]";5const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--emit none|bin|c] [--zig-cc-binary /path/to/zig]";
6
7const EmitMode = enum {
8 none,
9 bin,
10 c,
11};
612
7pub fn main() !void {13pub fn main() !void {
8 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);14 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
...@@ -12,19 +18,24 @@ pub fn main() !void {...@@ -12,19 +18,24 @@ pub fn main() !void {
12 var opt_zig_exe: ?[]const u8 = null;18 var opt_zig_exe: ?[]const u8 = null;
13 var opt_input_file_name: ?[]const u8 = null;19 var opt_input_file_name: ?[]const u8 = null;
14 var opt_lib_dir: ?[]const u8 = null;20 var opt_lib_dir: ?[]const u8 = null;
15 var no_bin = false;21 var opt_cc_zig: ?[]const u8 = null;
22 var emit: EmitMode = .bin;
16 var debug_zcu = false;23 var debug_zcu = false;
1724
18 var arg_it = try std.process.argsWithAllocator(arena);25 var arg_it = try std.process.argsWithAllocator(arena);
19 _ = arg_it.skip();26 _ = arg_it.skip();
20 while (arg_it.next()) |arg| {27 while (arg_it.next()) |arg| {
21 if (arg.len > 0 and arg[0] == '-') {28 if (arg.len > 0 and arg[0] == '-') {
22 if (std.mem.eql(u8, arg, "-fno-emit-bin")) {29 if (std.mem.eql(u8, arg, "--emit")) {
23 no_bin = true;30 const emit_str = arg_it.next() orelse fatal("expected arg after '--emit'\n{s}", .{usage});
24 } else if (std.mem.eql(u8, arg, "--debug-zcu")) {31 emit = std.meta.stringToEnum(EmitMode, emit_str) orelse
25 debug_zcu = true;32 fatal("invalid emit mode '{s}'\n{s}", .{ emit_str, usage });
26 } else if (std.mem.eql(u8, arg, "--zig-lib-dir")) {33 } else if (std.mem.eql(u8, arg, "--zig-lib-dir")) {
27 opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage});34 opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage});
35 } else if (std.mem.eql(u8, arg, "--debug-zcu")) {
36 debug_zcu = true;
37 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {
38 opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage});
28 } else {39 } else {
29 fatal("unknown option '{s}'\n{s}", .{ arg, usage });40 fatal("unknown option '{s}'\n{s}", .{ arg, usage });
30 }41 }
...@@ -51,20 +62,19 @@ pub fn main() !void {...@@ -51,20 +62,19 @@ pub fn main() !void {
51 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);62 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);
52 const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{});63 const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{});
5364
54 if (opt_lib_dir) |lib_dir| {
55 if (!std.fs.path.isAbsolute(lib_dir)) {
56 // The cwd of the subprocess is within the tmp dir, so prepend `..` to the path.
57 opt_lib_dir = try std.fs.path.join(arena, &.{ "..", lib_dir });
58 }
59 }
60
61 const child_prog_node = prog_node.start("zig build-exe", 0);65 const child_prog_node = prog_node.start("zig build-exe", 0);
62 defer child_prog_node.end();66 defer child_prog_node.end();
6367
68 // Convert paths to be relative to the cwd of the subprocess.
69 const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe);
70 const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir|
71 try std.fs.path.relative(arena, tmp_dir_path, lib_dir)
72 else
73 null;
74
64 var child_args: std.ArrayListUnmanaged([]const u8) = .{};75 var child_args: std.ArrayListUnmanaged([]const u8) = .{};
65 try child_args.appendSlice(arena, &.{76 try child_args.appendSlice(arena, &.{
66 // Convert incr-check-relative path to subprocess-relative path.77 resolved_zig_exe,
67 try std.fs.path.relative(arena, tmp_dir_path, zig_exe),
68 "build-exe",78 "build-exe",
69 case.root_source_file,79 case.root_source_file,
70 "-fincremental",80 "-fincremental",
...@@ -76,13 +86,13 @@ pub fn main() !void {...@@ -76,13 +86,13 @@ pub fn main() !void {
76 ".global_cache",86 ".global_cache",
77 "--listen=-",87 "--listen=-",
78 });88 });
79 if (opt_lib_dir) |lib_dir| {89 if (opt_resolved_lib_dir) |resolved_lib_dir| {
80 try child_args.appendSlice(arena, &.{ "--zig-lib-dir", lib_dir });90 try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir });
81 }91 }
82 if (no_bin) {92 switch (emit) {
83 try child_args.append(arena, "-fno-emit-bin");93 .bin => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }),
84 } else {94 .none => try child_args.append(arena, "-fno-emit-bin"),
85 try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" });95 .c => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }),
86 }96 }
87 if (debug_zcu) {97 if (debug_zcu) {
88 try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" });98 try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" });
...@@ -96,6 +106,24 @@ pub fn main() !void {...@@ -96,6 +106,24 @@ pub fn main() !void {
96 child.cwd_dir = tmp_dir;106 child.cwd_dir = tmp_dir;
97 child.cwd = tmp_dir_path;107 child.cwd = tmp_dir_path;
98108
109 var cc_child_args: std.ArrayListUnmanaged([]const u8) = .{};
110 if (emit == .c) {
111 const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe|
112 try std.fs.path.relative(arena, tmp_dir_path, cc_zig_exe)
113 else
114 resolved_zig_exe;
115
116 try cc_child_args.appendSlice(arena, &.{
117 resolved_cc_zig_exe,
118 "cc",
119 "-target",
120 case.target_query,
121 "-I",
122 opt_resolved_lib_dir orelse fatal("'--zig-lib-dir' required when using '--emit c'", .{}),
123 "-o",
124 });
125 }
126
99 var eval: Eval = .{127 var eval: Eval = .{
100 .arena = arena,128 .arena = arena,
101 .case = case,129 .case = case,
...@@ -103,6 +131,8 @@ pub fn main() !void {...@@ -103,6 +131,8 @@ pub fn main() !void {
103 .tmp_dir_path = tmp_dir_path,131 .tmp_dir_path = tmp_dir_path,
104 .child = &child,132 .child = &child,
105 .allow_stderr = debug_zcu,133 .allow_stderr = debug_zcu,
134 .emit = emit,
135 .cc_child_args = &cc_child_args,
106 };136 };
107137
108 try child.spawn();138 try child.spawn();
...@@ -123,7 +153,7 @@ pub fn main() !void {...@@ -123,7 +153,7 @@ pub fn main() !void {
123153
124 eval.write(update);154 eval.write(update);
125 try eval.requestUpdate();155 try eval.requestUpdate();
126 try eval.check(&poller, update);156 try eval.check(&poller, update, update_node);
127 }157 }
128158
129 try eval.end(&poller);159 try eval.end(&poller);
...@@ -138,6 +168,10 @@ const Eval = struct {...@@ -138,6 +168,10 @@ const Eval = struct {
138 tmp_dir_path: []const u8,168 tmp_dir_path: []const u8,
139 child: *std.process.Child,169 child: *std.process.Child,
140 allow_stderr: bool,170 allow_stderr: bool,
171 emit: EmitMode,
172 /// When `emit == .c`, this contains the first few arguments to `zig cc` to build the generated binary.
173 /// The arguments `out.c in.c` must be appended before spawning the subprocess.
174 cc_child_args: *std.ArrayListUnmanaged([]const u8),
141175
142 const StreamEnum = enum { stdout, stderr };176 const StreamEnum = enum { stdout, stderr };
143 const Poller = std.io.Poller(StreamEnum);177 const Poller = std.io.Poller(StreamEnum);
...@@ -159,7 +193,7 @@ const Eval = struct {...@@ -159,7 +193,7 @@ const Eval = struct {
159 }193 }
160 }194 }
161195
162 fn check(eval: *Eval, poller: *Poller, update: Case.Update) !void {196 fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void {
163 const arena = eval.arena;197 const arena = eval.arena;
164 const Header = std.zig.Server.Message.Header;198 const Header = std.zig.Server.Message.Header;
165 const stdout = poller.fifo(.stdout);199 const stdout = poller.fifo(.stdout);
...@@ -201,12 +235,7 @@ const Eval = struct {...@@ -201,12 +235,7 @@ const Eval = struct {
201 }235 }
202 if (result_error_bundle.errorMessageCount() == 0) {236 if (result_error_bundle.errorMessageCount() == 0) {
203 // Empty bundle indicates successful update in a `-fno-emit-bin` build.237 // Empty bundle indicates successful update in a `-fno-emit-bin` build.
204 // We can't do a full success check since we don't have a binary, but let's238 try eval.checkSuccessOutcome(update, null, prog_node);
205 // at least check that no errors were expected.
206 switch (update.outcome) {
207 .unknown, .stdout, .exit_code => {},
208 .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),
209 }
210 } else {239 } else {
211 try eval.checkErrorOutcome(update, result_error_bundle);240 try eval.checkErrorOutcome(update, result_error_bundle);
212 }241 }
...@@ -227,7 +256,7 @@ const Eval = struct {...@@ -227,7 +256,7 @@ const Eval = struct {
227 fatal("emit_bin_path included unexpected stderr:\n{s}", .{stderr_data});256 fatal("emit_bin_path included unexpected stderr:\n{s}", .{stderr_data});
228 }257 }
229 }258 }
230 try eval.checkSuccessOutcome(update, result_binary);259 try eval.checkSuccessOutcome(update, result_binary, prog_node);
231 // This message indicates the end of the update.260 // This message indicates the end of the update.
232 stdout.discard(body.len);261 stdout.discard(body.len);
233 return;262 return;
...@@ -270,12 +299,28 @@ const Eval = struct {...@@ -270,12 +299,28 @@ const Eval = struct {
270 }299 }
271 }300 }
272301
273 fn checkSuccessOutcome(eval: *Eval, update: Case.Update, binary_path: []const u8) !void {302 fn checkSuccessOutcome(eval: *Eval, update: Case.Update, opt_emitted_path: ?[]const u8, prog_node: std.Progress.Node) !void {
274 switch (update.outcome) {303 switch (update.outcome) {
275 .unknown => return,304 .unknown => return,
276 .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),305 .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),
277 .stdout, .exit_code => {},306 .stdout, .exit_code => {},
278 }307 }
308 const emitted_path = opt_emitted_path orelse {
309 std.debug.assert(eval.emit == .none);
310 return;
311 };
312
313 const binary_path = switch (eval.emit) {
314 .none => unreachable,
315 .bin => emitted_path,
316 .c => bin: {
317 const rand_int = std.crypto.random.int(u64);
318 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
319 try eval.buildCOutput(update, emitted_path, out_bin_name, prog_node);
320 break :bin out_bin_name;
321 },
322 };
323
279 const result = std.process.Child.run(.{324 const result = std.process.Child.run(.{
280 .allocator = eval.arena,325 .allocator = eval.arena,
281 .argv = &.{binary_path},326 .argv = &.{binary_path},
...@@ -345,6 +390,50 @@ const Eval = struct {...@@ -345,6 +390,50 @@ const Eval = struct {
345 fatal("unexpected stderr:\n{s}", .{stderr_data});390 fatal("unexpected stderr:\n{s}", .{stderr_data});
346 }391 }
347 }392 }
393
394 fn buildCOutput(eval: *Eval, update: Case.Update, c_path: []const u8, out_path: []const u8, prog_node: std.Progress.Node) !void {
395 std.debug.assert(eval.cc_child_args.items.len > 0);
396
397 const child_prog_node = prog_node.start("build cbe output", 0);
398 defer child_prog_node.end();
399
400 try eval.cc_child_args.appendSlice(eval.arena, &.{ out_path, c_path });
401 defer eval.cc_child_args.items.len -= 2;
402
403 const result = std.process.Child.run(.{
404 .allocator = eval.arena,
405 .argv = eval.cc_child_args.items,
406 .cwd_dir = eval.tmp_dir,
407 .cwd = eval.tmp_dir_path,
408 .progress_node = child_prog_node,
409 }) catch |err| {
410 fatal("update '{s}': failed to spawn zig cc for '{s}': {s}", .{
411 update.name, c_path, @errorName(err),
412 });
413 };
414 switch (result.term) {
415 .Exited => |code| if (code != 0) {
416 if (result.stderr.len != 0) {
417 std.log.err("update '{s}': zig cc stderr:\n{s}", .{
418 update.name, result.stderr,
419 });
420 }
421 fatal("update '{s}': zig cc for '{s}' failed with code {d}", .{
422 update.name, c_path, code,
423 });
424 },
425 .Signal, .Stopped, .Unknown => {
426 if (result.stderr.len != 0) {
427 std.log.err("update '{s}': zig cc stderr:\n{s}", .{
428 update.name, result.stderr,
429 });
430 }
431 fatal("update '{s}': zig cc for '{s}' terminated unexpectedly", .{
432 update.name, c_path,
433 });
434 },
435 }
436 }
348};437};
349438
350const Case = struct {439const Case = struct {