mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
tools: add CBE option to incr-check
This commit is contained in:
parent
5a8780838f
commit
65cbdefe4d
@ -2,7 +2,13 @@ const std = @import("std");
|
|||||||
const fatal = std.process.fatal;
|
const fatal = std.process.fatal;
|
||||||
const Allocator = std.mem.Allocator;
|
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 {
|
pub fn main() !void {
|
||||||
var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
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_zig_exe: ?[]const u8 = null;
|
||||||
var opt_input_file_name: ?[]const u8 = null;
|
var opt_input_file_name: ?[]const u8 = null;
|
||||||
var opt_lib_dir: ?[]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 debug_zcu = false;
|
||||||
|
|
||||||
var arg_it = try std.process.argsWithAllocator(arena);
|
var arg_it = try std.process.argsWithAllocator(arena);
|
||||||
_ = arg_it.skip();
|
_ = arg_it.skip();
|
||||||
while (arg_it.next()) |arg| {
|
while (arg_it.next()) |arg| {
|
||||||
if (arg.len > 0 and arg[0] == '-') {
|
if (arg.len > 0 and arg[0] == '-') {
|
||||||
if (std.mem.eql(u8, arg, "-fno-emit-bin")) {
|
if (std.mem.eql(u8, arg, "--emit")) {
|
||||||
no_bin = true;
|
const emit_str = arg_it.next() orelse fatal("expected arg after '--emit'\n{s}", .{usage});
|
||||||
} else if (std.mem.eql(u8, arg, "--debug-zcu")) {
|
emit = std.meta.stringToEnum(EmitMode, emit_str) orelse
|
||||||
debug_zcu = true;
|
fatal("invalid emit mode '{s}'\n{s}", .{ emit_str, usage });
|
||||||
} else if (std.mem.eql(u8, arg, "--zig-lib-dir")) {
|
} 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});
|
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 {
|
} else {
|
||||||
fatal("unknown option '{s}'\n{s}", .{ arg, usage });
|
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_path = "tmp_" ++ std.fmt.hex(rand_int);
|
||||||
const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{});
|
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);
|
const child_prog_node = prog_node.start("zig build-exe", 0);
|
||||||
defer child_prog_node.end();
|
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) = .{};
|
var child_args: std.ArrayListUnmanaged([]const u8) = .{};
|
||||||
try child_args.appendSlice(arena, &.{
|
try child_args.appendSlice(arena, &.{
|
||||||
// Convert incr-check-relative path to subprocess-relative path.
|
resolved_zig_exe,
|
||||||
try std.fs.path.relative(arena, tmp_dir_path, zig_exe),
|
|
||||||
"build-exe",
|
"build-exe",
|
||||||
case.root_source_file,
|
case.root_source_file,
|
||||||
"-fincremental",
|
"-fincremental",
|
||||||
@ -76,13 +86,13 @@ pub fn main() !void {
|
|||||||
".global_cache",
|
".global_cache",
|
||||||
"--listen=-",
|
"--listen=-",
|
||||||
});
|
});
|
||||||
if (opt_lib_dir) |lib_dir| {
|
if (opt_resolved_lib_dir) |resolved_lib_dir| {
|
||||||
try child_args.appendSlice(arena, &.{ "--zig-lib-dir", lib_dir });
|
try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir });
|
||||||
}
|
}
|
||||||
if (no_bin) {
|
switch (emit) {
|
||||||
try child_args.append(arena, "-fno-emit-bin");
|
.bin => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }),
|
||||||
} else {
|
.none => try child_args.append(arena, "-fno-emit-bin"),
|
||||||
try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" });
|
.c => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }),
|
||||||
}
|
}
|
||||||
if (debug_zcu) {
|
if (debug_zcu) {
|
||||||
try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" });
|
try child_args.appendSlice(arena, &.{ "--debug-log", "zcu" });
|
||||||
@ -96,6 +106,24 @@ pub fn main() !void {
|
|||||||
child.cwd_dir = tmp_dir;
|
child.cwd_dir = tmp_dir;
|
||||||
child.cwd = tmp_dir_path;
|
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 = .{
|
var eval: Eval = .{
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
.case = case,
|
.case = case,
|
||||||
@ -103,6 +131,8 @@ pub fn main() !void {
|
|||||||
.tmp_dir_path = tmp_dir_path,
|
.tmp_dir_path = tmp_dir_path,
|
||||||
.child = &child,
|
.child = &child,
|
||||||
.allow_stderr = debug_zcu,
|
.allow_stderr = debug_zcu,
|
||||||
|
.emit = emit,
|
||||||
|
.cc_child_args = &cc_child_args,
|
||||||
};
|
};
|
||||||
|
|
||||||
try child.spawn();
|
try child.spawn();
|
||||||
@ -123,7 +153,7 @@ pub fn main() !void {
|
|||||||
|
|
||||||
eval.write(update);
|
eval.write(update);
|
||||||
try eval.requestUpdate();
|
try eval.requestUpdate();
|
||||||
try eval.check(&poller, update);
|
try eval.check(&poller, update, update_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
try eval.end(&poller);
|
try eval.end(&poller);
|
||||||
@ -138,6 +168,10 @@ const Eval = struct {
|
|||||||
tmp_dir_path: []const u8,
|
tmp_dir_path: []const u8,
|
||||||
child: *std.process.Child,
|
child: *std.process.Child,
|
||||||
allow_stderr: bool,
|
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 StreamEnum = enum { stdout, stderr };
|
||||||
const Poller = std.io.Poller(StreamEnum);
|
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 arena = eval.arena;
|
||||||
const Header = std.zig.Server.Message.Header;
|
const Header = std.zig.Server.Message.Header;
|
||||||
const stdout = poller.fifo(.stdout);
|
const stdout = poller.fifo(.stdout);
|
||||||
@ -201,12 +235,7 @@ const Eval = struct {
|
|||||||
}
|
}
|
||||||
if (result_error_bundle.errorMessageCount() == 0) {
|
if (result_error_bundle.errorMessageCount() == 0) {
|
||||||
// Empty bundle indicates successful update in a `-fno-emit-bin` build.
|
// 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
|
try eval.checkSuccessOutcome(update, null, prog_node);
|
||||||
// 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", .{}),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
try eval.checkErrorOutcome(update, result_error_bundle);
|
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});
|
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.
|
// This message indicates the end of the update.
|
||||||
stdout.discard(body.len);
|
stdout.discard(body.len);
|
||||||
return;
|
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) {
|
switch (update.outcome) {
|
||||||
.unknown => return,
|
.unknown => return,
|
||||||
.compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),
|
.compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),
|
||||||
.stdout, .exit_code => {},
|
.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(.{
|
const result = std.process.Child.run(.{
|
||||||
.allocator = eval.arena,
|
.allocator = eval.arena,
|
||||||
.argv = &.{binary_path},
|
.argv = &.{binary_path},
|
||||||
@ -345,6 +390,50 @@ const Eval = struct {
|
|||||||
fatal("unexpected stderr:\n{s}", .{stderr_data});
|
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 {
|
const Case = struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user