| ... | ... | @@ -4,32 +4,44 @@ const Dir = std.Io.Dir; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | 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 | 22 | pub const std_options: std.Options = .{ |
| 10 | 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 | 26 | fn logImpl( |
| 14 | 27 | comptime level: std.log.Level, |
| 15 | 28 | comptime scope: @EnumLiteral(), |
| 16 | 29 | comptime format: []const u8, |
| 17 | 30 | args: anytype, |
| 18 | 31 | ) void { |
| 19 | | const target, const update = log_cur_update orelse { |
| 32 | const update = log_cur_update orelse { |
| 20 | 33 | return std.log.defaultLog(level, scope, format, args); |
| 21 | 34 | }; |
| 22 | 35 | std.log.defaultLog( |
| 23 | 36 | level, |
| 24 | 37 | scope, |
| 25 | | "[{s}-{t} '{s}'] " ++ format, |
| 26 | | .{ target.query, target.backend, update.name } ++ args, |
| 38 | "['{s}'] " ++ format, |
| 39 | .{update.name} ++ args, |
| 27 | 40 | ); |
| 28 | 41 | } |
| 29 | 42 | |
| 30 | 43 | pub fn main(init: std.process.Init) !void { |
| 31 | 44 | const gpa = init.gpa; |
| 32 | | const fatal = std.process.fatal; |
| 33 | 45 | const arena = init.arena.allocator(); |
| 34 | 46 | const io = init.io; |
| 35 | 47 | const environ_map = init.environ_map; |
| ... | ... | @@ -39,11 +51,13 @@ pub fn main(init: std.process.Init) !void { |
| 39 | 51 | var opt_input_file_name: ?[]const u8 = null; |
| 40 | 52 | var opt_lib_dir: ?[]const u8 = null; |
| 41 | 53 | var opt_cc_zig: ?[]const u8 = null; |
| 54 | var opt_target: ?struct { std.Target.Query, Backend } = null; |
| 42 | 55 | var preserve_tmp = false; |
| 43 | 56 | var enable_qemu: bool = false; |
| 44 | 57 | var enable_wine: bool = false; |
| 45 | 58 | var enable_wasmtime: bool = false; |
| 46 | 59 | var enable_darling: bool = false; |
| 60 | var quiet: bool = false; |
| 47 | 61 | |
| 48 | 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 | 66 | while (arg_it.next()) |arg| { |
| 53 | 67 | if (arg.len > 0 and arg[0] == '-') { |
| 54 | 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 | 75 | } else if (std.mem.eql(u8, arg, "--debug-log")) { |
| 57 | 76 | try debug_log_args.append( |
| 58 | 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 | 80 | } else if (std.mem.eql(u8, arg, "--preserve-tmp")) { |
| 62 | 81 | preserve_tmp = true; |
| ... | ... | @@ -69,9 +88,9 @@ pub fn main(init: std.process.Init) !void { |
| 69 | 88 | } else if (std.mem.eql(u8, arg, "-fdarling")) { |
| 70 | 89 | enable_darling = true; |
| 71 | 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 | 92 | } else { |
| 74 | | fatal("unknown option '{s}'\n{s}", .{ arg, usage }); |
| 93 | badUsage("unknown option '{s}'", .{arg}); |
| 75 | 94 | } |
| 76 | 95 | continue; |
| 77 | 96 | } |
| ... | ... | @@ -80,24 +99,29 @@ pub fn main(init: std.process.Init) !void { |
| 80 | 99 | } else if (opt_input_file_name == null) { |
| 81 | 100 | opt_input_file_name = arg; |
| 82 | 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}); |
| 87 | | const input_file_name = opt_input_file_name orelse fatal("missing input file\n{s}", .{usage}); |
| 105 | const zig_exe = opt_zig_exe orelse badUsage("missing path to zig", .{}); |
| 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 | 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. |
| 93 | | if (opt_lib_dir == null) { |
| 94 | | for (case.targets) |target| { |
| 95 | | if (target.backend == .cbe) { |
| 96 | | fatal("'--zig-lib-dir' requried when using backend 'cbe'", .{}); |
| 97 | | } |
| 116 | for (case.skip_targets) |skip| { |
| 117 | if (target_query.eql(skip.query) and backend == skip.backend) { |
| 118 | if (!quiet) std.log.warn("skipping test because of a 'skip_target' match", .{}); |
| 119 | return; |
| 98 | 120 | } |
| 99 | 121 | } |
| 100 | 122 | |
| 123 | const target = try std.zig.system.resolveTargetQuery(io, target_query); |
| 124 | |
| 101 | 125 | const prog_node = std.Progress.start(io, .{}); |
| 102 | 126 | defer prog_node.end(); |
| 103 | 127 | |
| ... | ... | @@ -122,143 +146,137 @@ pub fn main(init: std.process.Init) !void { |
| 122 | 146 | |
| 123 | 147 | const host = try std.zig.system.resolveTargetQuery(io, .{}); |
| 124 | 148 | |
| 125 | | const debug_log_verbose = debug_log_args.items.len != 0; |
| 126 | | |
| 127 | | for (case.targets) |target| { |
| 128 | | const target_prog_node = node: { |
| 129 | | var name_buf: [std.Progress.Node.max_name_len]u8 = undefined; |
| 130 | | const name = std.fmt.bufPrint(&name_buf, "{s}-{t}", .{ target.query, target.backend }) catch &name_buf; |
| 131 | | break :node prog_node.start(name, case.updates.len); |
| 132 | | }; |
| 133 | | defer target_prog_node.end(); |
| 134 | | |
| 135 | | if (debug_log_verbose) { |
| 136 | | std.log.scoped(.status).info("target: '{s}-{t}'", .{ target.query, target.backend }); |
| 137 | | } |
| 138 | | var child_args: std.ArrayList([]const u8) = .empty; |
| 139 | | try child_args.appendSlice(arena, &.{ |
| 140 | | resolved_zig_exe, |
| 141 | | "build-exe", |
| 142 | | "-fincremental", |
| 143 | | "-fno-ubsan-rt", |
| 144 | | "-target", |
| 145 | | target.query, |
| 146 | | "--cache-dir", |
| 147 | | ".local-cache", |
| 148 | | "--global-cache-dir", |
| 149 | | ".global-cache", |
| 150 | | }); |
| 151 | | try child_args.append(arena, "--listen=-"); |
| 152 | | |
| 153 | | if (opt_resolved_lib_dir) |resolved_lib_dir| { |
| 154 | | try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir }); |
| 155 | | } |
| 156 | | switch (target.backend) { |
| 157 | | .sema => try child_args.append(arena, "-fno-emit-bin"), |
| 158 | | .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }), |
| 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 | | } |
| 149 | var child_args: std.ArrayList([]const u8) = .empty; |
| 150 | try child_args.appendSlice(arena, &.{ |
| 151 | resolved_zig_exe, |
| 152 | "build-exe", |
| 153 | "-fincremental", |
| 154 | "-fno-ubsan-rt", |
| 155 | "-target", |
| 156 | try target_query.zigTriple(arena), |
| 157 | "--cache-dir", |
| 158 | ".local-cache", |
| 159 | "--global-cache-dir", |
| 160 | ".global-cache", |
| 161 | }); |
| 162 | try child_args.append(arena, "--listen=-"); |
| 163 | |
| 164 | if (opt_resolved_lib_dir) |resolved_lib_dir| { |
| 165 | try child_args.appendSlice(arena, &.{ "--zig-lib-dir", resolved_lib_dir }); |
| 166 | } |
| 167 | switch (backend) { |
| 168 | .sema => try child_args.append(arena, "-fno-emit-bin"), |
| 169 | .selfhosted => try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }), |
| 170 | .llvm => try child_args.appendSlice(arena, &.{ "-fllvm", "-flld" }), |
| 171 | .cbe => try child_args.appendSlice(arena, &.{ "-ofmt=c", "-lc" }), |
| 172 | } |
| 173 | for (debug_log_args.items) |arg| { |
| 174 | try child_args.appendSlice(arena, &.{ "--debug-log", arg }); |
| 175 | } |
| 176 | for (case.modules) |mod| { |
| 177 | try child_args.appendSlice(arena, &.{ "--dep", mod.name }); |
| 178 | } |
| 179 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-Mroot={s}", .{case.root_source_file})); |
| 180 | for (case.modules) |mod| { |
| 181 | try child_args.append(arena, try std.fmt.allocPrint(arena, "-M{s}={s}", .{ mod.name, mod.file })); |
| 182 | } |
| 172 | 183 | |
| 173 | | const zig_prog_node = target_prog_node.start("zig build-exe", 0); |
| 174 | | 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 | | }); |
| 184 | const zig_prog_node = prog_node.start("zig", 0); |
| 185 | defer zig_prog_node.end(); |
| 191 | 186 | |
| 192 | | try cc_child_args.append(arena, "-o"); |
| 193 | | } |
| 187 | var cc_child_args: std.ArrayList([]const u8) = .empty; |
| 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, .{ |
| 196 | | .argv = child_args.items, |
| 197 | | .stdin = .pipe, |
| 198 | | .stdout = .pipe, |
| 199 | | .stderr = .pipe, |
| 200 | | .progress_node = zig_prog_node, |
| 201 | | .cwd = .{ .path = tmp_dir_path }, |
| 194 | try cc_child_args.appendSlice(arena, &.{ |
| 195 | resolved_cc_zig_exe, |
| 196 | "cc", |
| 197 | "-target", |
| 198 | try target_query.zigTriple(arena), |
| 199 | "-I", |
| 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; |
| 224 | | var multi_reader: Io.File.MultiReader = undefined; |
| 225 | | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); |
| 226 | | defer multi_reader.deinit(); |
| 203 | try cc_child_args.append(arena, "-o"); |
| 204 | } |
| 227 | 205 | |
| 228 | | for (case.updates) |update| { |
| 229 | | var update_node = target_prog_node.start(update.name, 0); |
| 230 | | defer update_node.end(); |
| 206 | var child = try std.process.spawn(io, .{ |
| 207 | .argv = child_args.items, |
| 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) { |
| 233 | | std.log.scoped(.status).info("update: '{s}'", .{update.name}); |
| 234 | | } |
| 239 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; |
| 240 | var multi_reader: Io.File.MultiReader = undefined; |
| 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 }; |
| 237 | | defer log_cur_update = null; |
| 244 | for (case.updates) |update| { |
| 245 | var update_prog_node = updates_prog_node.start(update.name, 0); |
| 246 | defer update_prog_node.end(); |
| 238 | 247 | |
| 239 | | eval.write(update); |
| 240 | | try eval.requestUpdate(); |
| 241 | | try eval.check(&multi_reader, update, update_node); |
| 248 | if (debug_log_args.items.len != 0) { |
| 249 | // Print a line separating the debug logs from the compiler in the stderr output. |
| 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 | 266 | const Eval = struct { |
| 251 | 267 | arena: Allocator, |
| 252 | 268 | io: Io, |
| 253 | | host: std.Target, |
| 254 | 269 | case: Case, |
| 255 | | target: Case.Target, |
| 270 | host: std.Target, |
| 271 | target: std.Target, |
| 272 | backend: Backend, |
| 256 | 273 | tmp_dir: Dir, |
| 257 | 274 | tmp_dir_path: []const u8, |
| 258 | 275 | child: *std.process.Child, |
| 259 | | allow_stderr: bool, |
| 276 | allow_compiler_stderr: bool, |
| 277 | quiet: bool, |
| 260 | 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 | 280 | /// The arguments `out.c in.c` must be appended before spawning the subprocess. |
| 263 | 281 | cc_child_args: *std.ArrayList([]const u8), |
| 264 | 282 | |
| ... | ... | @@ -307,7 +325,7 @@ const Eval = struct { |
| 307 | 325 | .error_bundle => { |
| 308 | 326 | const result_error_bundle = try std.zig.Server.allocErrorBundle(arena, body); |
| 309 | 327 | if (stderr.bufferedLen() > 0) { |
| 310 | | if (eval.allow_stderr) { |
| 328 | if (eval.allow_compiler_stderr) { |
| 311 | 329 | std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()}); |
| 312 | 330 | } else { |
| 313 | 331 | eval.fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()}); |
| ... | ... | @@ -325,14 +343,14 @@ const Eval = struct { |
| 325 | 343 | _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; |
| 326 | 344 | |
| 327 | 345 | if (stderr.bufferedLen() > 0) { |
| 328 | | if (eval.allow_stderr) { |
| 346 | if (eval.allow_compiler_stderr) { |
| 329 | 347 | std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()}); |
| 330 | 348 | } else { |
| 331 | 349 | eval.fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()}); |
| 332 | 350 | } |
| 333 | 351 | stderr.tossBuffered(); |
| 334 | 352 | } |
| 335 | | if (eval.target.backend == .sema) { |
| 353 | if (eval.backend == .sema) { |
| 336 | 354 | try eval.checkSuccessOutcome(update, null, prog_node); |
| 337 | 355 | continue; |
| 338 | 356 | } |
| ... | ... | @@ -342,7 +360,7 @@ const Eval = struct { |
| 342 | 360 | |
| 343 | 361 | const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ |
| 344 | 362 | .root_name = "root", // corresponds to the module name "root" |
| 345 | | .target = &eval.target.resolved, |
| 363 | .target = &eval.target, |
| 346 | 364 | .output_mode = .Exe, |
| 347 | 365 | }); |
| 348 | 366 | const bin_path = try Dir.path.join(arena, &.{ result_dir, bin_name }); |
| ... | ... | @@ -357,7 +375,7 @@ const Eval = struct { |
| 357 | 375 | |
| 358 | 376 | const buffered_stderr = stderr.buffered(); |
| 359 | 377 | if (buffered_stderr.len > 0) { |
| 360 | | if (eval.allow_stderr) { |
| 378 | if (eval.allow_compiler_stderr) { |
| 361 | 379 | std.log.info("stderr:\n{s}", .{buffered_stderr}); |
| 362 | 380 | } else { |
| 363 | 381 | eval.fatal("unexpected stderr:\n{s}", .{buffered_stderr}); |
| ... | ... | @@ -450,12 +468,12 @@ const Eval = struct { |
| 450 | 468 | .stdout, .exit_code => {}, |
| 451 | 469 | } |
| 452 | 470 | const emitted_path = opt_emitted_path orelse { |
| 453 | | std.debug.assert(eval.target.backend == .sema); |
| 471 | std.debug.assert(eval.backend == .sema); |
| 454 | 472 | return; |
| 455 | 473 | }; |
| 456 | 474 | const io = eval.io; |
| 457 | 475 | |
| 458 | | const binary_path = switch (eval.target.backend) { |
| 476 | const binary_path = switch (eval.backend) { |
| 459 | 477 | .sema => unreachable, |
| 460 | 478 | .selfhosted, .llvm => emitted_path, |
| 461 | 479 | .cbe => bin: { |
| ... | ... | @@ -470,15 +488,15 @@ const Eval = struct { |
| 470 | 488 | const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor( |
| 471 | 489 | io, |
| 472 | 490 | &eval.host, |
| 473 | | &eval.target.resolved, |
| 474 | | .{ .link_libc = eval.target.backend == .cbe }, |
| 491 | &eval.target, |
| 492 | .{ .link_libc = eval.backend == .cbe }, |
| 475 | 493 | )) { |
| 476 | 494 | .bad_dl, .bad_os_or_cpu => { |
| 477 | 495 | // This binary cannot be executed on this host. |
| 478 | | if (eval.allow_stderr) { |
| 496 | if (!eval.quiet) { |
| 479 | 497 | std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{ |
| 480 | 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 | 502 | return; |
| ... | ... | @@ -534,10 +552,10 @@ const Eval = struct { |
| 534 | 552 | }) catch |err| { |
| 535 | 553 | if (is_foreign) { |
| 536 | 554 | // Chances are the foreign executor isn't available. Skip this evaluation. |
| 537 | | if (eval.allow_stderr) { |
| 555 | if (!eval.quiet) { |
| 538 | 556 | std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{ |
| 539 | 557 | binary_path, |
| 540 | | try eval.target.resolved.zigTriple(eval.arena), |
| 558 | try eval.target.zigTriple(eval.arena), |
| 541 | 559 | err, |
| 542 | 560 | }); |
| 543 | 561 | } |
| ... | ... | @@ -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 | 694 | const Case = struct { |
| 662 | 695 | updates: []Update, |
| 663 | 696 | root_source_file: []const u8, |
| 664 | | targets: []const Target, |
| 697 | skip_targets: []const SkipTarget, |
| 665 | 698 | modules: []const Module, |
| 666 | 699 | |
| 667 | | const Target = struct { |
| 668 | | query: []const u8, |
| 669 | | resolved: std.Target, |
| 700 | const SkipTarget = struct { |
| 701 | query: std.Target.Query, |
| 670 | 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 | 705 | const Module = 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 | 743 | const fatal = std.process.fatal; |
| 726 | 744 | |
| 727 | | var targets: std.ArrayList(Target) = .empty; |
| 745 | var skip_targets: std.ArrayList(SkipTarget) = .empty; |
| 728 | 746 | var modules: std.ArrayList(Module) = .empty; |
| 729 | 747 | var updates: std.ArrayList(Update) = .empty; |
| 730 | 748 | var changes: std.ArrayList(FullContents) = .empty; |
| ... | ... | @@ -739,29 +757,13 @@ const Case = struct { |
| 739 | 757 | const val = std.mem.trimEnd(u8, line_it.rest(), "\r"); // windows moment |
| 740 | 758 | if (val.len == 0) { |
| 741 | 759 | fatal("line {d}: missing value", .{line_n}); |
| 742 | | } else if (std.mem.eql(u8, key, "target")) { |
| 743 | | const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse |
| 744 | | fatal("line {d}: target does not include backend", .{line_n}); |
| 745 | | |
| 746 | | const query = val[0..split_idx]; |
| 747 | | |
| 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, .{ |
| 760 | } else if (std.mem.eql(u8, key, "skip_target")) { |
| 761 | const query, const backend = parseTargetQueryAndBackend( |
| 762 | val, |
| 763 | try std.fmt.allocPrint(arena, "line {d}: ", .{line_n}), |
| 764 | ); |
| 765 | try skip_targets.append(arena, .{ |
| 763 | 766 | .query = query, |
| 764 | | .resolved = resolved, |
| 765 | 767 | .backend = backend, |
| 766 | 768 | }); |
| 767 | 769 | } else if (std.mem.eql(u8, key, "module")) { |
| ... | ... | @@ -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 | 877 | if (changes.items.len > 0) { |
| 880 | 878 | const last_update = &updates.items[updates.items.len - 1]; |
| 881 | 879 | last_update.changes = changes.items; // arena so no need for toOwnedSlice |
| ... | ... | @@ -885,7 +883,7 @@ const Case = struct { |
| 885 | 883 | return .{ |
| 886 | 884 | .updates = updates.items, |
| 887 | 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 | 887 | .modules = modules.items, |
| 890 | 888 | }; |
| 891 | 889 | } |
| ... | ... | @@ -970,3 +968,32 @@ fn rand64(io: Io) u64 { |
| 970 | 968 | io.random(@ptrCast(&x)); |
| 971 | 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 | } |