| ... | ... | @@ -8,8 +8,24 @@ const Args = struct { |
| 8 | 8 | zig_file: ?std.Io.File, |
| 9 | 9 | lib_dir: ?std.Io.Dir, |
| 10 | 10 | src_dir: ?std.Io.Dir, |
| 11 | targets: std.ArrayList(u32), |
| 12 | target_bytes: std.ArrayList(u8), |
| 13 | |
| 14 | fn deinit(args: *Args, gpa: std.mem.Allocator) void { |
| 15 | args.targets.deinit(gpa); |
| 16 | args.target_bytes.deinit(gpa); |
| 17 | } |
| 18 | |
| 19 | fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void { |
| 20 | try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1); |
| 21 | try args.targets.append(gpa, @intCast(args.target_bytes.items.len)); |
| 22 | args.target_bytes.appendSliceAssumeCapacity(target); |
| 23 | args.target_bytes.appendAssumeCapacity(0); |
| 24 | } |
| 11 | 25 | }; |
| 12 | 26 | pub fn main(init: std.process.Init) !void { |
| 27 | const arena = init.arena.allocator(); |
| 28 | |
| 13 | 29 | var stdin_buffer: [512]u8 = undefined; |
| 14 | 30 | var stdout_buffer: [512]u8 = undefined; |
| 15 | 31 | var stderr_buffer: [512]u8 = undefined; |
| ... | ... | @@ -20,14 +36,19 @@ pub fn main(init: std.process.Init) !void { |
| 20 | 36 | .in = &stdin.interface, |
| 21 | 37 | .out = &stdout.interface, |
| 22 | 38 | }; |
| 23 | | try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string); |
| 39 | |
| 24 | 40 | var args: Args = .{ |
| 25 | 41 | .@"test" = null, |
| 26 | 42 | .manifest_file = null, |
| 27 | 43 | .zig_file = null, |
| 28 | 44 | .lib_dir = null, |
| 29 | 45 | .src_dir = null, |
| 46 | .targets = .empty, |
| 47 | .target_bytes = .empty, |
| 30 | 48 | }; |
| 49 | defer args.deinit(init.gpa); |
| 50 | |
| 51 | try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string); |
| 31 | 52 | while (true) { |
| 32 | 53 | const hdr = try server.receiveMessage(); |
| 33 | 54 | switch (hdr.tag) { |
| ... | ... | @@ -35,13 +56,11 @@ pub fn main(init: std.process.Init) !void { |
| 35 | 56 | std.debug.print("unsupported message: {t}\n", .{hdr.tag}); |
| 36 | 57 | std.process.exit(1); |
| 37 | 58 | }, |
| 38 | | .exit => { |
| 39 | | std.process.exit(0); |
| 40 | | }, |
| 59 | .exit => std.process.exit(0), |
| 41 | 60 | .args => { |
| 42 | | const args_body = try init.arena.allocator().alloc(u8, hdr.bytes_len); |
| 61 | const args_body = try arena.alloc(u8, hdr.bytes_len); |
| 43 | 62 | try server.in.readSliceAll(args_body); |
| 44 | | var state: enum { positional, zig, lib, src } = .positional; |
| 63 | var state: enum { positional, zig, lib, src, target } = .positional; |
| 45 | 64 | var input_dir: std.zig.Server.Message.InputDir = .cwd; |
| 46 | 65 | var args_body_offset: usize = 0; |
| 47 | 66 | while (args_body.len - args_body_offset > 0) { |
| ... | ... | @@ -53,7 +72,18 @@ pub fn main(init: std.process.Init) !void { |
| 53 | 72 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; |
| 54 | 73 | const string = args_body[args_body_offset..end]; |
| 55 | 74 | args_body_offset = end + 1; |
| 56 | | _ = string; |
| 75 | switch (state) { |
| 76 | .positional => if (std.mem.eql(u8, string, "--target")) { |
| 77 | state = .target; |
| 78 | continue; |
| 79 | } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { |
| 80 | try args.addTarget(init.gpa, target); |
| 81 | }, |
| 82 | .zig => unreachable, |
| 83 | .lib => unreachable, |
| 84 | .src => unreachable, |
| 85 | .target => try args.addTarget(init.gpa, string), |
| 86 | } |
| 57 | 87 | }, |
| 58 | 88 | .prefix => { |
| 59 | 89 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; |
| ... | ... | @@ -109,6 +139,7 @@ pub fn main(init: std.process.Init) !void { |
| 109 | 139 | std.debug.assert(args.src_dir == null); |
| 110 | 140 | args.src_dir = dir; |
| 111 | 141 | }, |
| 142 | .target => unreachable, |
| 112 | 143 | } |
| 113 | 144 | }, |
| 114 | 145 | .input_file, .input_file_content, .output_file => { |
| ... | ... | @@ -129,22 +160,30 @@ pub fn main(init: std.process.Init) !void { |
| 129 | 160 | std.debug.assert(args.zig_file == null); |
| 130 | 161 | args.zig_file = file; |
| 131 | 162 | }, |
| 132 | | .lib, .src => unreachable, |
| 163 | .lib => unreachable, |
| 164 | .src => unreachable, |
| 165 | .target => unreachable, |
| 133 | 166 | } |
| 134 | 167 | }, |
| 135 | 168 | } |
| 136 | 169 | state = .positional; |
| 137 | 170 | } |
| 138 | 171 | }, |
| 139 | | .query_test_metadata => try server.serveTestMetadata(.{ |
| 140 | | .names = &.{0}, |
| 141 | | .expected_panic_msgs = &.{0}, |
| 142 | | .string_bytes = "runner\x00", |
| 143 | | }), |
| 172 | .query_test_metadata => { |
| 173 | const expected_panic_msgs = try arena.alloc(u32, args.targets.items.len); |
| 174 | @memset(expected_panic_msgs, 0); |
| 175 | try server.serveTestMetadata(.{ |
| 176 | .names = args.targets.items, |
| 177 | .expected_panic_msgs = expected_panic_msgs, |
| 178 | .string_bytes = args.target_bytes.items, |
| 179 | }); |
| 180 | }, |
| 144 | 181 | .run_test => { |
| 145 | 182 | const test_index = try server.receiveBody_u32(); |
| 183 | const target = |
| 184 | std.mem.sliceTo(args.target_bytes.items[args.targets.items[test_index]..], 0); |
| 146 | 185 | try server.serveBodylessMessage(.test_started); |
| 147 | | const test_result = runTest(init.io, &stderr.interface, &server, &args); |
| 186 | const test_result = runTest(init.io, &stderr.interface, &server, &args, target); |
| 148 | 187 | try stderr.flush(); |
| 149 | 188 | try server.serveTestResults(.{ |
| 150 | 189 | .index = test_index, |
| ... | ... | @@ -170,11 +209,13 @@ fn runTest( |
| 170 | 209 | stderr: *std.Io.Writer, |
| 171 | 210 | server: *std.zig.Server, |
| 172 | 211 | args: *const Args, |
| 173 | | ) anyerror!void { |
| 212 | target: []const u8, |
| 213 | ) !void { |
| 174 | 214 | const manifest_file = args.manifest_file orelse return error.MissingManifestArg; |
| 175 | 215 | const src_dir = args.src_dir orelse return error.MissingSrcDir; |
| 176 | 216 | var manifest_buffer: [512]u8 = undefined; |
| 177 | 217 | var manifest_fr = manifest_file.reader(io, &manifest_buffer); |
| 218 | var allow_skip = true; |
| 178 | 219 | var skip_delimiter = false; |
| 179 | 220 | var update_mtime = std.Io.Clock.real.now(io); |
| 180 | 221 | var update_num: usize = 0; |
| ... | ... | @@ -188,7 +229,7 @@ fn runTest( |
| 188 | 229 | continue; |
| 189 | 230 | } |
| 190 | 231 | const cmd = std.meta.stringToEnum( |
| 191 | | enum { obj, exe, lib, write, delete, update, check }, |
| 232 | enum { skip, obj, exe, lib, write, delete, update, check }, |
| 192 | 233 | cmd_str, |
| 193 | 234 | ) orelse return error.UnknownCommand; |
| 194 | 235 | var maybe_arg = line_it.next(); |
| ... | ... | @@ -198,38 +239,40 @@ fn runTest( |
| 198 | 239 | dr: DelimitedReader, |
| 199 | 240 | fr: std.Io.File.Reader, |
| 200 | 241 | } = undefined; |
| 201 | | const contents_r: *std.Io.Reader = contents_r: { |
| 202 | | switch (cmd) { |
| 203 | | else => {}, |
| 204 | | .delete => break :contents_r .ending, |
| 205 | | } |
| 206 | | |
| 207 | | const contents_path = contents_path: { |
| 208 | | const line_rest = line_it.rest(); |
| 209 | | if (line_rest.len > 0) break :contents_path line_rest; |
| 210 | | const contents_path = maybe_arg orelse break :contents_r .ending; |
| 211 | | maybe_arg = null; |
| 212 | | break :contents_path contents_path; |
| 213 | | }; |
| 214 | | if (std.mem.eql(u8, contents_path, "{")) { |
| 215 | | contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) }; |
| 216 | | skip_delimiter = true; |
| 217 | | break :contents_r &contents_impl.dr.interface; |
| 218 | | } |
| 242 | const contents_r: *std.Io.Reader = contents_r: switch (cmd) { |
| 243 | .skip, .delete => { |
| 244 | if (line_it.next()) |_| return error.UnexpectedArg; |
| 245 | break :contents_r .ending; |
| 246 | }, |
| 247 | else => { |
| 248 | const contents_path = contents_path: { |
| 249 | const line_rest = line_it.rest(); |
| 250 | if (line_rest.len > 0) break :contents_path line_rest; |
| 251 | const contents_path = maybe_arg orelse break :contents_r .ending; |
| 252 | maybe_arg = null; |
| 253 | break :contents_path contents_path; |
| 254 | }; |
| 255 | if (std.mem.eql(u8, contents_path, "{")) { |
| 256 | contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) }; |
| 257 | skip_delimiter = true; |
| 258 | break :contents_r &contents_impl.dr.interface; |
| 259 | } |
| 219 | 260 | |
| 220 | | const @"test" = args.@"test" orelse return error.ContentsFileInFileTest; |
| 221 | | try server.serveMessageHeader(.{ |
| 222 | | .tag = .discovered_inputs, |
| 223 | | .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + contents_path.len + 1), |
| 224 | | }); |
| 225 | | try server.out.writeInt(u32, @backingInt(@"test".input_dir), .little); |
| 226 | | try server.out.writeAll(contents_path); |
| 227 | | try server.out.writeByte(0); |
| 228 | | try server.out.flush(); |
| 261 | const @"test" = args.@"test" orelse return error.ContentsFileInFileTest; |
| 262 | try server.serveMessageHeader(.{ |
| 263 | .tag = .discovered_inputs, |
| 264 | .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + |
| 265 | contents_path.len + 1), |
| 266 | }); |
| 267 | try server.out.writeInt(u32, @backingInt(@"test".input_dir), .little); |
| 268 | try server.out.writeAll(contents_path); |
| 269 | try server.out.writeByte(0); |
| 270 | try server.out.flush(); |
| 229 | 271 | |
| 230 | | contents_file = try @"test".dir.openFile(io, contents_path, .{}); |
| 231 | | contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) }; |
| 232 | | break :contents_r &contents_impl.fr.interface; |
| 272 | contents_file = try @"test".dir.openFile(io, contents_path, .{}); |
| 273 | contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) }; |
| 274 | break :contents_r &contents_impl.fr.interface; |
| 275 | }, |
| 233 | 276 | }; |
| 234 | 277 | switch (cmd) { |
| 235 | 278 | else => { |
| ... | ... | @@ -237,6 +280,10 @@ fn runTest( |
| 237 | 280 | _ = try contents_r.streamRemaining(stderr); |
| 238 | 281 | try stderr.writeAll("\n}\n"); |
| 239 | 282 | }, |
| 283 | .skip => if (!allow_skip) |
| 284 | return error.NonInitialSkipCommand // #skip must appear first |
| 285 | else if (std.mem.eql(u8, target, maybe_arg orelse return error.MissingArg)) |
| 286 | return error.SkipZigTest, |
| 240 | 287 | .write => { |
| 241 | 288 | const file = try src_dir.createFile(io, maybe_arg orelse return error.MissingArg, .{}); |
| 242 | 289 | defer file.close(io); |
| ... | ... | @@ -262,6 +309,7 @@ fn runTest( |
| 262 | 309 | }, |
| 263 | 310 | } |
| 264 | 311 | std.debug.assert(try contents_r.discardRemaining() == 0); |
| 312 | if (cmd != .skip) allow_skip = false; |
| 265 | 313 | } else |err| switch (err) { |
| 266 | 314 | else => |e| return e, |
| 267 | 315 | error.EndOfStream => if (skip_delimiter) return error.MissingDelimiter, |