diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2024-08-16 12:46:24 +0100 |
|---|---|---|
| committer | Jacob Young <jacobly0@users.noreply.github.com> | 2024-08-17 18:50:10 -0400 |
| commit | 65cbdefe4d923efd8fd2cfb555cc02a52c5635fc (patch) | |
| tree | 4d317a1c0c0916e52eeee3e7daee5e844020ba0b /tools | |
| parent | 5a8780838fb3fc18f7c7ebfbf164b37032c2f829 (diff) | |
| download | zig-65cbdefe4d923efd8fd2cfb555cc02a52c5635fc.tar.gz zig-65cbdefe4d923efd8fd2cfb555cc02a52c5635fc.zip | |
tools: add CBE option to incr-check
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/incr-check.zig | 151 |
1 files changed, 120 insertions, 31 deletions
diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 6bf2de921a..0386f1a12d 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -2,7 +2,13 @@ const std = @import("std"); const fatal = std.process.fatal; const Allocator = std.mem.Allocator; -const usage = "usage: incr-check <zig binary path> <input file> [-fno-emit-bin] [--zig-lib-dir lib] [--debug-zcu]"; +const 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]"; + +const EmitMode = enum { + none, + bin, + c, +}; pub fn main() !void { var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); @@ -12,19 +18,24 @@ pub fn main() !void { var opt_zig_exe: ?[]const u8 = null; var opt_input_file_name: ?[]const u8 = null; var opt_lib_dir: ?[]const u8 = null; - var no_bin = false; + var opt_cc_zig: ?[]const u8 = null; + var emit: EmitMode = .bin; var debug_zcu = false; var arg_it = try std.process.argsWithAllocator(arena); _ = arg_it.skip(); while (arg_it.next()) |arg| { if (arg.len > 0 and arg[0] == '-') { - if (std.mem.eql(u8, arg, "-fno-emit-bin")) { - no_bin = true; - } else if (std.mem.eql(u8, arg, "--debug-zcu")) { - debug_zcu = true; + if (std.mem.eql(u8, arg, "--emit")) { + const emit_str = arg_it.next() orelse fatal("expected arg after '--emit'\n{s}", .{usage}); + emit = std.meta.stringToEnum(EmitMode, emit_str) orelse + fatal("invalid emit mode '{s}'\n{s}", .{ emit_str, usage }); } else if (std.mem.eql(u8, arg, "--zig-lib-dir")) { opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage}); + } else if (std.mem.eql(u8, arg, "--debug-zcu")) { + debug_zcu = true; + } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) { + opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage}); } else { fatal("unknown option '{s}'\n{s}", .{ arg, usage }); } @@ -51,20 +62,19 @@ pub fn main() !void { const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int); const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{}); - if (opt_lib_dir) |lib_dir| { - if (!std.fs.path.isAbsolute(lib_dir)) { - // The cwd of the subprocess is within the tmp dir, so prepend `..` to the path. - opt_lib_dir = try std.fs.path.join(arena, &.{ "..", lib_dir }); - } - } - const child_prog_node = prog_node.start("zig build-exe", 0); defer child_prog_node.end(); + // Convert paths to be relative to the cwd of the subprocess. + const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe); + const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir| + try std.fs.path.relative(arena, tmp_dir_path, lib_dir) + else + null; + var child_args: std.ArrayListUnmanaged([]const u8) = .{}; try child_args.appendSlice(arena, &.{ - // Convert incr-check-relative path to subprocess-relative path. - try std.fs.path.relative(arena, tmp_dir_path, zig_exe), + resolved_zig_exe, "build-exe", case.root_source_file, "-fincremental", @@ -76,13 +86,13 @@ pub fn main() !void { ".global_cache", "--listen=-", }); - if (opt_lib_dir) |lib_dir| { - try child_args.appendSlice(arena, &.{ "--zig-lib-dir", lib_dir }); + if (opt_resolved_lib_dir) |resolved_lib_dir| { + try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir }); } - if (no_bin) { - try child_args.append(arena, "-fno-emit-bin"); - } else { - try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }); + switch (emit) { + .bin => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }), + .none => try child_args.append(arena, "-fno-emit-bin"), + .c => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }), } if (debug_zcu) { try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" }); @@ -96,6 +106,24 @@ pub fn main() !void { child.cwd_dir = tmp_dir; child.cwd = tmp_dir_path; + var cc_child_args: std.ArrayListUnmanaged([]const u8) = .{}; + if (emit == .c) { + const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe| + try std.fs.path.relative(arena, tmp_dir_path, cc_zig_exe) + else + resolved_zig_exe; + + try cc_child_args.appendSlice(arena, &.{ + resolved_cc_zig_exe, + "cc", + "-target", + case.target_query, + "-I", + opt_resolved_lib_dir orelse fatal("'--zig-lib-dir' required when using '--emit c'", .{}), + "-o", + }); + } + var eval: Eval = .{ .arena = arena, .case = case, @@ -103,6 +131,8 @@ pub fn main() !void { .tmp_dir_path = tmp_dir_path, .child = &child, .allow_stderr = debug_zcu, + .emit = emit, + .cc_child_args = &cc_child_args, }; try child.spawn(); @@ -123,7 +153,7 @@ pub fn main() !void { eval.write(update); try eval.requestUpdate(); - try eval.check(&poller, update); + try eval.check(&poller, update, update_node); } try eval.end(&poller); @@ -138,6 +168,10 @@ const Eval = struct { tmp_dir_path: []const u8, child: *std.process.Child, allow_stderr: bool, + emit: EmitMode, + /// When `emit == .c`, this contains the first few arguments to `zig cc` to build the generated binary. + /// The arguments `out.c in.c` must be appended before spawning the subprocess. + cc_child_args: *std.ArrayListUnmanaged([]const u8), const StreamEnum = enum { stdout, stderr }; const Poller = std.io.Poller(StreamEnum); @@ -159,7 +193,7 @@ const Eval = struct { } } - fn check(eval: *Eval, poller: *Poller, update: Case.Update) !void { + fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void { const arena = eval.arena; const Header = std.zig.Server.Message.Header; const stdout = poller.fifo(.stdout); @@ -201,12 +235,7 @@ const Eval = struct { } if (result_error_bundle.errorMessageCount() == 0) { // Empty bundle indicates successful update in a `-fno-emit-bin` build. - // We can't do a full success check since we don't have a binary, but let's - // at least check that no errors were expected. - switch (update.outcome) { - .unknown, .stdout, .exit_code => {}, - .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}), - } + try eval.checkSuccessOutcome(update, null, prog_node); } else { try eval.checkErrorOutcome(update, result_error_bundle); } @@ -227,7 +256,7 @@ const Eval = struct { fatal("emit_bin_path included unexpected stderr:\n{s}", .{stderr_data}); } } - try eval.checkSuccessOutcome(update, result_binary); + try eval.checkSuccessOutcome(update, result_binary, prog_node); // This message indicates the end of the update. stdout.discard(body.len); return; @@ -270,12 +299,28 @@ const Eval = struct { } } - fn checkSuccessOutcome(eval: *Eval, update: Case.Update, binary_path: []const u8) !void { + fn checkSuccessOutcome(eval: *Eval, update: Case.Update, opt_emitted_path: ?[]const u8, prog_node: std.Progress.Node) !void { switch (update.outcome) { .unknown => return, .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}), .stdout, .exit_code => {}, } + const emitted_path = opt_emitted_path orelse { + std.debug.assert(eval.emit == .none); + return; + }; + + const binary_path = switch (eval.emit) { + .none => unreachable, + .bin => emitted_path, + .c => bin: { + const rand_int = std.crypto.random.int(u64); + const out_bin_name = "./out_" ++ std.fmt.hex(rand_int); + try eval.buildCOutput(update, emitted_path, out_bin_name, prog_node); + break :bin out_bin_name; + }, + }; + const result = std.process.Child.run(.{ .allocator = eval.arena, .argv = &.{binary_path}, @@ -345,6 +390,50 @@ const Eval = struct { fatal("unexpected stderr:\n{s}", .{stderr_data}); } } + + fn buildCOutput(eval: *Eval, update: Case.Update, c_path: []const u8, out_path: []const u8, prog_node: std.Progress.Node) !void { + std.debug.assert(eval.cc_child_args.items.len > 0); + + const child_prog_node = prog_node.start("build cbe output", 0); + defer child_prog_node.end(); + + try eval.cc_child_args.appendSlice(eval.arena, &.{ out_path, c_path }); + defer eval.cc_child_args.items.len -= 2; + + const result = std.process.Child.run(.{ + .allocator = eval.arena, + .argv = eval.cc_child_args.items, + .cwd_dir = eval.tmp_dir, + .cwd = eval.tmp_dir_path, + .progress_node = child_prog_node, + }) catch |err| { + fatal("update '{s}': failed to spawn zig cc for '{s}': {s}", .{ + update.name, c_path, @errorName(err), + }); + }; + switch (result.term) { + .Exited => |code| if (code != 0) { + if (result.stderr.len != 0) { + std.log.err("update '{s}': zig cc stderr:\n{s}", .{ + update.name, result.stderr, + }); + } + fatal("update '{s}': zig cc for '{s}' failed with code {d}", .{ + update.name, c_path, code, + }); + }, + .Signal, .Stopped, .Unknown => { + if (result.stderr.len != 0) { + std.log.err("update '{s}': zig cc stderr:\n{s}", .{ + update.name, result.stderr, + }); + } + fatal("update '{s}': zig cc for '{s}' terminated unexpectedly", .{ + update.name, c_path, + }); + }, + } + } }; const Case = struct { |
