| author | |
| committer | |
| log | 488c15320b6e2848dd61245303e75f7577313ff2 |
| tree | 6201cb5b097c9b04705335f70f1430353c02f466 |
| parent | 8a6475c4f79374975be61ac7be13e164956d1a5c |
9 files changed, 762 insertions(+), 354 deletions(-)
lib/compiler/Maker/Step/Run.zig+21-19| ... | @@ -231,6 +231,7 @@ pub fn make( | ... | @@ -231,6 +231,7 @@ pub fn make( |
| 231 | argv_list.appendAssumeCapacity("--listen=-"); | 231 | argv_list.appendAssumeCapacity("--listen=-"); |
| 232 | }, | 232 | }, |
| 233 | .protocol => { | 233 | .protocol => { |
| 234 | argv_list.appendAssumeCapacity("--listen=-"); | ||
| 234 | try input_dirs.append(gpa, if (conf_run.cwd.value) |lazy_cwd| | 235 | try input_dirs.append(gpa, if (conf_run.cwd.value) |lazy_cwd| |
| 235 | try maker.resolveLazyPathIndex(arena, lazy_cwd, run_index) | 236 | try maker.resolveLazyPathIndex(arena, lazy_cwd, run_index) |
| 236 | else | 237 | else |
| ... | @@ -742,7 +743,8 @@ fn waitZigTest( | ... | @@ -742,7 +743,8 @@ fn waitZigTest( |
| 742 | run_index: Configuration.Step.Index, | 743 | run_index: Configuration.Step.Index, |
| 743 | maker: *Maker, | 744 | maker: *Maker, |
| 744 | child: *process.Child, | 745 | child: *process.Child, |
| 745 | progress_node: std.Progress.Node, | 746 | tests_prog_node: std.Progress.Node, |
| 747 | test_prog_node: std.Progress.Node, | ||
| 746 | protocol_args: []const u8, | 748 | protocol_args: []const u8, |
| 747 | input_dirs: []const Cache.Path, | 749 | input_dirs: []const Cache.Path, |
| 748 | man: ?*Cache.Manifest, | 750 | man: ?*Cache.Manifest, |
| ... | @@ -765,9 +767,6 @@ fn waitZigTest( | ... | @@ -765,9 +767,6 @@ fn waitZigTest( |
| 765 | const io = graph.io; | 767 | const io = graph.io; |
| 766 | const step = maker.stepByIndex(run_index); | 768 | const step = maker.stepByIndex(run_index); |
| 767 | 769 | ||
| 768 | var sub_prog_node: ?std.Progress.Node = null; | ||
| 769 | defer if (sub_prog_node) |n| n.end(); | ||
| 770 | |||
| 771 | const stdout = multi_reader.reader(0); | 770 | const stdout = multi_reader.reader(0); |
| 772 | const stderr = multi_reader.reader(1); | 771 | const stderr = multi_reader.reader(1); |
| 773 | 772 | ||
| ... | @@ -788,7 +787,7 @@ fn waitZigTest( | ... | @@ -788,7 +787,7 @@ fn waitZigTest( |
| 788 | 787 | ||
| 789 | if (opt_metadata.*) |*md| { | 788 | if (opt_metadata.*) |*md| { |
| 790 | // Previous unit test process died or was killed; we're continuing where it left off | 789 | // Previous unit test process died or was killed; we're continuing where it left off |
| 791 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 790 | requestNextTest(&client, md, test_prog_node) catch |err| return .{ .write_failed = err }; |
| 792 | } else { | 791 | } else { |
| 793 | // Running unit tests normally | 792 | // Running unit tests normally |
| 794 | run.fuzz_tests.clearRetainingCapacity(); | 793 | run.fuzz_tests.clearRetainingCapacity(); |
| ... | @@ -859,27 +858,27 @@ fn waitZigTest( | ... | @@ -859,27 +858,27 @@ fn waitZigTest( |
| 859 | 858 | ||
| 860 | const string_bytes = body_r.take(tm_hdr.string_bytes_len) catch unreachable; | 859 | const string_bytes = body_r.take(tm_hdr.string_bytes_len) catch unreachable; |
| 861 | 860 | ||
| 862 | progress_node.setEstimatedTotalItems(names.len); | 861 | tests_prog_node.setEstimatedTotalItems(names.len); |
| 863 | opt_metadata.* = .{ | 862 | opt_metadata.* = .{ |
| 864 | .string_bytes = try arena.dupe(u8, string_bytes), | 863 | .string_bytes = try arena.dupe(u8, string_bytes), |
| 865 | .ns_per_test = try arena.alloc(u64, results.test_count), | 864 | .ns_per_test = try arena.alloc(u64, results.test_count), |
| 866 | .names = names, | 865 | .names = names, |
| 867 | .expected_panic_msgs = expected_panic_msgs, | 866 | .expected_panic_msgs = expected_panic_msgs, |
| 868 | .next_index = 0, | 867 | .next_index = 0, |
| 869 | .prog_node = progress_node, | ||
| 870 | }; | 868 | }; |
| 871 | @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64)); | 869 | @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64)); |
| 872 | 870 | ||
| 873 | active_test_index = null; | 871 | active_test_index = null; |
| 874 | last_update = .now(io, .awake); | 872 | last_update = .now(io, .awake); |
| 875 | 873 | ||
| 876 | requestNextTest(&client, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 874 | requestNextTest(&client, &opt_metadata.*.?, test_prog_node) catch |err| return .{ .write_failed = err }; |
| 877 | }, | 875 | }, |
| 878 | .test_started => { | 876 | .test_started => { |
| 879 | active_test_index = opt_metadata.*.?.next_index - 1; | 877 | active_test_index = opt_metadata.*.?.next_index - 1; |
| 880 | last_update = .now(io, .awake); | 878 | last_update = .now(io, .awake); |
| 881 | }, | 879 | }, |
| 882 | .test_results => { | 880 | .test_results => { |
| 881 | tests_prog_node.completeOne(); | ||
| 883 | const md = &opt_metadata.*.?; | 882 | const md = &opt_metadata.*.?; |
| 884 | 883 | ||
| 885 | const tr_hdr = body_r.takeStruct(std.zig.Server.Message.TestResults, .little) catch unreachable; | 884 | const tr_hdr = body_r.takeStruct(std.zig.Server.Message.TestResults, .little) catch unreachable; |
| ... | @@ -924,7 +923,7 @@ fn waitZigTest( | ... | @@ -924,7 +923,7 @@ fn waitZigTest( |
| 924 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); | 923 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); |
| 925 | last_update = now; | 924 | last_update = now; |
| 926 | 925 | ||
| 927 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | 926 | requestNextTest(&client, md, test_prog_node) catch |err| return .{ .write_failed = err }; |
| 928 | }, | 927 | }, |
| 929 | .discovered_inputs => while (body_r.takeEnum( | 928 | .discovered_inputs => while (body_r.takeEnum( |
| 930 | std.zig.Server.Message.InputDir, | 929 | std.zig.Server.Message.InputDir, |
| ... | @@ -1549,6 +1548,7 @@ fn evalZigTest( | ... | @@ -1549,6 +1548,7 @@ fn evalZigTest( |
| 1549 | maker, | 1548 | maker, |
| 1550 | &child, | 1549 | &child, |
| 1551 | progress_node, | 1550 | progress_node, |
| 1551 | spawn_options.progress_node, | ||
| 1552 | protocol_args, | 1552 | protocol_args, |
| 1553 | input_dirs, | 1553 | input_dirs, |
| 1554 | man, | 1554 | man, |
| ... | @@ -1681,7 +1681,6 @@ const TestMetadata = struct { | ... | @@ -1681,7 +1681,6 @@ const TestMetadata = struct { |
| 1681 | expected_panic_msgs: []const u32, | 1681 | expected_panic_msgs: []const u32, |
| 1682 | string_bytes: []const u8, | 1682 | string_bytes: []const u8, |
| 1683 | next_index: u32, | 1683 | next_index: u32, |
| 1684 | prog_node: std.Progress.Node, | ||
| 1685 | 1684 | ||
| 1686 | fn toCachedTestMetadata(tm: TestMetadata) CachedTestMetadata { | 1685 | fn toCachedTestMetadata(tm: TestMetadata) CachedTestMetadata { |
| 1687 | return .{ | 1686 | return .{ |
| ... | @@ -1704,16 +1703,14 @@ pub const CachedTestMetadata = struct { | ... | @@ -1704,16 +1703,14 @@ pub const CachedTestMetadata = struct { |
| 1704 | } | 1703 | } |
| 1705 | }; | 1704 | }; |
| 1706 | 1705 | ||
| 1707 | fn requestNextTest(client: *std.zig.Client, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | 1706 | fn requestNextTest(client: *std.zig.Client, metadata: *TestMetadata, test_prog_node: std.Progress.Node) !void { |
| 1708 | while (metadata.next_index < metadata.names.len) { | 1707 | while (metadata.next_index < metadata.names.len) { |
| 1709 | const i = metadata.next_index; | 1708 | const i = metadata.next_index; |
| 1710 | metadata.next_index += 1; | 1709 | metadata.next_index += 1; |
| 1711 | 1710 | ||
| 1712 | if (metadata.expected_panic_msgs[i] != 0) continue; | 1711 | if (metadata.expected_panic_msgs[i] != 0) continue; |
| 1713 | 1712 | ||
| 1714 | const name = metadata.testName(i); | 1713 | test_prog_node.setName(metadata.testName(i)); |
| 1715 | if (sub_prog_node.*) |n| n.end(); | ||
| 1716 | sub_prog_node.* = metadata.prog_node.start(name, 0); | ||
| 1717 | 1714 | ||
| 1718 | try client.serveRunTest(i); | 1715 | try client.serveRunTest(i); |
| 1719 | return; | 1716 | return; |
| ... | @@ -2664,9 +2661,9 @@ fn spawnChildAndCollect( | ... | @@ -2664,9 +2661,9 @@ fn spawnChildAndCollect( |
| 2664 | .cwd = child_cwd, | 2661 | .cwd = child_cwd, |
| 2665 | .environ_map = environ_map, | 2662 | .environ_map = environ_map, |
| 2666 | .request_resource_usage_statistics = true, | 2663 | .request_resource_usage_statistics = true, |
| 2667 | .stdin = if (conf_run.stdin.u != .none) s: { | 2664 | .stdin = if (conf_run.stdin.u != .none) stdin: { |
| 2668 | assert(conf_run.flags.stdio != .inherit); | 2665 | assert(conf_run.flags.stdio != .inherit); |
| 2669 | break :s .pipe; | 2666 | break :stdin .pipe; |
| 2670 | } else switch (conf_run.flags.stdio) { | 2667 | } else switch (conf_run.flags.stdio) { |
| 2671 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, | 2668 | .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore, |
| 2672 | .inherit => .inherit, | 2669 | .inherit => .inherit, |
| ... | @@ -2691,10 +2688,10 @@ fn spawnChildAndCollect( | ... | @@ -2691,10 +2688,10 @@ fn spawnChildAndCollect( |
| 2691 | 2688 | ||
| 2692 | if (maker.protocol_server != null) { | 2689 | if (maker.protocol_server != null) { |
| 2693 | if (spawn_options.stdin == .inherit) { | 2690 | if (spawn_options.stdin == .inherit) { |
| 2694 | return step.fail(maker, "Cannot inherit stdin when running through over the build system protocol", .{}); | 2691 | return step.fail(maker, "Cannot inherit stdin when running over the build system protocol", .{}); |
| 2695 | } | 2692 | } |
| 2696 | if (spawn_options.stdout == .inherit) { | 2693 | if (spawn_options.stdout == .inherit) { |
| 2697 | return step.fail(maker, "Cannot inherit stdout when running through over the build system protocol", .{}); | 2694 | return step.fail(maker, "Cannot inherit stdout when running over the build system protocol", .{}); |
| 2698 | } | 2695 | } |
| 2699 | assert(spawn_options.stderr != .inherit); | 2696 | assert(spawn_options.stderr != .inherit); |
| 2700 | } | 2697 | } |
| ... | @@ -2720,7 +2717,12 @@ fn spawnChildAndCollect( | ... | @@ -2720,7 +2717,12 @@ fn spawnChildAndCollect( |
| 2720 | step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); | 2717 | step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); |
| 2721 | return try result; | 2718 | return try result; |
| 2722 | }, | 2719 | }, |
| 2723 | .zig_test, .protocol => { | 2720 | .zig_test, .protocol => |stdio| { |
| 2721 | spawn_options.progress_node = if (conf_run.flags.disable_zig_progress or fuzz_context != null) | ||
| 2722 | .none | ||
| 2723 | else | ||
| 2724 | progress_node.start(@tagName(stdio), 0); | ||
| 2725 | defer spawn_options.progress_node.end(); | ||
| 2724 | try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?); | 2726 | try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?); |
| 2725 | const started: Io.Clock.Timestamp = .now(io, .awake); | 2727 | const started: Io.Clock.Timestamp = .now(io, .awake); |
| 2726 | const result = evalZigTest( | 2728 | const result = evalZigTest( |
lib/std/Io.zig-18| ... | @@ -213,9 +213,7 @@ pub const VTable = struct { | ... | @@ -213,9 +213,7 @@ pub const VTable = struct { |
| 213 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, | 213 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, |
| 214 | processSetCurrentPath: *const fn (?*anyopaque, []const u8) std.process.SetCurrentPathError!void, | 214 | processSetCurrentPath: *const fn (?*anyopaque, []const u8) std.process.SetCurrentPathError!void, |
| 215 | processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError, | 215 | processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError, |
| 216 | processReplacePath: *const fn (?*anyopaque, Dir, std.process.ReplaceOptions) std.process.ReplaceError, | ||
| 217 | processSpawn: *const fn (?*anyopaque, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, | 216 | processSpawn: *const fn (?*anyopaque, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, |
| 218 | processSpawnPath: *const fn (?*anyopaque, Dir, std.process.SpawnOptions) std.process.SpawnError!std.process.Child, | ||
| 219 | childWait: *const fn (?*anyopaque, *std.process.Child) std.process.Child.WaitError!std.process.Child.Term, | 217 | childWait: *const fn (?*anyopaque, *std.process.Child) std.process.Child.WaitError!std.process.Child.Term, |
| 220 | childKill: *const fn (?*anyopaque, *std.process.Child) void, | 218 | childKill: *const fn (?*anyopaque, *std.process.Child) void, |
| 221 | 219 | ||
| ... | @@ -2797,9 +2795,7 @@ pub const failing: std.Io = .{ | ... | @@ -2797,9 +2795,7 @@ pub const failing: std.Io = .{ |
| 2797 | .processSetCurrentDir = failingProcessSetCurrentDir, | 2795 | .processSetCurrentDir = failingProcessSetCurrentDir, |
| 2798 | .processSetCurrentPath = failingProcessSetCurrentPath, | 2796 | .processSetCurrentPath = failingProcessSetCurrentPath, |
| 2799 | .processReplace = failingProcessReplace, | 2797 | .processReplace = failingProcessReplace, |
| 2800 | .processReplacePath = failingProcessReplacePath, | ||
| 2801 | .processSpawn = failingProcessSpawn, | 2798 | .processSpawn = failingProcessSpawn, |
| 2802 | .processSpawnPath = failingProcessSpawnPath, | ||
| 2803 | .childWait = unreachableChildWait, | 2799 | .childWait = unreachableChildWait, |
| 2804 | .childKill = unreachableChildKill, | 2800 | .childKill = unreachableChildKill, |
| 2805 | 2801 | ||
| ... | @@ -3451,26 +3447,12 @@ pub fn failingProcessReplace(userdata: ?*anyopaque, options: std.process.Replace | ... | @@ -3451,26 +3447,12 @@ pub fn failingProcessReplace(userdata: ?*anyopaque, options: std.process.Replace |
| 3451 | return error.OperationUnsupported; | 3447 | return error.OperationUnsupported; |
| 3452 | } | 3448 | } |
| 3453 | 3449 | ||
| 3454 | pub fn failingProcessReplacePath(userdata: ?*anyopaque, dir: Dir, options: std.process.ReplaceOptions) std.process.ReplaceError { | ||
| 3455 | _ = userdata; | ||
| 3456 | _ = dir; | ||
| 3457 | _ = options; | ||
| 3458 | return error.OperationUnsupported; | ||
| 3459 | } | ||
| 3460 | |||
| 3461 | pub fn failingProcessSpawn(userdata: ?*anyopaque, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child { | 3450 | pub fn failingProcessSpawn(userdata: ?*anyopaque, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child { |
| 3462 | _ = userdata; | 3451 | _ = userdata; |
| 3463 | _ = options; | 3452 | _ = options; |
| 3464 | return error.OperationUnsupported; | 3453 | return error.OperationUnsupported; |
| 3465 | } | 3454 | } |
| 3466 | 3455 | ||
| 3467 | pub fn failingProcessSpawnPath(userdata: ?*anyopaque, dir: Dir, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child { | ||
| 3468 | _ = userdata; | ||
| 3469 | _ = dir; | ||
| 3470 | _ = options; | ||
| 3471 | return error.OperationUnsupported; | ||
| 3472 | } | ||
| 3473 | |||
| 3474 | pub fn unreachableChildWait(userdata: ?*anyopaque, child: *std.process.Child) std.process.Child.WaitError!std.process.Child.Term { | 3456 | pub fn unreachableChildWait(userdata: ?*anyopaque, child: *std.process.Child) std.process.Child.WaitError!std.process.Child.Term { |
| 3475 | _ = userdata; | 3457 | _ = userdata; |
| 3476 | _ = child; | 3458 | _ = child; |
lib/std/Io/Threaded.zig+98-68| ... | @@ -1901,9 +1901,7 @@ pub fn io(t: *Threaded) Io { | ... | @@ -1901,9 +1901,7 @@ pub fn io(t: *Threaded) Io { |
| 1901 | .processSetCurrentDir = processSetCurrentDir, | 1901 | .processSetCurrentDir = processSetCurrentDir, |
| 1902 | .processSetCurrentPath = processSetCurrentPath, | 1902 | .processSetCurrentPath = processSetCurrentPath, |
| 1903 | .processReplace = processReplace, | 1903 | .processReplace = processReplace, |
| 1904 | .processReplacePath = processReplacePath, | ||
| 1905 | .processSpawn = processSpawn, | 1904 | .processSpawn = processSpawn, |
| 1906 | .processSpawnPath = processSpawnPath, | ||
| 1907 | .childWait = childWait, | 1905 | .childWait = childWait, |
| 1908 | .childKill = childKill, | 1906 | .childKill = childKill, |
| 1909 | 1907 | ||
| ... | @@ -15133,23 +15131,7 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces | ... | @@ -15133,23 +15131,7 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 15133 | }); | 15131 | }); |
| 15134 | }; | 15132 | }; |
| 15135 | 15133 | ||
| 15136 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); | 15134 | return posixExec(options.exe, argv_buf.ptr, options.expand_arg0, env_block, PATH); |
| 15137 | } | ||
| 15138 | |||
| 15139 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { | ||
| 15140 | if (!process.can_replace) return error.OperationUnsupported; | ||
| 15141 | _ = userdata; | ||
| 15142 | _ = dir; | ||
| 15143 | _ = options; | ||
| 15144 | @panic("TODO processReplacePath"); | ||
| 15145 | } | ||
| 15146 | |||
| 15147 | fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child { | ||
| 15148 | if (!process.can_spawn) return error.OperationUnsupported; | ||
| 15149 | _ = userdata; | ||
| 15150 | _ = dir; | ||
| 15151 | _ = options; | ||
| 15152 | @panic("TODO processSpawnPath"); | ||
| 15153 | } | 15135 | } |
| 15154 | 15136 | ||
| 15155 | const processSpawn = switch (native_os) { | 15137 | const processSpawn = switch (native_os) { |
| ... | @@ -15263,15 +15245,15 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -15263,15 +15245,15 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 15263 | if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked; | 15245 | if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked; |
| 15264 | const ep1 = err_pipe[1]; | 15246 | const ep1 = err_pipe[1]; |
| 15265 | 15247 | ||
| 15266 | // Must happen befor clobbering fds below. | 15248 | // Must happen before clobbering fds below. |
| 15267 | switch (options.cwd) { | 15249 | switch (options.cwd) { |
| 15268 | .inherit => {}, | 15250 | .inherit => {}, |
| 15269 | .dir => |cwd| fchdir(cwd.handle) catch |err| forkBail(ep1, err), | 15251 | .dir => |cwd| fchdir(cwd.handle) catch |err| forkBail(ep1, err), |
| 15270 | .path => |cwd| chdir(cwd) catch |err| forkBail(ep1, err), | 15252 | .path => |cwd| chdir(cwd) catch |err| forkBail(ep1, err), |
| 15271 | } | 15253 | } |
| 15272 | 15254 | ||
| 15273 | for (options.inherit_dirs) |dir| switch (posix.errno( | 15255 | for (options.inherit_dirs) |inherit_dir| switch (posix.errno( |
| 15274 | posix.system.fcntl(dir.handle, posix.F.SETFD, @as(u32, 0)), | 15256 | posix.system.fcntl(inherit_dir.handle, posix.F.SETFD, @as(u32, 0)), |
| 15275 | )) { | 15257 | )) { |
| 15276 | .SUCCESS => {}, | 15258 | .SUCCESS => {}, |
| 15277 | else => |err| forkBail(ep1, posix.unexpectedErrno(err)), | 15259 | else => |err| forkBail(ep1, posix.unexpectedErrno(err)), |
| ... | @@ -15331,14 +15313,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -15331,14 +15313,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 15331 | } | 15313 | } |
| 15332 | 15314 | ||
| 15333 | if (options.start_suspended) { | 15315 | if (options.start_suspended) { |
| 15334 | switch (posix.errno(posix.system.kill(0, .STOP))) { | 15316 | switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) { |
| 15335 | .SUCCESS => {}, | 15317 | .SUCCESS => {}, |
| 15336 | .PERM => forkBail(ep1, error.PermissionDenied), | 15318 | .PERM => forkBail(ep1, error.PermissionDenied), |
| 15337 | else => forkBail(ep1, error.Unexpected), | 15319 | else => forkBail(ep1, error.Unexpected), |
| 15338 | } | 15320 | } |
| 15339 | } | 15321 | } |
| 15340 | 15322 | ||
| 15341 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); | 15323 | const err = posixExec(options.exe, argv_buf.ptr, options.expand_arg0, env_block, PATH); |
| 15342 | forkBail(ep1, err); | 15324 | forkBail(ep1, err); |
| 15343 | } | 15325 | } |
| 15344 | 15326 | ||
| ... | @@ -17042,61 +17024,109 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c | ... | @@ -17042,61 +17024,109 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c |
| 17042 | try std.testing.expectEqualStrings(expected_cmd_line, cmd_line); | 17024 | try std.testing.expectEqualStrings(expected_cmd_line, cmd_line); |
| 17043 | } | 17025 | } |
| 17044 | 17026 | ||
| 17045 | fn posixExecv( | 17027 | fn posixExec( |
| 17046 | arg0_expand: process.ArgExpansion, | 17028 | exe: process.ReplaceOptions.Exe, |
| 17047 | file: [*:0]const u8, | 17029 | argv: [*:null]?[*:0]const u8, |
| 17048 | child_argv: [*:null]?[*:0]const u8, | 17030 | expand_arg0: process.ArgExpansion, |
| 17049 | env_block: process.Environ.PosixBlock, | 17031 | env_block: process.Environ.PosixBlock, |
| 17050 | PATH: []const u8, | 17032 | PATH: []const u8, |
| 17051 | ) process.ReplaceError { | 17033 | ) process.ReplaceError { |
| 17052 | const file_slice = std.mem.sliceTo(file, 0); | 17034 | const arg0_slice = std.mem.span(argv[0].?); |
| 17053 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, env_block); | 17035 | exe: switch (exe) { |
| 17054 | 17036 | .detect => continue :exe if (std.mem.findScalar(u8, arg0_slice, '/')) |_| .{ | |
| 17055 | // Use of PATH_MAX here is valid as the path_buf will be passed | 17037 | .path = .cwd(), |
| 17056 | // directly to the operating system in posixExecvPath. | 17038 | } else .search, |
| 17057 | var path_buf: [posix.PATH_MAX]u8 = undefined; | 17039 | .search => { |
| 17058 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); | 17040 | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 17059 | var seen_eacces = false; | 17041 | // directly to the operating system in posixExecveat. |
| 17060 | var err: process.ReplaceError = error.FileNotFound; | 17042 | var path_buf: [posix.PATH_MAX]u8 = undefined; |
| 17061 | 17043 | var it = std.mem.tokenizeScalar(u8, PATH, ':'); | |
| 17062 | // In case of expanding arg0 we must put it back if we return with an error. | 17044 | var err: error{ AccessDenied, FileNotFound, NotDir } = error.FileNotFound; |
| 17063 | const prev_arg0 = child_argv[0]; | ||
| 17064 | defer switch (arg0_expand) { | ||
| 17065 | .expand => child_argv[0] = prev_arg0, | ||
| 17066 | .no_expand => {}, | ||
| 17067 | }; | ||
| 17068 | 17045 | ||
| 17069 | while (it.next()) |search_path| { | 17046 | while (it.next()) |search_path| { |
| 17070 | const path_len = search_path.len + file_slice.len + 1; | 17047 | const path_len = search_path.len + arg0_slice.len + 1; |
| 17071 | if (path_buf.len < path_len + 1) return error.NameTooLong; | 17048 | if (path_buf.len < path_len + 1) return error.NameTooLong; |
| 17072 | @memcpy(path_buf[0..search_path.len], search_path); | 17049 | @memcpy(path_buf[0..search_path.len], search_path); |
| 17073 | path_buf[search_path.len] = '/'; | 17050 | path_buf[search_path.len] = '/'; |
| 17074 | @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice); | 17051 | @memcpy(path_buf[search_path.len + 1 ..][0..arg0_slice.len], arg0_slice); |
| 17075 | path_buf[path_len] = 0; | 17052 | path_buf[path_len] = 0; |
| 17076 | const full_path = path_buf[0..path_len :0].ptr; | 17053 | const full_path = path_buf[0..path_len :0].ptr; |
| 17077 | switch (arg0_expand) { | 17054 | switch (expand_arg0) { |
| 17078 | .expand => child_argv[0] = full_path, | 17055 | .expand => argv[0] = full_path, |
| 17079 | .no_expand => {}, | 17056 | .no_expand => {}, |
| 17080 | } | 17057 | } |
| 17081 | err = posixExecvPath(full_path, child_argv, env_block); | 17058 | switch (posixExecveat(posix.AT.FDCWD, full_path, argv, env_block)) { |
| 17082 | switch (err) { | 17059 | error.AccessDenied, error.FileNotFound, error.NotDir => |e| switch (err) { |
| 17083 | error.AccessDenied => seen_eacces = true, | 17060 | error.AccessDenied => {}, |
| 17084 | error.FileNotFound, error.NotDir => {}, | 17061 | error.FileNotFound, error.NotDir => err = e, |
| 17085 | else => |e| return e, | 17062 | }, |
| 17086 | } | 17063 | else => |e| return e, |
| 17064 | } | ||
| 17065 | } | ||
| 17066 | return err; | ||
| 17067 | }, | ||
| 17068 | .path => |path| return posixExecveat(path.handle, argv[0], argv, env_block), | ||
| 17069 | .file => |file| return posixExecveat(file.handle, null, argv, env_block), | ||
| 17070 | .explicit => |explicit| { | ||
| 17071 | var path_buffer: [posix.PATH_MAX]u8 = undefined; | ||
| 17072 | const path_posix = try pathToPosix(explicit.path, &path_buffer); | ||
| 17073 | return posixExecveat(explicit.dir.handle, path_posix, argv, env_block); | ||
| 17074 | }, | ||
| 17087 | } | 17075 | } |
| 17088 | if (seen_eacces) return error.AccessDenied; | ||
| 17089 | return err; | ||
| 17090 | } | 17076 | } |
| 17091 | 17077 | ||
| 17092 | /// This function ignores PATH environment variable. | 17078 | /// This function ignores PATH environment variable. |
| 17093 | pub fn posixExecvPath( | 17079 | pub fn posixExecveat( |
| 17094 | path: [*:0]const u8, | 17080 | dir: posix.fd_t, |
| 17095 | child_argv: [*:null]const ?[*:0]const u8, | 17081 | path: ?[*:0]const u8, |
| 17082 | argv: [*:null]const ?[*:0]const u8, | ||
| 17096 | env_block: process.Environ.PosixBlock, | 17083 | env_block: process.Environ.PosixBlock, |
| 17097 | ) process.ReplaceError { | 17084 | ) process.ReplaceError { |
| 17098 | try Thread.checkCancel(); | 17085 | try Thread.checkCancel(); |
| 17099 | switch (posix.errno(posix.system.execve(path, child_argv, env_block.slice.ptr))) { | 17086 | switch (err: { |
| 17087 | if (path) |p| if (dir == posix.AT.FDCWD or Dir.path.isAbsoluteZ(p)) { | ||
| 17088 | break :err posix.errno(posix.system.execve(p, argv, env_block.slice.ptr)); | ||
| 17089 | }; | ||
| 17090 | if (comptime native_os == .linux and (!builtin.link_libc or | ||
| 17091 | (builtin.target.abi.isGnu() and builtin.target.os.version_range.linux.glibc.order( | ||
| 17092 | .{ .major = 2, .minor = 34, .patch = 0 }, | ||
| 17093 | ).compare(.gte)))) | ||
| 17094 | { | ||
| 17095 | switch (posix.errno(posix.system.execveat(dir, path orelse "", argv, env_block.slice.ptr, .{ | ||
| 17096 | .EMPTY_PATH = path == null, | ||
| 17097 | .SYMLINK_NOFOLLOW = false, | ||
| 17098 | }))) { | ||
| 17099 | else => |err| break :err err, | ||
| 17100 | .NOSYS => {}, | ||
| 17101 | } | ||
| 17102 | } | ||
| 17103 | var path_buffer: [posix.PATH_MAX:0]u8 = undefined; | ||
| 17104 | var path_len = realPathPosix(dir, &path_buffer) catch |err| switch (err) { | ||
| 17105 | else => |e| return e, | ||
| 17106 | error.InputOutput, | ||
| 17107 | error.FileTooBig, | ||
| 17108 | error.NoSpaceLeft, | ||
| 17109 | error.PathAlreadyExists, | ||
| 17110 | error.SymLinkLoop, | ||
| 17111 | error.NetworkNotFound, | ||
| 17112 | error.PipeBusy, | ||
| 17113 | error.AntivirusInterference, | ||
| 17114 | => return error.FileNotFound, | ||
| 17115 | error.DeviceBusy, | ||
| 17116 | error.NoDevice, | ||
| 17117 | error.UnrecognizedVolume, | ||
| 17118 | => return error.FileSystem, | ||
| 17119 | }; | ||
| 17120 | if (path) |p| { | ||
| 17121 | const path_slice = std.mem.span(p); | ||
| 17122 | path_buffer[path_len] = Dir.path.sep; | ||
| 17123 | path_len += 1; | ||
| 17124 | @memcpy(path_buffer[path_len..][0..path_slice.len], path_slice); | ||
| 17125 | path_len += path_slice.len; | ||
| 17126 | } | ||
| 17127 | path_buffer[path_len] = 0; | ||
| 17128 | break :err posix.errno(posix.system.execve(&path_buffer, argv, env_block.slice.ptr)); | ||
| 17129 | }) { | ||
| 17100 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. | 17130 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| 17101 | .@"2BIG" => return error.SystemResources, | 17131 | .@"2BIG" => return error.SystemResources, |
| 17102 | .MFILE => return error.ProcessFdQuotaExceeded, | 17132 | .MFILE => return error.ProcessFdQuotaExceeded, |
lib/std/c.zig+6| ... | @@ -8809,6 +8809,11 @@ pub const O = switch (native_os) { | ... | @@ -8809,6 +8809,11 @@ pub const O = switch (native_os) { |
| 8809 | else => void, | 8809 | else => void, |
| 8810 | }; | 8810 | }; |
| 8811 | 8811 | ||
| 8812 | pub const EXECVEAT = switch (native_os) { | ||
| 8813 | .linux => linux.EXECVEAT, | ||
| 8814 | else => void, | ||
| 8815 | }; | ||
| 8816 | |||
| 8812 | pub const MAP = switch (native_os) { | 8817 | pub const MAP = switch (native_os) { |
| 8813 | .linux => linux.MAP, | 8818 | .linux => linux.MAP, |
| 8814 | .emscripten => packed struct(u32) { | 8819 | .emscripten => packed struct(u32) { |
| ... | @@ -11045,6 +11050,7 @@ pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, n | ... | @@ -11045,6 +11050,7 @@ pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, n |
| 11045 | pub extern "c" fn chdir(path: [*:0]const u8) c_int; | 11050 | pub extern "c" fn chdir(path: [*:0]const u8) c_int; |
| 11046 | pub extern "c" fn fchdir(fd: fd_t) c_int; | 11051 | pub extern "c" fn fchdir(fd: fd_t) c_int; |
| 11047 | pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; | 11052 | pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; |
| 11053 | pub extern "c" fn execveat(dirfd: fd_t, path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8, flags: EXECVEAT) c_int; | ||
| 11048 | pub extern "c" fn dup(fd: fd_t) c_int; | 11054 | pub extern "c" fn dup(fd: fd_t) c_int; |
| 11049 | pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; | 11055 | pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; |
| 11050 | pub extern "c" fn dup3(old: c_int, new: c_int, flags: c_uint) c_int; | 11056 | pub extern "c" fn dup3(old: c_int, new: c_int, flags: c_uint) c_int; |
lib/std/process.zig+23-29| ... | @@ -282,37 +282,40 @@ pub const ReplaceError = error{ | ... | @@ -282,37 +282,40 @@ pub const ReplaceError = error{ |
| 282 | } || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; | 282 | } || Allocator.Error || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; |
| 283 | 283 | ||
| 284 | pub const ReplaceOptions = struct { | 284 | pub const ReplaceOptions = struct { |
| 285 | exe: Exe = .detect, | ||
| 285 | argv: []const []const u8, | 286 | argv: []const []const u8, |
| 286 | expand_arg0: ArgExpansion = .no_expand, | 287 | expand_arg0: ArgExpansion = .no_expand, |
| 287 | /// Replaces the environment when provided. The PATH value from here is | 288 | /// Replaces the environment when provided. The PATH value from here is |
| 288 | /// never used to resolve `argv[0]`. | 289 | /// never used to resolve `argv[0]`. |
| 289 | environ_map: ?*const Environ.Map = null, | 290 | environ_map: ?*const Environ.Map = null, |
| 291 | |||
| 292 | pub const Exe = union(enum) { | ||
| 293 | /// `argv[0]` is the name of the program to execute. If it is not already a | ||
| 294 | /// file path (i.e. it contains '/'), it is resolved into a file path based on | ||
| 295 | /// PATH from the parent environment. | ||
| 296 | detect, | ||
| 297 | /// `argv[0]` is the name of the program to execute, resolved into a file path | ||
| 298 | /// based on PATH from the parent environment. | ||
| 299 | search, | ||
| 300 | /// `argv[0]` is the file path of the program to execute, relative to `Dir` payload. | ||
| 301 | /// It is *always* treated as a file path, even if it does not contain '/'. | ||
| 302 | path: Dir, | ||
| 303 | /// `File` payload is the program to execute. | ||
| 304 | file: File, | ||
| 305 | /// `path` is the file path of the program to execute, relative to `dir`. | ||
| 306 | /// It is *always* treated as a file path, even if it does not contain '/'. | ||
| 307 | explicit: struct { dir: Dir, path: []const u8 }, | ||
| 308 | }; | ||
| 290 | }; | 309 | }; |
| 291 | 310 | ||
| 292 | /// Replaces the current process image with the executed process. If this | 311 | /// Replaces the current process image with the executed process. If this |
| 293 | /// function succeeds, it does not return. | 312 | /// function succeeds, it does not return. |
| 294 | /// | 313 | /// |
| 295 | /// `argv[0]` is the name of the process to replace the current one with. If it | ||
| 296 | /// is not already a file path (i.e. it contains '/'), it is resolved into a | ||
| 297 | /// file path based on PATH from the parent environment. | ||
| 298 | /// | ||
| 299 | /// It is illegal to call this function in a fork() child. | 314 | /// It is illegal to call this function in a fork() child. |
| 300 | pub fn replace(io: Io, options: ReplaceOptions) ReplaceError { | 315 | pub fn replace(io: Io, options: ReplaceOptions) ReplaceError { |
| 301 | return io.vtable.processReplace(io.userdata, options); | 316 | return io.vtable.processReplace(io.userdata, options); |
| 302 | } | 317 | } |
| 303 | 318 | ||
| 304 | /// Replaces the current process image with the executed process. If this | ||
| 305 | /// function succeeds, it does not return. | ||
| 306 | /// | ||
| 307 | /// `argv[0]` is the file path of the process to replace the current one with, | ||
| 308 | /// relative to `dir`. It is *always* treated as a file path, even if it does | ||
| 309 | /// not contain '/'. | ||
| 310 | /// | ||
| 311 | /// It is illegal to call this function in a fork() child. | ||
| 312 | pub fn replacePath(io: Io, dir: Io.Dir, options: ReplaceOptions) ReplaceError { | ||
| 313 | return io.vtable.processReplacePath(io.userdata, dir, options); | ||
| 314 | } | ||
| 315 | |||
| 316 | pub const ArgExpansion = enum { expand, no_expand }; | 319 | pub const ArgExpansion = enum { expand, no_expand }; |
| 317 | 320 | ||
| 318 | /// File name extensions supported natively by `CreateProcess()` on Windows. | 321 | /// File name extensions supported natively by `CreateProcess()` on Windows. |
| ... | @@ -368,6 +371,7 @@ pub const SpawnError = error{ | ... | @@ -368,6 +371,7 @@ pub const SpawnError = error{ |
| 368 | } || Io.File.OpenError || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; | 371 | } || Io.File.OpenError || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError; |
| 369 | 372 | ||
| 370 | pub const SpawnOptions = struct { | 373 | pub const SpawnOptions = struct { |
| 374 | exe: ReplaceOptions.Exe = .detect, | ||
| 371 | argv: []const []const u8, | 375 | argv: []const []const u8, |
| 372 | 376 | ||
| 373 | /// Set to change the current working directory when spawning the child process. | 377 | /// Set to change the current working directory when spawning the child process. |
| ... | @@ -385,7 +389,7 @@ pub const SpawnOptions = struct { | ... | @@ -385,7 +389,7 @@ pub const SpawnOptions = struct { |
| 385 | /// | 389 | /// |
| 386 | /// The child's progress tree will be grafted into the parent's progress tree, | 390 | /// The child's progress tree will be grafted into the parent's progress tree, |
| 387 | /// by substituting this node with the child's root node. | 391 | /// by substituting this node with the child's root node. |
| 388 | progress_node: std.Progress.Node = std.Progress.Node.none, | 392 | progress_node: std.Progress.Node = .none, |
| 389 | 393 | ||
| 390 | stdin: StdIo = .inherit, | 394 | stdin: StdIo = .inherit, |
| 391 | stdout: StdIo = .inherit, | 395 | stdout: StdIo = .inherit, |
| ... | @@ -445,27 +449,16 @@ pub const SpawnOptions = struct { | ... | @@ -445,27 +449,16 @@ pub const SpawnOptions = struct { |
| 445 | }; | 449 | }; |
| 446 | 450 | ||
| 447 | /// Creates a child process. | 451 | /// Creates a child process. |
| 448 | /// | ||
| 449 | /// `argv[0]` is the name of the program to execute. If it is not already a | ||
| 450 | /// file path (i.e. it contains '/'), it is resolved into a file path based on | ||
| 451 | /// PATH from the parent environment. | ||
| 452 | pub fn spawn(io: Io, options: SpawnOptions) SpawnError!Child { | 452 | pub fn spawn(io: Io, options: SpawnOptions) SpawnError!Child { |
| 453 | return io.vtable.processSpawn(io.userdata, options); | 453 | return io.vtable.processSpawn(io.userdata, options); |
| 454 | } | 454 | } |
| 455 | 455 | ||
| 456 | /// Creates a child process. | ||
| 457 | /// | ||
| 458 | /// `argv[0]` is the file path of the program to execute, relative to `dir`. It | ||
| 459 | /// is *always* treated as a file path, even if it does not contain '/'. | ||
| 460 | pub fn spawnPath(io: Io, dir: Io.Dir, options: SpawnOptions) SpawnError!Child { | ||
| 461 | return io.vtable.processSpawnPath(io.userdata, dir, options); | ||
| 462 | } | ||
| 463 | |||
| 464 | pub const RunError = error{ | 456 | pub const RunError = error{ |
| 465 | StreamTooLong, | 457 | StreamTooLong, |
| 466 | } || SpawnError || Io.File.MultiReader.UnendingError || Io.Timeout.Error; | 458 | } || SpawnError || Io.File.MultiReader.UnendingError || Io.Timeout.Error; |
| 467 | 459 | ||
| 468 | pub const RunOptions = struct { | 460 | pub const RunOptions = struct { |
| 461 | exe: ReplaceOptions.Exe = .detect, | ||
| 469 | argv: []const []const u8, | 462 | argv: []const []const u8, |
| 470 | stderr_limit: Io.Limit = .unlimited, | 463 | stderr_limit: Io.Limit = .unlimited, |
| 471 | stdout_limit: Io.Limit = .unlimited, | 464 | stdout_limit: Io.Limit = .unlimited, |
| ... | @@ -503,6 +496,7 @@ pub const RunResult = struct { | ... | @@ -503,6 +496,7 @@ pub const RunResult = struct { |
| 503 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. | 496 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. |
| 504 | pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult { | 497 | pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult { |
| 505 | var child = try spawn(io, .{ | 498 | var child = try spawn(io, .{ |
| 499 | .exe = options.exe, | ||
| 506 | .argv = options.argv, | 500 | .argv = options.argv, |
| 507 | .cwd = options.cwd, | 501 | .cwd = options.cwd, |
| 508 | .environ_map = options.environ_map, | 502 | .environ_map = options.environ_map, |
test/src/runner.zig+613-41| ... | @@ -1,11 +1,218 @@ | ... | @@ -1,11 +1,218 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | |||
| 3 | pub fn main(init: std.process.Init) !void { | ||
| 4 | const arena = init.arena.allocator(); | ||
| 5 | |||
| 6 | var test_path_arg: ?[]const u8 = null; | ||
| 7 | var zig_path_arg: ?[]const u8 = null; | ||
| 8 | var lib_path_arg: ?[]const u8 = null; | ||
| 9 | var target_args: std.ArrayList([]const u8) = .empty; | ||
| 10 | defer target_args.deinit(init.gpa); | ||
| 11 | var keep_src = false; | ||
| 12 | |||
| 13 | var arg_it = try init.minimal.args.iterateAllocator(arena); | ||
| 14 | const self = arg_it.next().?; | ||
| 15 | while (arg_it.next()) |arg| { | ||
| 16 | if (std.mem.eql(u8, arg, "--listen=-")) { | ||
| 17 | return protocol(init.gpa, init.arena, init.io); | ||
| 18 | } else if (std.mem.eql(u8, arg, "--zig")) { | ||
| 19 | zig_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg}); | ||
| 20 | } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| { | ||
| 21 | zig_path_arg = zig_path; | ||
| 22 | } else if (std.mem.eql(u8, arg, "--lib")) { | ||
| 23 | lib_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg}); | ||
| 24 | } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { | ||
| 25 | lib_path_arg = lib_path; | ||
| 26 | } else if (std.mem.eql(u8, arg, "--target")) { | ||
| 27 | try target_args.append( | ||
| 28 | init.gpa, | ||
| 29 | try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after '{s}'", .{arg})), | ||
| 30 | ); | ||
| 31 | } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { | ||
| 32 | try target_args.append(init.gpa, try arena.dupe(u8, target)); | ||
| 33 | } else if (std.mem.eql(u8, arg, "--keep-src")) { | ||
| 34 | keep_src = true; | ||
| 35 | } else { | ||
| 36 | if (test_path_arg) |_| fatal("unknown arg '{s}'", .{arg}); | ||
| 37 | test_path_arg = arg; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | const test_path = test_path_arg orelse fatal("missing 'path/to/test'", .{}); | ||
| 42 | const zig_path = zig_path_arg orelse fatal("missing '--zig=path/to/zig'", .{}); | ||
| 43 | const lib_path = lib_path_arg orelse fatal("missing '--lib=path/to/lib'", .{}); | ||
| 44 | |||
| 45 | const test_file = try std.Io.Dir.cwd().openFile(init.io, test_path, .{ | ||
| 46 | .allow_directory = true, | ||
| 47 | }); | ||
| 48 | const zig_exe = try std.Io.Dir.cwd().openFile(init.io, zig_path, .{}); | ||
| 49 | defer zig_exe.close(init.io); | ||
| 50 | const lib_dir = try std.Io.Dir.cwd().openDir(init.io, lib_path, .{}); | ||
| 51 | defer lib_dir.close(init.io); | ||
| 52 | |||
| 53 | const src_dir_path = "src_" ++ std.fmt.hex(rand_int: { | ||
| 54 | var rand_int: u64 = undefined; | ||
| 55 | init.io.random(@ptrCast(&rand_int)); | ||
| 56 | break :rand_int rand_int; | ||
| 57 | }); | ||
| 58 | var src_dir = try std.Io.Dir.cwd().createDirPathOpen(init.io, src_dir_path, .{}); | ||
| 59 | defer { | ||
| 60 | src_dir.close(init.io); | ||
| 61 | if (!keep_src) std.Io.Dir.cwd().deleteTree(init.io, src_dir_path) catch |err| { | ||
| 62 | std.log.warn("fataled to delete tree '{s}': {t}", .{ src_dir_path, err }); | ||
| 63 | }; | ||
| 64 | } | ||
| 65 | |||
| 66 | var stdout_buffer: [512]u8 = undefined; | ||
| 67 | var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer); | ||
| 68 | const stdout = &stdout_fw.interface; | ||
| 69 | |||
| 70 | var child = try std.process.spawn(init.io, .{ | ||
| 71 | .argv = &.{ self, "--listen=-" }, | ||
| 72 | .stdin = .pipe, | ||
| 73 | .stdout = .pipe, | ||
| 74 | .inherit_dirs = &.{ lib_dir, src_dir }, | ||
| 75 | .inherit_files = &.{ zig_exe, test_file }, | ||
| 76 | }); | ||
| 77 | |||
| 78 | var child_reader_buffer: [512]u8 = undefined; | ||
| 79 | var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer); | ||
| 80 | var child_writer_buffer: [512]u8 = undefined; | ||
| 81 | var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer); | ||
| 82 | var client: std.zig.Client = .{ | ||
| 83 | .in = &child_reader.interface, | ||
| 84 | .out = &child_writer.interface, | ||
| 85 | }; | ||
| 86 | { | ||
| 87 | var args: std.ArrayList(u8) = .empty; | ||
| 88 | defer args.deinit(init.gpa); | ||
| 89 | const Arg = std.zig.Client.Message.Arg; | ||
| 90 | |||
| 91 | try args.append(init.gpa, @backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) { | ||
| 92 | else => unreachable, | ||
| 93 | .file => .input_file, | ||
| 94 | .directory => .input_dir, | ||
| 95 | }))); | ||
| 96 | try args.appendSlice(init.gpa, @ptrCast(&test_file.handle)); | ||
| 97 | |||
| 98 | try args.append(init.gpa, @backingInt(Arg.prefix)); | ||
| 99 | try args.appendSlice(init.gpa, "--zig="); | ||
| 100 | try args.append(init.gpa, 0); | ||
| 101 | |||
| 102 | try args.append(init.gpa, @backingInt(Arg.input_file)); | ||
| 103 | try args.appendSlice(init.gpa, @ptrCast(&zig_exe.handle)); | ||
| 104 | |||
| 105 | try args.append(init.gpa, @backingInt(Arg.prefix)); | ||
| 106 | try args.appendSlice(init.gpa, "--lib="); | ||
| 107 | try args.append(init.gpa, 0); | ||
| 108 | |||
| 109 | try args.append(init.gpa, @backingInt(Arg.input_dir)); | ||
| 110 | try args.appendSlice(init.gpa, @ptrCast(&lib_dir.handle)); | ||
| 111 | |||
| 112 | try args.append(init.gpa, @backingInt(Arg.prefix)); | ||
| 113 | try args.appendSlice(init.gpa, "--src="); | ||
| 114 | try args.append(init.gpa, 0); | ||
| 115 | |||
| 116 | try args.append(init.gpa, @backingInt(Arg.output_dir)); | ||
| 117 | try args.appendSlice(init.gpa, @ptrCast(&src_dir.handle)); | ||
| 118 | |||
| 119 | for (target_args.items) |target_arg| { | ||
| 120 | try args.append(init.gpa, @backingInt(Arg.string)); | ||
| 121 | try args.appendSlice(init.gpa, "--target"); | ||
| 122 | try args.append(init.gpa, 0); | ||
| 123 | |||
| 124 | try args.append(init.gpa, @backingInt(Arg.string)); | ||
| 125 | try args.appendSlice(init.gpa, target_arg); | ||
| 126 | try args.append(init.gpa, 0); | ||
| 127 | } | ||
| 128 | |||
| 129 | try client.serveMessageHeader(.{ | ||
| 130 | .tag = .args, | ||
| 131 | .bytes_len = @intCast(args.items.len), | ||
| 132 | }); | ||
| 133 | try client.out.writeAll(args.items); | ||
| 134 | |||
| 135 | var metadata: struct { | ||
| 136 | index: u32, | ||
| 137 | len: u32, | ||
| 138 | names: []u32, | ||
| 139 | expected_panic_msgs: []u32, | ||
| 140 | string_bytes: []u8, | ||
| 141 | |||
| 142 | fn name(m: *@This()) []const u8 { | ||
| 143 | return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0); | ||
| 144 | } | ||
| 145 | |||
| 146 | fn expectedPanicMsg(m: *@This()) ?[:0]const u8 { | ||
| 147 | return switch (m.expected_panic_msgs[m.index - 1]) { | ||
| 148 | 0 => null, | ||
| 149 | else => |offset| m.string_bytes[offset..std.mem.findScalarPos( | ||
| 150 | u8, | ||
| 151 | m.string_bytes, | ||
| 152 | offset, | ||
| 153 | 0, | ||
| 154 | ).? :0], | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | fn next(m: *@This()) ?u32 { | ||
| 159 | if (m.len - m.index == 0) return null; | ||
| 160 | defer m.index += 1; | ||
| 161 | return m.index; | ||
| 162 | } | ||
| 163 | } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} }; | ||
| 164 | try client.serveBodylessMessage(.query_test_metadata); | ||
| 165 | while (true) { | ||
| 166 | const hdr = try client.receiveMessage(); | ||
| 167 | switch (hdr.tag) { | ||
| 168 | else => try client.in.discardAll(hdr.bytes_len), | ||
| 169 | .zig_version => { | ||
| 170 | const body = try arena.alloc(u8, hdr.bytes_len); | ||
| 171 | try client.in.readSliceAll(body); | ||
| 172 | if (!std.mem.eql(u8, @import("builtin").zig_version_string, body)) return fatal( | ||
| 173 | "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", | ||
| 174 | .{ @import("builtin").zig_version_string, body }, | ||
| 175 | ); | ||
| 176 | }, | ||
| 177 | .test_metadata => { | ||
| 178 | const tm_hdr = | ||
| 179 | try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); | ||
| 180 | metadata = .{ | ||
| 181 | .index = 0, | ||
| 182 | .len = tm_hdr.tests_len, | ||
| 183 | .names = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 184 | .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 185 | .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len), | ||
| 186 | }; | ||
| 187 | try client.in.readSliceEndian(u32, metadata.names, .little); | ||
| 188 | try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little); | ||
| 189 | try client.in.readSliceAll(metadata.string_bytes); | ||
| 190 | try client.serveRunTest(metadata.next() orelse break); | ||
| 191 | }, | ||
| 192 | .test_results => { | ||
| 193 | const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little); | ||
| 194 | try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() }); | ||
| 195 | try stdout.flush(); | ||
| 196 | try client.serveRunTest(metadata.next() orelse break); | ||
| 197 | }, | ||
| 198 | } | ||
| 199 | } | ||
| 200 | try client.serveBodylessMessage(.exit); | ||
| 201 | } | ||
| 202 | _ = try child.wait(init.io); | ||
| 203 | } | ||
| 204 | fn fatal(comptime fmt: []const u8, args: anytype) noreturn { | ||
| 205 | std.log.err(fmt, args); | ||
| 206 | std.process.exit(1); | ||
| 207 | } | ||
| 208 | |||
| 2 | const Args = struct { | 209 | const Args = struct { |
| 3 | @"test": ?struct { | 210 | @"test": ?struct { |
| 4 | input_dir: std.zig.Server.Message.InputDir, | 211 | input_dir: std.zig.Server.Message.InputDir, |
| 5 | dir: std.Io.Dir, | 212 | dir: std.Io.Dir, |
| 6 | }, | 213 | }, |
| 7 | manifest_file: ?std.Io.File, | 214 | manifest_file: ?std.Io.File, |
| 8 | zig_file: ?std.Io.File, | 215 | zig_exe: ?std.Io.File, |
| 9 | lib_dir: ?std.Io.Dir, | 216 | lib_dir: ?std.Io.Dir, |
| 10 | src_dir: ?std.Io.Dir, | 217 | src_dir: ?std.Io.Dir, |
| 11 | targets: std.ArrayList(u32), | 218 | targets: std.ArrayList(u32), |
| ... | @@ -23,15 +230,14 @@ const Args = struct { | ... | @@ -23,15 +230,14 @@ const Args = struct { |
| 23 | args.target_bytes.appendAssumeCapacity(0); | 230 | args.target_bytes.appendAssumeCapacity(0); |
| 24 | } | 231 | } |
| 25 | }; | 232 | }; |
| 26 | pub fn main(init: std.process.Init) !void { | 233 | pub fn protocol(gpa: std.mem.Allocator, arena_impl: *std.heap.ArenaAllocator, io: std.Io) !void { |
| 27 | const arena = init.arena.allocator(); | 234 | const prog_node = std.Progress.start(io, .{}); |
| 235 | defer prog_node.end(); | ||
| 28 | 236 | ||
| 29 | var stdin_buffer: [512]u8 = undefined; | 237 | var stdin_buffer: [512]u8 = undefined; |
| 30 | var stdout_buffer: [512]u8 = undefined; | 238 | var stdout_buffer: [512]u8 = undefined; |
| 31 | var stderr_buffer: [512]u8 = undefined; | 239 | var stdin: std.Io.File.Reader = .initStreaming(.stdin(), io, &stdin_buffer); |
| 32 | var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer); | 240 | var stdout: std.Io.File.Writer = .initStreaming(.stdout(), io, &stdout_buffer); |
| 33 | var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer); | ||
| 34 | var stderr: std.Io.File.Writer = .initStreaming(.stderr(), init.io, &stderr_buffer); | ||
| 35 | var server: std.zig.Server = .{ | 241 | var server: std.zig.Server = .{ |
| 36 | .in = &stdin.interface, | 242 | .in = &stdin.interface, |
| 37 | .out = &stdout.interface, | 243 | .out = &stdout.interface, |
| ... | @@ -40,16 +246,18 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -40,16 +246,18 @@ pub fn main(init: std.process.Init) !void { |
| 40 | var args: Args = .{ | 246 | var args: Args = .{ |
| 41 | .@"test" = null, | 247 | .@"test" = null, |
| 42 | .manifest_file = null, | 248 | .manifest_file = null, |
| 43 | .zig_file = null, | 249 | .zig_exe = null, |
| 44 | .lib_dir = null, | 250 | .lib_dir = null, |
| 45 | .src_dir = null, | 251 | .src_dir = null, |
| 46 | .targets = .empty, | 252 | .targets = .empty, |
| 47 | .target_bytes = .empty, | 253 | .target_bytes = .empty, |
| 48 | }; | 254 | }; |
| 49 | defer args.deinit(init.gpa); | 255 | defer args.deinit(gpa); |
| 50 | 256 | ||
| 51 | try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string); | 257 | try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string); |
| 52 | while (true) { | 258 | while (true) { |
| 259 | _ = arena_impl.reset(.retain_capacity); | ||
| 260 | const arena = arena_impl.allocator(); | ||
| 53 | const hdr = try server.receiveMessage(); | 261 | const hdr = try server.receiveMessage(); |
| 54 | switch (hdr.tag) { | 262 | switch (hdr.tag) { |
| 55 | else => { | 263 | else => { |
| ... | @@ -77,12 +285,12 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -77,12 +285,12 @@ pub fn main(init: std.process.Init) !void { |
| 77 | state = .target; | 285 | state = .target; |
| 78 | continue; | 286 | continue; |
| 79 | } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { | 287 | } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { |
| 80 | try args.addTarget(init.gpa, target); | 288 | try args.addTarget(gpa, target); |
| 81 | }, | 289 | }, |
| 82 | .zig => unreachable, | 290 | .zig => unreachable, |
| 83 | .lib => unreachable, | 291 | .lib => unreachable, |
| 84 | .src => unreachable, | 292 | .src => unreachable, |
| 85 | .target => try args.addTarget(init.gpa, string), | 293 | .target => try args.addTarget(gpa, string), |
| 86 | } | 294 | } |
| 87 | }, | 295 | }, |
| 88 | .prefix => { | 296 | .prefix => { |
| ... | @@ -118,7 +326,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -118,7 +326,7 @@ pub fn main(init: std.process.Init) !void { |
| 118 | args.@"test" = .{ .input_dir = input_dir, .dir = dir }; | 326 | args.@"test" = .{ .input_dir = input_dir, .dir = dir }; |
| 119 | std.debug.assert(args.manifest_file == null); | 327 | std.debug.assert(args.manifest_file == null); |
| 120 | const manifest_path = "manifest"; | 328 | const manifest_path = "manifest"; |
| 121 | args.manifest_file = try dir.openFile(init.io, manifest_path, .{}); | 329 | args.manifest_file = try dir.openFile(io, manifest_path, .{}); |
| 122 | 330 | ||
| 123 | try server.serveMessageHeader(.{ | 331 | try server.serveMessageHeader(.{ |
| 124 | .tag = .discovered_inputs, | 332 | .tag = .discovered_inputs, |
| ... | @@ -157,8 +365,8 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -157,8 +365,8 @@ pub fn main(init: std.process.Init) !void { |
| 157 | args.manifest_file = file; | 365 | args.manifest_file = file; |
| 158 | }, | 366 | }, |
| 159 | .zig => { | 367 | .zig => { |
| 160 | std.debug.assert(args.zig_file == null); | 368 | std.debug.assert(args.zig_exe == null); |
| 161 | args.zig_file = file; | 369 | args.zig_exe = file; |
| 162 | }, | 370 | }, |
| 163 | .lib => unreachable, | 371 | .lib => unreachable, |
| 164 | .src => unreachable, | 372 | .src => unreachable, |
| ... | @@ -180,16 +388,17 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -180,16 +388,17 @@ pub fn main(init: std.process.Init) !void { |
| 180 | }, | 388 | }, |
| 181 | .run_test => { | 389 | .run_test => { |
| 182 | const test_index = try server.receiveBody_u32(); | 390 | const test_index = try server.receiveBody_u32(); |
| 183 | const target = | 391 | const target_query = |
| 184 | std.mem.sliceTo(args.target_bytes.items[args.targets.items[test_index]..], 0); | 392 | std.mem.sliceTo(args.target_bytes.items[args.targets.items[test_index]..], 0); |
| 185 | try server.serveBodylessMessage(.test_started); | 393 | try server.serveBodylessMessage(.test_started); |
| 186 | const test_result = runTest(init.io, &stderr.interface, &server, &args, target); | 394 | const test_result = |
| 187 | try stderr.flush(); | 395 | runTest(gpa, arena, io, prog_node, &server, &args, target_query); |
| 188 | try server.serveTestResults(.{ | 396 | try server.serveTestResults(.{ |
| 189 | .index = test_index, | 397 | .index = test_index, |
| 190 | .flags = .{ | 398 | .flags = .{ |
| 191 | .status = if (test_result) .pass else |err| switch (err) { | 399 | .status = if (test_result) .pass else |err| switch (err) { |
| 192 | else => status: { | 400 | else => status: { |
| 401 | std.debug.print("{t}\n", .{err}); | ||
| 193 | if (@errorReturnTrace()) |trace| std.debug.dumpErrorReturnTrace(trace); | 402 | if (@errorReturnTrace()) |trace| std.debug.dumpErrorReturnTrace(trace); |
| 194 | break :status .fail; | 403 | break :status .fail; |
| 195 | }, | 404 | }, |
| ... | @@ -205,18 +414,49 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -205,18 +414,49 @@ pub fn main(init: std.process.Init) !void { |
| 205 | } | 414 | } |
| 206 | } | 415 | } |
| 207 | fn runTest( | 416 | fn runTest( |
| 417 | gpa: std.mem.Allocator, | ||
| 418 | arena: std.mem.Allocator, | ||
| 208 | io: std.Io, | 419 | io: std.Io, |
| 209 | stderr: *std.Io.Writer, | 420 | prog_node: std.Progress.Node, |
| 210 | server: *std.zig.Server, | 421 | server: *std.zig.Server, |
| 211 | args: *const Args, | 422 | args: *const Args, |
| 212 | target: []const u8, | 423 | target_string: []const u8, |
| 213 | ) !void { | 424 | ) !void { |
| 214 | const manifest_file = args.manifest_file orelse return error.MissingManifestArg; | 425 | const manifest_file = args.manifest_file orelse return error.MissingManifestArg; |
| 215 | const src_dir = args.src_dir orelse return error.MissingSrcDir; | 426 | const src_dir = args.src_dir orelse return error.MissingSrcDir; |
| 427 | const target: Compiler.Target = target: { | ||
| 428 | const backend_split = std.mem.findScalarLast(u8, target_string, '-') orelse | ||
| 429 | return error.TargetMissingBackend; | ||
| 430 | const mode_split = | ||
| 431 | std.mem.findScalarLast(u8, target_string[0..backend_split], '-') orelse | ||
| 432 | return error.TargetMissingMode; | ||
| 433 | const triple = target_string[0..mode_split]; | ||
| 434 | const backend = std.meta.stringToEnum( | ||
| 435 | Compiler.Target.Backend, | ||
| 436 | target_string[backend_split + 1 ..], | ||
| 437 | ) orelse return error.TargetMissingBackend; | ||
| 438 | const mode = std.meta.stringToEnum( | ||
| 439 | Compiler.Target.Mode, | ||
| 440 | target_string[mode_split + 1 .. backend_split], | ||
| 441 | ) orelse return error.TargetMissingMode; | ||
| 442 | break :target .{ | ||
| 443 | .triple = triple, | ||
| 444 | .resolved = try std.zig.system.resolveTargetQuery(io, try std.Build.parseTargetQuery(.{ | ||
| 445 | .arch_os_abi = triple, | ||
| 446 | .object_format = switch (backend) { | ||
| 447 | .sema, .selfhosted, .llvm => null, | ||
| 448 | .cbe => "c", | ||
| 449 | }, | ||
| 450 | })), | ||
| 451 | .mode = mode, | ||
| 452 | .backend = backend, | ||
| 453 | }; | ||
| 454 | }; | ||
| 216 | var manifest_buffer: [512]u8 = undefined; | 455 | var manifest_buffer: [512]u8 = undefined; |
| 217 | var manifest_fr = manifest_file.reader(io, &manifest_buffer); | 456 | var manifest_fr = manifest_file.reader(io, &manifest_buffer); |
| 218 | var allow_skip = true; | 457 | var allow_skip = true; |
| 219 | var skip_delimiter = false; | 458 | var skip_delimiter = false; |
| 459 | var compiler: ?Compiler = null; | ||
| 220 | var update_mtime = std.Io.Clock.real.now(io); | 460 | var update_mtime = std.Io.Clock.real.now(io); |
| 221 | var update_num: usize = 0; | 461 | var update_num: usize = 0; |
| 222 | while (manifest_fr.interface.takeSentinel('\n')) |untrimmed_line| { | 462 | while (manifest_fr.interface.takeSentinel('\n')) |untrimmed_line| { |
| ... | @@ -229,12 +469,13 @@ fn runTest( | ... | @@ -229,12 +469,13 @@ fn runTest( |
| 229 | continue; | 469 | continue; |
| 230 | } | 470 | } |
| 231 | const cmd = std.meta.stringToEnum( | 471 | const cmd = std.meta.stringToEnum( |
| 232 | enum { skip, obj, exe, lib, write, delete, update, check }, | 472 | enum { skip, exe, lib, obj, write, delete, update, check }, |
| 233 | cmd_str, | 473 | cmd_str, |
| 234 | ) orelse return error.UnknownCommand; | 474 | ) orelse return error.UnknownCommand; |
| 235 | var maybe_arg = line_it.next(); | 475 | var maybe_arg = line_it.next(); |
| 236 | var contents_file: ?std.Io.File = null; | 476 | var contents_file: ?std.Io.File = null; |
| 237 | defer if (contents_file) |file| file.close(io); | 477 | defer if (contents_file) |file| file.close(io); |
| 478 | var contents_buffer: [512]u8 = undefined; | ||
| 238 | var contents_impl: union { | 479 | var contents_impl: union { |
| 239 | dr: DelimitedReader, | 480 | dr: DelimitedReader, |
| 240 | fr: std.Io.File.Reader, | 481 | fr: std.Io.File.Reader, |
| ... | @@ -253,7 +494,7 @@ fn runTest( | ... | @@ -253,7 +494,7 @@ fn runTest( |
| 253 | break :contents_path contents_path; | 494 | break :contents_path contents_path; |
| 254 | }; | 495 | }; |
| 255 | if (std.mem.eql(u8, contents_path, "{")) { | 496 | if (std.mem.eql(u8, contents_path, "{")) { |
| 256 | contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) }; | 497 | contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &contents_buffer) }; |
| 257 | skip_delimiter = true; | 498 | skip_delimiter = true; |
| 258 | break :contents_r &contents_impl.dr.interface; | 499 | break :contents_r &contents_impl.dr.interface; |
| 259 | } | 500 | } |
| ... | @@ -270,43 +511,263 @@ fn runTest( | ... | @@ -270,43 +511,263 @@ fn runTest( |
| 270 | try server.out.flush(); | 511 | try server.out.flush(); |
| 271 | 512 | ||
| 272 | contents_file = try @"test".dir.openFile(io, contents_path, .{}); | 513 | contents_file = try @"test".dir.openFile(io, contents_path, .{}); |
| 273 | contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) }; | 514 | contents_impl = .{ .fr = contents_file.?.reader(io, &contents_buffer) }; |
| 274 | break :contents_r &contents_impl.fr.interface; | 515 | break :contents_r &contents_impl.fr.interface; |
| 275 | }, | 516 | }, |
| 276 | }; | 517 | }; |
| 277 | switch (cmd) { | 518 | const cmd_prog_node = cmd_prog_node: { |
| 278 | else => { | 519 | var cmd_name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; |
| 279 | try stderr.print("{t} {s} {{\n", .{ cmd, maybe_arg orelse "" }); | 520 | break :cmd_prog_node prog_node.start(std.mem.print(&cmd_name_buffer, "#{t} {s}", .{ |
| 280 | _ = try contents_r.streamRemaining(stderr); | 521 | cmd, maybe_arg orelse "", |
| 281 | try stderr.writeAll("\n}\n"); | 522 | }) catch &cmd_name_buffer, 0); |
| 282 | }, | 523 | }; |
| 524 | defer cmd_prog_node.end(); | ||
| 525 | cmd: switch (cmd) { | ||
| 283 | .skip => if (!allow_skip) | 526 | .skip => if (!allow_skip) |
| 284 | return error.NonInitialSkipCommand // #skip must appear first | 527 | return error.NonInitialSkipCommand // #skip must appear first |
| 285 | else if (std.mem.eql(u8, target, maybe_arg orelse return error.MissingArg)) | 528 | else if (std.mem.eql(u8, target_string, maybe_arg orelse return error.MissingArg)) |
| 286 | return error.SkipZigTest, | 529 | return error.SkipZigTest, |
| 530 | .exe, .lib, .obj => { | ||
| 531 | if (compiler) |*comp| { | ||
| 532 | try comp.exit(io); | ||
| 533 | compiler = null; | ||
| 534 | } | ||
| 535 | var argv: std.ArrayList([]const u8) = .empty; | ||
| 536 | defer argv.deinit(gpa); | ||
| 537 | try argv.appendSlice(gpa, &.{ | ||
| 538 | "zig", | ||
| 539 | switch (cmd) { | ||
| 540 | else => unreachable, | ||
| 541 | .obj => "build-obj", | ||
| 542 | .exe => "build-exe", | ||
| 543 | .lib => "build-lib", | ||
| 544 | }, | ||
| 545 | "-target", | ||
| 546 | target.triple, | ||
| 547 | }); | ||
| 548 | switch (target.mode) { | ||
| 549 | .whole => {}, | ||
| 550 | .incremental => try argv.append(gpa, "-fincremental"), | ||
| 551 | } | ||
| 552 | switch (target.backend) { | ||
| 553 | .sema => try argv.append(gpa, "-fno-emit-bin"), | ||
| 554 | .selfhosted => try argv.append(gpa, "-fno-llvm"), | ||
| 555 | .llvm => try argv.append(gpa, "-fllvm"), | ||
| 556 | .cbe => try argv.append(gpa, "-ofmt=c"), | ||
| 557 | } | ||
| 558 | try argv.append(gpa, "--listen=-"); | ||
| 559 | var need: usize = 1; | ||
| 560 | var root_name: ?[]const u8 = null; | ||
| 561 | while (contents_r.peekGreedy(need)) |buffered| { | ||
| 562 | const start = std.mem.findNone(u8, buffered, "\n ") orelse buffered.len; | ||
| 563 | if (std.mem.findAnyPos(u8, buffered, start, "\n ")) |end| { | ||
| 564 | const arg = try arena.dupe(u8, buffered[start..end]); | ||
| 565 | if (std.mem.cutPrefix(u8, arg, "-M")) |module| { | ||
| 566 | var module_name_it = std.mem.splitScalar(u8, module, '='); | ||
| 567 | const module_name = module_name_it.next().?; | ||
| 568 | root_name = root_name orelse module_name; | ||
| 569 | } | ||
| 570 | try argv.append(gpa, arg); | ||
| 571 | contents_r.toss(end); | ||
| 572 | need = 1; | ||
| 573 | } else { | ||
| 574 | contents_r.toss(start); | ||
| 575 | need = buffered.len - start + 1; | ||
| 576 | } | ||
| 577 | } else |err| switch (err) { | ||
| 578 | else => |e| return e, | ||
| 579 | error.EndOfStream => {}, | ||
| 580 | } | ||
| 581 | const compile_prog_node = prog_node.start("initial update", 0); | ||
| 582 | const child = try std.process.spawn(io, .{ | ||
| 583 | .exe = .{ .file = args.zig_exe orelse return error.MissingZigExe }, | ||
| 584 | .argv = argv.items, | ||
| 585 | .cwd = .{ .dir = src_dir }, | ||
| 586 | .progress_node = compile_prog_node, | ||
| 587 | .stdin = .pipe, | ||
| 588 | .stdout = .pipe, | ||
| 589 | .stderr = .pipe, | ||
| 590 | }); | ||
| 591 | compiler = .{ | ||
| 592 | .prog_node = compile_prog_node, | ||
| 593 | .allow_compiler_stderr = true, | ||
| 594 | .output_mode = switch (cmd) { | ||
| 595 | else => unreachable, | ||
| 596 | .exe => .Exe, | ||
| 597 | .lib => .Lib, | ||
| 598 | .obj => .Obj, | ||
| 599 | }, | ||
| 600 | .root_name = root_name orelse return error.MissingRootModule, | ||
| 601 | .state = .idle, | ||
| 602 | .child = child, | ||
| 603 | .fw = undefined, | ||
| 604 | .mr_buffer = undefined, | ||
| 605 | .mr = undefined, | ||
| 606 | .client = undefined, | ||
| 607 | }; | ||
| 608 | const comp = &compiler.?; | ||
| 609 | comp.fw = child.stdin.?.writerStreaming(io, &.{}); | ||
| 610 | comp.mr.init(gpa, io, comp.mr_buffer.toStreams(), &.{ | ||
| 611 | child.stdout.?, child.stderr.?, | ||
| 612 | }); | ||
| 613 | comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface }; | ||
| 614 | }, | ||
| 287 | .write => { | 615 | .write => { |
| 288 | const file = try src_dir.createFile(io, maybe_arg orelse return error.MissingArg, .{}); | 616 | const file = |
| 617 | try src_dir.createFile(io, maybe_arg orelse return error.MissingFileArg, .{}); | ||
| 289 | defer file.close(io); | 618 | defer file.close(io); |
| 290 | var fw = file.writer(io, &.{}); | 619 | var fw = file.writer(io, &.{}); |
| 291 | _ = try contents_r.streamRemaining(&fw.interface); | 620 | _ = try contents_r.streamRemaining(&fw.interface); |
| 292 | try file.setTimestamps(io, .{ .modify_timestamp = .{ .new = update_mtime } }); | 621 | try file.setTimestamps(io, .{ .modify_timestamp = .{ .new = update_mtime } }); |
| 293 | }, | 622 | }, |
| 294 | .delete => try src_dir.deleteFile(io, maybe_arg orelse return error.MissingArg), | 623 | .delete => try src_dir.deleteFile(io, maybe_arg orelse return error.MissingFileArg), |
| 295 | .update => { | 624 | .update => { |
| 296 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | 625 | const comp = &(compiler orelse return error.MissingCompiler); |
| 297 | const name = switch (try contents_r.readSliceShort(&name_buffer)) { | 626 | name: { |
| 298 | 0 => switch (update_num) { | 627 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; |
| 299 | 0 => "initial update", | 628 | comp.prog_node.setName(switch (try contents_r.readSliceShort(&name_buffer)) { |
| 300 | else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch | 629 | 0 => switch (update_num) { |
| 301 | &name_buffer, | 630 | 0 => break :name, |
| 302 | }, | 631 | else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch |
| 303 | else => |name_len| name_buffer[0..name_len], | 632 | &name_buffer, |
| 304 | }; | 633 | }, |
| 634 | else => |name_len| name_buffer[0..name_len], | ||
| 635 | }); | ||
| 636 | } | ||
| 305 | _ = try contents_r.discardRemaining(); | 637 | _ = try contents_r.discardRemaining(); |
| 306 | try stderr.print("update({q})\n", .{name}); | 638 | if (comp.state != .idle) return error.MissingCheck; |
| 639 | try comp.client.serveBodylessMessage(.update); | ||
| 640 | comp.state = .update; | ||
| 307 | update_mtime = update_mtime.addDuration(.fromSeconds(2)); | 641 | update_mtime = update_mtime.addDuration(.fromSeconds(2)); |
| 308 | update_num += 1; | 642 | update_num += 1; |
| 309 | }, | 643 | }, |
| 644 | .check => { | ||
| 645 | const expected = try contents_r.allocRemaining(gpa, .unlimited); | ||
| 646 | defer gpa.free(expected); | ||
| 647 | const check = std.meta.stringToEnum( | ||
| 648 | enum { errors, stdout, exit, lldb }, | ||
| 649 | maybe_arg orelse return error.MissingCheckArg, | ||
| 650 | ) orelse return error.UnknownCheck; | ||
| 651 | |||
| 652 | const comp = &(compiler orelse return error.MissingCompiler); | ||
| 653 | if (comp.state != .update) return error.MissingUpdate; | ||
| 654 | const stderr = comp.mr.reader(1); | ||
| 655 | while (true) { | ||
| 656 | const header = comp.client.receiveMessageWithMultiReader( | ||
| 657 | &comp.mr, | ||
| 658 | .none, | ||
| 659 | ) catch |err| switch (err) { | ||
| 660 | error.Timeout => unreachable, | ||
| 661 | error.EndOfStream => break, | ||
| 662 | else => |e| return e, | ||
| 663 | }; | ||
| 664 | const body = comp.client.in.take(header.bytes_len) catch unreachable; | ||
| 665 | switch (header.tag) { | ||
| 666 | .error_bundle => { | ||
| 667 | const error_bundle = try std.zig.Server.allocErrorBundle(arena, body); | ||
| 668 | if (stderr.bufferedLen() > 0) { | ||
| 669 | if (comp.allow_compiler_stderr) { | ||
| 670 | std.log.info("error_bundle stderr:\n{s}", .{stderr.buffered()}); | ||
| 671 | } else { | ||
| 672 | fatal("error_bundle unexpected stderr:\n{s}", .{stderr.buffered()}); | ||
| 673 | } | ||
| 674 | stderr.tossBuffered(); | ||
| 675 | } | ||
| 676 | switch (check) { | ||
| 677 | .errors => if (error_bundle.errorMessageCount() != 0) { | ||
| 678 | @panic("TODO"); | ||
| 679 | } else fatal("expected compile errors", .{}), | ||
| 680 | .stdout, .exit, .lldb => if (error_bundle.errorMessageCount() != 0) { | ||
| 681 | try error_bundle.renderToStderr(io, .{}, .auto); | ||
| 682 | fatal("unexpected compile errors", .{}); | ||
| 683 | }, | ||
| 684 | } | ||
| 685 | comp.state = .idle; | ||
| 686 | break :cmd; | ||
| 687 | }, | ||
| 688 | .emit_digest => { | ||
| 689 | var r: std.Io.Reader = .fixed(body); | ||
| 690 | _ = r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch | ||
| 691 | unreachable; | ||
| 692 | |||
| 693 | if (stderr.bufferedLen() > 0) { | ||
| 694 | if (comp.allow_compiler_stderr) { | ||
| 695 | std.log.info("emit_digest stderr:\n{s}", .{stderr.buffered()}); | ||
| 696 | } else { | ||
| 697 | fatal("emit_digest unexpected stderr:\n{s}", .{stderr.buffered()}); | ||
| 698 | } | ||
| 699 | stderr.tossBuffered(); | ||
| 700 | } | ||
| 701 | switch (target.backend) { | ||
| 702 | .sema => continue, | ||
| 703 | .selfhosted, .llvm, .cbe => {}, | ||
| 704 | } | ||
| 705 | switch (check) { | ||
| 706 | .errors => continue, | ||
| 707 | .stdout, .exit, .lldb => {}, | ||
| 708 | } | ||
| 709 | const digest = r.takeArray(std.Build.Cache.bin_digest_len) catch | ||
| 710 | unreachable; | ||
| 711 | const result_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ | ||
| 712 | "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); | ||
| 713 | const bin_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ | ||
| 714 | .root_name = comp.root_name, | ||
| 715 | .cpu_arch = target.resolved.cpu.arch, | ||
| 716 | .os_tag = target.resolved.os.tag, | ||
| 717 | .ofmt = target.resolved.ofmt, | ||
| 718 | .abi = target.resolved.abi, | ||
| 719 | .output_mode = comp.output_mode, | ||
| 720 | }); | ||
| 721 | const bin_path = | ||
| 722 | try std.Io.Dir.path.join(arena, &.{ result_dir, bin_name }); | ||
| 723 | const bin_file = try std.Io.Dir.cwd().openFile(io, bin_path, .{}); | ||
| 724 | defer bin_file.close(io); | ||
| 725 | switch (check) { | ||
| 726 | .errors => unreachable, | ||
| 727 | .stdout, .exit => { | ||
| 728 | const result = std.process.run(arena, io, .{ | ||
| 729 | .exe = .{ .file = bin_file }, | ||
| 730 | .argv = &.{bin_path}, | ||
| 731 | }) catch continue; | ||
| 732 | switch (result.term) { | ||
| 733 | .exited => |code| switch (check) { | ||
| 734 | .errors, .lldb => unreachable, | ||
| 735 | .stdout => { | ||
| 736 | if (code != 0) fatal("generated executable '{s}' failed with code {d}", .{ bin_path, code }); | ||
| 737 | try std.testing.expectEqualStrings(expected, result.stdout); | ||
| 738 | }, | ||
| 739 | .exit => { | ||
| 740 | var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 = undefined; | ||
| 741 | try std.testing.expectEqualStrings( | ||
| 742 | expected, | ||
| 743 | std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable, | ||
| 744 | ); | ||
| 745 | }, | ||
| 746 | }, | ||
| 747 | .signal => |sig| fatal("generated executable '{s}' terminated with signal {t}", .{ bin_path, sig }), | ||
| 748 | .stopped => |sig| fatal("generated executable '{s}' stopped with signal {t}", .{ bin_path, sig }), | ||
| 749 | .unknown => fatal("generated executable '{s}' terminated unexpectedly", .{bin_path}), | ||
| 750 | } | ||
| 751 | }, | ||
| 752 | .lldb => {}, | ||
| 753 | } | ||
| 754 | }, | ||
| 755 | else => {}, // Ignore other messages, | ||
| 756 | } | ||
| 757 | } | ||
| 758 | |||
| 759 | const buffered_stderr = stderr.buffered(); | ||
| 760 | if (buffered_stderr.len > 0) { | ||
| 761 | if (comp.allow_compiler_stderr) { | ||
| 762 | std.log.info("stderr:\n{s}", .{buffered_stderr}); | ||
| 763 | } else { | ||
| 764 | fatal("unexpected stderr:\n{s}", .{buffered_stderr}); | ||
| 765 | } | ||
| 766 | } | ||
| 767 | |||
| 768 | try comp.exit(io); | ||
| 769 | fatal("compiler failed to send terminating error_bundle", .{}); | ||
| 770 | }, | ||
| 310 | } | 771 | } |
| 311 | std.debug.assert(try contents_r.discardRemaining() == 0); | 772 | std.debug.assert(try contents_r.discardRemaining() == 0); |
| 312 | if (cmd != .skip) allow_skip = false; | 773 | if (cmd != .skip) allow_skip = false; |
| ... | @@ -314,6 +775,7 @@ fn runTest( | ... | @@ -314,6 +775,7 @@ fn runTest( |
| 314 | else => |e| return e, | 775 | else => |e| return e, |
| 315 | error.EndOfStream => if (skip_delimiter) return error.MissingDelimiter, | 776 | error.EndOfStream => if (skip_delimiter) return error.MissingDelimiter, |
| 316 | } | 777 | } |
| 778 | if (compiler) |*comp| try comp.exit(io); | ||
| 317 | } | 779 | } |
| 318 | const DelimitedReader = struct { | 780 | const DelimitedReader = struct { |
| 319 | const Io = std.Io; | 781 | const Io = std.Io; |
| ... | @@ -360,3 +822,113 @@ const DelimitedReader = struct { | ... | @@ -360,3 +822,113 @@ const DelimitedReader = struct { |
| 360 | return n; | 822 | return n; |
| 361 | } | 823 | } |
| 362 | }; | 824 | }; |
| 825 | const Compiler = struct { | ||
| 826 | prog_node: std.Progress.Node, | ||
| 827 | allow_compiler_stderr: bool, | ||
| 828 | output_mode: std.lang.OutputMode, | ||
| 829 | root_name: []const u8, | ||
| 830 | state: enum { idle, update }, | ||
| 831 | child: std.process.Child, | ||
| 832 | fw: std.Io.File.Writer, | ||
| 833 | mr_buffer: std.Io.File.MultiReader.Buffer(2), | ||
| 834 | mr: std.Io.File.MultiReader, | ||
| 835 | client: std.zig.Client, | ||
| 836 | |||
| 837 | const Target = struct { | ||
| 838 | triple: []const u8, | ||
| 839 | resolved: std.Target, | ||
| 840 | mode: Mode, | ||
| 841 | backend: Backend, | ||
| 842 | |||
| 843 | const Mode = enum { | ||
| 844 | whole, | ||
| 845 | incremental, | ||
| 846 | }; | ||
| 847 | |||
| 848 | const Backend = enum { | ||
| 849 | /// Run semantic analysis only. Runtime output will not be tested, but we still verify | ||
| 850 | /// that compilation succeeds. Corresponds to `-fno-emit-bin`. | ||
| 851 | sema, | ||
| 852 | /// Use the self-hosted code generation backend for this target. | ||
| 853 | /// Corresponds to `-fno-llvm -fno-lld`. | ||
| 854 | selfhosted, | ||
| 855 | /// Use the LLVM backend. | ||
| 856 | /// Corresponds to `-fllvm -flld`. | ||
| 857 | llvm, | ||
| 858 | /// Use the C backend. The output is compiled with `zig cc`. | ||
| 859 | /// Corresponds to `-ofmt=c`. | ||
| 860 | cbe, | ||
| 861 | }; | ||
| 862 | }; | ||
| 863 | |||
| 864 | fn update(comp: *Compiler, io: std.Io) !void { | ||
| 865 | comp.client.serveBodylessMessage(.update) catch |err| switch (err) { | ||
| 866 | error.WriteFailed => return comp.fw.err.?, | ||
| 867 | }; | ||
| 868 | while (true) { | ||
| 869 | const header = comp.client.receiveMessageWithMultiReader( | ||
| 870 | &comp.mr, | ||
| 871 | .none, | ||
| 872 | ) catch |err| switch (err) { | ||
| 873 | error.Timeout => unreachable, | ||
| 874 | error.EndOfStream => break, | ||
| 875 | else => |e| return e, | ||
| 876 | }; | ||
| 877 | const body = comp.client.in.take(header.bytes_len) catch unreachable; | ||
| 878 | switch (header.tag) { | ||
| 879 | .error_bundle => { | ||
| 880 | return; | ||
| 881 | }, | ||
| 882 | .emit_digest => { | ||
| 883 | _ = body; | ||
| 884 | }, | ||
| 885 | else => {}, // Ignore other messages, | ||
| 886 | } | ||
| 887 | } | ||
| 888 | |||
| 889 | const buffered_stderr = comp.mr.reader(1).buffered(); | ||
| 890 | if (buffered_stderr.len > 0) { | ||
| 891 | if (comp.allow_compiler_stderr) { | ||
| 892 | std.log.info("stderr:\n{s}", .{buffered_stderr}); | ||
| 893 | } else { | ||
| 894 | fatal("unexpected stderr:\n{s}", .{buffered_stderr}); | ||
| 895 | } | ||
| 896 | } | ||
| 897 | |||
| 898 | try comp.exit(io); | ||
| 899 | fatal("compiler failed to send terminating error_bundle", .{}); | ||
| 900 | } | ||
| 901 | |||
| 902 | fn exit(comp: *Compiler, io: std.Io) !void { | ||
| 903 | comp.client.serveBodylessMessage(.exit) catch |err| switch (err) { | ||
| 904 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 905 | error.BrokenPipe => {}, | ||
| 906 | else => |e| fatal("failed to send exit: {t}", .{e}), | ||
| 907 | }, | ||
| 908 | }; | ||
| 909 | comp.child.stdin.?.close(io); | ||
| 910 | comp.child.stdin = null; | ||
| 911 | while (true) { | ||
| 912 | const header = comp.client.receiveMessageWithMultiReader( | ||
| 913 | &comp.mr, | ||
| 914 | .none, | ||
| 915 | ) catch |err| switch (err) { | ||
| 916 | error.Timeout => unreachable, | ||
| 917 | error.EndOfStream => |e| { | ||
| 918 | if (comp.client.in.bufferedLen() == 0) break; | ||
| 919 | return e; | ||
| 920 | }, | ||
| 921 | else => |e| return e, | ||
| 922 | }; | ||
| 923 | try comp.client.in.discardAll(header.bytes_len); | ||
| 924 | } | ||
| 925 | const term = comp.child.wait(io) catch |err| fatal("child process failed: {t}", .{err}); | ||
| 926 | comp.prog_node.end(); | ||
| 927 | switch (term) { | ||
| 928 | .exited => |code| if (code != 0) fatal("compiler failed with code {d}", .{code}), | ||
| 929 | .signal => |sig| fatal("compiler terminated with signal {t}", .{sig}), | ||
| 930 | .stopped => |sig| fatal("compiler stopped unexpectedly with signal {t}", .{sig}), | ||
| 931 | .unknown => fatal("compiler terminated unexpectedly", .{}), | ||
| 932 | } | ||
| 933 | } | ||
| 934 | }; |
test/standalone/build.zig-1| ... | @@ -44,7 +44,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -44,7 +44,6 @@ pub fn build(b: *std.Build) void { |
| 44 | "../../tools/generate_linux_syscalls.zig", | 44 | "../../tools/generate_linux_syscalls.zig", |
| 45 | "../../tools/process_headers.zig", | 45 | "../../tools/process_headers.zig", |
| 46 | "../../tools/migrate_langref.zig", | 46 | "../../tools/migrate_langref.zig", |
| 47 | "../../tools/runner-check.zig", | ||
| 48 | "../../tools/unskip_spirv_tests.zig", | 47 | "../../tools/unskip_spirv_tests.zig", |
| 49 | "../../tools/update-linux-headers.zig", | 48 | "../../tools/update-linux-headers.zig", |
| 50 | "../../tools/update_clang_options.zig", | 49 | "../../tools/update_clang_options.zig", |
test/tests.zig+1-1| ... | @@ -3488,7 +3488,7 @@ pub fn addNewIncrementalTests( | ... | @@ -3488,7 +3488,7 @@ pub fn addNewIncrementalTests( |
| 3488 | 3488 | ||
| 3489 | if (options.skip_llvm and test_target.backend == .llvm) continue; | 3489 | if (options.skip_llvm and test_target.backend == .llvm) continue; |
| 3490 | 3490 | ||
| 3491 | const target_str = b.fmt("{s}-{t}", .{ | 3491 | const target_str = b.fmt("{s}-incremental-{t}", .{ |
| 3492 | resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"), | 3492 | resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"), |
| 3493 | test_target.backend, | 3493 | test_target.backend, |
| 3494 | }); | 3494 | }); |
tools/runner-check.zig deleted-177| ... | @@ -1,177 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | pub fn main(init: std.process.Init) !void { | ||
| 3 | const arena = init.arena.allocator(); | ||
| 4 | |||
| 5 | var test_path_arg: ?[]const u8 = null; | ||
| 6 | var zig_path_arg: ?[]const u8 = null; | ||
| 7 | var lib_path_arg: ?[]const u8 = null; | ||
| 8 | var target_args: std.ArrayList([]const u8) = .empty; | ||
| 9 | defer target_args.deinit(init.gpa); | ||
| 10 | var keep_src = false; | ||
| 11 | |||
| 12 | var arg_it = try init.minimal.args.iterateAllocator(arena); | ||
| 13 | std.debug.assert(arg_it.skip()); | ||
| 14 | while (arg_it.next()) |arg| { | ||
| 15 | if (std.mem.eql(u8, arg, "--zig")) { | ||
| 16 | zig_path_arg = arg_it.next() orelse fail("missing arg after '{s}'", .{arg}); | ||
| 17 | } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| { | ||
| 18 | zig_path_arg = zig_path; | ||
| 19 | } else if (std.mem.eql(u8, arg, "--lib")) { | ||
| 20 | lib_path_arg = arg_it.next() orelse fail("missing arg after '{s}'", .{arg}); | ||
| 21 | } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { | ||
| 22 | lib_path_arg = lib_path; | ||
| 23 | } else if (std.mem.eql(u8, arg, "--target")) { | ||
| 24 | try target_args.append( | ||
| 25 | init.gpa, | ||
| 26 | try arena.dupe(u8, arg_it.next() orelse fail("missing arg after '{s}'", .{arg})), | ||
| 27 | ); | ||
| 28 | } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { | ||
| 29 | try target_args.append(init.gpa, try arena.dupe(u8, target)); | ||
| 30 | } else if (std.mem.eql(u8, arg, "--keep-src")) { | ||
| 31 | keep_src = true; | ||
| 32 | } else { | ||
| 33 | if (test_path_arg) |_| fail("unknown arg '{s}'", .{arg}); | ||
| 34 | test_path_arg = arg; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | const test_path = test_path_arg orelse fail("missing 'path/to/test'", .{}); | ||
| 39 | const zig_path = zig_path_arg orelse fail("missing '--zig=path/to/zig'", .{}); | ||
| 40 | const lib_path = lib_path_arg orelse fail("missing '--lib=path/to/lib'", .{}); | ||
| 41 | |||
| 42 | const test_file = try std.Io.Dir.cwd().openFile(init.io, test_path, .{ | ||
| 43 | .allow_directory = true, | ||
| 44 | }); | ||
| 45 | const zig_file = try std.Io.Dir.cwd().openFile(init.io, zig_path, .{}); | ||
| 46 | defer zig_file.close(init.io); | ||
| 47 | const lib_dir = try std.Io.Dir.cwd().openDir(init.io, lib_path, .{}); | ||
| 48 | defer lib_dir.close(init.io); | ||
| 49 | |||
| 50 | const src_dir_path = "src_" ++ std.fmt.hex(rand_int: { | ||
| 51 | var rand_int: u64 = undefined; | ||
| 52 | init.io.random(@ptrCast(&rand_int)); | ||
| 53 | break :rand_int rand_int; | ||
| 54 | }); | ||
| 55 | var src_dir = try std.Io.Dir.cwd().createDirPathOpen(init.io, src_dir_path, .{}); | ||
| 56 | defer { | ||
| 57 | src_dir.close(init.io); | ||
| 58 | if (!keep_src) std.Io.Dir.cwd().deleteTree(init.io, src_dir_path) catch |err| { | ||
| 59 | std.log.warn("failed to delete tree '{s}': {t}", .{ src_dir_path, err }); | ||
| 60 | }; | ||
| 61 | } | ||
| 62 | |||
| 63 | var child = try std.process.spawn(init.io, .{ | ||
| 64 | .argv = &.{ | ||
| 65 | zig_path, | ||
| 66 | "run", | ||
| 67 | try std.fs.path.resolveAlloc(arena, &.{ lib_path, "..", "test", "src", "runner.zig" }), | ||
| 68 | }, | ||
| 69 | .stdin = .pipe, | ||
| 70 | .stdout = .pipe, | ||
| 71 | .inherit_dirs = &.{ lib_dir, src_dir }, | ||
| 72 | .inherit_files = &.{ zig_file, test_file }, | ||
| 73 | }); | ||
| 74 | |||
| 75 | var stdout_buffer: [512]u8 = undefined; | ||
| 76 | var stdout_reader = child.stdout.?.readerStreaming(init.io, &stdout_buffer); | ||
| 77 | var stdin_buffer: [512]u8 = undefined; | ||
| 78 | var stdin_writer = child.stdin.?.writerStreaming(init.io, &stdin_buffer); | ||
| 79 | var client: std.zig.Client = .{ | ||
| 80 | .in = &stdout_reader.interface, | ||
| 81 | .out = &stdin_writer.interface, | ||
| 82 | }; | ||
| 83 | { | ||
| 84 | var args: std.ArrayList(u8) = .empty; | ||
| 85 | defer args.deinit(init.gpa); | ||
| 86 | const Arg = std.zig.Client.Message.Arg; | ||
| 87 | |||
| 88 | try args.append(init.gpa, @backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) { | ||
| 89 | else => unreachable, | ||
| 90 | .file => .input_file, | ||
| 91 | .directory => .input_dir, | ||
| 92 | }))); | ||
| 93 | try args.appendSlice(init.gpa, @ptrCast(&test_file.handle)); | ||
| 94 | |||
| 95 | try args.append(init.gpa, @backingInt(Arg.prefix)); | ||
| 96 | try args.appendSlice(init.gpa, "--zig="); | ||
| 97 | try args.append(init.gpa, 0); | ||
| 98 | |||
| 99 | try args.append(init.gpa, @backingInt(Arg.input_file)); | ||
| 100 | try args.appendSlice(init.gpa, @ptrCast(&zig_file.handle)); | ||
| 101 | |||
| 102 | try args.append(init.gpa, @backingInt(Arg.prefix)); | ||
| 103 | try args.appendSlice(init.gpa, "--lib="); | ||
| 104 | try args.append(init.gpa, 0); | ||
| 105 | |||
| 106 | try args.append(init.gpa, @backingInt(Arg.input_dir)); | ||
| 107 | try args.appendSlice(init.gpa, @ptrCast(&lib_dir.handle)); | ||
| 108 | |||
| 109 | try args.append(init.gpa, @backingInt(Arg.prefix)); | ||
| 110 | try args.appendSlice(init.gpa, "--src="); | ||
| 111 | try args.append(init.gpa, 0); | ||
| 112 | |||
| 113 | try args.append(init.gpa, @backingInt(Arg.output_dir)); | ||
| 114 | try args.appendSlice(init.gpa, @ptrCast(&src_dir.handle)); | ||
| 115 | |||
| 116 | for (target_args.items) |target_arg| { | ||
| 117 | try args.append(init.gpa, @backingInt(Arg.string)); | ||
| 118 | try args.appendSlice(init.gpa, "--target"); | ||
| 119 | try args.append(init.gpa, 0); | ||
| 120 | |||
| 121 | try args.append(init.gpa, @backingInt(Arg.string)); | ||
| 122 | try args.appendSlice(init.gpa, target_arg); | ||
| 123 | try args.append(init.gpa, 0); | ||
| 124 | } | ||
| 125 | |||
| 126 | try client.serveMessageHeader(.{ | ||
| 127 | .tag = .args, | ||
| 128 | .bytes_len = @intCast(args.items.len), | ||
| 129 | }); | ||
| 130 | try client.out.writeAll(args.items); | ||
| 131 | |||
| 132 | var metadata: struct { | ||
| 133 | index: u32, | ||
| 134 | len: u32, | ||
| 135 | |||
| 136 | fn next(m: *@This()) ?u32 { | ||
| 137 | if (m.len - m.index == 0) return null; | ||
| 138 | defer m.index += 1; | ||
| 139 | return m.index; | ||
| 140 | } | ||
| 141 | } = .{ .index = 0, .len = 0 }; | ||
| 142 | try client.serveBodylessMessage(.query_test_metadata); | ||
| 143 | while (true) { | ||
| 144 | const hdr = try client.receiveMessage(); | ||
| 145 | switch (hdr.tag) { | ||
| 146 | else => try client.in.discardAll(hdr.bytes_len), | ||
| 147 | .zig_version => { | ||
| 148 | const body = try arena.alloc(u8, hdr.bytes_len); | ||
| 149 | try client.in.readSliceAll(body); | ||
| 150 | if (!std.mem.eql(u8, @import("builtin").zig_version_string, body)) return fail( | ||
| 151 | "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", | ||
| 152 | .{ @import("builtin").zig_version_string, body }, | ||
| 153 | ); | ||
| 154 | }, | ||
| 155 | .test_metadata => { | ||
| 156 | const tm_hdr = | ||
| 157 | try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); | ||
| 158 | try client.in.discardAll( | ||
| 159 | hdr.bytes_len - @sizeOf(std.zig.Server.Message.TestMetadata), | ||
| 160 | ); | ||
| 161 | metadata = .{ .index = 0, .len = tm_hdr.tests_len }; | ||
| 162 | try client.serveRunTest(metadata.next() orelse break); | ||
| 163 | }, | ||
| 164 | .test_results => { | ||
| 165 | try client.in.discardAll(hdr.bytes_len); | ||
| 166 | try client.serveRunTest(metadata.next() orelse break); | ||
| 167 | }, | ||
| 168 | } | ||
| 169 | } | ||
| 170 | try client.serveBodylessMessage(.exit); | ||
| 171 | } | ||
| 172 | _ = try child.wait(init.io); | ||
| 173 | } | ||
| 174 | fn fail(comptime fmt: []const u8, args: anytype) noreturn { | ||
| 175 | std.log.err(fmt, args); | ||
| 176 | std.process.exit(1); | ||
| 177 | } | ||