| ... | @@ -4,32 +4,44 @@ const Dir = std.Io.Dir; | ... | @@ -4,32 +4,44 @@ const Dir = std.Io.Dir; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Cache = std.Build.Cache; | 5 | const Cache = std.Build.Cache; |
| 6 | | 6 | |
| 7 | const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-log foo] [--preserve-tmp] [--zig-cc-binary /path/to/zig]"; | 7 | const usage = |
| | 8 | \\Usage: incr-check <zig binary path> <input file> [options] |
| | 9 | \\Options: |
| | 10 | \\ --target triple-backend |
| | 11 | \\ --quiet |
| | 12 | \\ --zig-lib-dir /path/to/zig/lib |
| | 13 | \\ --zig-cc-binary /path/to/zig |
| | 14 | \\ -fqemu |
| | 15 | \\ -fwine |
| | 16 | \\ -fwasmtime |
| | 17 | \\Debug Options: |
| | 18 | \\ --preserve-tmp |
| | 19 | \\ --debug-log foo |
| | 20 | ; |
| 8 | | 21 | |
| 9 | pub const std_options: std.Options = .{ | 22 | pub const std_options: std.Options = .{ |
| 10 | .logFn = logImpl, | 23 | .logFn = logImpl, |
| 11 | }; | 24 | }; |
| 12 | var log_cur_update: ?struct { *const Case.Target, *const Case.Update } = null; | 25 | var log_cur_update: ?*const Case.Update = null; |
| 13 | fn logImpl( | 26 | fn logImpl( |
| 14 | comptime level: std.log.Level, | 27 | comptime level: std.log.Level, |
| 15 | comptime scope: @EnumLiteral(), | 28 | comptime scope: @EnumLiteral(), |
| 16 | comptime format: []const u8, | 29 | comptime format: []const u8, |
| 17 | args: anytype, | 30 | args: anytype, |
| 18 | ) void { | 31 | ) void { |
| 19 | const target, const update = log_cur_update orelse { | 32 | const update = log_cur_update orelse { |
| 20 | return std.log.defaultLog(level, scope, format, args); | 33 | return std.log.defaultLog(level, scope, format, args); |
| 21 | }; | 34 | }; |
| 22 | std.log.defaultLog( | 35 | std.log.defaultLog( |
| 23 | level, | 36 | level, |
| 24 | scope, | 37 | scope, |
| 25 | "[{s}-{t} '{s}'] " ++ format, | 38 | "['{s}'] " ++ format, |
| 26 | .{ target.query, target.backend, update.name } ++ args, | 39 | .{update.name} ++ args, |
| 27 | ); | 40 | ); |
| 28 | } | 41 | } |
| 29 | | 42 | |
| 30 | pub fn main(init: std.process.Init) !void { | 43 | pub fn main(init: std.process.Init) !void { |
| 31 | const gpa = init.gpa; | 44 | const gpa = init.gpa; |
| 32 | const fatal = std.process.fatal; | | |
| 33 | const arena = init.arena.allocator(); | 45 | const arena = init.arena.allocator(); |
| 34 | const io = init.io; | 46 | const io = init.io; |
| 35 | const environ_map = init.environ_map; | 47 | const environ_map = init.environ_map; |
| ... | @@ -39,11 +51,13 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -39,11 +51,13 @@ pub fn main(init: std.process.Init) !void { |
| 39 | var opt_input_file_name: ?[]const u8 = null; | 51 | var opt_input_file_name: ?[]const u8 = null; |
| 40 | var opt_lib_dir: ?[]const u8 = null; | 52 | var opt_lib_dir: ?[]const u8 = null; |
| 41 | var opt_cc_zig: ?[]const u8 = null; | 53 | var opt_cc_zig: ?[]const u8 = null; |
| | 54 | var opt_target: ?struct { std.Target.Query, Backend } = null; |
| 42 | var preserve_tmp = false; | 55 | var preserve_tmp = false; |
| 43 | var enable_qemu: bool = false; | 56 | var enable_qemu: bool = false; |
| 44 | var enable_wine: bool = false; | 57 | var enable_wine: bool = false; |
| 45 | var enable_wasmtime: bool = false; | 58 | var enable_wasmtime: bool = false; |
| 46 | var enable_darling: bool = false; | 59 | var enable_darling: bool = false; |
| | 60 | var quiet: bool = false; |
| 47 | | 61 | |
| 48 | var debug_log_args: std.ArrayList([]const u8) = .empty; | 62 | var debug_log_args: std.ArrayList([]const u8) = .empty; |
| 49 | | 63 | |
| ... | @@ -52,11 +66,16 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -52,11 +66,16 @@ pub fn main(init: std.process.Init) !void { |
| 52 | while (arg_it.next()) |arg| { | 66 | while (arg_it.next()) |arg| { |
| 53 | if (arg.len > 0 and arg[0] == '-') { | 67 | if (arg.len > 0 and arg[0] == '-') { |
| 54 | if (std.mem.eql(u8, arg, "--zig-lib-dir")) { | 68 | if (std.mem.eql(u8, arg, "--zig-lib-dir")) { |
| 55 | opt_lib_dir = arg_it.next() orelse fatal("expected arg after --zig-lib-dir\n{s}", .{usage}); | 69 | opt_lib_dir = arg_it.next() orelse badUsage("expected arg after --zig-lib-dir", .{}); |
| | 70 | } else if (std.mem.eql(u8, arg, "--target")) { |
| | 71 | const str = arg_it.next() orelse badUsage("expected arg after --zig-cc-binary", .{}); |
| | 72 | opt_target = parseTargetQueryAndBackend(str, ""); |
| | 73 | } else if (std.mem.eql(u8, arg, "--quiet")) { |
| | 74 | quiet = true; |
| 56 | } else if (std.mem.eql(u8, arg, "--debug-log")) { | 75 | } else if (std.mem.eql(u8, arg, "--debug-log")) { |
| 57 | try debug_log_args.append( | 76 | try debug_log_args.append( |
| 58 | arena, | 77 | arena, |
| 59 | arg_it.next() orelse fatal("expected arg after --debug-log\n{s}", .{usage}), | 78 | arg_it.next() orelse badUsage("expected arg after --debug-log", .{}), |
| 60 | ); | 79 | ); |
| 61 | } else if (std.mem.eql(u8, arg, "--preserve-tmp")) { | 80 | } else if (std.mem.eql(u8, arg, "--preserve-tmp")) { |
| 62 | preserve_tmp = true; | 81 | preserve_tmp = true; |
| ... | @@ -69,9 +88,9 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -69,9 +88,9 @@ pub fn main(init: std.process.Init) !void { |
| 69 | } else if (std.mem.eql(u8, arg, "-fdarling")) { | 88 | } else if (std.mem.eql(u8, arg, "-fdarling")) { |
| 70 | enable_darling = true; | 89 | enable_darling = true; |
| 71 | } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) { | 90 | } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) { |
| 72 | opt_cc_zig = arg_it.next() orelse fatal("expected arg after --zig-cc-binary\n{s}", .{usage}); | 91 | opt_cc_zig = arg_it.next() orelse badUsage("expected arg after --zig-cc-binary", .{}); |
| 73 | } else { | 92 | } else { |
| 74 | fatal("unknown option '{s}'\n{s}", .{ arg, usage }); | 93 | badUsage("unknown option '{s}'", .{arg}); |
| 75 | } | 94 | } |
| 76 | continue; | 95 | continue; |
| 77 | } | 96 | } |
| ... | @@ -80,24 +99,29 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -80,24 +99,29 @@ pub fn main(init: std.process.Init) !void { |
| 80 | } else if (opt_input_file_name == null) { | 99 | } else if (opt_input_file_name == null) { |
| 81 | opt_input_file_name = arg; | 100 | opt_input_file_name = arg; |
| 82 | } else { | 101 | } else { |
| 83 | fatal("unknown argument '{s}'\n{s}", .{ arg, usage }); | 102 | badUsage("unknown argument '{s}'\n{s}", .{ arg, usage }); |
| 84 | } | 103 | } |
| 85 | } | 104 | } |
| 86 | const zig_exe = opt_zig_exe orelse fatal("missing path to zig\n{s}", .{usage}); | 105 | const zig_exe = opt_zig_exe orelse badUsage("missing path to zig", .{}); |
| 87 | const input_file_name = opt_input_file_name orelse fatal("missing input file\n{s}", .{usage}); | 106 | const input_file_name = opt_input_file_name orelse badUsage("missing input file", .{}); |
| | 107 | const target_query, const backend = opt_target orelse badUsage("missing required option '--target'", .{}); |
| | 108 | |
| | 109 | if (backend == .cbe and opt_lib_dir == null) { |
| | 110 | std.process.fatal("'--zig-lib-dir' required when using backend 'cbe'", .{}); |
| | 111 | } |
| 88 | | 112 | |
| 89 | const input_file_bytes = try Dir.cwd().readFileAlloc(io, input_file_name, arena, .limited(std.math.maxInt(u32))); | 113 | const input_file_bytes = try Dir.cwd().readFileAlloc(io, input_file_name, arena, .limited(std.math.maxInt(u32))); |
| 90 | const case = try Case.parse(arena, io, input_file_bytes); | 114 | const case: Case = try .parse(arena, input_file_bytes); |
| 91 | | 115 | |
| 92 | // Check now: if there are any targets using the `cbe` backend, we need the lib dir. | 116 | for (case.skip_targets) |skip| { |
| 93 | if (opt_lib_dir == null) { | 117 | if (target_query.eql(skip.query) and backend == skip.backend) { |
| 94 | for (case.targets) |target| { | 118 | if (!quiet) std.log.warn("skipping test because of a 'skip_target' match", .{}); |
| 95 | if (target.backend == .cbe) { | 119 | return; |
| 96 | fatal("'--zig-lib-dir' requried when using backend 'cbe'", .{}); | | |
| 97 | } | | |
| 98 | } | 120 | } |
| 99 | } | 121 | } |
| 100 | | 122 | |
| | 123 | const target = try std.zig.system.resolveTargetQuery(io, target_query); |
| | 124 | |
| 101 | const prog_node = std.Progress.start(io, .{}); | 125 | const prog_node = std.Progress.start(io, .{}); |
| 102 | defer prog_node.end(); | 126 | defer prog_node.end(); |
| 103 | | 127 | |
| ... | @@ -122,143 +146,137 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -122,143 +146,137 @@ pub fn main(init: std.process.Init) !void { |
| 122 | | 146 | |
| 123 | const host = try std.zig.system.resolveTargetQuery(io, .{}); | 147 | const host = try std.zig.system.resolveTargetQuery(io, .{}); |
| 124 | | 148 | |
| 125 | const debug_log_verbose = debug_log_args.items.len != 0; | 149 | var child_args: std.ArrayList([]const u8) = .empty; |
| 126 | | 150 | try child_args.appendSlice(arena, &.{ |
| 127 | for (case.targets) |target| { | 151 | resolved_zig_exe, |
| 128 | const target_prog_node = node: { | 152 | "build-exe", |
| 129 | var name_buf: [std.Progress.Node.max_name_len]u8 = undefined; | 153 | "-fincremental", |
| 130 | const name = std.fmt.bufPrint(&name_buf, "{s}-{t}", .{ target.query, target.backend }) catch &name_buf; | 154 | "-fno-ubsan-rt", |
| 131 | break :node prog_node.start(name, case.updates.len); | 155 | "-target", |
| 132 | }; | 156 | try target_query.zigTriple(arena), |
| 133 | defer target_prog_node.end(); | 157 | "--cache-dir", |
| 134 | | 158 | ".local-cache", |
| 135 | if (debug_log_verbose) { | 159 | "--global-cache-dir", |
| 136 | std.log.scoped(.status).info("target: '{s}-{t}'", .{ target.query, target.backend }); | 160 | ".global-cache", |
| 137 | } | 161 | }); |
| 138 | var child_args: std.ArrayList([]const u8) = .empty; | 162 | try child_args.append(arena, "--listen=-"); |
| 139 | try child_args.appendSlice(arena, &.{ | 163 | |
| 140 | resolved_zig_exe, | 164 | if (opt_resolved_lib_dir) |resolved_lib_dir| { |
| 141 | "build-exe", | 165 | try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir }); |
| 142 | "-fincremental", | 166 | } |
| 143 | "-fno-ubsan-rt", | 167 | switch (backend) { |
| 144 | "-target", | 168 | .sema => try child_args.append(arena, "-fno-emit-bin"), |
| 145 | target.query, | 169 | .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }), |
| 146 | "--cache-dir", | 170 | .llvm => try child_args.appendSlice(arena, &.{ "-fllvm", "-flld" }), |
| 147 | ".local-cache", | 171 | .cbe => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }), |
| 148 | "--global-cache-dir", | 172 | } |
| 149 | ".global-cache", | 173 | for (debug_log_args.items) |arg| { |
| 150 | }); | 174 | try child_args.appendSlice(arena, &.{ "--debug-log", arg }); |
| 151 | try child_args.append(arena, "--listen=-"); | 175 | } |
| 152 | | 176 | for (case.modules) |mod| { |
| 153 | if (opt_resolved_lib_dir) |resolved_lib_dir| { | 177 | try child_args.appendSlice(arena, &.{ "--dep", mod.name }); |
| 154 | try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir }); | 178 | } |
| 155 | } | 179 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-Mroot={s}", .{case.root_source_file})); |
| 156 | switch (target.backend) { | 180 | for (case.modules) |mod| { |
| 157 | .sema => try child_args.append(arena, "-fno-emit-bin"), | 181 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-M{s}={s}", .{ mod.name, mod.file })); |
| 158 | .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }), | 182 | } |
| 159 | .llvm => try child_args.appendSlice(arena, &.{ "-fllvm", "-flld" }), | | |
| 160 | .cbe => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }), | | |
| 161 | } | | |
| 162 | for (debug_log_args.items) |arg| { | | |
| 163 | try child_args.appendSlice(arena, &.{ "--debug-log", arg }); | | |
| 164 | } | | |
| 165 | for (case.modules) |mod| { | | |
| 166 | try child_args.appendSlice(arena, &.{ "--dep", mod.name }); | | |
| 167 | } | | |
| 168 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-Mroot={s}", .{case.root_source_file})); | | |
| 169 | for (case.modules) |mod| { | | |
| 170 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-M{s}={s}", .{ mod.name, mod.file })); | | |
| 171 | } | | |
| 172 | | 183 | |
| 173 | const zig_prog_node = target_prog_node.start("zig build-exe", 0); | 184 | const zig_prog_node = prog_node.start("zig", 0); |
| 174 | defer zig_prog_node.end(); | 185 | defer zig_prog_node.end(); |
| 175 | | | |
| 176 | var cc_child_args: std.ArrayList([]const u8) = .empty; | | |
| 177 | if (target.backend == .cbe) { | | |
| 178 | const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe| | | |
| 179 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, cc_zig_exe) | | |
| 180 | else | | |
| 181 | resolved_zig_exe; | | |
| 182 | | | |
| 183 | try cc_child_args.appendSlice(arena, &.{ | | |
| 184 | resolved_cc_zig_exe, | | |
| 185 | "cc", | | |
| 186 | "-target", | | |
| 187 | target.query, | | |
| 188 | "-I", | | |
| 189 | opt_resolved_lib_dir.?, // verified earlier | | |
| 190 | }); | | |
| 191 | | 186 | |
| 192 | try cc_child_args.append(arena, "-o"); | 187 | var cc_child_args: std.ArrayList([]const u8) = .empty; |
| 193 | } | 188 | if (backend == .cbe) { |
| | 189 | const resolved_cc_zig_exe = if (opt_cc_zig) |cc_zig_exe| |
| | 190 | try Dir.path.relative(arena, cwd_path, environ_map, tmp_dir_path, cc_zig_exe) |
| | 191 | else |
| | 192 | resolved_zig_exe; |
| 194 | | 193 | |
| 195 | var child = try std.process.spawn(io, .{ | 194 | try cc_child_args.appendSlice(arena, &.{ |
| 196 | .argv = child_args.items, | 195 | resolved_cc_zig_exe, |
| 197 | .stdin = .pipe, | 196 | "cc", |
| 198 | .stdout = .pipe, | 197 | "-target", |
| 199 | .stderr = .pipe, | 198 | try target_query.zigTriple(arena), |
| 200 | .progress_node = zig_prog_node, | 199 | "-I", |
| 201 | .cwd = .{ .path = tmp_dir_path }, | 200 | opt_resolved_lib_dir.?, // verified earlier |
| 202 | }); | 201 | }); |
| 203 | defer child.kill(io); | | |
| 204 | | | |
| 205 | var eval: Eval = .{ | | |
| 206 | .arena = arena, | | |
| 207 | .io = io, | | |
| 208 | .case = case, | | |
| 209 | .host = host, | | |
| 210 | .target = target, | | |
| 211 | .tmp_dir = tmp_dir, | | |
| 212 | .tmp_dir_path = tmp_dir_path, | | |
| 213 | .child = &child, | | |
| 214 | .allow_stderr = debug_log_verbose, | | |
| 215 | .preserve_tmp_on_fatal = preserve_tmp, | | |
| 216 | .cc_child_args = &cc_child_args, | | |
| 217 | .enable_qemu = enable_qemu, | | |
| 218 | .enable_wine = enable_wine, | | |
| 219 | .enable_wasmtime = enable_wasmtime, | | |
| 220 | .enable_darling = enable_darling, | | |
| 221 | }; | | |
| 222 | | 202 | |
| 223 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; | 203 | try cc_child_args.append(arena, "-o"); |
| 224 | var multi_reader: Io.File.MultiReader = undefined; | 204 | } |
| 225 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); | | |
| 226 | defer multi_reader.deinit(); | | |
| 227 | | 205 | |
| 228 | for (case.updates) |update| { | 206 | var child = try std.process.spawn(io, .{ |
| 229 | var update_node = target_prog_node.start(update.name, 0); | 207 | .argv = child_args.items, |
| 230 | defer update_node.end(); | 208 | .stdin = .pipe, |
| | 209 | .stdout = .pipe, |
| | 210 | .stderr = .pipe, |
| | 211 | .progress_node = zig_prog_node, |
| | 212 | .cwd = .{ .path = tmp_dir_path }, |
| | 213 | }); |
| | 214 | defer child.kill(io); |
| | 215 | |
| | 216 | const updates_prog_node = prog_node.start("updates", case.updates.len); |
| | 217 | defer updates_prog_node.end(); |
| | 218 | |
| | 219 | var eval: Eval = .{ |
| | 220 | .arena = arena, |
| | 221 | .io = io, |
| | 222 | .case = case, |
| | 223 | .host = host, |
| | 224 | .target = target, |
| | 225 | .backend = backend, |
| | 226 | .tmp_dir = tmp_dir, |
| | 227 | .tmp_dir_path = tmp_dir_path, |
| | 228 | .child = &child, |
| | 229 | .allow_compiler_stderr = debug_log_args.items.len != 0, |
| | 230 | .quiet = quiet, |
| | 231 | .preserve_tmp_on_fatal = preserve_tmp, |
| | 232 | .cc_child_args = &cc_child_args, |
| | 233 | .enable_qemu = enable_qemu, |
| | 234 | .enable_wine = enable_wine, |
| | 235 | .enable_wasmtime = enable_wasmtime, |
| | 236 | .enable_darling = enable_darling, |
| | 237 | }; |
| 231 | | 238 | |
| 232 | if (debug_log_verbose) { | 239 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; |
| 233 | std.log.scoped(.status).info("update: '{s}'", .{update.name}); | 240 | var multi_reader: Io.File.MultiReader = undefined; |
| 234 | } | 241 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); |
| | 242 | defer multi_reader.deinit(); |
| 235 | | 243 | |
| 236 | log_cur_update = .{ &target, &update }; | 244 | for (case.updates) |update| { |
| 237 | defer log_cur_update = null; | 245 | var update_prog_node = updates_prog_node.start(update.name, 0); |
| | 246 | defer update_prog_node.end(); |
| 238 | | 247 | |
| 239 | eval.write(update); | 248 | if (debug_log_args.items.len != 0) { |
| 240 | try eval.requestUpdate(); | 249 | // Print a line separating the debug logs from the compiler in the stderr output. |
| 241 | try eval.check(&multi_reader, update, update_node); | 250 | std.log.scoped(.status).info("update: '{s}'", .{update.name}); |
| 242 | } | 251 | } |
| 243 | | 252 | |
| 244 | try eval.end(&multi_reader); | 253 | log_cur_update = &update; |
| | 254 | defer log_cur_update = null; |
| 245 | | 255 | |
| 246 | waitChild(&child, &eval); | 256 | eval.write(update); |
| | 257 | try eval.requestUpdate(); |
| | 258 | try eval.check(&multi_reader, update, update_prog_node); |
| 247 | } | 259 | } |
| | 260 | |
| | 261 | try eval.end(&multi_reader); |
| | 262 | |
| | 263 | waitChild(&child, &eval); |
| 248 | } | 264 | } |
| 249 | | 265 | |
| 250 | const Eval = struct { | 266 | const Eval = struct { |
| 251 | arena: Allocator, | 267 | arena: Allocator, |
| 252 | io: Io, | 268 | io: Io, |
| 253 | host: std.Target, | | |
| 254 | case: Case, | 269 | case: Case, |
| 255 | target: Case.Target, | 270 | host: std.Target, |
| | 271 | target: std.Target, |
| | 272 | backend: Backend, |
| 256 | tmp_dir: Dir, | 273 | tmp_dir: Dir, |
| 257 | tmp_dir_path: []const u8, | 274 | tmp_dir_path: []const u8, |
| 258 | child: *std.process.Child, | 275 | child: *std.process.Child, |
| 259 | allow_stderr: bool, | 276 | allow_compiler_stderr: bool, |
| | 277 | quiet: bool, |
| 260 | preserve_tmp_on_fatal: bool, | 278 | preserve_tmp_on_fatal: bool, |
| 261 | /// When `target.backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary. | 279 | /// When `backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary. |
| 262 | /// The arguments `out.c in.c` must be appended before spawning the subprocess. | 280 | /// The arguments `out.c in.c` must be appended before spawning the subprocess. |
| 263 | cc_child_args: *std.ArrayList([]const u8), | 281 | cc_child_args: *std.ArrayList([]const u8), |
| 264 | | 282 | |
| ... | @@ -307,7 +325,7 @@ const Eval = struct { | ... | @@ -307,7 +325,7 @@ const Eval = struct { |
| 307 | .error_bundle => { | 325 | .error_bundle => { |
| 308 | const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); | 326 | const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); |
| 309 | if (stderr.bufferedLen() > 0) { | 327 | if (stderr.bufferedLen() > 0) { |
| 310 | if (eval.allow_stderr) { | 328 | if (eval.allow_compiler_stderr) { |
| 311 | std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()}); | 329 | std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()}); |
| 312 | } else { | 330 | } else { |
| 313 | eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()}); | 331 | eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()}); |
| ... | @@ -325,14 +343,14 @@ const Eval = struct { | ... | @@ -325,14 +343,14 @@ const Eval = struct { |
| 325 | _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; | 343 | _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; |
| 326 | | 344 | |
| 327 | if (stderr.bufferedLen() > 0) { | 345 | if (stderr.bufferedLen() > 0) { |
| 328 | if (eval.allow_stderr) { | 346 | if (eval.allow_compiler_stderr) { |
| 329 | std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()}); | 347 | std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()}); |
| 330 | } else { | 348 | } else { |
| 331 | eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()}); | 349 | eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()}); |
| 332 | } | 350 | } |
| 333 | stderr.tossBuffered(); | 351 | stderr.tossBuffered(); |
| 334 | } | 352 | } |
| 335 | if (eval.target.backend == .sema) { | 353 | if (eval.backend == .sema) { |
| 336 | try eval.checkSuccessOutcome(update, null, prog_node); | 354 | try eval.checkSuccessOutcome(update, null, prog_node); |
| 337 | continue; | 355 | continue; |
| 338 | } | 356 | } |
| ... | @@ -342,7 +360,7 @@ const Eval = struct { | ... | @@ -342,7 +360,7 @@ const Eval = struct { |
| 342 | | 360 | |
| 343 | const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ | 361 | const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ |
| 344 | .root_name = "root", // corresponds to the module name "root" | 362 | .root_name = "root", // corresponds to the module name "root" |
| 345 | .target = &eval.target.resolved, | 363 | .target = &eval.target, |
| 346 | .output_mode = .Exe, | 364 | .output_mode = .Exe, |
| 347 | }); | 365 | }); |
| 348 | const bin_path = try Dir.path.join(arena, &.{ result_dir, bin_name }); | 366 | const bin_path = try Dir.path.join(arena, &.{ result_dir, bin_name }); |
| ... | @@ -357,7 +375,7 @@ const Eval = struct { | ... | @@ -357,7 +375,7 @@ const Eval = struct { |
| 357 | | 375 | |
| 358 | const buffered_stderr = stderr.buffered(); | 376 | const buffered_stderr = stderr.buffered(); |
| 359 | if (buffered_stderr.len > 0) { | 377 | if (buffered_stderr.len > 0) { |
| 360 | if (eval.allow_stderr) { | 378 | if (eval.allow_compiler_stderr) { |
| 361 | std.log.info("stderr:\n{s}", .{buffered_stderr}); | 379 | std.log.info("stderr:\n{s}", .{buffered_stderr}); |
| 362 | } else { | 380 | } else { |
| 363 | eval.fatal("unexpected stderr:\n{s}", .{buffered_stderr}); | 381 | eval.fatal("unexpected stderr:\n{s}", .{buffered_stderr}); |
| ... | @@ -450,12 +468,12 @@ const Eval = struct { | ... | @@ -450,12 +468,12 @@ const Eval = struct { |
| 450 | .stdout, .exit_code => {}, | 468 | .stdout, .exit_code => {}, |
| 451 | } | 469 | } |
| 452 | const emitted_path = opt_emitted_path orelse { | 470 | const emitted_path = opt_emitted_path orelse { |
| 453 | std.debug.assert(eval.target.backend == .sema); | 471 | std.debug.assert(eval.backend == .sema); |
| 454 | return; | 472 | return; |
| 455 | }; | 473 | }; |
| 456 | const io = eval.io; | 474 | const io = eval.io; |
| 457 | | 475 | |
| 458 | const binary_path = switch (eval.target.backend) { | 476 | const binary_path = switch (eval.backend) { |
| 459 | .sema => unreachable, | 477 | .sema => unreachable, |
| 460 | .selfhosted, .llvm => emitted_path, | 478 | .selfhosted, .llvm => emitted_path, |
| 461 | .cbe => bin: { | 479 | .cbe => bin: { |
| ... | @@ -470,15 +488,15 @@ const Eval = struct { | ... | @@ -470,15 +488,15 @@ const Eval = struct { |
| 470 | const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor( | 488 | const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor( |
| 471 | io, | 489 | io, |
| 472 | &eval.host, | 490 | &eval.host, |
| 473 | &eval.target.resolved, | 491 | &eval.target, |
| 474 | .{ .link_libc = eval.target.backend == .cbe }, | 492 | .{ .link_libc = eval.backend == .cbe }, |
| 475 | )) { | 493 | )) { |
| 476 | .bad_dl, .bad_os_or_cpu => { | 494 | .bad_dl, .bad_os_or_cpu => { |
| 477 | // This binary cannot be executed on this host. | 495 | // This binary cannot be executed on this host. |
| 478 | if (eval.allow_stderr) { | 496 | if (!eval.quiet) { |
| 479 | std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{ | 497 | std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{ |
| 480 | try eval.host.zigTriple(eval.arena), | 498 | try eval.host.zigTriple(eval.arena), |
| 481 | try eval.target.resolved.zigTriple(eval.arena), | 499 | try eval.target.zigTriple(eval.arena), |
| 482 | }); | 500 | }); |
| 483 | } | 501 | } |
| 484 | return; | 502 | return; |
| ... | @@ -534,10 +552,10 @@ const Eval = struct { | ... | @@ -534,10 +552,10 @@ const Eval = struct { |
| 534 | }) catch |err| { | 552 | }) catch |err| { |
| 535 | if (is_foreign) { | 553 | if (is_foreign) { |
| 536 | // Chances are the foreign executor isn't available. Skip this evaluation. | 554 | // Chances are the foreign executor isn't available. Skip this evaluation. |
| 537 | if (eval.allow_stderr) { | 555 | if (!eval.quiet) { |
| 538 | std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{ | 556 | std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{ |
| 539 | binary_path, | 557 | binary_path, |
| 540 | try eval.target.resolved.zigTriple(eval.arena), | 558 | try eval.target.zigTriple(eval.arena), |
| 541 | err, | 559 | err, |
| 542 | }); | 560 | }); |
| 543 | } | 561 | } |
| ... | @@ -658,30 +676,30 @@ const Eval = struct { | ... | @@ -658,30 +676,30 @@ const Eval = struct { |
| 658 | } | 676 | } |
| 659 | }; | 677 | }; |
| 660 | | 678 | |
| | 679 | const Backend = enum { |
| | 680 | /// Run semantic analysis only. Runtime output will not be tested, but we still verify |
| | 681 | /// that compilation succeeds. Corresponds to `-fno-emit-bin`. |
| | 682 | sema, |
| | 683 | /// Use the self-hosted code generation backend for this target. |
| | 684 | /// Corresponds to `-fno-llvm -fno-lld`. |
| | 685 | selfhosted, |
| | 686 | /// Use the LLVM backend. |
| | 687 | /// Corresponds to `-fllvm -flld`. |
| | 688 | llvm, |
| | 689 | /// Use the C backend. The output is compiled with `zig cc`. |
| | 690 | /// Corresponds to `-ofmt=c`. |
| | 691 | cbe, |
| | 692 | }; |
| | 693 | |
| 661 | const Case = struct { | 694 | const Case = struct { |
| 662 | updates: []Update, | 695 | updates: []Update, |
| 663 | root_source_file: []const u8, | 696 | root_source_file: []const u8, |
| 664 | targets: []const Target, | 697 | skip_targets: []const SkipTarget, |
| 665 | modules: []const Module, | 698 | modules: []const Module, |
| 666 | | 699 | |
| 667 | const Target = struct { | 700 | const SkipTarget = struct { |
| 668 | query: []const u8, | 701 | query: std.Target.Query, |
| 669 | resolved: std.Target, | | |
| 670 | backend: Backend, | 702 | backend: Backend, |
| 671 | const Backend = enum { | | |
| 672 | /// Run semantic analysis only. Runtime output will not be tested, but we still verify | | |
| 673 | /// that compilation succeeds. Corresponds to `-fno-emit-bin`. | | |
| 674 | sema, | | |
| 675 | /// Use the self-hosted code generation backend for this target. | | |
| 676 | /// Corresponds to `-fno-llvm -fno-lld`. | | |
| 677 | selfhosted, | | |
| 678 | /// Use the LLVM backend. | | |
| 679 | /// Corresponds to `-fllvm -flld`. | | |
| 680 | llvm, | | |
| 681 | /// Use the C backend. The output is compiled with `zig cc`. | | |
| 682 | /// Corresponds to `-ofmt=c`. | | |
| 683 | cbe, | | |
| 684 | }; | | |
| 685 | }; | 703 | }; |
| 686 | | 704 | |
| 687 | const Module = struct { | 705 | const Module = struct { |
| ... | @@ -721,10 +739,10 @@ const Case = struct { | ... | @@ -721,10 +739,10 @@ const Case = struct { |
| 721 | }, | 739 | }, |
| 722 | }; | 740 | }; |
| 723 | | 741 | |
| 724 | fn parse(arena: Allocator, io: Io, bytes: []const u8) !Case { | 742 | fn parse(arena: Allocator, bytes: []const u8) !Case { |
| 725 | const fatal = std.process.fatal; | 743 | const fatal = std.process.fatal; |
| 726 | | 744 | |
| 727 | var targets: std.ArrayList(Target) = .empty; | 745 | var skip_targets: std.ArrayList(SkipTarget) = .empty; |
| 728 | var modules: std.ArrayList(Module) = .empty; | 746 | var modules: std.ArrayList(Module) = .empty; |
| 729 | var updates: std.ArrayList(Update) = .empty; | 747 | var updates: std.ArrayList(Update) = .empty; |
| 730 | var changes: std.ArrayList(FullContents) = .empty; | 748 | var changes: std.ArrayList(FullContents) = .empty; |
| ... | @@ -739,29 +757,13 @@ const Case = struct { | ... | @@ -739,29 +757,13 @@ const Case = struct { |
| 739 | const val = std.mem.trimEnd(u8, line_it.rest(), "\r"); // windows moment | 757 | const val = std.mem.trimEnd(u8, line_it.rest(), "\r"); // windows moment |
| 740 | if (val.len == 0) { | 758 | if (val.len == 0) { |
| 741 | fatal("line {d}: missing value", .{line_n}); | 759 | fatal("line {d}: missing value", .{line_n}); |
| 742 | } else if (std.mem.eql(u8, key, "target")) { | 760 | } else if (std.mem.eql(u8, key, "skip_target")) { |
| 743 | const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse | 761 | const query, const backend = parseTargetQueryAndBackend( |
| 744 | fatal("line {d}: target does not include backend", .{line_n}); | 762 | val, |
| 745 | | 763 | try std.fmt.allocPrint(arena, "line {d}: ", .{line_n}), |
| 746 | const query = val[0..split_idx]; | 764 | ); |
| 747 | | 765 | try skip_targets.append(arena, .{ |
| 748 | const backend_str = val[split_idx + 1 ..]; | | |
| 749 | const backend: Target.Backend = std.meta.stringToEnum(Target.Backend, backend_str) orelse | | |
| 750 | fatal("line {d}: invalid backend '{s}'", .{ line_n, backend_str }); | | |
| 751 | | | |
| 752 | const parsed_query = std.Build.parseTargetQuery(.{ | | |
| 753 | .arch_os_abi = query, | | |
| 754 | .object_format = switch (backend) { | | |
| 755 | .sema, .selfhosted, .llvm => null, | | |
| 756 | .cbe => "c", | | |
| 757 | }, | | |
| 758 | }) catch fatal("line {d}: invalid target query '{s}'", .{ line_n, query }); | | |
| 759 | | | |
| 760 | const resolved = try std.zig.system.resolveTargetQuery(io, parsed_query); | | |
| 761 | | | |
| 762 | try targets.append(arena, .{ | | |
| 763 | .query = query, | 766 | .query = query, |
| 764 | .resolved = resolved, | | |
| 765 | .backend = backend, | 767 | .backend = backend, |
| 766 | }); | 768 | }); |
| 767 | } else if (std.mem.eql(u8, key, "module")) { | 769 | } else if (std.mem.eql(u8, key, "module")) { |
| ... | @@ -872,10 +874,6 @@ const Case = struct { | ... | @@ -872,10 +874,6 @@ const Case = struct { |
| 872 | } | 874 | } |
| 873 | } | 875 | } |
| 874 | | 876 | |
| 875 | if (targets.items.len == 0) { | | |
| 876 | fatal("missing target", .{}); | | |
| 877 | } | | |
| 878 | | | |
| 879 | if (changes.items.len > 0) { | 877 | if (changes.items.len > 0) { |
| 880 | const last_update = &updates.items[updates.items.len - 1]; | 878 | const last_update = &updates.items[updates.items.len - 1]; |
| 881 | last_update.changes = changes.items; // arena so no need for toOwnedSlice | 879 | last_update.changes = changes.items; // arena so no need for toOwnedSlice |
| ... | @@ -885,7 +883,7 @@ const Case = struct { | ... | @@ -885,7 +883,7 @@ const Case = struct { |
| 885 | return .{ | 883 | return .{ |
| 886 | .updates = updates.items, | 884 | .updates = updates.items, |
| 887 | .root_source_file = root_source_file orelse fatal("missing root source file", .{}), | 885 | .root_source_file = root_source_file orelse fatal("missing root source file", .{}), |
| 888 | .targets = targets.items, // arena so no need for toOwnedSlice | 886 | .skip_targets = skip_targets.items, // arena so no need for toOwnedSlice |
| 889 | .modules = modules.items, | 887 | .modules = modules.items, |
| 890 | }; | 888 | }; |
| 891 | } | 889 | } |
| ... | @@ -970,3 +968,32 @@ fn rand64(io: Io) u64 { | ... | @@ -970,3 +968,32 @@ fn rand64(io: Io) u64 { |
| 970 | io.random(@ptrCast(&x)); | 968 | io.random(@ptrCast(&x)); |
| 971 | return x; | 969 | return x; |
| 972 | } | 970 | } |
| | 971 | |
| | 972 | /// Calls `std.process.fatal` on error. The error messages are prefixed with `err_prefix`. |
| | 973 | fn parseTargetQueryAndBackend(input_str: []const u8, err_prefix: []const u8) struct { std.Target.Query, Backend } { |
| | 974 | const fatal = std.process.fatal; |
| | 975 | |
| | 976 | const split_idx = std.mem.lastIndexOfScalar(u8, input_str, '-') orelse |
| | 977 | fatal("{s}target does not include backend", .{err_prefix}); |
| | 978 | |
| | 979 | const query = input_str[0..split_idx]; |
| | 980 | |
| | 981 | const backend_str = input_str[split_idx + 1 ..]; |
| | 982 | const backend: Backend = std.meta.stringToEnum(Backend, backend_str) orelse |
| | 983 | fatal("{s}invalid backend '{s}'", .{ err_prefix, backend_str }); |
| | 984 | |
| | 985 | const parsed_query = std.Build.parseTargetQuery(.{ |
| | 986 | .arch_os_abi = query, |
| | 987 | .object_format = switch (backend) { |
| | 988 | .sema, .selfhosted, .llvm => null, |
| | 989 | .cbe => "c", |
| | 990 | }, |
| | 991 | }) catch fatal("{s}invalid target query '{s}'", .{ err_prefix, query }); |
| | 992 | |
| | 993 | return .{ parsed_query, backend }; |
| | 994 | } |
| | 995 | |
| | 996 | fn badUsage(comptime fmt: []const u8, args: anytype) noreturn { |
| | 997 | std.log.err(fmt ++ "\n{s}", args ++ .{usage}); |
| | 998 | std.process.exit(1); |
| | 999 | } |